diff --git a/common/src/main/scala/scalan/TypeDesc.scala b/common/src/main/scala/scalan/TypeDesc.scala index 082d23c04b..0d15988399 100644 --- a/common/src/main/scala/scalan/TypeDesc.scala +++ b/common/src/main/scala/scalan/TypeDesc.scala @@ -20,6 +20,9 @@ abstract class RType[A] { /** Returns true is data size of `x: A` is the same for all `x`. * This is useful optimizations of calculating sizes of collections. */ def isConstantSize: Boolean + + /** Creates empty immutable array of this type. */ + def emptyArray: Array[A] = Array.empty[A](classTag) } object RType { @@ -61,7 +64,8 @@ object RType { } /** Descriptor used to represent primitive types. */ - case class PrimitiveType[A](classTag: ClassTag[A]) extends RType[A] { + case class PrimitiveType[A](classTag: ClassTag[A], + override val emptyArray: Array[A]) extends RType[A] { override def name: String = classTag.toString() /** We assume all primitive types have inhabitants of the same size. */ override def isConstantSize: Boolean = true @@ -71,15 +75,15 @@ object RType { val AnyRefType : RType[AnyRef] = GeneralType[AnyRef] (ClassTag.AnyRef) val NothingType : RType[Nothing] = GeneralType[Nothing] (ClassTag.Nothing) - implicit val BooleanType : RType[Boolean] = PrimitiveType[Boolean] (ClassTag.Boolean) - implicit val ByteType : RType[Byte] = PrimitiveType[Byte] (ClassTag.Byte) - implicit val ShortType : RType[Short] = PrimitiveType[Short] (ClassTag.Short) - implicit val IntType : RType[Int] = PrimitiveType[Int] (ClassTag.Int) - implicit val LongType : RType[Long] = PrimitiveType[Long] (ClassTag.Long) - implicit val CharType : RType[Char] = PrimitiveType[Char] (ClassTag.Char) - implicit val FloatType : RType[Float] = PrimitiveType[Float] (ClassTag.Float) - implicit val DoubleType : RType[Double] = PrimitiveType[Double] (ClassTag.Double) - implicit val UnitType : RType[Unit] = PrimitiveType[Unit] (ClassTag.Unit) + implicit val BooleanType : RType[Boolean] = PrimitiveType[Boolean] (ClassTag.Boolean, Array.emptyBooleanArray) + implicit val ByteType : RType[Byte] = PrimitiveType[Byte] (ClassTag.Byte, Array.emptyByteArray) + implicit val ShortType : RType[Short] = PrimitiveType[Short] (ClassTag.Short, Array.emptyShortArray) + implicit val IntType : RType[Int] = PrimitiveType[Int] (ClassTag.Int, Array.emptyIntArray) + implicit val LongType : RType[Long] = PrimitiveType[Long] (ClassTag.Long, Array.emptyLongArray) + implicit val CharType : RType[Char] = PrimitiveType[Char] (ClassTag.Char, Array.emptyCharArray) + implicit val FloatType : RType[Float] = PrimitiveType[Float] (ClassTag.Float, Array.emptyFloatArray) + implicit val DoubleType : RType[Double] = PrimitiveType[Double] (ClassTag.Double, Array.emptyDoubleArray) + implicit val UnitType : RType[Unit] = PrimitiveType[Unit] (ClassTag.Unit, Array[Unit]()(ClassTag.Unit)) /** Descriptor of the type A narrowed to the single inhabitant `value`. */ case class SingletonType[A](value: A, classTag: ClassTag[A])() extends RType[A] { diff --git a/core/src/main/scala/scalan/Entities.scala b/core/src/main/scala/scalan/Entities.scala index cb8a333150..376bd5a529 100644 --- a/core/src/main/scala/scalan/Entities.scala +++ b/core/src/main/scala/scalan/Entities.scala @@ -1,14 +1,14 @@ package scalan -import java.util.Objects - -import scala.annotation.tailrec import scala.language.higherKinds import scalan.util.ReflectionUtil.ClassOps +/** A slice in the Scalan cake with base classes for various descriptors. */ trait Entities extends TypeDescs { self: Scalan => - /** Base class for all descriptors of staged traits. */ + /** Base class for all descriptors of staged traits. + * See derived classes in `impl` packages. + */ abstract class EntityElem[A] extends Elem[A] with scala.Equals { /** Optional parent type in inheritance hierarchy */ def parent: Option[Elem[_]] = None @@ -28,7 +28,7 @@ trait Entities extends TypeDescs { self: Scalan => case _ => false }) - override def hashCode = Objects.hash(getClass, typeArgsDescs) + override def hashCode = getClass.hashCode() * 31 + typeArgsDescs.hashCode() } /** Base class for all descriptors of staged traits with one type parameter. */ diff --git a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala index bc0cc0ae28..dce17ef130 100644 --- a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala +++ b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala @@ -305,7 +305,7 @@ class CollOverArrayBuilder extends CollBuilder { val rs = emptyColl(pt.tSnd) asColl[T](pairColl(ls, rs)) case _ => - new CollOverArray[T](Array[T]()) + new CollOverArray[T](cT.emptyArray) } @NeverInline diff --git a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala index 9f7511c80a..78488d11cf 100644 --- a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala @@ -9,18 +9,34 @@ import scalan._ @scalan.Liftable trait CostModel { + /** Cost of accessing SELF box and/or each item in INPUTS, OUTPUTS, dataInputs. */ def AccessBox: Int // costOf("AccessBox: Context => Box") + + // TODO refactor: remove not used def AccessAvlTree: Int // costOf("AccessAvlTree: Context => AvlTree") + /** Cost of accessing context variable (`getVar` operation in ErgoScript). */ def GetVar: Int // costOf("ContextVar: (Context, Byte) => Option[T]") + + // TODO refactor: remove not used def DeserializeVar: Int // costOf("DeserializeVar: (Context, Byte) => Option[T]") + /** Cost of accessing register in a box (e.g. `R4[Int]` operation in ErgoScript). */ def GetRegister: Int // costOf("AccessRegister: (Box,Byte) => Option[T]") + + // TODO refactor: remove not used def DeserializeRegister: Int // costOf("DeserializeRegister: (Box,Byte) => Option[T]") + /** Cost of accessing a property of an object like Header or AvlTree. */ def SelectField: Int // costOf("SelectField") + + // TODO refactor: remove not used def CollectionConst: Int // costOf("Const: () => Array[IV]") + + // TODO refactor: remove not used def AccessKiloByteOfData: Int // costOf("AccessKiloByteOfData") + + // TODO refactor: remove not used /** Size of public key in bytes */ def PubKeySize: Long } diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index bacbfa7312..438978fd34 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -194,7 +194,7 @@ object ErgoLikeContext { /** When interpreted evaluates to a ByteArrayConstant built from Context.minerPubkey */ case object MinerPubkey extends NotReadyValueByteArray with ValueCompanion { override def opCode: OpCode = OpCodes.MinerPubkeyCode - def opType = SFunc(SContext, SCollection.SByteArray) + override val opType = SFunc(SContext, SCollection.SByteArray) override def companion = this } @@ -202,30 +202,30 @@ case object MinerPubkey extends NotReadyValueByteArray with ValueCompanion { case object Height extends NotReadyValueInt with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.HeightCode - def opType = SFunc(SContext, SInt) + override val opType = SFunc(SContext, SInt) } /** When interpreted evaluates to a collection of BoxConstant built from Context.boxesToSpend */ case object Inputs extends LazyCollection[SBox.type] with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.InputsCode - val tpe = SCollection(SBox) - def opType = SFunc(SContext, tpe) + override def tpe = SCollection.SBoxArray + override val opType = SFunc(SContext, tpe) } /** When interpreted evaluates to a collection of BoxConstant built from Context.spendingTransaction.outputs */ case object Outputs extends LazyCollection[SBox.type] with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.OutputsCode - val tpe = SCollection(SBox) - def opType = SFunc(SContext, tpe) + override def tpe = SCollection.SBoxArray + override val opType = SFunc(SContext, tpe) } /** 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 - def opType = SFunc(SContext, tpe) + override val opType = SFunc(SContext, tpe) } @@ -233,19 +233,25 @@ case object LastBlockUtxoRootHash extends NotReadyValueAvlTree with ValueCompani case object Self extends NotReadyValueBox with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.SelfCode - def opType = SFunc(SContext, SBox) + override val opType = SFunc(SContext, SBox) } +/** When interpreted evaluates to the singleton instance of [[special.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 override def tpe: SContext.type = SContext - override def opType: SFunc = SFunc(SUnit, SContext) + override val opType: SFunc = SFunc(SUnit, SContext) } +/** When interpreted evaluates to the singleton instance of [[special.sigma.SigmaDslBuilder]]. + * Corresponds to `Global` variable in ErgoScript which can be used like `Global.groupGenerator`. + */ case object Global extends NotReadyValue[SGlobal.type] with ValueCompanion { override def companion = this override def opCode: OpCode = OpCodes.GlobalCode override def tpe: SGlobal.type = SGlobal - override def opType: SFunc = SFunc(SUnit, SGlobal) + override val opType: SFunc = SFunc(SUnit, SGlobal) } diff --git a/sigmastate/src/main/scala/sigmastate/Values.scala b/sigmastate/src/main/scala/sigmastate/Values.scala index d7192c1a47..cb029de642 100644 --- a/sigmastate/src/main/scala/sigmastate/Values.scala +++ b/sigmastate/src/main/scala/sigmastate/Values.scala @@ -6,12 +6,8 @@ import java.util.Objects import org.bitbucket.inkytonik.kiama.relation.Tree import org.bitbucket.inkytonik.kiama.rewriting.Rewriter.{strategy, everywherebu} -import org.ergoplatform.ErgoLikeContext import org.ergoplatform.validation.ValidationException import scalan.{Nullable, RType} -import scorex.crypto.authds.{ADDigest, SerializedAdProof} -import scorex.crypto.authds.avltree.batch.BatchAVLVerifier -import scorex.crypto.hash.{Digest32, Blake2b256} import scalan.util.CollectionUtil._ import sigmastate.SCollection.{SIntArray, SByteArray} import sigmastate.interpreter.CryptoConstants.EcPointType @@ -36,7 +32,7 @@ import sigmastate.lang.DefaultSigmaBuilder._ import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate.serialization.transformers.ProveDHTupleSerializer import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import special.sigma.{AnyValue, AvlTree, PreHeader, Header, _} +import special.sigma.{AvlTree, PreHeader, Header, _} import sigmastate.lang.SourceContext import special.collection.Coll @@ -153,7 +149,7 @@ object Values { ft.getGenericType case _ => tpe } - SFunc(Vector(), resType) + SFunc(mutable.WrappedArray.empty, resType) } } @@ -814,9 +810,9 @@ object Values { */ case class FuncValue(args: IndexedSeq[(Int,SType)], body: Value[SType]) extends NotReadyValue[SFunc] { override def companion = FuncValue - lazy val tpe: SFunc = SFunc(args.map(_._2), body.tpe) + lazy val tpe: SFunc = SFunc(args.toArray.map(_._2), body.tpe) /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = SFunc(Vector(), tpe) + override def opType: SFunc = SFunc(mutable.WrappedArray.empty, tpe) } object FuncValue extends ValueCompanion { override def opCode: OpCode = FuncValueCode @@ -1018,7 +1014,7 @@ object Values { def substConstants(root: SValue, constants: IndexedSeq[Constant[SType]]): SValue = { val store = new ConstantStore(constants) - val substRule = strategy[Value[_ <: SType]] { + val substRule = strategy[Any] { case ph: ConstantPlaceholder[_] => Some(store.get(ph.id)) case _ => None diff --git a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala index 1fad322b59..9163367e22 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala @@ -29,6 +29,7 @@ import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate.serialization.{SigmaSerializer, GroupElementSerializer} import special.Types.TupleType +import scala.collection.mutable import scala.reflect.ClassTag /** Interface implmented by wrappers to provide access to the underlying wrapped value. */ @@ -461,6 +462,29 @@ object CHeader { * @see [[CostModel]] for detailed descriptions */ class CCostModel extends CostModel { + import CCostModel._ + + override def AccessBox: Int = AccessBoxCost + + override def AccessAvlTree: Int = AccessAvlTreeCost + + override def GetVar: Int = GetVarCost + + def DeserializeVar: Int = DeserializeVarCost + + def GetRegister: Int = GetRegisterCost + + def DeserializeRegister: Int = DeserializeRegisterCost + + def SelectField: Int = SelectFieldCost + + def CollectionConst: Int = CollectionConstCost + + def AccessKiloByteOfData: Int = AccessKiloByteOfDataCost + + def PubKeySize: Long = CryptoConstants.EncodedGroupElementLength +} +object CCostModel { private def costOf(opName: String, opType: SFunc): Int = { val operId = OperationId(opName, opType) costOf(operId) @@ -471,28 +495,36 @@ class CCostModel extends CostModel { cost } - def AccessBox: Int = costOf("AccessBox", SFunc(SContext, SBox)) + // NOTE: lazy vals are necessary to avoid initialization exception - def AccessAvlTree: Int = costOf("AccessAvlTree", SFunc(SContext, SAvlTree)) + private val AccessBoxOpType: SFunc = SFunc(SContext, SBox) + private lazy val AccessBoxCost: Int = costOf("AccessBox", AccessBoxOpType) - def GetVar: Int = costOf("GetVar", SFunc(IndexedSeq(SContext, SByte), SOption(SOption.tT))) + private val AccessAvlTreeOpType: SFunc = SFunc(SContext, SAvlTree) + private lazy val AccessAvlTreeCost: Int = costOf("AccessAvlTree", AccessAvlTreeOpType) - def DeserializeVar: Int = costOf("DeserializeVar", SFunc(IndexedSeq(SContext, SByte), SOption(SOption.tT))) + private val GetVarOpType: SFunc = SFunc(Array(SContext, SByte), SOption.ThisType) + private lazy val GetVarCost: Int = costOf("GetVar", GetVarOpType) - def GetRegister: Int = costOf("GetRegister", SFunc(IndexedSeq(SBox, SByte), SOption(SOption.tT))) + private val DeserializeVarOpType: SFunc = SFunc(Array(SContext, SByte), SOption.ThisType) + private lazy val DeserializeVarCost: Int = costOf("DeserializeVar", DeserializeVarOpType) - def DeserializeRegister: Int = costOf("DeserializeRegister", SFunc(IndexedSeq(SBox, SByte), SOption(SOption.tT))) + private val GetRegisterOpType: SFunc = SFunc(Array(SBox, SByte), SOption.ThisType) + private lazy val GetRegisterCost: Int = costOf("GetRegister", GetRegisterOpType) - def SelectField: Int = costOf("SelectField", SFunc(IndexedSeq(), SUnit)) + private val DeserializeRegisterOpType: SFunc = SFunc(Array(SBox, SByte), SOption.ThisType) + private lazy val DeserializeRegisterCost: Int = costOf("DeserializeRegister", DeserializeRegisterOpType) - def CollectionConst: Int = costOf("Const", SFunc(IndexedSeq(), SCollection(STypeVar("IV")))) + private val SelectFieldOpType: SFunc = SFunc(mutable.WrappedArray.empty, SUnit) + private lazy val SelectFieldCost: Int = costOf("SelectField", SelectFieldOpType) - def AccessKiloByteOfData: Int = costOf("AccessKiloByteOfData", SFunc(IndexedSeq(), SUnit)) + private val CollectionConstOpType: SFunc = SFunc(mutable.WrappedArray.empty, SCollection.ThisType) + private lazy val CollectionConstCost: Int = costOf("Const", CollectionConstOpType) - def PubKeySize: Long = CryptoConstants.EncodedGroupElementLength + private val AccessKiloByteOfDataOpType: SFunc = SFunc(mutable.WrappedArray.empty, SUnit) + private lazy val AccessKiloByteOfDataCost: Int = costOf("AccessKiloByteOfData", AccessKiloByteOfDataOpType) } - /** A default implementation of [[SigmaDslBuilder]] interface. * @see [[SigmaDslBuilder]] for detailed descriptions */ diff --git a/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala b/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala index 380e7daf1a..c1a6fe3d54 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/Evaluation.scala @@ -913,7 +913,7 @@ object Evaluation { case PreHeaderRType => SPreHeader case SigmaPropRType => SSigmaProp case SigmaBooleanRType => SSigmaProp - case tup: TupleType => STuple(tup.items.map(t => rtypeToSType(t)).toIndexedSeq) + case tup: TupleType => STuple(tup.items.map(t => rtypeToSType(t))) case at: ArrayType[_] => SCollection(rtypeToSType(at.tA)) case ct: CollType[_] => SCollection(rtypeToSType(ct.tItem)) case ft: FuncType[_,_] => SFunc(rtypeToSType(ft.tDom), rtypeToSType(ft.tRange)) diff --git a/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala index aa9f7cfef7..9cf0178843 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1119,6 +1119,17 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => var ruleStack: List[CostingRuleStat] = Nil + /** Recursively translates each ErgoTree `node` into the corresponding cost formula. + * The cost formula is represented using graph-based IR defined by this IRContext cake. + * Each `node: Value[T]` which evaluates to the value of type `T` is transformed + * to a value of type `RCosted[A]` which is a synonym of `Ref[Costed[A]]` type. + * The translation is performed recursively on a structure of the ErgoTree expression. + * + * @param ctx reference to the graph node, which represents costed `CONTEXT` expression. + * @param env environment of costed ValDef nodes (see BlockValue case). + * @param node expression to be costed + * @return a reference to the graph node of type Costed[T#WrappedType]` + */ protected def evalNode[T <: SType](ctx: RCosted[Context], env: CostingEnv, node: Value[T]): RCosted[T#WrappedType] = { import WOption._ def eval[T <: SType](node: Value[T]): RCosted[T#WrappedType] = evalNode(ctx, env, node) @@ -1687,7 +1698,7 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => def tC = evalNode(ctx, env, t) def eC = evalNode(ctx, env, e) val resV = IF (cC.value) THEN tC.value ELSE eC.value - val resCost = opCost(resV, Array(cC.cost, tC.cost, eC.cost), costOf("If", SFunc(Vector(SBoolean, If.tT, If.tT), If.tT))) + val resCost = opCost(resV, Array(cC.cost, tC.cost, eC.cost), costOf("If", If.GenericOpType)) RCCostedPrim(resV, resCost, tC.size) // TODO costing: implement tC.size max eC.size case rel: Relation[t, _] => diff --git a/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala b/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala index 0e3a16e864..92e4018a96 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -1,7 +1,7 @@ package sigmastate.eval -import sigmastate.Values.{BlockValue, BoolValue, Constant, ConstantNode, EvaluatedCollection, SValue, SigmaPropConstant, ValDef, ValUse, Value} +import sigmastate.Values.{BlockValue, BoolValue, Constant, ConstantNode, SValue, SigmaPropConstant, ValDef, ValUse, Value} import org.ergoplatform._ import org.ergoplatform.{Height, Inputs, Outputs, Self} @@ -24,10 +24,8 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => import Box._ import CollBuilder._ import SigmaDslBuilder._ - import CCostedBuilder._ import BigInt._ import WOption._ - import AvlTree._ import GroupElement._ private val ContextM = ContextMethods @@ -116,7 +114,11 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => } } - def buildValue(ctx: Ref[Context], + /** Transforms the given graph node into the corresponding ErgoTree node. + * It is mutually recursive with processAstGraph, so it's part of the recursive + * algorithms required by buildTree method. + */ + private def buildValue(ctx: Ref[Context], mainG: PGraph, env: DefEnv, s: Sym, @@ -134,7 +136,7 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => val varId = defId + 1 // arguments are treated as ValDefs and occupy id space val env1 = env + (x -> (varId, elemToSType(x.elem))) val block = processAstGraph(ctx, mainG, env1, lam, varId + 1, constantsProcessing) - val rhs = mkFuncValue(Vector((varId, elemToSType(x.elem))), block) + val rhs = mkFuncValue(Array((varId, elemToSType(x.elem))), block) rhs case Def(Apply(fSym, xSym, _)) => val Seq(f, x) = Seq(fSym, xSym).map(recurse) @@ -387,6 +389,10 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => } } + /** Transforms the given AstGraph node (Lambda of Thunk) into the corresponding ErgoTree node. + * It is mutually recursive with buildValue, so it's part of the recursive + * algorithms required by buildTree method. + */ private def processAstGraph(ctx: Ref[Context], mainG: PGraph, env: DefEnv, @@ -420,6 +426,13 @@ trait TreeBuilding extends RuntimeCosting { IR: IRContext => res } + /** Transforms the given function `f` from graph-based IR to ErgoTree expression. + * + * @param f reference to the graph node representing function from Context. + * @param constantsProcessing if Some(store) is specified, then each constant is + * segregated and a placeholder is inserted in the resulting expression. + * @return expression of ErgoTree which corresponds to the function `f` + */ def buildTree[T <: SType](f: Ref[Context => Any], constantsProcessing: Option[ConstantStore] = None): Value[T] = { val Def(Lambda(lam,_,_,_)) = f diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala index ab4dc4f1de..1c78a7f82f 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -107,7 +107,7 @@ trait Interpreter extends ScorexLogging { * We can estimate cost of the tree evaluation only after this step.*/ def applyDeserializeContext(context: CTX, exp: Value[SType]): (BoolValue, CTX) = { val currContext = new MutableCell(context) - val substRule = strategy[Value[_ <: SType]] { case x => + val substRule = strategy[Any] { case x: SValue => substDeserialize(currContext.value, { ctx: CTX => currContext.value = ctx }, x) } val Some(substTree: SValue) = everywherebu(substRule)(exp) @@ -284,7 +284,7 @@ trait Interpreter extends ScorexLogging { * per the verifier algorithm of the leaf's Sigma-protocol. * If the verifier algorithm of the Sigma-protocol for any of the leaves rejects, then reject the entire proof. */ - val computeCommitments: Strategy = everywherebu(rule[UncheckedSigmaTree] { + val computeCommitments: Strategy = everywherebu(rule[Any] { case c: UncheckedConjecture => c // Do nothing for internal nodes case sn: UncheckedSchnorr => @@ -295,7 +295,7 @@ trait Interpreter extends ScorexLogging { val (a, b) = DiffieHellmanTupleInteractiveProver.computeCommitment(dh.proposition, dh.challenge, dh.secondMessage) dh.copy(commitmentOpt = Some(FirstDiffieHellmanTupleProverMessage(a, b))) - case _ => ??? + case _: UncheckedSigmaTree => ??? }) def verify(ergoTree: ErgoTree, diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala b/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala index 935428e157..1c7cc0082c 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala @@ -149,7 +149,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor * In a bottom-up traversal of the tree, do the following for each node: * */ - def markReal(hintsBag: HintsBag): Strategy = everywherebu(rule[UnprovenTree] { + def markReal(hintsBag: HintsBag): Strategy = everywherebu(rule[Any] { case and: CAndUnproven => // If the node is AND, mark it "real" if all of its children are marked real; else mark it "simulated" val simulated = and.children.exists(_.asInstanceOf[UnprovenTree].simulated) @@ -173,7 +173,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor case in: SigmaProtocolPrivateInput[_, _] => in.publicImage == ul.proposition } ul.withSimulated(!isReal) - case t => + case t: UnprovenTree => error(s"Don't know how to markReal($t)") }) @@ -196,7 +196,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor * the right number of simulated children. Also, children will get proper position set during this step. * In a top-down traversal of the tree, do the following for each node: */ - val polishSimulated: Strategy = everywheretd(rule[UnprovenTree] { + val polishSimulated: Strategy = everywheretd(rule[Any] { case and: CAndUnproven => // If the node is marked "simulated", mark all of its children "simulated" val a = if (and.simulated) { @@ -252,7 +252,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor setPositions(th) case su: UnprovenSchnorr => su case dhu: UnprovenDiffieHellmanTuple => dhu - case _ => ??? + case _: UnprovenTree => ??? }) /** @@ -262,7 +262,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor * Prover Step 6: For every leaf marked "real", use the first prover step of the Sigma-protocol for that leaf to * compute the commitment a. */ - def simulateAndCommit(hintsBag: HintsBag): Strategy = everywheretd(rule[ProofTree] { + def simulateAndCommit(hintsBag: HintsBag): Strategy = everywheretd(rule[Any] { // Step 4 part 1: If the node is marked "real", then each of its simulated children gets a fresh uniformly // random challenge in {0,1}^t. case and: CAndUnproven if and.real => and // A real AND node has no simulated children @@ -398,7 +398,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor } } - case a: Any => error(s"Don't know how to challengeSimulated($a)") + case t: ProofTree => error(s"Don't know how to challengeSimulated($t)") }) private def extractChallenge(pt: ProofTree): Option[Array[Byte]] = pt match { @@ -413,7 +413,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor * the challenge e for every node marked "real" below the root and, additionally, the response z for every leaf * marked "real" */ - def proving(hintsBag: HintsBag): Strategy = everywheretd(rule[ProofTree] { + def proving(hintsBag: HintsBag): Strategy = everywheretd(rule[Any] { // If the node is a non-leaf marked real whose challenge is e_0, proceed as follows: case and: CAndUnproven if and.real => assert(and.challengeOpt.isDefined) @@ -544,7 +544,9 @@ trait ProverInterpreter extends Interpreter with ProverUtils with AttributionCor case ut: UnprovenTree => ut - case a: Any => log.warn("Wrong input in prove(): ", a); ??? + case t: ProofTree => + log.warn("Wrong input in prove(): ", t); + ??? }) diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala b/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala index 99b79d442b..9c14d295c9 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/ProverResult.scala @@ -26,7 +26,7 @@ class ProverResult(val proof: Array[Byte], val extension: ContextExtension) { } object ProverResult { - val empty: ProverResult = ProverResult(Array[Byte](), ContextExtension.empty) + val empty: ProverResult = ProverResult(Array.emptyByteArray, ContextExtension.empty) def apply(proof: Array[Byte], extension: ContextExtension): ProverResult = new ProverResult(proof, extension) @@ -41,7 +41,10 @@ object ProverResult { override def parse(r: SigmaByteReader): ProverResult = { val sigBytesCount = r.getUShort() - val proofBytes = r.getBytes(sigBytesCount) + val proofBytes = if (sigBytesCount == 0) + Array.emptyByteArray // this allows to avoid hundreds of thousands of allocations + else + r.getBytes(sigBytesCount) val ce = ContextExtension.serializer.parse(r) ProverResult(proofBytes, ce) } diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala index 7b2b9aeb62..214a4abb01 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaBinder.scala @@ -37,7 +37,7 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder, /** Rewriting of AST with respect to environment to resolve all references to global names * and infer their types. */ - private def eval(e: SValue, env: ScriptEnv): SValue = rewrite(reduce(strategy[SValue]({ + private def eval(e: SValue, env: ScriptEnv): SValue = rewrite(reduce(strategy[Any]({ case i @ Ident(n, NoType) => env.get(n) match { case Some(v) => Option(liftAny(v).get.withPropagatedSrcCtx(i.sourceContext)) case None => n match { diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala index 07f6988e20..83810260eb 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaParser.scala @@ -69,10 +69,23 @@ object SigmaParser extends Exprs with Types with Core { mkConstant[SLong.type](-value, SLong) case _ => error(s"cannot prefix $arg with op $opName", arg.sourceContext) } + case "!" => mkLogicalNot(arg.asBoolValue) - case "-" => mkNegation(arg.asNumValue) - case "~" => mkBitInversion(arg.asNumValue) - case _ => error(s"Unknown prefix operation $opName for $arg", arg.sourceContext) + + case "-" => + if (arg.tpe.isNumTypeOrNoType) + mkNegation(arg.asNumValue) + else + error(s"Numeric argument expected for '$opName' operation: $arg", arg.sourceContext) + + case "~" => + if (arg.tpe.isNumTypeOrNoType) + mkBitInversion(arg.asNumValue) + else + error(s"Numeric argument expected for '$opName' operation: $arg", arg.sourceContext) + + case _ => + error(s"Unknown prefix operation $opName for $arg", arg.sourceContext) } } @@ -87,12 +100,23 @@ object SigmaParser extends Exprs with Types with Core { case ">" => mkGT(l, r) case "<=" => mkLE(l, r) case "<" => mkLT(l, r) - case "-" => mkMinus(l.asValue[SLong.type], r.asValue[SLong.type]) - case "|" => mkBitOr(l.asNumValue, r.asNumValue) - case "&" => mkBitAnd(l.asNumValue, r.asNumValue) + case "-" => mkMinus(l.asNumValue, r.asNumValue) + + case "|" => + if (l.tpe.isNumTypeOrNoType && r.tpe.isNumTypeOrNoType) + mkBitOr(l.asNumValue, r.asNumValue) + else + error(s"Numeric arguments expected for '$opName' operation: ($l, $r)", l.sourceContext) + + case "&" => + if (l.tpe.isNumTypeOrNoType && r.tpe.isNumTypeOrNoType) + mkBitAnd(l.asNumValue, r.asNumValue) + else + error(s"Numeric arguments expected for '$opName' operation: ($l, $r)", l.sourceContext) + case _ if parseAsMethods.contains(opName) => mkMethodCallLike(l, opName, IndexedSeq(r)) - case "/" => mkDivide(l.asValue[SLong.type], r.asValue[SLong.type]) - case "%" => mkModulo(l.asValue[SLong.type], r.asValue[SLong.type]) + case "/" => mkDivide(l.asNumValue, r.asNumValue) + case "%" => mkModulo(l.asNumValue, r.asNumValue) case _ => error(s"Unknown binary operation $opName", l.sourceContext) } } diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala index 22719d741d..ca7d94e147 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala @@ -44,52 +44,48 @@ object SigmaPredef { import builder._ /** Type variable used in the signatures of global functions below. */ - private val tT = STypeVar("T") - private val tK = STypeVar("K") - private val tL = STypeVar("L") - private val tR = STypeVar("R") - private val tO = STypeVar("O") + import SType.{tT, tR, tK, tL, tO, paramT, paramR} private val undefined: IrBuilderFunc = PartialFunction.empty[(SValue, Seq[SValue]), SValue] val AllOfFunc = PredefinedFunc("allOf", - Lambda(IndexedSeq("conditions" -> SCollection(SBoolean)), SBoolean, None), + Lambda(Array("conditions" -> SCollection(SBoolean)), SBoolean, None), PredefFuncInfo({ case (_, Seq(col: Value[SCollection[SBoolean.type]]@unchecked)) => mkAND(col) }), OperationInfo(AND, "Returns true if \\emph{all} the elements in collection are \\lst{true}.", Seq(ArgInfo("conditions", "a collection of conditions"))) ) val AnyOfFunc = PredefinedFunc("anyOf", - Lambda(Vector("conditions" -> SCollection(SBoolean)), SBoolean, None), + Lambda(Array("conditions" -> SCollection(SBoolean)), SBoolean, None), PredefFuncInfo( { case (_, Seq(col: Value[SCollection[SBoolean.type]]@unchecked)) => mkOR(col) }), OperationInfo(OR, "Returns true if \\emph{any} the elements in collection are \\lst{true}.", Seq(ArgInfo("conditions", "a collection of conditions"))) ) val XorOfFunc = PredefinedFunc("xorOf", - Lambda(Vector("conditions" -> SCollection(SBoolean)), SBoolean, None), + Lambda(Array("conditions" -> SCollection(SBoolean)), SBoolean, None), PredefFuncInfo({ case (_, Seq(col: Value[SCollection[SBoolean.type]]@unchecked)) => mkXorOf(col) }), OperationInfo(XorOf, "Similar to \\lst{allOf}, but performing logical XOR operation between all conditions instead of \\lst{&&}", Seq(ArgInfo("conditions", "a collection of conditions"))) ) val AllZKFunc = PredefinedFunc("allZK", - Lambda(IndexedSeq("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), + Lambda(Array("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), PredefFuncInfo(undefined), OperationInfo(SigmaAnd, "Returns sigma proposition which is proven when \\emph{all} the elements in collection are proven.", Seq(ArgInfo("propositions", "a collection of propositions"))) ) val AnyZKFunc = PredefinedFunc("anyZK", - Lambda(IndexedSeq("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), + Lambda(Array("propositions" -> SCollection(SSigmaProp)), SSigmaProp, None), PredefFuncInfo(undefined), OperationInfo(SigmaOr, "Returns sigma proposition which is proven when \\emph{any} of the elements in collection is proven.", Seq(ArgInfo("propositions", "a collection of propositions"))) ) val AtLeastFunc = PredefinedFunc("atLeast", - Lambda(Vector("k" -> SInt, "conditions" -> SCollection(SSigmaProp)), SSigmaProp, None), + Lambda(Array("k" -> SInt, "conditions" -> SCollection(SSigmaProp)), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(bound: IntValue@unchecked, arr: Value[SCollection[SSigmaProp.type]]@unchecked)) => mkAtLeast(bound, arr) @@ -106,13 +102,13 @@ object SigmaPredef { val OuterJoinFunc = PredefinedFunc( "outerJoin", Lambda( - Seq(STypeParam(tK), STypeParam(tL), STypeParam(tR), STypeParam(tO)), - Vector( + Array(STypeParam(tK), STypeParam(tL), STypeParam(tR), STypeParam(tO)), + Array( "left" -> SCollection(STuple(tK, tL)), "right" -> SCollection(STuple(tK, tR)), - "l" -> SFunc(IndexedSeq(tK, tL), tO), - "r" -> SFunc(IndexedSeq(tK, tR), tO), - "inner" -> SFunc(IndexedSeq(tK, tL, tR), tO) + "l" -> SFunc(Array(tK, tL), tO), + "r" -> SFunc(Array(tK, tR), tO), + "inner" -> SFunc(Array(tK, tL, tR), tO) ), SCollection(STuple(tK, tO)), None), PredefFuncInfo(undefined), @@ -121,14 +117,14 @@ object SigmaPredef { ) val ZKProofFunc = PredefinedFunc("ZKProof", - Lambda(Vector("block" -> SSigmaProp), SBoolean, None), + Lambda(Array("block" -> SSigmaProp), SBoolean, None), PredefFuncInfo({ case (_, Seq(block: SigmaPropValue@unchecked)) => mkZKProofBlock(block) }), OperationInfo(ZKProofBlock, "", Seq(ArgInfo("", ""))) ) val SigmaPropFunc = PredefinedFunc("sigmaProp", - Lambda(Vector("condition" -> SBoolean), SSigmaProp, None), + Lambda(Array("condition" -> SBoolean), SSigmaProp, None), PredefFuncInfo({ case (_, Seq(b: BoolValue@unchecked)) => mkBoolToSigmaProp(b) }), OperationInfo(BoolToSigmaProp, """Embedding of \lst{Boolean} values to \lst{SigmaProp} values. @@ -140,7 +136,7 @@ object SigmaPredef { ) val GetVarFunc = PredefinedFunc("getVar", - Lambda(Seq(STypeParam(tT)), Vector("varId" -> SByte), SOption(tT), None), + Lambda(Array(paramT), Array("varId" -> SByte), SOption(tT), None), PredefFuncInfo( { case (Ident(_, SFunc(_, SOption(rtpe), _)), Seq(id: Constant[SNumericType]@unchecked)) => mkGetVar(SByte.downcast(id.value.asInstanceOf[AnyVal]), rtpe) @@ -151,7 +147,7 @@ object SigmaPredef { ) def PKFunc(networkPrefix: NetworkPrefix) = PredefinedFunc("PK", - Lambda(Vector("input" -> SString), SSigmaProp, None), + Lambda(Array("input" -> SString), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => ErgoAddressEncoder(networkPrefix).fromString(arg.value).get match { @@ -164,7 +160,7 @@ object SigmaPredef { ) val DeserializeFunc = PredefinedFunc("deserialize", - Lambda(Seq(STypeParam(tT)), Vector("str" -> SString), tT, None), + Lambda(Array(paramT), Array("str" -> SString), tT, None), PredefFuncInfo( { case (Ident(_, SFunc(_, tpe, _)), args) => if (args.length != 1) @@ -185,7 +181,7 @@ object SigmaPredef { ) val FromBase58Func = PredefinedFunc("fromBase58", - Lambda(Vector("input" -> SString), SByteArray, None), + Lambda(Array("input" -> SString), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => ByteArrayConstant(Base58.decode(arg.value).get) @@ -195,7 +191,7 @@ object SigmaPredef { ) val FromBase64Func = PredefinedFunc("fromBase64", - Lambda(Vector("input" -> SString), SByteArray, None), + Lambda(Array("input" -> SString), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => ByteArrayConstant(Base64.decode(arg.value).get) @@ -205,7 +201,7 @@ object SigmaPredef { ) val Blake2b256Func = PredefinedFunc("blake2b256", - Lambda(Vector("input" -> SByteArray), SByteArray, None), + Lambda(Array("input" -> SByteArray), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkCalcBlake2b256(arg) @@ -215,7 +211,7 @@ object SigmaPredef { ) val Sha256Func = PredefinedFunc("sha256", - Lambda(Vector("input" -> SByteArray), SByteArray, None), + Lambda(Array("input" -> SByteArray), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkCalcSha256(arg) @@ -225,7 +221,7 @@ object SigmaPredef { ) val ByteArrayToBigIntFunc = PredefinedFunc("byteArrayToBigInt", - Lambda(Vector("input" -> SByteArray), SBigInt, None), + Lambda(Array("input" -> SByteArray), SBigInt, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkByteArrayToBigInt(arg) @@ -236,7 +232,7 @@ object SigmaPredef { ) val ByteArrayToLongFunc = PredefinedFunc("byteArrayToLong", - Lambda(Vector("input" -> SByteArray), SLong, None), + Lambda(Array("input" -> SByteArray), SLong, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkByteArrayToLong(arg) @@ -246,7 +242,7 @@ object SigmaPredef { ) val DecodePointFunc = PredefinedFunc("decodePoint", - Lambda(Vector("input" -> SByteArray), SGroupElement, None), + Lambda(Array("input" -> SByteArray), SGroupElement, None), PredefFuncInfo( { case (_, Seq(arg: Value[SByteArray]@unchecked)) => mkDecodePoint(arg) @@ -257,7 +253,7 @@ object SigmaPredef { ) val LongToByteArrayFunc = PredefinedFunc("longToByteArray", - Lambda(Vector("input" -> SLong), SByteArray, None), + Lambda(Array("input" -> SLong), SByteArray, None), PredefFuncInfo( { case (_, Seq(arg: Value[SLong.type]@unchecked)) => mkLongToByteArray(arg) @@ -268,7 +264,7 @@ object SigmaPredef { ) val ProveDHTupleFunc = PredefinedFunc("proveDHTuple", - Lambda(Vector("g" -> SGroupElement, "h" -> SGroupElement, "u" -> SGroupElement, "v" -> SGroupElement), SSigmaProp, None), + Lambda(Array("g" -> SGroupElement, "h" -> SGroupElement, "u" -> SGroupElement, "v" -> SGroupElement), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(g, h, u, v)) => mkCreateProveDHTuple(g.asGroupElement, h.asGroupElement, u.asGroupElement, v.asGroupElement) @@ -282,7 +278,7 @@ object SigmaPredef { ) val ProveDlogFunc = PredefinedFunc("proveDlog", - Lambda(Vector("value" -> SGroupElement), SSigmaProp, None), + Lambda(Array("value" -> SGroupElement), SSigmaProp, None), PredefFuncInfo( { case (_, Seq(arg: Value[SGroupElement.type]@unchecked)) => mkCreateProveDlog(arg) @@ -295,7 +291,7 @@ object SigmaPredef { ) val AvlTreeFunc = PredefinedFunc("avlTree", - Lambda(Vector("operationFlags" -> SByte, "digest" -> SByteArray, "keyLength" -> SInt, "valueLengthOpt" -> SIntOption), SAvlTree, None), + Lambda(Array("operationFlags" -> SByte, "digest" -> SByteArray, "keyLength" -> SInt, "valueLengthOpt" -> SIntOption), SAvlTree, None), PredefFuncInfo( { case (_, Seq(flags, digest, keyLength, valueLength)) => mkCreateAvlTree(flags.asByteValue, digest.asByteArray, keyLength.asIntValue, valueLength.asOption[SInt.type]) @@ -311,8 +307,8 @@ object SigmaPredef { val SubstConstantsFunc = PredefinedFunc("substConstants", Lambda( - Seq(STypeParam(tT)), - Vector("scriptBytes" -> SByteArray, "positions" -> SIntArray, "newValues" -> SCollection(tT)), + Seq(paramT), + Array("scriptBytes" -> SByteArray, "positions" -> SIntArray, "newValues" -> SCollection(tT)), SByteArray, None ), PredefFuncInfo( @@ -338,8 +334,8 @@ object SigmaPredef { val ExecuteFromVarFunc = PredefinedFunc("executeFromVar", Lambda( - Seq(STypeParam(tT)), - Vector("id" -> SByte), + Seq(paramT), + Array("id" -> SByte), tT, None ), PredefFuncInfo(undefined), @@ -356,8 +352,8 @@ object SigmaPredef { val ExecuteFromSelfRegFunc = PredefinedFunc("executeFromSelfReg", Lambda( - Seq(STypeParam(tT)), - Vector("id" -> SByte, "default" -> SOption(tT)), + Seq(paramT), + Array("id" -> SByte, "default" -> SOption(tT)), tT, None ), PredefFuncInfo(undefined), @@ -403,21 +399,21 @@ object SigmaPredef { def comparisonOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { PredefinedFunc(symbolName, - Lambda(Seq(STypeParam(tT)), Vector("left" -> tT, "right" -> tT), SBoolean, None), + Lambda(Seq(paramT), Array("left" -> tT, "right" -> tT), SBoolean, None), PredefFuncInfo(undefined), OperationInfo(opDesc, desc, args) ) } def binaryOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { PredefinedFunc(symbolName, - Lambda(Seq(STypeParam(tT)), Vector("left" -> tT, "right" -> tT), tT, None), + Lambda(Seq(paramT), Array("left" -> tT, "right" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(opDesc, desc, args) ) } def logicalOp(symbolName: String, opDesc: ValueCompanion, desc: String, args: Seq[ArgInfo]) = { PredefinedFunc(symbolName, - Lambda(Vector("left" -> SBoolean, "right" -> SBoolean), SBoolean, None), + Lambda(Array("left" -> SBoolean, "right" -> SBoolean), SBoolean, None), PredefFuncInfo(undefined), OperationInfo(opDesc, desc, args) ) @@ -472,37 +468,37 @@ object SigmaPredef { Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), PredefinedFunc("binary_|", - Lambda(Vector("left" -> SByteArray, "right" -> SByteArray), SByteArray, None), + Lambda(Array("left" -> SByteArray, "right" -> SByteArray), SByteArray, None), PredefFuncInfo(undefined), OperationInfo(Xor, "Byte-wise XOR of two collections of bytes", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) ), logicalOp("||", BinOr, "Logical OR of two operands", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), logicalOp("&&", BinAnd, "Logical AND of two operands", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))), logicalOp("^", BinXor, "Logical XOR of two operands", - Seq(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) + Array(ArgInfo("left", "left operand"), ArgInfo("right", "right operand"))) ).map(f => f.name -> f).toMap val unaryFuncs: Map[String, PredefinedFunc] = Seq( PredefinedFunc("unary_!", - Lambda(Vector("input" -> SBoolean), SBoolean, None), + Lambda(Array("input" -> SBoolean), SBoolean, None), PredefFuncInfo(undefined), OperationInfo(LogicalNot, "Logical NOT operation. Returns \\lst{true} if input is \\lst{false} and \\lst{false} if input is \\lst{true}.", Seq(ArgInfo("input", "input \\lst{Boolean} value"))) ), PredefinedFunc("unary_-", - Lambda(Seq(STypeParam(tT)), Vector("input" -> tT), tT, None), + Lambda(Array(paramT), Array("input" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(Negation, "Negates numeric value \\lst{x} by returning \\lst{-x}.", Seq(ArgInfo("input", "value of numeric type"))) ), PredefinedFunc("unary_~", - Lambda(Seq(STypeParam(tT)), Vector("input" -> tT), tT, None), + Lambda(Seq(paramT), Array("input" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(BitInversion, "Invert every bit of the numeric value.", @@ -517,14 +513,14 @@ object SigmaPredef { */ val specialFuncs: Map[String, PredefinedFunc] = Seq( PredefinedFunc("selectField", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("input" -> tT, "fieldIndex" -> SByte), tR, None), + Lambda(Array(paramT, paramR), Array("input" -> tT, "fieldIndex" -> SByte), tR, None), PredefFuncInfo(undefined), OperationInfo(SelectField, "Select tuple field by its 1-based index. E.g. \\lst{input._1} is transformed to \\lst{SelectField(input, 1)}", Seq(ArgInfo("input", "tuple of items"), ArgInfo("fieldIndex", "index of an item to select"))) ), PredefinedFunc("treeLookup", - Lambda(Vector("tree" -> SAvlTree, "key" -> SByteArray, "proof" -> SByteArray), SOption(SByteArray), None), + Lambda(Array("tree" -> SAvlTree, "key" -> SByteArray, "proof" -> SByteArray), SOption(SByteArray), None), PredefFuncInfo(undefined), OperationInfo(TreeLookup, "", @@ -533,7 +529,7 @@ object SigmaPredef { ArgInfo("proof", "proof to perform verification of the operation"))) ), PredefinedFunc("if", - Lambda(Seq(STypeParam(tT)), Vector("condition" -> SBoolean, "trueBranch" -> tT, "falseBranch" -> tT), tT, None), + Lambda(Array(paramT), Array("condition" -> SBoolean, "trueBranch" -> tT, "falseBranch" -> tT), tT, None), PredefFuncInfo(undefined), OperationInfo(If, "Compute condition, if true then compute trueBranch else compute falseBranch", @@ -542,21 +538,21 @@ object SigmaPredef { ArgInfo("falseBranch", "expression to execute when \\lst{condition == false}"))) ), PredefinedFunc("upcast", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("input" -> tT), tR, None), + Lambda(Array(paramT, paramR), Array("input" -> tT), tR, None), PredefFuncInfo(undefined), OperationInfo(Upcast, "Cast this numeric value to a bigger type (e.g. Int to Long)", Seq(ArgInfo("input", "value to cast"))) ), PredefinedFunc("downcast", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("input" -> tT), tR, None), + Lambda(Array(paramT, paramR), Array("input" -> tT), tR, None), PredefFuncInfo(undefined), OperationInfo(Downcast, "Cast this numeric value to a smaller type (e.g. Long to Int). Throws exception if overflow.", Seq(ArgInfo("input", "value to cast"))) ), PredefinedFunc("apply", - Lambda(Seq(STypeParam(tT), STypeParam(tR)), Vector("func" -> SFunc(tT, tR), "args" -> tT), tR, None), + Lambda(Array(paramT, paramR), Array("func" -> SFunc(tT, tR), "args" -> tT), tR, None), PredefFuncInfo(undefined), OperationInfo(Apply, "Apply the function to the arguments. ", @@ -564,7 +560,7 @@ object SigmaPredef { ArgInfo("args", "list of arguments"))) ), PredefinedFunc("placeholder", - Lambda(Seq(STypeParam(tT)), Vector("id" -> SInt), tT, None), + Lambda(Array(paramT), Array("id" -> SInt), tT, None), PredefFuncInfo(undefined), OperationInfo(ConstantPlaceholder, "Create special ErgoTree node which can be replaced by constant with given id.", diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala index ccc05721e5..e2e6869904 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaSpecializer.scala @@ -29,7 +29,7 @@ class SigmaSpecializer(val builder: SigmaBuilder) { /** Rewriting of AST with respect to environment to resolve all references * to let bound and lambda bound names. */ - private def eval(env: Map[String, SValue], e: SValue): SValue = rewrite(reduce(strategy[SValue]({ + private def eval(env: Map[String, SValue], e: SValue): SValue = rewrite(reduce(strategy[Any]({ case Ident(n, _) => env.get(n) case _ @ Block(binds, res) => diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala index 93adb756c5..e1b6c1df03 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaTyper.scala @@ -25,7 +25,7 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe private implicit val implicitPredefFuncRegistry: PredefinedFuncRegistry = predefFuncRegistry - private val tT = STypeVar("T") // to be used in typing rules + import SType.tT private val predefinedEnv: Map[String, SType] = predefFuncRegistry.funcs.mapValues(f => f.declaration.tpe) @@ -598,8 +598,8 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe // traverse the tree bottom-up checking that all the nodes have a type var untyped: SValue = null - rewrite(everywherebu(rule[SValue]{ - case v => + rewrite(everywherebu(rule[Any]{ + case v: SValue => if (v.tpe == NoType) untyped = v v }))(assigned) @@ -671,7 +671,7 @@ object SigmaTyper { val remainingVars = tparams.filterNot { p => subst.contains(p.ident) } SFunc(args.map(applySubst(_, subst)), applySubst(res, subst), remainingVars) case _ => - val substRule = rule[SType] { + val substRule = rule[Any] { case id: STypeVar if subst.contains(id) => subst(id) } rewrite(everywherebu(substRule))(tpe) diff --git a/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala b/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala index 29d0178c2a..d9f4f3415e 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SourceContext.scala @@ -23,7 +23,14 @@ object SourceContext { .map { case (((start, _), line), lineIndex) => SourceContext(lineIndex + 1, index - start + 1, line) - }.get + }.getOrElse { + // at least one line in the input + // point to the last character of the last line + val lastLine = lines.last + val iLine = lines.length - 1 + val iCol = lastLine.length - 1 + SourceContext(iLine, iCol, lastLine) + } } def fromParserFailure(e: Failure[_, String]): SourceContext = diff --git a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala index 5dc00703cd..1e4d86af83 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala @@ -45,10 +45,11 @@ object Terms { case class ZKProofBlock(body: SigmaPropValue) extends BoolValue { override def companion = ZKProofBlock override def tpe = SBoolean - override def opType: SFunc = SFunc(SSigmaProp, SBoolean) + override def opType: SFunc = ZKProofBlock.OpType } object ZKProofBlock extends ValueCompanion { override def opCode: OpCode = OpCodes.Undefined + val OpType = SFunc(SSigmaProp, SBoolean) } trait Val extends Value[SType] { @@ -272,8 +273,8 @@ object Terms { * @return AST where all nodes with missing source context are set to the given srcCtx */ def withPropagatedSrcCtx[T <: SType](srcCtx: Nullable[SourceContext]): Value[T] = { - rewrite(everywherebu(rule[SValue] { - case node if node != null && node.sourceContext.isEmpty => + rewrite(everywherebu(rule[Any] { + case node: SValue if node != null && node.sourceContext.isEmpty => node.withSrcCtx(srcCtx) }))(v).asValue[T] } diff --git a/sigmastate/src/main/scala/sigmastate/lang/Types.scala b/sigmastate/src/main/scala/sigmastate/lang/Types.scala index ebd66a2627..486944bd89 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/Types.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/Types.scala @@ -39,7 +39,7 @@ trait Types extends Core { case STuple(items) => SFunc(items, r) case _ => - SFunc(IndexedSeq(d), r) + SFunc(Array(d), r) } } val Type: P[SType] = P( `=>`.? ~~ PostfixType ~ TypeBounds ~ `*`.? ) @@ -101,7 +101,7 @@ trait Types extends Core { val SimpleType = { // Can't `cut` after the opening paren, because we might be trying to parse `()` // or `() => T`! only cut after parsing one type - val TupleType = P( "(" ~/ Type.repTC() ~ ")" ).map(items => STuple(items.toIndexedSeq)) + val TupleType = P( "(" ~/ Type.repTC() ~ ")" ).map(items => STuple(items.toArray)) val BasicType = P( TupleType | TypeId ) P( Index ~ BasicType ~ TypeArgs.rep ).map { case (_, t: STuple, Seq()) => t diff --git a/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala index 7743e5e3d2..3c6659d056 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/TypeSerializer.scala @@ -6,6 +6,7 @@ import org.ergoplatform.validation.ValidationRules.{CheckPrimitiveTypeCode, Chec import sigmastate._ import sigmastate.lang.exceptions.InvalidTypePrefix import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import spire.syntax.all.cfor /** Serialization of types according to specification in TypeSerialization.md. */ object TypeSerializer extends ByteBufferSerializer[SType] { @@ -173,7 +174,10 @@ object TypeSerializer extends ByteBufferSerializer[SType] { c match { case STuple.TupleTypeCode => { val len = r.getUByte() - val items = (0 until len).map(_ => deserialize(r, depth + 1)) + val items = new Array[SType](len) + cfor(0)(_ < len, _ + 1) { i => + items(i) = deserialize(r, depth + 1) + } STuple(items) } case SAny.typeCode => SAny diff --git a/sigmastate/src/main/scala/sigmastate/trees.scala b/sigmastate/src/main/scala/sigmastate/trees.scala index 642968c662..eb56661cd2 100644 --- a/sigmastate/src/main/scala/sigmastate/trees.scala +++ b/sigmastate/src/main/scala/sigmastate/trees.scala @@ -125,11 +125,12 @@ object TrivialProp { */ case class BoolToSigmaProp(value: BoolValue) extends SigmaPropValue { override def companion = BoolToSigmaProp - def tpe = SSigmaProp - val opType = SFunc(SBoolean, SSigmaProp) + override def tpe = SSigmaProp + override def opType = BoolToSigmaProp.OpType } object BoolToSigmaProp extends ValueCompanion { override def opCode: OpCode = OpCodes.BoolToSigmaPropCode + val OpType = SFunc(SBoolean, SSigmaProp) } /** ErgoTree operation to create a new SigmaProp value representing public key @@ -137,10 +138,11 @@ object BoolToSigmaProp extends ValueCompanion { case class CreateProveDlog(value: Value[SGroupElement.type]) extends SigmaPropValue { override def companion = CreateProveDlog override def tpe = SSigmaProp - override def opType = SFunc(SGroupElement, SSigmaProp) + override def opType = CreateProveDlog.OpType } object CreateProveDlog extends ValueCompanion { override def opCode: OpCode = OpCodes.ProveDlogCode + val OpType = SFunc(SGroupElement, SSigmaProp) } // TODO refactor: remove not used class @@ -184,10 +186,11 @@ trait SigmaTransformerCompanion extends ValueCompanion { */ case class SigmaAnd(items: Seq[SigmaPropValue]) extends SigmaTransformer[SigmaPropValue, SigmaPropValue] { override def companion = SigmaAnd - def tpe = SSigmaProp - val opType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) + override def tpe = SSigmaProp + override def opType = SigmaAnd.OpType } object SigmaAnd extends SigmaTransformerCompanion { + val OpType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) override def opCode: OpCode = OpCodes.SigmaAndCode override def argInfos: Seq[ArgInfo] = SigmaAndInfo.argInfos def apply(first: SigmaPropValue, second: SigmaPropValue, tail: SigmaPropValue*): SigmaAnd = SigmaAnd(Array(first, second) ++ tail) @@ -198,16 +201,22 @@ object SigmaAnd extends SigmaTransformerCompanion { */ case class SigmaOr(items: Seq[SigmaPropValue]) extends SigmaTransformer[SigmaPropValue, SigmaPropValue] { override def companion = SigmaOr - def tpe = SSigmaProp - val opType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) + override def tpe = SSigmaProp + override def opType = SigmaOr.OpType } object SigmaOr extends SigmaTransformerCompanion { + val OpType = SFunc(SCollection.SSigmaPropArray, SSigmaProp) override def opCode: OpCode = OpCodes.SigmaOrCode 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 @@ -215,7 +224,7 @@ object SigmaOr extends SigmaTransformerCompanion { case class OR(input: Value[SCollection[SBoolean.type]]) extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean { override def companion = OR - override val opType = SFunc(SCollection.SBooleanArray, SBoolean) + override def opType = OR.OpType } object OR extends LogicalTransformerCompanion { @@ -233,7 +242,7 @@ object OR extends LogicalTransformerCompanion { case class XorOf(input: Value[SCollection[SBoolean.type]]) extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean { override def companion = XorOf - override val opType = SFunc(SCollection.SBooleanArray, SBoolean) + override def opType = XorOf.OpType } object XorOf extends LogicalTransformerCompanion { @@ -251,11 +260,7 @@ case class AND(input: Value[SCollection[SBoolean.type]]) extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean { override def companion = AND - override val opType = SFunc(SCollection.SBooleanArray, SBoolean) -} - -trait LogicalTransformerCompanion extends ValueCompanion { - def argInfos: Seq[ArgInfo] + override def opType = AND.OpType } object AND extends LogicalTransformerCompanion { @@ -279,18 +284,20 @@ case class AtLeast(bound: Value[SInt.type], input: Value[SCollection[SSigmaProp. with NotReadyValue[SSigmaProp.type] { override def companion = AtLeast override def tpe: SSigmaProp.type = SSigmaProp - override def opType: SFunc = SFunc(IndexedSeq(SInt, SCollection.SBooleanArray), SBoolean) + override def opType: SFunc = AtLeast.OpType } object AtLeast extends ValueCompanion { override def opCode: OpCode = AtLeastCode + 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) + 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 = { @@ -351,19 +358,19 @@ object AtLeast extends ValueCompanion { */ case class Upcast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R) extends Transformer[T, R] { - import Upcast._ require(input.tpe.isInstanceOf[SNumericType], s"Cannot create Upcast node for non-numeric type ${input.tpe}") override def companion = Upcast - override val opType = SFunc(Vector(tT), tR) + override def opType = Upcast.OpType } trait NumericCastCompanion extends ValueCompanion { def argInfos: Seq[ArgInfo] + val OpType = SFunc(Array(SType.tT), SType.tR) } object Upcast extends NumericCastCompanion { override def opCode: OpCode = OpCodes.UpcastCode override def argInfos: Seq[ArgInfo] = UpcastInfo.argInfos - val tT = STypeVar("T") - val tR = STypeVar("R") + def tT = SType.tT + def tR = SType.tR } /** @@ -371,17 +378,16 @@ object Upcast extends NumericCastCompanion { */ case class Downcast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R) extends Transformer[T, R] { - import Downcast._ require(input.tpe.isInstanceOf[SNumericType], s"Cannot create Downcast node for non-numeric type ${input.tpe}") override def companion = Downcast - override val opType = SFunc(Vector(tT), tR) + override def opType = Downcast.OpType } object Downcast extends NumericCastCompanion { override def opCode: OpCode = OpCodes.DowncastCode override def argInfos: Seq[ArgInfo] = DowncastInfo.argInfos - val tT = STypeVar("T") - val tR = STypeVar("R") + def tT = SType.tT + def tR = SType.tR } /** @@ -390,9 +396,10 @@ object Downcast extends NumericCastCompanion { case class LongToByteArray(input: Value[SLong.type]) extends Transformer[SLong.type, SByteArray] with NotReadyValueByteArray { override def companion = LongToByteArray - override val opType = SFunc(SLong, SByteArray) + override def opType = LongToByteArray.OpType } object LongToByteArray extends SimpleTransformerCompanion { + val OpType = SFunc(SLong, SByteArray) override def opCode: OpCode = OpCodes.LongToByteArrayCode override def argInfos: Seq[ArgInfo] = LongToByteArrayInfo.argInfos } @@ -403,9 +410,10 @@ object LongToByteArray extends SimpleTransformerCompanion { case class ByteArrayToLong(input: Value[SByteArray]) extends Transformer[SByteArray, SLong.type] with NotReadyValueLong { override def companion = ByteArrayToLong - override val opType = SFunc(SByteArray, SLong) + override def opType = ByteArrayToLong.OpType } object ByteArrayToLong extends SimpleTransformerCompanion { + val OpType = SFunc(SByteArray, SLong) override def opCode: OpCode = OpCodes.ByteArrayToLongCode override def argInfos: Seq[ArgInfo] = ByteArrayToLongInfo.argInfos } @@ -416,9 +424,10 @@ object ByteArrayToLong extends SimpleTransformerCompanion { case class ByteArrayToBigInt(input: Value[SByteArray]) extends Transformer[SByteArray, SBigInt.type] with NotReadyValueBigInt { override def companion = ByteArrayToBigInt - override val opType = SFunc(SByteArray, SBigInt) + override val opType = ByteArrayToBigInt.OpType } object ByteArrayToBigInt extends SimpleTransformerCompanion { + val OpType = SFunc(SByteArray, SBigInt) override def opCode: OpCode = OpCodes.ByteArrayToBigIntCode override def argInfos: Seq[ArgInfo] = ByteArrayToBigIntInfo.argInfos } @@ -429,17 +438,20 @@ object ByteArrayToBigInt extends SimpleTransformerCompanion { case class DecodePoint(input: Value[SByteArray]) extends Transformer[SByteArray, SGroupElement.type] with NotReadyValueGroupElement { override def companion = DecodePoint - override val opType = SFunc(SByteArray, SGroupElement) + override def opType = DecodePoint.OpType } object DecodePoint extends SimpleTransformerCompanion { + val OpType = SFunc(SByteArray, SGroupElement) override def opCode: OpCode = OpCodes.DecodePointCode override def argInfos: Seq[ArgInfo] = DecodePointInfo.argInfos } -trait CalcHash extends Transformer[SByteArray, SByteArray] with NotReadyValueByteArray { - val input: Value[SByteArray] - val hashFn: CryptographicHash32 - override val opType = SFunc(SByteArray, SByteArray) +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) } /** @@ -483,14 +495,12 @@ object CalcSha256 extends SimpleTransformerCompanion { */ case class SubstConstants[T <: SType](scriptBytes: Value[SByteArray], positions: Value[SIntArray], newValues: Value[SCollection[T]]) extends NotReadyValueByteArray { - import SubstConstants._ override def companion = SubstConstants - override val opType = SFunc(Vector(SByteArray, SIntArray, SCollection(tT)), SByteArray) + override val opType = SFunc(Array(SByteArray, SIntArray, SCollection(SType.tT)), SByteArray) } object SubstConstants extends ValueCompanion { override def opCode: OpCode = OpCodes.SubstConstantsCode - val tT = STypeVar("T") def eval(scriptBytes: Array[Byte], positions: Array[Int], @@ -504,7 +514,6 @@ object SubstConstants extends ValueCompanion { sealed trait Triple[LIV <: SType, RIV <: SType, OV <: SType] extends NotReadyValue[OV] { val left: Value[LIV] val right: Value[RIV] - override def opType = SFunc(Vector(left.tpe, right.tpe), tpe) } sealed trait OneArgumentOperation[IV <: SType, OV <: SType] extends NotReadyValue[OV] { @@ -527,6 +536,7 @@ case class ArithOp[T <: SType](left: Value[T], right: Value[T], override val opC extends TwoArgumentsOperation[T, T, T] with NotReadyValue[T] { override def companion: ValueCompanion = 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 @@ -564,7 +574,8 @@ object ArithOp { } /** Negation operation on numeric type T. */ -case class Negation[T <: SNumericType](input: Value[T]) extends OneArgumentOperation[T, 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 } @@ -573,7 +584,8 @@ object Negation extends OneArgumentOperationCompanion { override def argInfos: Seq[ArgInfo] = NegationInfo.argInfos } -case class BitInversion[T <: SNumericType](input: Value[T]) extends OneArgumentOperation[T, T] { +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 } @@ -582,10 +594,12 @@ object BitInversion extends OneArgumentOperationCompanion { override def argInfos: Seq[ArgInfo] = BitInversionInfo.argInfos } -case class BitOp[T <: SNumericType](left: Value[T], right: Value[T], override val opCode: 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. */ class BitOpCompanion(val opCode: OpCode, val name: String, _argInfos: => Seq[ArgInfo]) extends TwoArgumentOperationCompanion { @@ -625,7 +639,7 @@ case class ModQArithOp(left: Value[SBigInt.type], right: Value[SBigInt.type], ov extends NotReadyValue[SBigInt.type] { override def companion = ModQArithOp.operations(opCode) override def tpe: SBigInt.type = SBigInt - override def opType: SFunc = SFunc(Vector(left.tpe, right.tpe), tpe) + 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] @@ -663,8 +677,10 @@ case class Xor(override val left: Value[SByteArray], extends TwoArgumentsOperation[SByteArray, SByteArray, SByteArray] with NotReadyValueByteArray { override def companion = Xor + override def opType = Xor.OpType } object Xor extends TwoArgumentOperationCompanion { + val OpType = SFunc(Array(SByteArray, SByteArray), SByteArray) override def opCode: OpCode = XorCode override def argInfos: Seq[ArgInfo] = XorInfo.argInfos } @@ -674,8 +690,10 @@ case class Exponentiate(override val left: Value[SGroupElement.type], extends TwoArgumentsOperation[SGroupElement.type, SBigInt.type, SGroupElement.type] with NotReadyValueGroupElement { override def companion = Exponentiate + override def opType = Exponentiate.OpType } object Exponentiate extends TwoArgumentOperationCompanion { + val OpType = SFunc(Array(SGroupElement, SBigInt), SGroupElement) override def opCode: OpCode = ExponentiateCode override def argInfos: Seq[ArgInfo] = ExponentiateInfo.argInfos } @@ -685,8 +703,10 @@ case class MultiplyGroup(override val left: Value[SGroupElement.type], extends TwoArgumentsOperation[SGroupElement.type, SGroupElement.type, SGroupElement.type] with NotReadyValueGroupElement { override def companion = MultiplyGroup + override def opType = MultiplyGroup.OpType } object MultiplyGroup extends TwoArgumentOperationCompanion { + val OpType = SFunc(Array(SGroupElement, SGroupElement), SGroupElement) override def opCode: OpCode = MultiplyGroupCode override def argInfos: Seq[ArgInfo] = MultiplyGroupInfo.argInfos } @@ -696,8 +716,10 @@ sealed trait Relation[LIV <: SType, RIV <: SType] extends Triple[LIV, RIV, SBool with NotReadyValueBoolean trait SimpleRelation[T <: SType] extends Relation[T, T] { - val tT = STypeVar("T") - override val opType = SFunc(Vector(tT, tT), SBoolean) + override def opType = SimpleRelation.GenericOpType +} +object SimpleRelation { + val GenericOpType = SFunc(SType.IndexedSeqOfT2, SBoolean) } trait RelationCompanion extends ValueCompanion { @@ -777,8 +799,10 @@ object NEQ extends RelationCompanion { 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 } object BinOr extends RelationCompanion { + val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean) override def opCode: OpCode = BinOrCode override def argInfos: Seq[ArgInfo] = BinOrInfo.argInfos } @@ -790,8 +814,10 @@ object BinOr extends RelationCompanion { 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 } object BinAnd extends RelationCompanion { + val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean) override def opCode: OpCode = BinAndCode override def argInfos: Seq[ArgInfo] = BinAndInfo.argInfos } @@ -799,8 +825,10 @@ object BinAnd extends RelationCompanion { 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 } object BinXor extends RelationCompanion { + val OpType = SFunc(Array(SBoolean, SBoolean), SBoolean) override def opCode: OpCode = BinXorCode override def argInfos: Seq[ArgInfo] = BinXorInfo.argInfos } @@ -814,7 +842,7 @@ sealed trait Quadruple[IV1 <: SType, IV2 <: SType, IV3 <: SType, OV <: SType] ex val second: Value[IV2] val third: Value[IV3] - val opType = SFunc(Vector(first.tpe, second.tpe, third.tpe), tpe) + val opType = SFunc(Array(first.tpe, second.tpe, third.tpe), tpe) } /** @@ -859,14 +887,15 @@ case class If[T <: SType](condition: Value[SBoolean.type], trueBranch: Value[T], object If extends QuadrupleCompanion { override def opCode: OpCode = OpCodes.IfCode override def argInfos: Seq[ArgInfo] = IfInfo.argInfos - val tT = STypeVar("T") + 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 val opType = SFunc(Vector(SBoolean), SBoolean) + override def opType = LogicalNot.OpType } object LogicalNot extends ValueCompanion { + val OpType = SFunc(Array(SBoolean), SBoolean) override def opCode: OpCode = OpCodes.LogicalNotCode } diff --git a/sigmastate/src/main/scala/sigmastate/types.scala b/sigmastate/src/main/scala/sigmastate/types.scala index df0b2e3066..2cec907904 100644 --- a/sigmastate/src/main/scala/sigmastate/types.scala +++ b/sigmastate/src/main/scala/sigmastate/types.scala @@ -64,6 +64,11 @@ sealed trait SType extends SigmaNode { /** Approximate size of the given value in bytes. It is actual size only for primitive types.*/ def dataSize(v: SType#WrappedType): Long + /** Returns true if this type embeddable, i.e. a type that can be combined with type + * constructor for optimized encoding. For each embeddable type `T`, and type + * constructor `C`, the type `C[T]` can be represented by a single byte. + * @see [[sigmastate.serialization.TypeSerializer]] + */ def isEmbeddable: Boolean = false /** Returns true if dataSize doesn't depend on data value. @@ -91,9 +96,6 @@ sealed trait SType extends SigmaNode { object SType { /** Representation of type codes used in serialization. */ type TypeCode = Byte - object Codes { - - } val DummyValue = 0.asWrappedType @@ -118,6 +120,38 @@ object SType { } implicit val ErgoLikeContextRType: RType[ErgoLikeContext] = RType.fromClassTag(classTag[ErgoLikeContext]) + /** Named type variables and parameters used in generic types and method signatures. + * Generic type terms like `(Coll[IV],(IV) => Boolean) => Boolean` are used to represent + * method types of `Coll`` and `Option`` types. Each such type is an instance of [[SFunc]]. + * To represent variables (such as `IV` in the example above) [[STypeVar]] instances + * are used. + * + * Generic types are not supported by ErgoTree serialization format and STypeVars are + * used internally and never serialized (there is no serializer for STypeVar). + * Thus the usage of type variables is limited. + * + * All necessary type variables can be declared in advance and reused across all code + * base. This allows to avoid allocation of many duplicates and also improve + * performance of SType values. + */ + val tT = STypeVar("T") + val tR = STypeVar("R") + val tK = STypeVar("K") + val tL = STypeVar("L") + val tO = STypeVar("O") + val tD = STypeVar("D") + val tV = STypeVar("V") + val tIV = STypeVar("IV") + val tOV = STypeVar("OV") + + val paramT = STypeParam(tT) + val paramR = STypeParam(tR) + val paramIV = STypeParam(tIV) + val paramOV = STypeParam(tOV) + + val IndexedSeqOfT1: IndexedSeq[SType] = Array(SType.tT) + val IndexedSeqOfT2: IndexedSeq[SType] = Array(SType.tT, SType.tT) + /** All pre-defined types should be listed here. Note, NoType is not listed. * Should be in sync with sigmastate.lang.Types.predefTypes. */ val allPredefTypes = Seq(SBoolean, SByte, SShort, SInt, SLong, SBigInt, SContext, SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, SUnit, SAny) @@ -150,7 +184,17 @@ object SType { val okRange = f1.tRange.canBeTypedAs(f2.tRange) okDom && okRange } + + /** Returns true if this type is numeric (Byte, Short, etc.) + * @see [[sigmastate.SNumericType]] + */ def isNumType: Boolean = tpe.isInstanceOf[SNumericType] + + /** Returns true if this type is either numeric (Byte, Short, etc.) or is NoType. + * @see [[sigmastate.SNumericType]] + */ + def isNumTypeOrNoType: Boolean = isNumType || tpe == NoType + def asNumType: SNumericType = tpe.asInstanceOf[SNumericType] def asFunc: SFunc = tpe.asInstanceOf[SFunc] def asProduct: SProduct = tpe.asInstanceOf[SProduct] @@ -466,7 +510,7 @@ object SNumericType extends STypeCompanion { | Each boolean corresponds to one bit. """.stripMargin) - override val methods: Seq[SMethod] = Vector( + override val methods: Seq[SMethod] = Array( ToByteMethod, // see Downcast ToShortMethod, // see Downcast ToIntMethod, // see Downcast @@ -602,7 +646,7 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM override def typeId = typeCode /** Type of Relation binary op like GE, LE, etc. */ - val RelationOpType = SFunc(Vector(SBigInt, SBigInt), SBoolean) + val RelationOpType = SFunc(Array(SBigInt, SBigInt), SBoolean) /** The maximum size of BigInteger value in byte array representation. */ val MaxSizeInBytes: Long = SigmaConstants.MaxBigIntSizeInBytes.value @@ -670,22 +714,22 @@ case object SGroupElement extends SProduct with SPrimType with SEmbeddable with override def typeId = typeCode override def coster: Option[CosterFactory] = Some(Coster(_.GroupElementCoster)) - val GetEncodedMethod: SMethod = SMethod(this, "getEncoded", SFunc(IndexedSeq(this), SByteArray), 2) + lazy val GetEncodedMethod: SMethod = SMethod(this, "getEncoded", SFunc(IndexedSeq(this), SByteArray), 2) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Get an encoding of the point value.") - val ExponentiateMethod: SMethod = SMethod(this, "exp", SFunc(IndexedSeq(this, SBigInt), this), 3) + lazy val ExponentiateMethod: SMethod = SMethod(this, "exp", SFunc(IndexedSeq(this, SBigInt), this), 3) .withIRInfo({ case (builder, obj, _, Seq(arg), _) => builder.mkExponentiate(obj.asGroupElement, arg.asBigInt) }) .withInfo(Exponentiate, "Exponentiate this \\lst{GroupElement} to the given number. Returns this to the power of k", ArgInfo("k", "The power")) - val MultiplyMethod: SMethod = SMethod(this, "multiply", SFunc(IndexedSeq(this, SGroupElement), this), 4) + lazy val MultiplyMethod: SMethod = SMethod(this, "multiply", SFunc(IndexedSeq(this, SGroupElement), this), 4) .withIRInfo({ case (builder, obj, _, Seq(arg), _) => builder.mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) }) .withInfo(MultiplyGroup, "Group operation.", ArgInfo("other", "other element of the group")) - val NegateMethod: SMethod = SMethod(this, "negate", SFunc(this, this), 5) + lazy val NegateMethod: SMethod = SMethod(this, "negate", SFunc(this, this), 5) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, "Inverse element of the group.") @@ -717,9 +761,9 @@ case object SSigmaProp extends SProduct with SPrimType with SEmbeddable with SLo override def isConstantSize = true val PropBytes = "propBytes" val IsProven = "isProven" - val PropBytesMethod = SMethod(this, PropBytes, SFunc(this, SByteArray), 1) + lazy val PropBytesMethod = SMethod(this, PropBytes, SFunc(this, SByteArray), 1) .withInfo(SigmaPropBytes, "Serialized bytes of this sigma proposition taken as ErgoTree.") - val IsProvenMethod = SMethod(this, IsProven, SFunc(this, SBoolean), 2) + lazy val IsProvenMethod = SMethod(this, IsProven, SFunc(this, SBoolean), 2) .withInfo(// available only at frontend of ErgoScript "Verify that sigma proposition is proven.") protected override def getMethods() = super.getMethods() ++ Seq( @@ -765,7 +809,7 @@ case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct with override def toString = s"Option[$elemType]" override def toTermString: String = s"Option[${elemType.toTermString}]" - val typeParams: Seq[STypeParam] = Seq(STypeParam(tT)) + lazy val typeParams: Seq[STypeParam] = Array(SType.paramT) } object SOption extends STypeCompanion { @@ -775,7 +819,7 @@ object SOption extends STypeCompanion { val OptionCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionCollectionTypeConstrId).toByte override def typeId = OptionTypeCode - override def coster: Option[CosterFactory] = Some(Coster(_.OptionCoster)) + override val coster: Option[CosterFactory] = Some(Coster(_.OptionCoster)) type SBooleanOption = SOption[SBoolean.type] type SByteOption = SOption[SByte.type] @@ -804,8 +848,7 @@ object SOption extends STypeCompanion { val GetOrElse = "getOrElse" val Fold = "fold" - val tT = STypeVar("T") - val tR = STypeVar("R") + import SType.{tT, tR, paramT, paramR} val ThisType = SOption(tT) val IsDefinedMethod = SMethod(this, IsDefined, SFunc(ThisType, SBoolean), 2) @@ -822,7 +865,7 @@ object SOption extends STypeCompanion { |return the result of evaluating \lst{default}. """.stripMargin, ArgInfo("default", "the default value")) - val FoldMethod = SMethod(this, Fold, SFunc(IndexedSeq(ThisType, tR, SFunc(tT, tR)), tR, Seq(tT, tR)), 5) + val FoldMethod = SMethod(this, Fold, SFunc(Array(ThisType, tR, SFunc(tT, tR)), tR, Seq(tT, tR)), 5) .withInfo(MethodCall, """Returns the result of applying \lst{f} to this option's | value if the option is nonempty. Otherwise, evaluates @@ -833,7 +876,7 @@ object SOption extends STypeCompanion { ArgInfo("f", "the function to apply if nonempty")) val MapMethod = SMethod(this, "map", - SFunc(IndexedSeq(ThisType, SFunc(tT, tR)), SOption(tR), Seq(STypeParam(tT), STypeParam(tR))), 7) + SFunc(Array(ThisType, SFunc(tT, tR)), SOption(tR), Array(paramT, paramR)), 7) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, """Returns a \lst{Some} containing the result of applying \lst{f} to this option's @@ -842,7 +885,7 @@ object SOption extends STypeCompanion { """.stripMargin, ArgInfo("f", "the function to apply")) val FilterMethod = SMethod(this, "filter", - SFunc(IndexedSeq(ThisType, SFunc(tT, SBoolean)), ThisType, Seq(STypeParam(tT))), 8) + SFunc(Array(ThisType, SFunc(tT, SBoolean)), ThisType, Array(paramT)), 8) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, """Returns this option if it is nonempty and applying the predicate \lst{p} to @@ -888,19 +931,18 @@ object SCollectionType { val CollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * CollectionTypeConstrId).toByte val NestedCollectionTypeConstrId = 2 val NestedCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * NestedCollectionTypeConstrId).toByte - val typeParams = Seq(STypeParam(tIV.name)) + val typeParams: Seq[STypeParam] = Array(SType.paramIV) } object SCollection extends STypeCompanion with MethodByNameUnapply { override def typeId = SCollectionType.CollectionTypeCode override def coster: Option[CosterFactory] = Some(Coster(_.CollCoster)) - val tIV = STypeVar("IV") - val paramIV = STypeParam(tIV) - val tOV = STypeVar("OV") - val paramOV = STypeParam(tOV) - val tK = STypeVar("K") - val tV = STypeVar("V") + import SType.{tK, tV, paramIV, paramOV} + + def tIV = SType.tIV + def tOV = SType.tOV + val ThisType = SCollection(tIV) val tOVColl = SCollection(tOV) val tPredicate = SFunc(tIV, SBoolean) @@ -1204,7 +1246,7 @@ object STuple extends STypeCompanion { def typeId = TupleTypeCode lazy val colMethods = { - val subst = Map(SCollection.tIV -> SAny) + val subst = Map(SType.tIV -> SAny) // TODO: implement other val activeMethods = Set(1.toByte, 10.toByte) SCollection.methods.filter(m => activeMethods.contains(m.methodId)).map { m => @@ -1214,7 +1256,7 @@ object STuple extends STypeCompanion { def methods: Seq[SMethod] = sys.error(s"Shouldn't be called.") - def apply(items: SType*): STuple = STuple(items.toIndexedSeq) + def apply(items: SType*): STuple = STuple(items.toArray) val MaxTupleLength: Int = SigmaConstants.MaxTupleLength.value private val componentNames = Array.tabulate(MaxTupleLength){ i => s"_${i + 1}" } def componentNameByIndex(i: Int): String = @@ -1228,7 +1270,7 @@ object STuple extends STypeCompanion { /** Helper constuctor/extractor for tuples of two types. */ object SPair { - def apply(l: SType, r: SType) = STuple(Vector(l, r)) + def apply(l: SType, r: SType) = STuple(Array(l, r)) def unapply(t: STuple): Nullable[(SType, SType)] = t match { case STuple(IndexedSeq(l, r)) => Nullable((l, r)) case _ => Nullable.None @@ -1255,7 +1297,7 @@ case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypePa def getGenericType: SFunc = { val typeParams: Seq[STypeParam] = tDom.zipWithIndex - .map { case (_, i) => STypeParam(tD.name + (i + 1)) } :+ STypeParam(tR.name) + .map { case (_, i) => STypeParam(SType.tD.name + (i + 1)) } :+ STypeParam(SType.tR.name) val ts = typeParams.map(_.ident) SFunc(ts.init.toIndexedSeq, ts.last, Nil) } @@ -1263,8 +1305,6 @@ case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypePa } object SFunc { - val tD = STypeVar("D") - val tR = STypeVar("R") final val FuncTypeCode: TypeCode = OpCodes.FirstFuncType def apply(tDom: SType, tRange: SType): SFunc = SFunc(Array(tDom), tRange) // @hotspot val identity = { x: Any => x } @@ -1309,15 +1349,18 @@ case object SBox extends SProduct with SPredefType with SMonoType { } override def isConstantSize = false - val tT = STypeVar("T") + import SType.{tT, paramT} + + lazy val GetRegFuncType = SFunc(Array(SBox), SOption(tT), Array(paramT)) + def registers(idOfs: Int): Seq[SMethod] = { allRegisters.map { i => i match { case r: MandatoryRegisterId => - SMethod(this, s"R${i.asIndex}", SFunc(IndexedSeq(SBox), SOption(tT), Seq(STypeParam(tT))), (idOfs + i.asIndex + 1).toByte) + SMethod(this, s"R${i.asIndex}", GetRegFuncType, (idOfs + i.asIndex + 1).toByte) .withInfo(ExtractRegisterAs, r.purpose) case _ => - SMethod(this, s"R${i.asIndex}", SFunc(IndexedSeq(SBox), SOption(tT), Seq(STypeParam(tT))), (idOfs + i.asIndex + 1).toByte) + SMethod(this, s"R${i.asIndex}", GetRegFuncType, (idOfs + i.asIndex + 1).toByte) .withInfo(ExtractRegisterAs, "Non-mandatory register") } } @@ -1358,7 +1401,7 @@ case object SBox extends SProduct with SPredefType with SMonoType { | identifier followed by box index in the transaction outputs. """.stripMargin ) // see ExtractCreationInfo - lazy val getRegMethod = SMethod(this, "getReg", SFunc(IndexedSeq(SBox, SInt), SOption(tT), Seq(STypeParam(tT))), 7) + lazy val getRegMethod = SMethod(this, "getReg", SFunc(Array(SBox, SInt), SOption(tT), Array(paramT)), 7) .withInfo(ExtractRegisterAs, """ Extracts register by id and type. | Type param \lst{T} expected type of the register. @@ -1372,7 +1415,7 @@ case object SBox extends SProduct with SPredefType with SMonoType { // should be lazy to solve recursive initialization - protected override def getMethods() = super.getMethods() ++ Vector( + protected override def getMethods() = super.getMethods() ++ Array( ValueMethod, // see ExtractAmount PropositionBytesMethod, // see ExtractScriptBytes BytesMethod, // see ExtractBytes @@ -1394,17 +1437,17 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { override def isConstantSize = true import SOption._ - val TCollOptionCollByte = SCollection(SByteArrayOption) - val CollKeyValue = SCollection(STuple(SByteArray, SByteArray)) + lazy val TCollOptionCollByte = SCollection(SByteArrayOption) + lazy val CollKeyValue = SCollection(STuple(SByteArray, SByteArray)) - val digestMethod = SMethod(this, "digest", SFunc(this, SByteArray), 1) + lazy val digestMethod = SMethod(this, "digest", SFunc(this, SByteArray), 1) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """Returns digest of the state represented by this tree. | Authenticated tree \lst{digest} = \lst{root hash bytes} ++ \lst{tree height} """.stripMargin) - val enabledOperationsMethod = SMethod(this, "enabledOperations", SFunc(this, SByte), 2) + lazy val enabledOperationsMethod = SMethod(this, "enabledOperations", SFunc(this, SByte), 2) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ Flags of enabled operations packed in single byte. @@ -1412,38 +1455,38 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | \lst{isUpdateAllowed == (enabledOperations & 0x02) != 0}\newline | \lst{isRemoveAllowed == (enabledOperations & 0x04) != 0} """.stripMargin) - val keyLengthMethod = SMethod(this, "keyLength", SFunc(this, SInt), 3) + lazy val keyLengthMethod = SMethod(this, "keyLength", SFunc(this, SInt), 3) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val valueLengthOptMethod = SMethod(this, "valueLengthOpt", SFunc(this, SIntOption), 4) + lazy val valueLengthOptMethod = SMethod(this, "valueLengthOpt", SFunc(this, SIntOption), 4) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val isInsertAllowedMethod = SMethod(this, "isInsertAllowed", SFunc(this, SBoolean), 5) + lazy val isInsertAllowedMethod = SMethod(this, "isInsertAllowed", SFunc(this, SBoolean), 5) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val isUpdateAllowedMethod = SMethod(this, "isUpdateAllowed", SFunc(this, SBoolean), 6) + lazy val isUpdateAllowedMethod = SMethod(this, "isUpdateAllowed", SFunc(this, SBoolean), 6) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val isRemoveAllowedMethod = SMethod(this, "isRemoveAllowed", SFunc(this, SBoolean), 7) + lazy val isRemoveAllowedMethod = SMethod(this, "isRemoveAllowed", SFunc(this, SBoolean), 7) .withIRInfo(MethodCallIrBuilder) .withInfo(PropertyCall, """ | """.stripMargin) - val updateOperationsMethod = SMethod(this, "updateOperations", + lazy val updateOperationsMethod = SMethod(this, "updateOperations", SFunc(IndexedSeq(SAvlTree, SByte), SAvlTree), 8) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1451,7 +1494,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val containsMethod = SMethod(this, "contains", + lazy val containsMethod = SMethod(this, "contains", SFunc(IndexedSeq(SAvlTree, SByteArray, SByteArray), SBoolean), 9) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1468,7 +1511,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val getMethod = SMethod(this, "get", + lazy val getMethod = SMethod(this, "get", SFunc(IndexedSeq(SAvlTree, SByteArray, SByteArray), SByteArrayOption), 10) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1485,7 +1528,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val getManyMethod = SMethod(this, "getMany", + lazy val getManyMethod = SMethod(this, "getMany", SFunc(IndexedSeq(SAvlTree, SByteArray2, SByteArray), TCollOptionCollByte), 11) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1500,7 +1543,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val insertMethod = SMethod(this, "insert", + lazy val insertMethod = SMethod(this, "insert", SFunc(IndexedSeq(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 12) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1517,7 +1560,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val updateMethod = SMethod(this, "update", + lazy val updateMethod = SMethod(this, "update", SFunc(IndexedSeq(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 13) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1534,7 +1577,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val removeMethod = SMethod(this, "remove", + lazy val removeMethod = SMethod(this, "remove", SFunc(IndexedSeq(SAvlTree, SByteArray2, SByteArray), SAvlTreeOption), 14) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1551,7 +1594,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { | """.stripMargin) - val updateDigestMethod = SMethod(this, "updateDigest", + lazy val updateDigestMethod = SMethod(this, "updateDigest", SFunc(IndexedSeq(SAvlTree, SByteArray), SAvlTree), 15) .withIRInfo(MethodCallIrBuilder) .withInfo(MethodCall, @@ -1590,18 +1633,19 @@ case object SContext extends SProduct with SPredefType with SMonoType { } override def isConstantSize = false - val tT = STypeVar("T") - val dataInputsMethod = property("dataInputs", SBoxArray, 1) - val headersMethod = property("headers", SHeaderArray, 2) - val preHeaderMethod = property("preHeader", SPreHeader, 3) - val inputsMethod = property("INPUTS", SBoxArray, 4, Inputs) - val outputsMethod = property("OUTPUTS", SBoxArray, 5, Outputs) - val heightMethod = property("HEIGHT", SInt, 6, Height) - val selfMethod = property("SELF", SBox, 7, Self) - val selfBoxIndexMethod = property("selfBoxIndex", SInt, 8) - val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) - val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) - val getVarMethod = SMethod(this, "getVar", SFunc(IndexedSeq(SContext, SByte), SOption(tT), Seq(STypeParam(tT))), 11) + import SType.{tT, paramT} + + lazy val dataInputsMethod = property("dataInputs", SBoxArray, 1) + lazy val headersMethod = property("headers", SHeaderArray, 2) + lazy val preHeaderMethod = property("preHeader", SPreHeader, 3) + lazy val inputsMethod = property("INPUTS", SBoxArray, 4, Inputs) + lazy val outputsMethod = property("OUTPUTS", SBoxArray, 5, Outputs) + lazy val heightMethod = property("HEIGHT", SInt, 6, Height) + lazy val selfMethod = property("SELF", SBox, 7, Self) + lazy val selfBoxIndexMethod = property("selfBoxIndex", SInt, 8) + lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) + lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) + lazy val getVarMethod = SMethod(this, "getVar", SFunc(Array(SContext, SByte), SOption(tT), Array(paramT)), 11) .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", ArgInfo("varId", "\\lst{Byte} identifier of context variable")) @@ -1637,21 +1681,21 @@ case object SHeader extends SProduct with SPredefType with SMonoType { } override def isConstantSize = true - val idMethod = property("id", SByteArray, 1) - val versionMethod = property("version", SByte, 2) - val parentIdMethod = property("parentId", SByteArray, 3) - val ADProofsRootMethod = property("ADProofsRoot", SByteArray, 4) - val stateRootMethod = property("stateRoot", SAvlTree, 5) - val transactionsRootMethod = property("transactionsRoot", SByteArray, 6) - val timestampMethod = property("timestamp", SLong, 7) - val nBitsMethod = property("nBits", SLong, 8) - val heightMethod = property("height", SInt, 9) - val extensionRootMethod = property("extensionRoot", SByteArray, 10) - val minerPkMethod = property("minerPk", SGroupElement, 11) - val powOnetimePkMethod = property("powOnetimePk", SGroupElement, 12) - val powNonceMethod = property("powNonce", SByteArray, 13) - val powDistanceMethod = property("powDistance", SBigInt, 14) - val votesMethod = property("votes", SByteArray, 15) + lazy val idMethod = property("id", SByteArray, 1) + lazy val versionMethod = property("version", SByte, 2) + lazy val parentIdMethod = property("parentId", SByteArray, 3) + lazy val ADProofsRootMethod = property("ADProofsRoot", SByteArray, 4) + lazy val stateRootMethod = property("stateRoot", SAvlTree, 5) + lazy val transactionsRootMethod = property("transactionsRoot", SByteArray, 6) + lazy val timestampMethod = property("timestamp", SLong, 7) + lazy val nBitsMethod = property("nBits", SLong, 8) + lazy val heightMethod = property("height", SInt, 9) + lazy val extensionRootMethod = property("extensionRoot", SByteArray, 10) + lazy val minerPkMethod = property("minerPk", SGroupElement, 11) + lazy val powOnetimePkMethod = property("powOnetimePk", SGroupElement, 12) + lazy val powNonceMethod = property("powNonce", SByteArray, 13) + lazy val powDistanceMethod = property("powDistance", SBigInt, 14) + lazy val votesMethod = property("votes", SByteArray, 15) protected override def getMethods() = super.getMethods() ++ Seq( idMethod, versionMethod, parentIdMethod, ADProofsRootMethod, stateRootMethod, transactionsRootMethod, @@ -1678,13 +1722,13 @@ case object SPreHeader extends SProduct with SPredefType with SMonoType { } override def isConstantSize = true - val versionMethod = property("version", SByte, 1) - val parentIdMethod = property("parentId", SByteArray, 2) - val timestampMethod = property("timestamp", SLong, 3) - val nBitsMethod = property("nBits", SLong, 4) - val heightMethod = property("height", SInt, 5) - val minerPkMethod = property("minerPk", SGroupElement, 6) - val votesMethod = property("votes", SByteArray, 7) + lazy val versionMethod = property("version", SByte, 1) + lazy val parentIdMethod = property("parentId", SByteArray, 2) + lazy val timestampMethod = property("timestamp", SLong, 3) + lazy val nBitsMethod = property("nBits", SLong, 4) + lazy val heightMethod = property("height", SInt, 5) + lazy val minerPkMethod = property("minerPk", SGroupElement, 6) + lazy val votesMethod = property("votes", SByteArray, 7) protected override def getMethods() = super.getMethods() ++ Seq( versionMethod, parentIdMethod, timestampMethod, nBitsMethod, heightMethod, minerPkMethod, votesMethod @@ -1716,11 +1760,12 @@ case object SGlobal extends SProduct with SPredefType with SMonoType { } override def isConstantSize = true // only fixed amount of global information is allowed - val tT = STypeVar("T") - val groupGeneratorMethod = SMethod(this, "groupGenerator", SFunc(this, SGroupElement), 1) + import SType.tT + + lazy val groupGeneratorMethod = SMethod(this, "groupGenerator", SFunc(this, SGroupElement), 1) .withIRInfo({ case (builder, obj, method, args, tparamSubst) => GroupGenerator }) .withInfo(GroupGenerator, "") - val xorMethod = SMethod(this, "xor", SFunc(IndexedSeq(this, SByteArray, SByteArray), SByteArray), 2) + lazy val xorMethod = SMethod(this, "xor", SFunc(IndexedSeq(this, SByteArray, SByteArray), SByteArray), 2) .withIRInfo({ case (_, _, _, Seq(l, r), _) => Xor(l.asByteArray, r.asByteArray) }) diff --git a/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala b/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala index d133495663..1d679c3b53 100644 --- a/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala +++ b/sigmastate/src/main/scala/sigmastate/utils/SigmaByteReader.scala @@ -17,9 +17,12 @@ class SigmaByteReader(val r: Reader, if (position > positionLimit) throw new InputSizeLimitExceeded(s"SigmaByteReader position limit $positionLimit is reached at position $position") - - val valDefTypeStore: ValDefTypeStore = new ValDefTypeStore() - + /** The reader should be lightweight to create. In most cases ErgoTrees don't have + * ValDef nodes hence the store is not necessary and it's initialization dominates the + * reader instantiation time. Hence it's lazy. + * @hotspot + */ + lazy val valDefTypeStore: ValDefTypeStore = new ValDefTypeStore() override type CH = r.CH diff --git a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala index f3d0546853..bed8d10335 100644 --- a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala +++ b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala @@ -43,7 +43,7 @@ case class Slice[IV <: SType](input: Value[SCollection[IV]], from: Value[SInt.ty override val tpe = input.tpe override def opType = { val tpeColl = SCollection(input.tpe.typeParams.head.ident) - SFunc(Vector(tpeColl, SInt, SInt), tpeColl) + SFunc(Array(tpeColl, SInt, SInt), tpeColl) } } object Slice extends ValueCompanion { @@ -106,7 +106,7 @@ object Fold extends ValueCompanion { def sum[T <: SNumericType](input: Value[SCollection[T]], varId: Int)(implicit tT: T) = Fold(input, Constant(tT.upcast(0.toByte), tT), - FuncValue(Vector((varId, STuple(tT, tT))), + FuncValue(Array((varId, STuple(tT, tT))), Plus( SelectField(ValUse(varId, STuple(tT, tT)), 1).asNumValue, SelectField(ValUse(varId, STuple(tT, tT)), 2).asNumValue)) @@ -143,7 +143,7 @@ object SelectField extends ValueCompanion { case class SigmaPropIsProven(input: Value[SSigmaProp.type]) extends Transformer[SSigmaProp.type, SBoolean.type] with NotReadyValueBoolean { override def companion = SigmaPropIsProven - override def opType = SFunc(input.tpe, SBoolean) + override val opType = SFunc(input.tpe, SBoolean) } object SigmaPropIsProven extends ValueCompanion { override def opCode: OpCode = OpCodes.SigmaPropIsProvenCode @@ -165,9 +165,10 @@ trait SimpleTransformerCompanion extends ValueCompanion { case class SizeOf[V <: SType](input: Value[SCollection[V]]) extends Transformer[SCollection[V], SInt.type] with NotReadyValueInt { override def companion = SizeOf - override val opType = SFunc(SCollection(SCollection.tIV), SInt) + override def opType = SizeOf.OpType } object SizeOf extends SimpleTransformerCompanion { + val OpType = SFunc(SCollection(SType.tIV), SInt) override def opCode: OpCode = OpCodes.SizeOfCode override def argInfos: Seq[ArgInfo] = SizeOfInfo.argInfos } @@ -177,45 +178,50 @@ sealed trait Extract[V <: SType] extends Transformer[SBox.type, V] { case class ExtractAmount(input: Value[SBox.type]) extends Extract[SLong.type] with NotReadyValueLong { override def companion = ExtractAmount - override val opType = SFunc(SBox, SLong) + override def opType = ExtractAmount.OpType } object ExtractAmount extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SLong) override def opCode: OpCode = OpCodes.ExtractAmountCode override def argInfos: Seq[ArgInfo] = ExtractAmountInfo.argInfos } case class ExtractScriptBytes(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractScriptBytes - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractScriptBytes.OpType } object ExtractScriptBytes extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractScriptBytesCode override def argInfos: Seq[ArgInfo] = ExtractScriptBytesInfo.argInfos } case class ExtractBytes(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractBytes - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractBytes.OpType } object ExtractBytes extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractBytesCode override def argInfos: Seq[ArgInfo] = ExtractBytesInfo.argInfos } case class ExtractBytesWithNoRef(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractBytesWithNoRef - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractBytesWithNoRef.OpType } object ExtractBytesWithNoRef extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractBytesWithNoRefCode override def argInfos: Seq[ArgInfo] = ExtractBytesWithNoRefInfo.argInfos } case class ExtractId(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray { override def companion = ExtractId - override val opType = SFunc(SBox, SByteArray) + override def opType = ExtractId.OpType } object ExtractId extends SimpleTransformerCompanion { + val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractIdCode override def argInfos: Seq[ArgInfo] = ExtractIdInfo.argInfos } @@ -225,7 +231,7 @@ case class ExtractRegisterAs[V <: SType]( input: Value[SBox.type], override val tpe: SOption[V]) extends Extract[SOption[V]] with NotReadyValue[SOption[V]] { override def companion = ExtractRegisterAs - override def opType = SFunc(Vector(SBox, SByte), tpe) + override val opType = SFunc(Array(SBox, SByte), tpe) } object ExtractRegisterAs extends ValueCompanion { override def opCode: OpCode = OpCodes.ExtractRegisterAs @@ -263,7 +269,7 @@ trait Deserialize[V <: SType] extends NotReadyValue[V] */ case class DeserializeContext[V <: SType](id: Byte, tpe: V) extends Deserialize[V] { override def companion = DeserializeContext - override val opType = SFunc(Vector(SContext, SByte), tpe) + override val opType = SFunc(Array(SContext, SByte), tpe) } object DeserializeContext extends ValueCompanion { override def opCode: OpCode = OpCodes.DeserializeContextCode @@ -274,7 +280,7 @@ object DeserializeContext extends ValueCompanion { */ case class DeserializeRegister[V <: SType](reg: RegisterId, tpe: V, default: Option[Value[V]] = None) extends Deserialize[V] { override def companion = DeserializeRegister - override val opType = SFunc(Vector(SBox, SByte, SOption(tpe)), tpe) + override val opType = SFunc(Array(SBox, SByte, SOption(tpe)), tpe) } object DeserializeRegister extends ValueCompanion { override def opCode: OpCode = OpCodes.DeserializeRegisterCode @@ -282,7 +288,7 @@ object DeserializeRegister extends ValueCompanion { case class GetVar[V <: SType](varId: Byte, override val tpe: SOption[V]) extends NotReadyValue[SOption[V]] { override def companion = GetVar - override val opType = SFunc(Vector(SContext, SByte), tpe) + override val opType = SFunc(Array(SContext, SByte), tpe) } object GetVar extends ValueCompanion { override def opCode: OpCode = OpCodes.GetVarCode diff --git a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala index fe4ecdf61a..50158c74b2 100644 --- a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -154,7 +154,7 @@ class ErgoTreeSpecification extends SigmaTestingData { (SUnit, true), (SFunc(SInt, SAny), false), (STypeApply("T"), false), - (STypeVar("T"), false) + (SType.tT, false) )) { (t, isConst) => t.isConstantSize shouldBe isConst } diff --git a/sigmastate/src/test/scala/sigmastate/TypesSpecification.scala b/sigmastate/src/test/scala/sigmastate/TypesSpecification.scala index 63dc2c126e..af72d49781 100644 --- a/sigmastate/src/test/scala/sigmastate/TypesSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/TypesSpecification.scala @@ -77,7 +77,7 @@ class TypesSpecification extends SigmaTestingData { assertExceptionThrown(check(NoType, null, 0), assertError) assertExceptionThrown(check(SAny, null, 0), assertError) assertExceptionThrown(check(STypeApply("T"), null, 0), assertError) - assertExceptionThrown(check(STypeVar("T"), null, 0), assertError) + assertExceptionThrown(check(SType.tT, null, 0), assertError) assertExceptionThrown(check(SGlobal, null, 0), assertError) assertExceptionThrown(check(SContext, null, 0), assertError) } diff --git a/sigmastate/src/test/scala/sigmastate/helpers/NegativeTesting.scala b/sigmastate/src/test/scala/sigmastate/helpers/NegativeTesting.scala new file mode 100644 index 0000000000..82f1a11f10 --- /dev/null +++ b/sigmastate/src/test/scala/sigmastate/helpers/NegativeTesting.scala @@ -0,0 +1,36 @@ +package sigmastate.helpers + +import org.scalatest.Matchers + +import scala.annotation.tailrec + +trait NegativeTesting extends Matchers { + + /** Checks that a [[Throwable]] is thrown and satisfies the given predicate. + * @param fun block of code to execute + * @param assertion expected assertion on the thrown exception + * @param clue added to the error message + */ + def assertExceptionThrown(fun: => Any, assertion: Throwable => Boolean, clue: => String = ""): Unit = { + try { + fun + fail("exception is expected") + } + catch { + case e: Throwable => + if (!assertion(e)) + fail( + s"""exception check failed on $e (root cause: ${rootCause(e)}) + |clue: $clue + |trace: + |${e.getStackTrace.mkString("\n")}}""".stripMargin) + } + } + + /** Returns the root cause of the chain of exceptions. */ + @tailrec + final def rootCause(t: Throwable): Throwable = + if (t.getCause == null) t + else rootCause(t.getCause) + +} diff --git a/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index 18bd3b3596..e41d3cedad 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -25,14 +25,13 @@ import sigmastate.utils.Helpers._ import sigmastate.helpers.TestingHelpers._ import special.sigma -import scala.annotation.tailrec import scala.language.implicitConversions trait SigmaTestingCommons extends PropSpec with PropertyChecks with GeneratorDrivenPropertyChecks - with Matchers - with TestUtils with TestContexts with ValidationSpecification { + with Matchers with TestUtils with TestContexts with ValidationSpecification + with NegativeTesting { val fakeSelf: ErgoBox = createBox(0, TrueProp) @@ -220,27 +219,6 @@ trait SigmaTestingCommons extends PropSpec CompiledFunc(funcScript, bindings.toSeq, funcVal, f) } - def assertExceptionThrown(fun: => Any, assertion: Throwable => Boolean, clue: => String = ""): Unit = { - try { - fun - fail("exception is expected") - } - catch { - case e: Throwable => - if (!assertion(e)) - fail( - s"""exception check failed on $e (root cause: ${rootCause(e)}) - |clue: $clue - |trace: - |${e.getStackTrace.mkString("\n")}}""".stripMargin) - } - } - - @tailrec - final def rootCause(t: Throwable): Throwable = - if (t.getCause == null) t - else rootCause(t.getCause) - protected def roundTripTest[T](v: T)(implicit serializer: SigmaSerializer[T, T]): Assertion = { // using default sigma reader/writer val bytes = serializer.toBytes(v) diff --git a/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala b/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala index fbac4b6e2e..634bb8f103 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/LangTests.scala @@ -1,7 +1,7 @@ package sigmastate.lang import sigmastate.lang.Terms.{MethodCallLike, Ident} -import sigmastate.Values.{LongConstant, SValue, Value, SigmaBoolean, GroupElementConstant, ConcreteCollection} +import sigmastate.Values.{LongConstant, SValue, Value, SigmaBoolean, ConcreteCollection} import sigmastate._ import java.math.BigInteger @@ -14,9 +14,10 @@ import sigmastate.interpreter.CryptoConstants import sigmastate.interpreter.Interpreter.ScriptEnv import special.sigma._ import sigmastate.eval._ +import sigmastate.helpers.NegativeTesting import special.collection.Coll -trait LangTests extends Matchers { +trait LangTests extends Matchers with NegativeTesting { def BoolIdent(name: String): Value[SBoolean.type] = Ident(name).asValue[SBoolean.type] def IntIdent(name: String): Value[SLong.type] = Ident(name).asValue[SLong.type] @@ -75,8 +76,8 @@ trait LangTests extends Matchers { def assertSrcCtxForAllNodes(tree: SValue): Unit = { import org.bitbucket.inkytonik.kiama.rewriting.Rewriter._ - rewrite(everywherebu(rule[SValue] { - case node => + rewrite(everywherebu(rule[Any] { + case node: SValue => withClue(s"Missing sourceContext for $node") { node.sourceContext.isDefined shouldBe true } node }))(tree) diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 3b646a6d51..a2f005ba0f 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -320,7 +320,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe property("SOption.map") { comp("getVar[Int](1).map({(i: Int) => i + 1})") shouldBe mkMethodCall(GetVarInt(1), - SOption.MapMethod.withConcreteTypes(Map(SOption.tT -> SInt, SOption.tR -> SInt)), + SOption.MapMethod.withConcreteTypes(Map(SType.tT -> SInt, SType.tR -> SInt)), IndexedSeq(FuncValue( Vector((1, SInt)), Plus(ValUse(1, SInt), IntConstant(1)))), Map() @@ -330,7 +330,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe property("SOption.filter") { comp("getVar[Int](1).filter({(i: Int) => i > 0})") shouldBe mkMethodCall(GetVarInt(1), - SOption.FilterMethod.withConcreteTypes(Map(SOption.tT -> SInt)), + SOption.FilterMethod.withConcreteTypes(Map(SType.tT -> SInt)), IndexedSeq(FuncValue( Vector((1, SInt)), GT(ValUse(1, SInt), IntConstant(0)))), Map() @@ -498,7 +498,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe "Coll(1, 2).mapReduce({ (i: Int) => (i > 0, i.toLong) }, { (tl: (Long, Long)) => tl._1 + tl._2 })") shouldBe mkMethodCall( ConcreteCollection.fromItems(IntConstant(1), IntConstant(2)), - SCollection.MapReduceMethod.withConcreteTypes(Map(SCollection.tIV -> SInt, SCollection.tK -> SBoolean, SCollection.tV -> SLong)), + SCollection.MapReduceMethod.withConcreteTypes(Map(SCollection.tIV -> SInt, SType.tK -> SBoolean, SType.tV -> SLong)), Vector( Lambda(List(), Vector(("i", SInt)), diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index 93cad502d1..00286d5f25 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -14,6 +14,7 @@ import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.lang.SigmaPredef._ import sigmastate.lang.Terms.Select import sigmastate.lang.exceptions.TyperException +import sigmastate.lang.syntax.ParserException import sigmastate.serialization.ErgoTreeSerializer import sigmastate.serialization.generators.ObjectGenerators import sigmastate.utxo.{Append, ExtractCreationInfo} @@ -42,16 +43,23 @@ class SigmaTyperTest extends PropSpec with PropertyChecks with Matchers with Lan def typefail(env: ScriptEnv, x: String, expectedLine: Int, expectedCol: Int): Unit = { val builder = TransformingSigmaBuilder - val parsed = SigmaParser(x, builder).get.value - val predefinedFuncRegistry = new PredefinedFuncRegistry(builder) - val binder = new SigmaBinder(env, builder, TestnetNetworkPrefix, predefinedFuncRegistry) - val bound = binder.bind(parsed) - val typer = new SigmaTyper(builder, predefinedFuncRegistry) - val exception = the[TyperException] thrownBy typer.typecheck(bound) - withClue(s"Exception: $exception, is missing source context:") { exception.source shouldBe defined } - val sourceContext = exception.source.get - sourceContext.line shouldBe expectedLine - sourceContext.column shouldBe expectedCol + assertExceptionThrown({ + val parsed = SigmaParser(x, builder).get.value + val predefinedFuncRegistry = new PredefinedFuncRegistry(builder) + val binder = new SigmaBinder(env, builder, TestnetNetworkPrefix, predefinedFuncRegistry) + val bound = binder.bind(parsed) + val typer = new SigmaTyper(builder, predefinedFuncRegistry) + typer.typecheck(bound) + }, { + case te: TyperException => + withClue(s"Exception: $te, is missing source context:") { te.source shouldBe defined } + val sourceContext = te.source.get + sourceContext.line shouldBe expectedLine + sourceContext.column shouldBe expectedCol + true + case pe: ParserException => true + case t => throw t + }) } property("simple expressions") { diff --git a/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala b/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala index 0263b04399..0b5e1b27e4 100644 --- a/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala +++ b/sigmastate/src/test/scala/sigmastate/utils/SpecGen.scala @@ -22,7 +22,7 @@ object SpecGenUtils { trait SpecGen { import SpecGenUtils._ - val tT = STypeVar("T") + import SType.tT case class OpInfo( opDesc: ValueCompanion, diff --git a/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 02929e5a3d..5af6798676 100644 --- a/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -358,7 +358,7 @@ class BasicOpsSpecification extends SigmaTestingCommons { BoolToSigmaProp( EQ( MethodCall(Self, SBox.getRegMethod, - IndexedSeq(Plus(GetVarInt(1).get, IntConstant(4))), Map(SBox.tT -> SInt) + IndexedSeq(Plus(GetVarInt(1).get, IntConstant(4))), Map(SType.tT -> SInt) ).asInstanceOf[Value[SOption[SType]]].get, IntConstant(1) )