diff --git a/README.md b/README.md index 64f08f0277..6003daf55a 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,10 @@ For development of Ergo applications using JVM languages (Java/Scala/Kotlin/etc) a better alternative is to use [Appkit](https://github.com/ergoplatform/ergo-appkit). +The library is cross-compiled to JS using Scala.js and the main abstractions can be used +from JS directly by importing [NPM module](https://www.npmjs.com/package/sigmastate-js). +See [README](sigma-js/README.md) for details. + ## Sigma Language Background Every coin in Bitcoin is protected by a program in the stack-based Script @@ -93,19 +97,21 @@ This library is on Maven repository and can be added to the SBT configuration of Scala project. ```scala -libraryDependencies += "org.scorexfoundation" %% "sigma-state" % "5.0.6" +libraryDependencies += "org.scorexfoundation" %% "sigma-state" % "5.0.14" ``` ## Repository Organization -| sub-module | description | -|-------------|-------------------------------------------------------------------------------------------| -| common | Used in all other submodules and contain basic utility classes | -| core-lib | Contains core classes such as Coll, BigInt used by interpreter | -| docs | Collection of documents | -| graph-ir | Implementation of graph-based intermediate representation of ErgoTree, which is used in by ErgoScript compiler | -| interpreter | Implementation of ErgoTree Interpreter | -| sc | Implementation of ErgoScript compiler | +| sub-module | description | +|-------------|------------------------------------------------------------------------------------| +| core | contains core classes of Sigma library | +| data | contains classes for working with ErgoTree, addresses and all related serializers | +| docs | Collection of documents | +| interpreter | contains an implementation of ErgoTree Interpreter | +| sdk | contains and implementation of transaction reduction and signing | +| parsers | contains an implementation of ErgoScript parsers using FastParse library | +| sc | contains an implementation of ErgoScript compiler | +| sigma-js | root directory of sigmastate-js JS module (see [package.json](sigma-js/README.md)) | ## Contributing @@ -151,12 +157,13 @@ innovative and intelligent tools for profiling Java and .NET applications. - [Ergo Site](https://ergoplatform.org/en/) - [Ergo Sources](https://github.com/ergoplatform/ergo) +- [Sigma-js](https://www.npmjs.com/package/sigmastate-js) - [Ergo Appkit](https://github.com/ergoplatform/ergo-appkit) - [Ergo Appkit Examples](https://github.com/aslesarenko/ergo-appkit-examples) - [ergo-android](https://github.com/aslesarenko/ergo-android) - [ergo-wallet-android](https://github.com/MrStahlfelge/ergo-wallet-android) - [ErgoTree Specification](https://ergoplatform.org/docs/ErgoTree.pdf) -- [Ergo Documents](https://ergoplatform.org/en/documents/) +- [Ergo Documents](https://docs.ergoplatform.org/) diff --git a/build.sbt b/build.sbt index a76c73d885..85c59512d6 100644 --- a/build.sbt +++ b/build.sbt @@ -1,6 +1,6 @@ import scala.language.postfixOps -import scala.sys.process._ -import org.scalajs.linker.interface.CheckedBehavior +import scala.sys.process.* +import org.scalajs.linker.interface.{CheckedBehavior, ModuleSplitStyle} organization := "org.scorexfoundation" @@ -196,7 +196,12 @@ lazy val core = crossProject(JVMPlatform, JSPlatform) scorexUtilDependency, publish / skip := true ) - .jvmSettings( crossScalaSettings ) + .jvmSettings( + crossScalaSettings, + libraryDependencies ++= Seq( + bouncycastleBcprov + ) + ) .jsSettings( crossScalaSettingsJS, scalacOptions ++= Seq( @@ -212,10 +217,40 @@ lazy val core = crossProject(JVMPlatform, JSPlatform) ) lazy val coreJS = core.js .enablePlugins(ScalaJSBundlerPlugin) + .enablePlugins(ScalablyTypedConverterGenSourcePlugin) + .settings( + stOutputPackage := "sigmastate", + scalaJSLinkerConfig ~= { conf => + conf.withSourceMap(false) + .withModuleKind(ModuleKind.CommonJSModule) + }, + Compile / npmDependencies ++= Seq( + "sigmajs-crypto-facade" -> sigmajsCryptoFacadeVersion, + "@fleet-sdk/common" -> "0.1.3" + ) + ) + +lazy val data = crossProject(JVMPlatform, JSPlatform) + .in(file("data")) + .dependsOn(core % allConfigDependency) + .settings( + commonSettings ++ testSettings2, + commonDependenies2, + testingDependencies2, + scorexUtilDependency, fastparseDependency, circeDependency, scryptoDependency, + publish / skip := true + ) + .jvmSettings( crossScalaSettings ) + .jsSettings( + crossScalaSettingsJS, + useYarn := true + ) +lazy val dataJS = data.js + .enablePlugins(ScalaJSBundlerPlugin) lazy val interpreter = crossProject(JVMPlatform, JSPlatform) .in(file("interpreter")) - .dependsOn(core % allConfigDependency) + .dependsOn(core % allConfigDependency, data % allConfigDependency) .settings( commonSettings ++ testSettings2, commonDependenies2, @@ -226,24 +261,10 @@ lazy val interpreter = crossProject(JVMPlatform, JSPlatform) .jvmSettings( crossScalaSettings ) .jsSettings( crossScalaSettingsJS, - libraryDependencies ++= Seq ( - "org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0" - ), useYarn := true ) lazy val interpreterJS = interpreter.js .enablePlugins(ScalaJSBundlerPlugin) - .enablePlugins(ScalablyTypedConverterGenSourcePlugin) - .settings( - stOutputPackage := "sigmastate", - scalaJSLinkerConfig ~= { conf => - conf.withSourceMap(false) - }, - Compile / npmDependencies ++= Seq( - "sigmajs-crypto-facade" -> sigmajsCryptoFacadeVersion, - "@fleet-sdk/common" -> "0.1.3" - ) - ) lazy val parsers = crossProject(JVMPlatform, JSPlatform) .in(file("parsers")) @@ -260,9 +281,6 @@ lazy val parsers = crossProject(JVMPlatform, JSPlatform) ) .jsSettings( crossScalaSettingsJS, - libraryDependencies ++= Seq( - "org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0" - ), useYarn := true ) lazy val parsersJS = parsers.js @@ -271,18 +289,16 @@ lazy val parsersJS = parsers.js scalaJSLinkerConfig ~= { conf => conf.withSourceMap(false) }, - Compile / npmDependencies ++= Seq( - "sigmajs-crypto-facade" -> sigmajsCryptoFacadeVersion - ) ) lazy val sdk = crossProject(JVMPlatform, JSPlatform) .in(file("sdk")) - .dependsOn(core % allConfigDependency, interpreter % allConfigDependency, parsers % allConfigDependency) + .dependsOn(core % allConfigDependency, data % allConfigDependency, interpreter % allConfigDependency, parsers % allConfigDependency) .settings(commonSettings ++ testSettings2, commonDependenies2, testingDependencies2, scodecBitsDependency, + circeDependency, publish / skip := true ) .jvmSettings( @@ -290,9 +306,6 @@ lazy val sdk = crossProject(JVMPlatform, JSPlatform) ) .jsSettings( crossScalaSettingsJS, - libraryDependencies ++= Seq( - "org.scala-js" %%% "scala-js-macrotask-executor" % "1.0.0" - ), useYarn := true ) lazy val sdkJS = sdk.js @@ -302,9 +315,6 @@ lazy val sdkJS = sdk.js conf.withSourceMap(false) .withModuleKind(ModuleKind.CommonJSModule) }, - Compile / npmDependencies ++= Seq( - "sigmajs-crypto-facade" -> sigmajsCryptoFacadeVersion - ) ) lazy val sc = crossProject(JVMPlatform, JSPlatform) @@ -348,20 +358,17 @@ lazy val scJS = sc.js .withArrayIndexOutOfBounds(CheckedBehavior.Compliant) ) }, - Compile / npmDependencies ++= Seq( - "sigmajs-crypto-facade" -> sigmajsCryptoFacadeVersion - ) ) lazy val sigma = (project in file(".")) - .aggregate(core.jvm, interpreter.jvm, parsers.jvm, sdk.jvm, sc.jvm) + .aggregate(core.jvm, data.jvm, interpreter.jvm, parsers.jvm, sdk.jvm, sc.jvm) .settings(libraryDefSettings, rootSettings) .settings(publish / aggregate := false) .settings(publishLocal / aggregate := false) lazy val aggregateCompile = ScopeFilter( - inProjects(core.jvm, interpreter.jvm, parsers.jvm, sdk.jvm, sc.jvm), + inProjects(core.jvm, data.jvm, interpreter.jvm, parsers.jvm, sdk.jvm, sc.jvm), inConfigurations(Compile)) lazy val rootSettings = Seq( diff --git a/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala b/core/js/src/main/scala/sigma/crypto/Platform.scala similarity index 96% rename from interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala rename to core/js/src/main/scala/sigma/crypto/Platform.scala index fdb3b128fd..88001ba140 100644 --- a/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala +++ b/core/js/src/main/scala/sigma/crypto/Platform.scala @@ -1,10 +1,10 @@ -package sigmastate.crypto +package sigma.crypto import sigma.data.RType import scorex.util.encode.Base16 -import sigmastate._ import sigma.Coll import sigma._ +import sigma.ast._ import java.math.BigInteger import scala.scalajs.js @@ -128,7 +128,7 @@ object Platform { class Curve // TODO JS: Use JS library for secure source of randomness - type SecureRandom = sigmastate.crypto.SecureRandomJS + type SecureRandom = sigma.crypto.SecureRandomJS /** Opaque point type. */ @js.native @@ -184,28 +184,28 @@ object Platform { private val ctx = new CryptoContextJs /** The underlying elliptic curve descriptor. */ - override def curve: crypto.Curve = ??? + override def curve: Curve = ??? override def fieldCharacteristic: BigInteger = Convert.bigIntToBigInteger(ctx.getModulus()) override def order: BigInteger = Convert.bigIntToBigInteger(ctx.getOrder()) - override def validatePoint(x: BigInteger, y: BigInteger): crypto.Ecp = { + override def validatePoint(x: BigInteger, y: BigInteger): Ecp = { val point = ctx.validatePoint(Convert.bigIntegerToBigInt(x), Convert.bigIntegerToBigInt(y)) new Ecp(point) } - override def infinity(): crypto.Ecp = + override def infinity(): Ecp = new Ecp(ctx.getInfinity()) - override def decodePoint(encoded: Array[Byte]): crypto.Ecp = { + override def decodePoint(encoded: Array[Byte]): Ecp = { if (encoded(0) == 0) { return infinity() } new Ecp(ctx.decodePoint(Base16.encode(encoded))) } - override def generator: crypto.Ecp = + override def generator: Ecp = new Ecp(ctx.getGenerator()) } diff --git a/interpreter/js/src/main/scala/sigmastate/crypto/SigmaJsCryptoFacade.scala b/core/js/src/main/scala/sigma/crypto/SigmaJsCryptoFacade.scala similarity index 99% rename from interpreter/js/src/main/scala/sigmastate/crypto/SigmaJsCryptoFacade.scala rename to core/js/src/main/scala/sigma/crypto/SigmaJsCryptoFacade.scala index fa60015ed2..4d269534cc 100644 --- a/interpreter/js/src/main/scala/sigmastate/crypto/SigmaJsCryptoFacade.scala +++ b/core/js/src/main/scala/sigma/crypto/SigmaJsCryptoFacade.scala @@ -1,4 +1,4 @@ -package sigmastate.crypto +package sigma.crypto import debox.cfor diff --git a/core/js/src/main/scala/sigma/js/AvlTree.scala b/core/js/src/main/scala/sigma/js/AvlTree.scala new file mode 100644 index 0000000000..f882ef0863 --- /dev/null +++ b/core/js/src/main/scala/sigma/js/AvlTree.scala @@ -0,0 +1,50 @@ +package sigma.js + +import sigma.Extensions.ArrayOps +import sigma.data.Iso.{isoStringToArray, isoStringToColl} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, Iso} + +import scala.scalajs.js +import scala.scalajs.js.UndefOr +import scala.scalajs.js.annotation.JSExportTopLevel + +/** Equivalent of [[sigma.AvlTree]] available from JS. */ +@JSExportTopLevel("AvlTree") +class AvlTree( + val digest: String, + val insertAllowed: Boolean, + val updateAllowed: Boolean, + val removeAllowed: Boolean, + val keyLength: Int, + val valueLengthOpt: UndefOr[Int] +) extends js.Object + +object AvlTree { + + implicit val isoAvlTree: Iso[AvlTree, sigma.AvlTree] = new Iso[AvlTree, sigma.AvlTree] { + override def to(x: AvlTree): sigma.AvlTree = { + CAvlTree( + AvlTreeData( + digest = isoStringToArray.to(x.digest).toColl, + treeFlags = AvlTreeFlags(x.insertAllowed, x.updateAllowed, x.removeAllowed), + x.keyLength, + valueLengthOpt = sigma.js.Isos.isoUndefOr(Iso.identityIso[Int]).to(x.valueLengthOpt), + ), + ) + } + + override def from(x: sigma.AvlTree): AvlTree = { + val tree = x.asInstanceOf[CAvlTree] + val data = tree.treeData + new AvlTree( + digest = isoStringToColl.from(tree.digest), + insertAllowed = data.treeFlags.insertAllowed, + updateAllowed = data.treeFlags.updateAllowed, + removeAllowed = data.treeFlags.removeAllowed, + keyLength = data.keyLength, + valueLengthOpt = sigma.js.Isos.isoUndefOr(Iso.identityIso[Int]).from(data.valueLengthOpt), + ) + } + } + +} \ No newline at end of file diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/GroupElement.scala b/core/js/src/main/scala/sigma/js/GroupElement.scala similarity index 81% rename from sdk/js/src/main/scala/org/ergoplatform/sdk/js/GroupElement.scala rename to core/js/src/main/scala/sigma/js/GroupElement.scala index 857592d4a2..21b53b265d 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/GroupElement.scala +++ b/core/js/src/main/scala/sigma/js/GroupElement.scala @@ -1,7 +1,7 @@ -package org.ergoplatform.sdk.js +package sigma.js -import sigmastate.crypto.{CryptoFacade, CryptoFacadeJs, Ecp, Platform} -import sigmastate.eval.Extensions.ArrayByteOps +import sigma.Extensions.CoreArrayByteOps +import sigma.crypto.{CryptoFacade, CryptoFacadeJs, Ecp, Platform} import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel @@ -17,7 +17,7 @@ class GroupElement(val point: Ecp) extends js.Object { } } -@JSExportTopLevel("GroupElementObj") +@JSExportTopLevel("GroupElement$") object GroupElement extends js.Object { /** Creates a new [[GroupElement]] from the given hex string (ASN.1 encoding) * representation of the underlying [[sigmastate.crypto.Platform.Point]]. diff --git a/core/js/src/main/scala/sigma/js/Isos.scala b/core/js/src/main/scala/sigma/js/Isos.scala new file mode 100644 index 0000000000..b28a3337b8 --- /dev/null +++ b/core/js/src/main/scala/sigma/js/Isos.scala @@ -0,0 +1,47 @@ +package sigma.js + +import sigma.{Coll, Colls} +import sigma.data.{CBigInt, Iso, RType} + +import java.math.BigInteger +import scala.reflect.ClassTag +import scala.scalajs.js +import scala.scalajs.js.JSConverters.JSRichOption + +object Isos { + + implicit def isoUndefOr[A, B](implicit iso: Iso[A, B]): Iso[js.UndefOr[A], Option[B]] = new Iso[js.UndefOr[A], Option[B]] { + override def to(x: js.UndefOr[A]): Option[B] = x.toOption.map(iso.to) + override def from(x: Option[B]): js.UndefOr[A] = x.map(iso.from).orUndefined + } + + implicit def isoArrayToColl[A, B](iso: Iso[A, B]) + (implicit ctA: ClassTag[A], tB: RType[B]): Iso[js.Array[A], Coll[B]] = new Iso[js.Array[A], Coll[B]] { + override def to(x: js.Array[A]): Coll[B] = Colls.fromArray(x.map(iso.to).toArray(tB.classTag)) + override def from(x: Coll[B]): js.Array[A] = js.Array(x.toArray.map(iso.from): _*) + } + + implicit def isoArrayToIndexed[A, B](iso: Iso[A, B]) + (implicit cB: ClassTag[B]): Iso[js.Array[A], IndexedSeq[B]] = new Iso[js.Array[A], IndexedSeq[B]] { + override def to(x: js.Array[A]): IndexedSeq[B] = x.map(iso.to).toArray(cB).toIndexedSeq + override def from(x: IndexedSeq[B]): js.Array[A] = js.Array(x.map(iso.from): _*) + } + + implicit val isoBigInt: Iso[js.BigInt, sigma.BigInt] = new Iso[js.BigInt, sigma.BigInt] { + override def to(x: js.BigInt): sigma.BigInt = { + CBigInt(new BigInteger(x.toString(10))) + } + + override def from(x: sigma.BigInt): js.BigInt = { + val bi = x.asInstanceOf[CBigInt].wrappedValue + val s = bi.toString(10) + js.BigInt(s) + } + } + + implicit val isoBigIntToLong: Iso[js.BigInt, Long] = new Iso[js.BigInt, Long] { + override def to(x: js.BigInt): Long = java.lang.Long.parseLong(x.toString(10)) + + override def from(x: Long): js.BigInt = js.BigInt(x.toString) + } +} diff --git a/core/js/src/main/scala/sigma/js/JsWrapper.scala b/core/js/src/main/scala/sigma/js/JsWrapper.scala new file mode 100644 index 0000000000..e62cb70c11 --- /dev/null +++ b/core/js/src/main/scala/sigma/js/JsWrapper.scala @@ -0,0 +1,8 @@ +package sigma.js + +import scala.scalajs.js + +/** Base trait for all JS wrappers over some Scala type T. */ +trait JsWrapper[T] extends js.Object { + def wrappedValue: T +} diff --git a/core/js/src/main/scala/sigma/js/SigmaProp.scala b/core/js/src/main/scala/sigma/js/SigmaProp.scala new file mode 100644 index 0000000000..40d311717d --- /dev/null +++ b/core/js/src/main/scala/sigma/js/SigmaProp.scala @@ -0,0 +1,35 @@ +package sigma.js + +import sigma.data.{ProveDHTuple, ProveDlog, SigmaBoolean} + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel + +/** Equivalent of [[sigma.SigmaProp]] available from JS. */ +@JSExportTopLevel("SigmaProp") +class SigmaProp(val sigmaBoolean: SigmaBoolean) extends js.Object { +} + +@JSExportTopLevel("SigmaProp$") +object SigmaProp extends js.Object { + /** Creates a new [[SigmaProp]] from the given hex string of public key. + * @param pointHex hex representation of elliptic curve point (ASN.1 encoded) + * @see CryptoFacade.getASN1Encoding, GroupElement.fromPointHex, Point + */ + def fromPointHex(pointHex: String): SigmaProp = { + val ge = GroupElement.fromPointHex(pointHex) + dlog(ge) + } + + /** @param publicKey a [[GroupElement]] representing public key of discrete logarithm signature protocol + * @return a new [[SigmaProp]] value representing public key of discrete logarithm signature protocol. + */ + def dlog(publicKey: GroupElement): SigmaProp = { + new SigmaProp(ProveDlog(publicKey.point)) + } + + /** Construct a new [[SigmaProp]] value representing public key of Diffie Hellman signature protocol. */ + def dht(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement): SigmaProp = { + new SigmaProp(ProveDHTuple(g.point, h.point, u.point, v.point)) + } +} diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Type.scala b/core/js/src/main/scala/sigma/js/Type.scala similarity index 83% rename from sdk/js/src/main/scala/org/ergoplatform/sdk/js/Type.scala rename to core/js/src/main/scala/sigma/js/Type.scala index a0930cc99a..b323273a0c 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Type.scala +++ b/core/js/src/main/scala/sigma/js/Type.scala @@ -1,6 +1,7 @@ -package org.ergoplatform.sdk.js +package sigma.js -import sigma.data.RType +import sigma.Evaluation +import sigma.data.{Iso, RType} import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel @@ -10,14 +11,14 @@ import scala.scalajs.js.annotation.JSExportTopLevel * wrapper around {@link RType} type descriptor. */ @JSExportTopLevel("Type") -class Type(private[js] final val rtype: RType[_]) extends js.Object { +class Type(final val rtype: RType[_]) extends js.Object { /** Syntactically correct type name (type expression as String) */ def name: String = rtype.name override def toString = s"Type($rtype)" } -@JSExportTopLevel("TypeObj") +@JSExportTopLevel("Type$") object Type extends js.Object { /** Descriptor of ErgoScript type Byte. */ val Byte = new Type(sigma.ByteType) @@ -75,4 +76,9 @@ object Type extends js.Object { def collType(elemType: Type): Type = { new Type(sigma.collRType(elemType.rtype)) } + + implicit val isoToSType: Iso[Type, sigma.ast.SType] = new Iso[Type, sigma.ast.SType] { + override def to(x: Type): sigma.ast.SType = Evaluation.rtypeToSType(x.rtype) + override def from(x: sigma.ast.SType): Type = new Type(Evaluation.stypeToRType(x)) + } } \ No newline at end of file diff --git a/core/js/src/main/scala/sigma/js/Utils.scala b/core/js/src/main/scala/sigma/js/Utils.scala new file mode 100644 index 0000000000..d19ef68d92 --- /dev/null +++ b/core/js/src/main/scala/sigma/js/Utils.scala @@ -0,0 +1,15 @@ +package sigma.js + +import scorex.util.encode.Base16 + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array + +@JSExportTopLevel("Utils") +object Utils extends js.Object { + /** Convert an Int8Array to a hex string. */ + def int8ArrayToHex(arr: Int8Array): String = { + Base16.encode(arr.toArray) + } +} diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Value.scala b/core/js/src/main/scala/sigma/js/Value.scala similarity index 79% rename from sdk/js/src/main/scala/org/ergoplatform/sdk/js/Value.scala rename to core/js/src/main/scala/sigma/js/Value.scala index e3a3d15b9a..13b63c2abc 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Value.scala +++ b/core/js/src/main/scala/sigma/js/Value.scala @@ -1,19 +1,15 @@ -package org.ergoplatform.sdk.js +package sigma +package js -import org.ergoplatform.sdk.js.Value.toRuntimeData -import sigma.data.{CollType, RType} -import sigma.data.PairType import scorex.util.Extensions.{IntOps, LongOps} import scorex.util.encode.Base16 -import sigmastate.SType -import sigmastate.crypto.Platform -import sigmastate.eval.{CAvlTree, CGroupElement, CSigmaProp, CostingBox, Evaluation, SigmaDsl} -import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box -import sigmastate.fleetSdkCommon.distEsmTypesCommonMod -import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters -import sigmastate.lang.DeserializationSigmaBuilder -import sigmastate.serialization.{ConstantSerializer, DataSerializer, SigmaSerializer} -import sigma.{Coll, Colls} +import sigma.ast.SType +import sigma.crypto.Platform +import sigma.data._ +import sigma.js.Value.toRuntimeData +import sigma.serialization.{CoreDataSerializer, CoreSerializer} +import sigma.util.Extensions.BigIntOps +import sigma.{Coll, Colls, Evaluation} import java.math.BigInteger import scala.scalajs.js @@ -44,33 +40,29 @@ import scala.scalajs.js.annotation.JSExportTopLevel class Value(val data: Any, val tpe: Type) extends js.Object { /** Get Sigma runtime value which can be passed to interpreter, saved in register and - * [[sigmastate.Values.Constant]] nodes. + * [[sigma.ast.Constant]] nodes. */ - final private[js] def runtimeData: Any = toRuntimeData(data, tpe.rtype) + final def runtimeData: Any = toRuntimeData(data, tpe.rtype) /** * Encode this value as Base16 hex string. - * 1) it transforms this value into {@link sigmastate.Values.ConstantNode} of sigma. + * 1) it transforms this value into {@link sigma.ast.ConstantNode} of sigma. * 2) it serializes the constant into byte array using {@link sigmastate.serialization.ConstantSerializer} * 3) the bytes are encoded using Base16 encoder into string * * @return hex string of serialized bytes */ def toHex(): String = { - // this can be implemented using ConstantSerializer and isoValueToConstant, but this - // will add dependence on Constant and Values, which we want to avoid facilitate - // module splitting - // TODO simplify if module splitting fails val stype = Evaluation.rtypeToSType(tpe.rtype) val value = runtimeData.asInstanceOf[SType#WrappedType] - val w = SigmaSerializer.startWriter() + val w = CoreSerializer.startWriter() w.putType(stype) - DataSerializer.serialize(value, stype, w) + CoreDataSerializer.serialize(value, stype, w) Base16.encode(w.toBytes) } } -@JSExportTopLevel("ValueObj") +@JSExportTopLevel("Value$") object Value extends js.Object { /** Maximal positive value of ES type Long */ val MaxLong = js.BigInt("0x7fffffffffffffff") @@ -79,7 +71,7 @@ object Value extends js.Object { val MinLong = -js.BigInt("0x8000000000000000") /** Helper method to get Sigma runtime value which can be passed to interpreter, saved - * in register and [[sigmastate.Values.Constant]] nodes. + * in register and [[sigma.ast.Constant]] nodes. */ final private[js] def toRuntimeData(data: Any, rtype: RType[_]): Any = rtype match { case sigma.BooleanType => data @@ -87,19 +79,16 @@ object Value extends js.Object { case sigma.LongType => java.lang.Long.parseLong(data.asInstanceOf[js.BigInt].toString(10)) case sigma.BigIntRType => val v = data.asInstanceOf[js.BigInt] - SigmaDsl.BigInt(new BigInteger(v.toString(16), 16)) + CBigInt(new BigInteger(v.toString(16), 16)) case sigma.GroupElementRType => val ge = data.asInstanceOf[GroupElement] - SigmaDsl.GroupElement(ge.point) + CGroupElement(ge.point) case sigma.SigmaPropRType => val p = data.asInstanceOf[SigmaProp] - SigmaDsl.SigmaProp(p.sigmaBoolean) + CSigmaProp(p.sigmaBoolean) case sigma.AvlTreeRType => val t = data.asInstanceOf[AvlTree] - Isos.isoAvlTree.to(t) - case sigma.BoxRType => - val t = data.asInstanceOf[Box[distEsmTypesCommonMod.Amount, NonMandatoryRegisters]] - SigmaDsl.Box(Isos.isoBox.to(t)) + AvlTree.isoAvlTree.to(t) case ct: CollType[a] => val xs = data.asInstanceOf[js.Array[Any]] implicit val cT = ct.tItem.classTag @@ -121,12 +110,12 @@ object Value extends js.Object { * @param value runtime value of type given by `rtype` * @param rtype type descriptor of Sigma runtime value */ - final private[js] def fromRuntimeData(value: Any, rtype: RType[_]): Any = rtype match { + final def fromRuntimeData(value: Any, rtype: RType[_]): Any = rtype match { case sigma.BooleanType => value case sigma.ByteType | sigma.ShortType | sigma.IntType => value case sigma.LongType => js.BigInt(value.asInstanceOf[Long].toString) case sigma.BigIntRType => - val hex = SigmaDsl.toBigInteger(value.asInstanceOf[sigma.BigInt]).toString(10) + val hex = value.asInstanceOf[sigma.BigInt].toBigInteger.toString(10) js.BigInt(hex) case sigma.GroupElementRType => val point = value.asInstanceOf[CGroupElement].wrappedValue.asInstanceOf[Platform.Ecp] @@ -134,9 +123,7 @@ object Value extends js.Object { case sigma.SigmaPropRType => new SigmaProp(value.asInstanceOf[CSigmaProp].wrappedValue) case sigma.AvlTreeRType => - Isos.isoAvlTree.from(value.asInstanceOf[CAvlTree]) - case sigma.BoxRType => - Isos.isoBox.from(value.asInstanceOf[CostingBox].wrappedValue) + AvlTree.isoAvlTree.from(value.asInstanceOf[CAvlTree]) case ct: CollType[a] => val arr = value.asInstanceOf[Coll[a]].toArray js.Array(arr.map(x => fromRuntimeData(x, ct.tItem)):_*) @@ -263,9 +250,12 @@ object Value extends js.Object { * - and [[Type]] descriptor in its `tpe` field */ def fromHex(hex: String): Value = { - val bytes = Base16.decode(hex).fold(t => throw t, identity) - val S = ConstantSerializer(DeserializationSigmaBuilder) - val c = S.deserialize(SigmaSerializer.startReader(bytes)) - Isos.isoValueToConstant.from(c) + val bytes = Base16.decode(hex).fold(t => throw t, identity) + val r = CoreSerializer.startReader(bytes) + val stype = r.getType() + val value = CoreDataSerializer.deserialize(stype, r) + val rtype = Evaluation.stypeToRType(stype) + val jsvalue = Value.fromRuntimeData(value, rtype) + new Value(jsvalue, new Type(rtype)) } } diff --git a/interpreter/jvm/src/main/scala/sigmastate/crypto/CryptoContextJvm.scala b/core/jvm/src/main/scala/sigma/crypto/CryptoContextJvm.scala similarity index 97% rename from interpreter/jvm/src/main/scala/sigmastate/crypto/CryptoContextJvm.scala rename to core/jvm/src/main/scala/sigma/crypto/CryptoContextJvm.scala index 7b789f1db6..939e43619e 100644 --- a/interpreter/jvm/src/main/scala/sigmastate/crypto/CryptoContextJvm.scala +++ b/core/jvm/src/main/scala/sigma/crypto/CryptoContextJvm.scala @@ -1,4 +1,4 @@ -package sigmastate.crypto +package sigma.crypto import org.bouncycastle.asn1.x9.X9ECParameters diff --git a/interpreter/jvm/src/main/scala/sigmastate/crypto/HmacSHA512.scala b/core/jvm/src/main/scala/sigma/crypto/HmacSHA512.scala similarity index 96% rename from interpreter/jvm/src/main/scala/sigmastate/crypto/HmacSHA512.scala rename to core/jvm/src/main/scala/sigma/crypto/HmacSHA512.scala index 237ca9e176..c0c699921d 100644 --- a/interpreter/jvm/src/main/scala/sigmastate/crypto/HmacSHA512.scala +++ b/core/jvm/src/main/scala/sigma/crypto/HmacSHA512.scala @@ -1,4 +1,4 @@ -package sigmastate.crypto +package sigma.crypto import javax.crypto.Mac import javax.crypto.spec.SecretKeySpec diff --git a/interpreter/jvm/src/main/scala/sigmastate/crypto/Platform.scala b/core/jvm/src/main/scala/sigma/crypto/Platform.scala similarity index 96% rename from interpreter/jvm/src/main/scala/sigmastate/crypto/Platform.scala rename to core/jvm/src/main/scala/sigma/crypto/Platform.scala index 1c5c63b197..b71694e81b 100644 --- a/interpreter/jvm/src/main/scala/sigmastate/crypto/Platform.scala +++ b/core/jvm/src/main/scala/sigma/crypto/Platform.scala @@ -1,4 +1,4 @@ -package sigmastate.crypto +package sigma.crypto import org.bouncycastle.crypto.digests.SHA512Digest import org.bouncycastle.crypto.ec.CustomNamedCurves @@ -7,9 +7,9 @@ import org.bouncycastle.crypto.params.KeyParameter import org.bouncycastle.math.ec.{ECCurve, ECFieldElement, ECPoint} import java.math.BigInteger -import sigmastate._ import sigma.Coll import sigma._ +import sigma.ast._ import java.text.Normalizer.Form.NFKD import java.text.Normalizer @@ -206,8 +206,4 @@ object Platform { def multiply(n: BigInteger): Ecp = CryptoFacade.exponentiatePoint(p, n) } - /** This JVM specific methods are used in Ergo node which won't be JS cross-compiled. */ - implicit class BcDlogGroupOps(val group: BcDlogGroup) extends AnyVal { - def curve: Curve = group.ctx.asInstanceOf[CryptoContextJvm].curve - } } diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/Evaluation.scala b/core/shared/src/main/scala/sigma/Evaluation.scala similarity index 56% rename from interpreter/shared/src/main/scala/sigmastate/eval/Evaluation.scala rename to core/shared/src/main/scala/sigma/Evaluation.scala index 8771fe2b32..d86b7c1650 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/Evaluation.scala +++ b/core/shared/src/main/scala/sigma/Evaluation.scala @@ -1,48 +1,16 @@ -package sigmastate.eval +package sigma -import org.ergoplatform._ -import sigma.data._ -import sigma.data.RType._ -import sigmastate.SType._ -import sigmastate.Values.SigmaBoolean -import sigmastate._ import debox.cfor -import sigmastate.exceptions.CostLimitException +import sigma.ast.SType +import sigma.data.RType._ +import sigma.data._ +import sigma.ast._ -import scala.reflect.ClassTag -import scala.util.Try // TODO refactor: find better place for this methods after code cleanup and repo reorganization /** Helper methods used as part of ErgoTree evaluation. */ object Evaluation { - import sigma._ - import sigma._ - - def msgCostLimitError(cost: Long, limit: Long) = s"Estimated execution cost $cost exceeds the limit $limit" - - /** Helper method to accumulate cost while checking limit. - * - * @param current current cost value - * @param delta additional cost to add to the current value - * @param limit total cost limit - * @param msgSuffix use case-specific error message suffix - * @return new increased cost when it doesn't exceed the limit - * @throws CostLimitException - */ - def addCostChecked(current: Long, delta: Long, limit: Long, msgSuffix: => String = ""): Long = { - val newCost = java7.compat.Math.addExact(current, delta) - if (newCost > limit) { - throw new CostLimitException( - estimatedCost = newCost, - message = { - val suffix = if (msgSuffix.isEmpty) "" else s": $msgSuffix" - msgCostLimitError(newCost, limit) + suffix - }, - cause = None) - } - newCost - } /** Transforms a serializable ErgoTree type descriptor to the corresponding RType descriptor of SigmaDsl, * which is used during evaluation. @@ -101,14 +69,14 @@ object Evaluation { case BigIntRType => SBigInt case GroupElementRType => SGroupElement case AvlTreeRType => SAvlTree - case ot: OptionType[_] => sigmastate.SOption(rtypeToSType(ot.tA)) + case ot: OptionType[_] => SOption(rtypeToSType(ot.tA)) case BoxRType => SBox case ContextRType => SContext case SigmaDslBuilderRType => SGlobal case HeaderRType => SHeader case PreHeaderRType => SPreHeader case SigmaPropRType => SSigmaProp - case SigmaBooleanRType => SSigmaProp + case SigmaBooleanRType => SSigmaProp // TODO remove: this is not used in consensus code case tup: TupleType => STuple(tup.items.map(t => rtypeToSType(t))) case at: ArrayType[_] => SCollection(rtypeToSType(at.tA)) case ct: CollType[_] => SCollection(rtypeToSType(ct.tItem)) @@ -117,47 +85,6 @@ object Evaluation { case _ => sys.error(s"Don't know how to convert RType $t to SType") } - /** Tries to reconstruct RType of the given value. - * If not successfull returns failure. - * NOTE, this method is NOT used in consensus. */ - def rtypeOf(value: Any): Try[RType[_]] = Try { value match { - case arr if arr.getClass.isArray => - val itemClass = arr.getClass.getComponentType - if (itemClass.isPrimitive) { - val itemTag = ClassTag[Any](itemClass) - RType.fromClassTag(itemTag) - } else - sys.error(s"Cannot compute rtypeOf($value): non-primitive type of array items") - - case coll: Coll[_] => collRType(coll.tItem) - - // all primitive types - case _: Boolean => BooleanType - case _: Byte => ByteType - case _: Short => ShortType - case _: Int => IntType - case _: Long => LongType - case _: String => StringType - case _: Unit => UnitType - case _: sigma.BigInt => BigIntRType - case _: GroupElement => GroupElementRType - // TODO remove this case to allow removing of RType instances - // for ErgoBox, AvlTreeData, SigmaBoolean. - // RType describes only the types that can be put into registers, context variables and - // used as ErgoTree evaluation intermediate values. - case _: ErgoBox => ErgoBoxRType - case _: Box => BoxRType - - case _: AvlTreeData => AvlTreeDataRType // TODO remove this RType - case _: AvlTree => AvlTreeRType - - case _: SigmaBoolean => SigmaBooleanRType // TODO remove this RType - case _: SigmaProp => SigmaPropRType - case _: Context => ContextRType - case _ => - sys.error(s"Don't know how to compute typeOf($value)") - }} - /** Convert SigmaDsl representation of tuple to ErgoTree serializable representation. */ def fromDslTuple(value: Any, tupleTpe: STuple): Coll[Any] = value match { case t: Tuple2[_,_] => TupleColl(t._1, t._2) diff --git a/core/shared/src/main/scala/sigma/Extensions.scala b/core/shared/src/main/scala/sigma/Extensions.scala index 08087eced0..81976a3c12 100644 --- a/core/shared/src/main/scala/sigma/Extensions.scala +++ b/core/shared/src/main/scala/sigma/Extensions.scala @@ -3,8 +3,26 @@ package sigma import debox.cfor import scorex.util.encode.Base16 import scorex.util.{ModifierId, bytesToId} +import sigma.data.RType +/** Declaration of extension methods introduced in `sigma-core` module. + * See `implicit class ...` wrappers below. + */ object Extensions { + /** Extension methods for `Array[Byte]` not available for generic `Array[T]`. */ + implicit class CoreArrayByteOps(val arr: Array[Byte]) extends AnyVal { + /** Encodes array into hex string */ + @inline def toHex: String = Base16.encode(arr) + } + + /** Extension methods for `Array[T]` where implicit descriptor `RType[T]` is also + * required. + */ + implicit class ArrayOps[T: RType](arr: Array[T]) { + /** Wraps array into Coll instance. The source array in not cloned. */ + @inline def toColl: Coll[T] = Colls.fromArray(arr) + } + /** Extension methods for `Coll[T]`. */ implicit class CollOps[T](val source: Coll[T]) extends AnyVal { /** Applies a function `f` to each element of the `source` collection. */ diff --git a/core/shared/src/main/scala/sigma/SigmaDsl.scala b/core/shared/src/main/scala/sigma/SigmaDsl.scala index 7609d38356..df2b419273 100644 --- a/core/shared/src/main/scala/sigma/SigmaDsl.scala +++ b/core/shared/src/main/scala/sigma/SigmaDsl.scala @@ -37,12 +37,6 @@ trait BigInt { */ def toBytes: Coll[Byte] - /** Returns a big-endian representation of this BigInt in a collection of Booleans. - * Each boolean corresponds to one bit of the representation. - * @since 2.0 - */ - def toBits: Coll[Boolean] - /** Absolute value of this numeric value. * @since 2.0 */ @@ -53,31 +47,6 @@ trait BigInt { */ def compareTo(that: BigInt): Int - /** Returns this `mod` Q, i.e. remainder of division by Q, where Q is an order of the cryprographic group. - * @since 2.0 - */ - def modQ: BigInt - - /** Adds this number with `other` by module Q. - * @since 2.0 - */ - def plusModQ(other: BigInt): BigInt - - /** Subtracts this number with `other` by module Q. - * @since 2.0 - */ - def minusModQ(other: BigInt): BigInt - - /** Multiply this number with `other` by module Q. - * @since 2.0 - */ - def multModQ(other: BigInt): BigInt - - /** Multiply this number with `other` by module Q. - * @since Mainnet - */ - def inverseModQ: BigInt // ??? @kushti do we need it - /** Returns the signum function of this BigInt. * * @return -1, 0 or 1 as the value of this BigInt is negative, zero or @@ -414,79 +383,6 @@ trait AvlTree { * replaced by `newOperations` */ def updateOperations(newOperations: Byte): AvlTree - - /** Checks if an entry with key `key` exists in this tree using proof `proof`. - * Throws exception if proof is incorrect - * - * @note CAUTION! Does not support multiple keys check, use [[getMany]] instead. - * Return `true` if a leaf with the key `key` exists - * Return `false` if leaf with provided key does not exist. - * @param key a key of an element of this authenticated dictionary. - * @param proof data to reconstruct part of the tree enough to perform the check - */ - def contains(key: Coll[Byte], proof: Coll[Byte]): Boolean - - /** Perform a lookup of key `key` in this tree using proof `proof`. - * Throws exception if proof is incorrect - * - * @note CAUTION! Does not support multiple keys check, use [[getMany]] instead. - * Return Some(bytes) of leaf with key `key` if it exists - * Return None if leaf with provided key does not exist. - * @param key a key of an element of this authenticated dictionary. - * @param proof data to reconstruct part of the tree enough to get the value - * by the key - */ - def get(key: Coll[Byte], proof: Coll[Byte]): Option[Coll[Byte]] - - /** Perform a lookup of many keys `keys` in this tree using proof `proof`. - * - * @note CAUTION! Keys must be ordered the same way they were in lookup before proof was generated. - * For each key return Some(bytes) of leaf if it exists and None if is doesn't. - * @param keys keys of elements of this authenticated dictionary. - * @param proof - */ - def getMany(keys: Coll[Coll[Byte]], proof: Coll[Byte]): Coll[Option[Coll[Byte]]] - - /** Perform insertions of key-value entries into this tree using proof `proof`. - * Throws exception if proof is incorrect - * - * @note CAUTION! Pairs must be ordered the same way they were in insert ops before proof was generated. - * Return Some(newTree) if successful - * Return None if operations were not performed. - * @param operations collection of key-value pairs to insert in this authenticated dictionary. - * @param proof data to reconstruct part of the tree - */ - def insert(operations: Coll[(Coll[Byte], Coll[Byte])], proof: Coll[Byte]): Option[AvlTree] - - /** Perform updates of key-value entries into this tree using proof `proof`. - * Throws exception if proof is incorrect - * - * @note CAUTION! Pairs must be ordered the same way they were in update ops before proof was generated. - * Return Some(newTree) if successful - * Return None if operations were not performed. - * @param operations collection of key-value pairs to update in this authenticated dictionary. - * @param proof data to reconstruct part of the tree - */ - def update(operations: Coll[(Coll[Byte], Coll[Byte])], proof: Coll[Byte]): Option[AvlTree] - - /** Perform removal of entries into this tree using proof `proof`. - * Throws exception if proof is incorrect - * Return Some(newTree) if successful - * Return None if operations were not performed. - * - * @note CAUTION! Keys must be ordered the same way they were in remove ops before proof was generated. - * @param operations collection of keys to remove from this authenticated dictionary. - * @param proof data to reconstruct part of the tree - */ - def remove(operations: Coll[Coll[Byte]], proof: Coll[Byte]): Option[AvlTree] - -// /** Creates a new instance of [[AvlTreeVerifier]] with the given `proof` and using -// * properties of this AvlTree (digest, keyLength, valueLengthOpt) for constructor -// * arguments. -// * -// * @param proof bytes of the serialized proof which is used to represent the tree. -// */ -// def createVerifier(proof: Coll[Byte]): AvlTreeVerifier } /** Only header fields that can be predicted by a miner. */ diff --git a/core/shared/src/main/scala/sigma/SigmaException.scala b/core/shared/src/main/scala/sigma/SigmaException.scala new file mode 100644 index 0000000000..146844b583 --- /dev/null +++ b/core/shared/src/main/scala/sigma/SigmaException.scala @@ -0,0 +1,17 @@ +package sigma + +import scala.collection.compat.immutable.ArraySeq + +/** Base class for Sigma-related exceptions. + * + * @param message the error message + * @param cause an optional cause for the exception + * @param args an optional sequence of arguments to be passed with the exception + */ +class SigmaException( + val message: String, + val cause: Option[Throwable] = None, + val args: Seq[Any] = ArraySeq.empty) extends Exception(message, cause.orNull) + + + diff --git a/core/shared/src/main/scala/sigma/VersionContext.scala b/core/shared/src/main/scala/sigma/VersionContext.scala index a7928cd513..67b7079fea 100644 --- a/core/shared/src/main/scala/sigma/VersionContext.scala +++ b/core/shared/src/main/scala/sigma/VersionContext.scala @@ -39,8 +39,8 @@ object VersionContext { val JitActivationVersion: Byte = 2 private val _defaultContext = VersionContext( - activatedVersion = 1/* v4.x */, - ergoTreeVersion = 1 + activatedVersion = 1 /* v4.x */, + ergoTreeVersion = 1 ) /** Universally accessible version context which is used to version the code diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala new file mode 100644 index 0000000000..e9ea0d43f0 --- /dev/null +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -0,0 +1,838 @@ +package sigma.ast + +import sigma.Evaluation.stypeToRType +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType.TypeCode +import sigma.data.OverloadHack.Overloaded1 +import sigma.data.{CBigInt, Nullable, SigmaConstants} +import sigma.reflection.{RClass, RMethod, ReflectionData} +import sigma.util.Extensions.{IntOps, LongOps, ShortOps} +import sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp} + +import java.math.BigInteger + +/** Base type for all AST nodes of ErgoTree. */ +trait SigmaNode extends Product + +/** Every type descriptor is a tree represented by nodes in SType hierarchy. + * In order to extend type family: + * - Implement concrete class derived from SType + * - Implement serializer (see SCollectionSerializer) and register it in STypeSerializer.table + * Each SType is serialized to array of bytes by: + * - emitting typeCode of each node (see special case for collections below) + * - then recursively serializing subtrees from left to right on each level + * - for each collection of primitive type there is special type code to emit single byte instead of two bytes + * Types code intervals + * - (1 .. MaxPrimTypeCode) // primitive types + * - (CollectionTypeCode .. CollectionTypeCode + MaxPrimTypeCode) // collections of primitive types + * - (MaxCollectionTypeCode ..) // Other types + * Collection of non-primitive type is serialized as (CollectionTypeCode, serialize(elementType)) + * */ +sealed trait SType extends SigmaNode { + /** The underlying Scala type of data values described by this type descriptor. + * E.g. scala.Int for SInt descriptor. + */ + type WrappedType + + /** Type code used in serialization of SType values. + * @see TypeSerializer + */ + val typeCode: SType.TypeCode + + /** 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 + + /** Elvis operator for types. See https://en.wikipedia.org/wiki/Elvis_operator*/ + def ?:(whenNoType: => SType): SType = if (this == NoType) whenNoType else this + + + /** Returns parsable type term string of the type described by this type descriptor. + * For every type it should be inverse to SigmaTyper.parseType. + * This is default fallback implementation, should be overriden if it + * is not correct for a particular type. */ + def toTermString: String = { + val t = Evaluation.stypeToRType(this) + t.name + } +} + +object SType { + /** Representation of type codes used in serialization. */ + type TypeCode = Byte + + /** 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 paramIVSeq: Seq[STypeParam] = Array(paramIV) + + val IndexedSeqOfT1: IndexedSeq[SType] = Array(SType.tT) + val IndexedSeqOfT2: IndexedSeq[SType] = Array(SType.tT, SType.tT) + + /** Immutable empty array, can be used to avoid repeated allocations. */ + val EmptyArray = Array.empty[SType] + + /** Immutable empty IndexedSeq, can be used to avoid repeated allocations. */ + val EmptySeq: IndexedSeq[SType] = EmptyArray + + /** 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[SType] = Array[SType]( + SBoolean, SByte, SShort, SInt, SLong, SBigInt, SContext, + SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, + SUnit, SAny) + + /** A mapping of object types supporting MethodCall operations. For each serialized + * typeId this map contains a companion object which can be used to access the list of + * corresponding methods. + * + * NOTE: in the current implementation only monomorphic methods are supported (without + * type parameters) + * + * NOTE2: in v3.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of + * `toMap` operation. As a result, the methods collected into SByte.methods cannot be + * resolved (using SMethod.fromIds()) for all numeric types (SByte, SShort, SInt, + * SLong, SBigInt). See the corresponding regression `property("MethodCall on numerics")`. + * However, this "shadowing" is not a problem since all casting methods are implemented + * via Downcast, Upcast opcodes and the remaining `toBytes`, `toBits` methods are not + * implemented at all. + * In order to allow MethodCalls on numeric types in future versions the SNumericType.typeId + * should be changed and SGlobal.typeId should be preserved. The regression tests in + * `property("MethodCall Codes")` should pass. + */ + // TODO v6.0: should contain all numeric types (including also SNumericType) + // to support method calls like 10.toByte which encoded as MethodCall with typeId = 4, methodId = 1 + // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 + lazy val types: Map[Byte, STypeCompanion] = Seq( + SBoolean, SNumericType, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, + SAvlTree, SBox, SOption, SCollection, SBigInt + ).map { t => (t.typeId, t) }.toMap + + /** Checks that the type of the value corresponds to the descriptor `tpe`. + * If the value has complex structure only root type constructor is checked. + * NOTE, this method is used in ErgoTree evaluation to systematically check that each + * tree node evaluates to a value of the expected type. + * Shallow runtime checks are enough if: + * 1) ErgoTree is well-typed, i.e. each sub-expression has correct types (agree with + * the argument type). + * 2) `isValueOfType == true` for each tree leaf + * 3) `isValueOfType == true` for each sub-expression + * + * @param value value to check type + * @param tpe type descriptor to check value against + * @return true if the given `value` is of type tpe` + */ + def isValueOfType[T <: SType](x: Any, tpe: T): Boolean = tpe match { + case SBoolean => x.isInstanceOf[Boolean] + case SByte => x.isInstanceOf[Byte] + case SShort => x.isInstanceOf[Short] + case SInt => x.isInstanceOf[Int] + case SLong => x.isInstanceOf[Long] + case SBigInt => x.isInstanceOf[BigInt] + case SGroupElement => x.isInstanceOf[GroupElement] + case SSigmaProp => x.isInstanceOf[SigmaProp] + case SBox => x.isInstanceOf[Box] + case _: SCollectionType[_] => x.isInstanceOf[Coll[_]] + case _: SOption[_] => x.isInstanceOf[Option[_]] + case t: STuple => + if (t.items.length == 2) x.isInstanceOf[Tuple2[_,_]] + else sys.error(s"Unsupported tuple type $t") + case tF: SFunc => + if (tF.tDom.length == 1) x.isInstanceOf[Function1[_,_]] + else sys.error(s"Unsupported function type $tF") + case SContext => x.isInstanceOf[Context] + case SAvlTree => x.isInstanceOf[AvlTree] + case SGlobal => x.isInstanceOf[SigmaDslBuilder] + case SHeader => x.isInstanceOf[Header] + case SPreHeader => x.isInstanceOf[PreHeader] + case SUnit => x.isInstanceOf[Unit] + case _ => sys.error(s"Unknown type $tpe") + } + + + implicit class AnyOps(val x: Any) extends AnyVal { + /** Helper method to simplify type casts. */ + def asWrappedType: SType#WrappedType = x.asInstanceOf[SType#WrappedType] + } +} + +/** Basic interface for all type companions. + * This is necessary to make distinction between concrete type descriptor of a type like Coll[Int] + * and generic descriptor of Coll[T] type constructor. + * Some simple types like Int, GroupElement inherit from both SType and STypeCompanion. + * @see SInt, SGroupElement, SType + */ +trait STypeCompanion { + /** Force initialization of reflection. */ + val reflection = ReflectionData + + /** Type identifier to use in method serialization */ + def typeId: Byte + + /** If this is SType instance then returns the name of the corresponding RType. + * Otherwise returns the name of type companion object (e.g. SCollection). + */ + def typeName: String = { + this match { + case t: SType => + val rtype = stypeToRType(t) + rtype.name + case _ => this.getClass.getSimpleName.replace("$", "") + } + } + + /** Class which represents values of this type. When method call is executed, the corresponding method + * of this class is invoked via [[RMethod]].invoke(). */ + def reprClass: RClass[_] + + /** Represents class of `this`. */ + lazy val thisRClass: RClass[_] = RClass(this.getClass) +} + + +/** Special type to represent untyped values. + * Interpreter raises an error when encounter a Value with this type. + * All Value nodes with this type should be elimitanted during typing. + * If no specific type can be assigned statically during typing, + * then either error should be raised or type SAny should be assigned + * which is interpreted as dynamic typing. */ +case object NoType extends SType { + type WrappedType = Nothing + override val typeCode = 0: Byte +} + +/** Type variable which is used in generic method/func signatures. + * Used by ErgoScript compiler IR and eliminated during compilation. + * It is not used in ErgoTree. + */ +case class STypeVar(name: String) extends SType { + require(name.length <= 255, "name is too long") + override type WrappedType = Any + override val typeCode = STypeVar.TypeCode + override def toString = name + override def toTermString: String = name +} +object STypeVar { + val TypeCode: TypeCode = 103: Byte + implicit def liftString(n: String): STypeVar = STypeVar(n) + + /** Immutable empty array, can be used to avoid repeated allocations. */ + val EmptyArray = Array.empty[STypeVar] + + /** Immutable empty IndexedSeq, can be used to avoid repeated allocations. */ + val EmptySeq: IndexedSeq[STypeVar] = EmptyArray +} + +/** Base trait for all pre-defined types which are not necessary primitive (e.g. Box, AvlTree). + */ +trait SPredefType extends SType { +} + +/** Base type for SBoolean and SSigmaProp. */ +trait SLogical extends SType { +} + +/** Base trait implemented by all generic types (those which has type parameters, + * e.g. Coll[T], Option[T], etc.)*/ +trait SGenericType { + /** Type parameters of this generic type. */ + def typeParams: Seq[STypeParam] +} + +/** Base trait for all embeddable types. + */ +trait SEmbeddable extends SType { + override def isEmbeddable: Boolean = true + /** Type code of embeddable type can be combined with code of type constructor. + * Resulting code can be serialized. This simple convention allows to save space for most frequently used types. + * See TypeSerializer */ + @inline final def embedIn(typeConstrId: Byte): Byte = (typeConstrId + this.typeCode).toByte +} + +/** Base trait for all primitive types (aka atoms) which don't have internal type items. + * All primitive types can occupy a reserved interval of codes from 1 to MaxPrimTypeCode. */ +trait SPrimType extends SType with SPredefType { +} + +/** Primitive type recognizer to pattern match on TypeCode */ +object SPrimType { + def unapply(t: SType): Option[SType] = SType.allPredefTypes.find(_ == t) + + /** Type code of the last valid prim type so that (1 to LastPrimTypeCode) is a range of valid codes. */ + final val LastPrimTypeCode: Byte = 8: Byte + + /** Upper limit of the interval of valid type codes for primitive types */ + final val MaxPrimTypeCode: Byte = 11: Byte + + /** Max possible number of primitive types. */ + final val PrimRange: Byte = (MaxPrimTypeCode + 1).toByte +} + +/** Base trait for all types which have methods (and properties) */ +trait SProduct extends SType { +} + +/** Monomorphic type descriptor i.e. a type without generic parameters. + * @see `SGenericType` + */ +trait SMonoType extends SType with STypeCompanion { +} + +/** Marker trait for all numeric types. */ +trait SNumericType extends SProduct with STypeCompanion { + /** Upcasts the given value of a smaller type to this larger type. + * Corresponds to section 5.1.2 Widening Primitive Conversion of Java Language Spec. + * @param n numeric value to be converted + * @return a value of WrappedType of this type descriptor's instance. + * @throw exception if `n` has actual type which is larger than this type. + */ + def upcast(n: AnyVal): WrappedType + + /** Downcasts the given value of a larger type to this smaller type. + * Corresponds to section 5.1.3 Narrowing Primitive Conversion of Java Language Spec. + * @param n numeric value to be converted + * @return a value of WrappedType of this type descriptor's instance. + * @throw exception if the actual value of `i` cannot fit into this type. + */ + def downcast(n: AnyVal): WrappedType + + /** Returns a type which is larger. */ + @inline def max(that: SNumericType): SNumericType = + if (this.numericTypeIndex > that.numericTypeIndex) this else that + + /** Returns true if this numeric type is larger than that. */ + @inline final def >(that: SNumericType): Boolean = this.numericTypeIndex > that.numericTypeIndex + + /** Numeric types are ordered by the number of bytes to store the numeric values. + * @return index in the array of all numeric types. */ + def numericTypeIndex: Int + + override def toString: String = this.getClass.getSimpleName +} + +object SNumericType extends STypeCompanion { + /** Array of all numeric types ordered by number of bytes in the representation. */ + final val allNumericTypes = Array(SByte, SShort, SInt, SLong, SBigInt) + + // TODO v6.0: this typeId is now shadowed by SGlobal.typeId + // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 + override def typeId: TypeCode = 106: Byte + + /** Since this object is not used in SMethod instances. */ + override def reprClass: RClass[_] = sys.error(s"Shouldn't be called.") +} + +/** Descriptor of ErgoTree type `Boolean` holding `true` or `false` values. */ +case object SBoolean extends SPrimType with SEmbeddable with SLogical with SProduct with SMonoType { + override type WrappedType = Boolean + override val typeCode: TypeCode = 1: Byte + override val reprClass: RClass[_] = RClass(classOf[Boolean]) + override def typeId = typeCode + implicit def typeBoolean: SBoolean.type = this +} + +/** Descriptor of ErgoTree type `Byte` - 8-bit signed integer. */ +case object SByte extends SPrimType with SEmbeddable with SNumericType with SMonoType { + override type WrappedType = Byte + override val typeCode: TypeCode = 2: Byte + override val reprClass: RClass[_] = RClass(classOf[Byte]) + implicit def typeByte: SByte.type = this + override def typeId = typeCode + override def numericTypeIndex: Int = 0 + override def upcast(v: AnyVal): Byte = v match { + case b: Byte => b + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + override def downcast(v: AnyVal): Byte = v match { + case b: Byte => b + case s: Short => s.toByteExact + case i: Int => i.toByteExact + case l: Long => l.toByteExact + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } +} + +/** Descriptor of ErgoTree type `Short` - 16-bit signed integer. */ +case object SShort extends SPrimType with SEmbeddable with SNumericType with SMonoType { + override type WrappedType = Short + override val typeCode: TypeCode = 3: Byte + override val reprClass: RClass[_] = RClass(classOf[Short]) + implicit val typeShort: SShort.type = this + override def typeId = typeCode + override def numericTypeIndex: Int = 1 + override def upcast(v: AnyVal): Short = v match { + case x: Byte => x.toShort + case x: Short => x + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + override def downcast(v: AnyVal): Short = v match { + case s: Short => s + case i: Int => i.toShortExact + case l: Long => l.toShortExact + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } +} + +/** Descriptor of ErgoTree type `Int` - 32-bit signed integer. */ +case object SInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { + override type WrappedType = Int + override val typeCode: TypeCode = 4: Byte + override val reprClass: RClass[_] = RClass(classOf[Int]) + override def typeId = typeCode + override def numericTypeIndex: Int = 2 + implicit def typeInt: SInt.type = this + override def upcast(v: AnyVal): Int = v match { + case x: Byte => x.toInt + case x: Short => x.toInt + case x: Int => x + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + override def downcast(v: AnyVal): Int = v match { + case b: Byte => b.toInt + case s: Short => s.toInt + case i: Int => i + case l: Long => l.toIntExact + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } +} + +/** Descriptor of ErgoTree type `Long` - 64-bit signed integer. */ +case object SLong extends SPrimType with SEmbeddable with SNumericType with SMonoType { + override type WrappedType = Long + override val typeCode: TypeCode = 5: Byte + override val reprClass: RClass[_] = RClass(classOf[Long]) + override def typeId = typeCode + override def numericTypeIndex: Int = 3 + implicit def typeLong: SLong.type = this + + override def upcast(v: AnyVal): Long = v match { + case x: Byte => x.toLong + case x: Short => x.toLong + case x: Int => x.toLong + case x: Long => x + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + override def downcast(v: AnyVal): Long = v match { + case b: Byte => b.toLong + case s: Short => s.toLong + case i: Int => i.toLong + case l: Long => l + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } +} + +/** Type of 256 bit integet values. Implemented using [[java.math.BigInteger]]. */ +case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { + override type WrappedType = BigInt + override val typeCode: TypeCode = 6: Byte + override val reprClass: RClass[_] = RClass(classOf[BigInt]) + override def typeId = typeCode + implicit def typeBigInt: SBigInt.type = this + + /** Type of Relation binary op like GE, LE, etc. */ + val RelationOpType = SFunc(Array(SBigInt, SBigInt), SBoolean) + + /** The maximum size of BigInteger value in byte array representation. */ + val MaxSizeInBytes: Long = SigmaConstants.MaxBigIntSizeInBytes.value + + override def numericTypeIndex: Int = 4 + + override def upcast(v: AnyVal): BigInt = { + val bi = v match { + case x: Byte => BigInteger.valueOf(x.toLong) + case x: Short => BigInteger.valueOf(x.toLong) + case x: Int => BigInteger.valueOf(x.toLong) + case x: Long => BigInteger.valueOf(x) + case _ => sys.error(s"Cannot upcast value $v to the type $this") + } + CBigInt(bi) + } + override def downcast(v: AnyVal): BigInt = { + val bi = v match { + case x: Byte => BigInteger.valueOf(x.toLong) + case x: Short => BigInteger.valueOf(x.toLong) + case x: Int => BigInteger.valueOf(x.toLong) + case x: Long => BigInteger.valueOf(x) + case _ => sys.error(s"Cannot downcast value $v to the type $this") + } + CBigInt(bi) + } +} + +/** Descriptor of type `String` which is not used in ErgoTree, but used in ErgoScript. + * NOTE: this descriptor both type and type companion */ +case object SString extends SProduct with SMonoType { + override type WrappedType = String + override val typeCode: TypeCode = 102: Byte + override def typeId = typeCode + override def reprClass: RClass[_] = RClass(classOf[String]) +} + +/** Descriptor of ErgoTree type `GroupElement`. + * NOTE: this descriptor both type and type companion */ +case object SGroupElement extends SProduct with SPrimType with SEmbeddable with SMonoType { + override type WrappedType = GroupElement + override val typeCode: TypeCode = 7: Byte + override val reprClass: RClass[_] = RClass(classOf[GroupElement]) + override def typeId = typeCode + implicit def typeGroupElement: SGroupElement.type = this +} + +/** Descriptor of ErgoTree type `SigmaProp` which represent sigma-protocol propositions. */ +case object SSigmaProp extends SProduct with SPrimType with SEmbeddable with SLogical with SMonoType { + import SType._ + override type WrappedType = SigmaProp + override val typeCode: TypeCode = 8: Byte + override val reprClass: RClass[_] = RClass(classOf[SigmaProp]) + override def typeId = typeCode + implicit def typeSigmaProp: SSigmaProp.type = this + + /** The maximum size of SigmaProp value in serialized byte array representation. */ + val MaxSizeInBytes: Long = SigmaConstants.MaxSigmaPropSizeInBytes.value +} + +/** Any other type is implicitly subtype of this type. */ +case object SAny extends SPrimType with SMonoType { + override type WrappedType = Any + override val typeCode: TypeCode = 97: Byte + + /** Type identifier to use in method serialization */ + override def typeId: TypeCode = typeCode + + /** Class which represents values of this type. When method call is executed, the corresponding method + * of this class is invoked via [[RMethod]].invoke(). */ + override def reprClass: RClass[_] = RClass(classOf[Any]) +} + +/** The type with single inhabitant value `()` */ +case object SUnit extends SPrimType with SMonoType { + override type WrappedType = Unit + override val typeCode: TypeCode = 98: Byte + /** Type identifier to use in method serialization */ + override def typeId: TypeCode = typeCode + + /** Class which represents values of this type. When method call is executed, the corresponding method + * of this class is invoked via [[RMethod]].invoke(). */ + override def reprClass: RClass[_] = RClass(classOf[Unit]) +} + +/** Helper constuctor/extractor for tuples of two types. */ +object SPair { + 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 + } +} + +/** Type descriptor of lambda types. */ +case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypeParam] = Nil) + extends SType with SGenericType +{ + override type WrappedType = Any => tRange.WrappedType + override val typeCode = SFunc.FuncTypeCode + override def toString = { + val args = if (tpeParams.isEmpty) "" else tpeParams.mkString("[", ",", "]") + s"$args(${tDom.mkString(",")}) => $tRange" + } + override def toTermString = { + val args = if (tpeParams.isEmpty) "" else tpeParams.mkString("[", ",", "]") + s"$args(${tDom.map(_.toTermString).mkString(",")}) => ${tRange.toTermString}" + } + override val typeParams: Seq[STypeParam] = tpeParams + + /** Generalize this type and return a new descriptor. */ + def getGenericType: SFunc = { + val typeParams: Seq[STypeParam] = tDom.zipWithIndex + .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) + } + + /** Transform function into method type by adding the given `objType` as the first + * argument type (aka method receiver type). + */ + def withReceiverType(objType: SType) = this.copy(tDom = objType +: tDom) +} + +object SFunc { + final val FuncTypeCode: TypeCode = TypeCodes.FirstFuncType + def apply(tDom: SType, tRange: SType): SFunc = SFunc(Array(tDom), tRange) // HOTSPOT: + val identity = { x: Any => x } +} + +/** Used by ErgoScript compiler IR and eliminated during compilation. + * It is not used in ErgoTree. + */ +case class STypeApply(name: String, args: IndexedSeq[SType] = IndexedSeq()) extends SType { + override type WrappedType = Any + override val typeCode = STypeApply.TypeCode +} +object STypeApply { + val TypeCode = 94: Byte +} + +/** Type description of optional values. Instances of `Option` + * are either constructed by `Some` or by `None` constructors. */ +case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct with SGenericType { + override type WrappedType = Option[ElemType#WrappedType] + override val typeCode: TypeCode = SOption.OptionTypeCode + override def toString = s"Option[$elemType]" + override def toTermString: String = s"Option[${elemType.toTermString}]" + override lazy val typeParams: Seq[STypeParam] = Array(SType.paramT) +} + +object SOption extends STypeCompanion { + /** Code of `Option[_]` type constructor. */ + val OptionTypeConstrId = 3 + /** Type code for `Option[T] for some T` type used in TypeSerializer. */ + val OptionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionTypeConstrId).toByte + /** Code of `Option[Coll[_]]` type constructor. */ + val OptionCollectionTypeConstrId = 4 + /** Type code for `Option[Coll[T]] for some T` type used in TypeSerializer. */ + val OptionCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionCollectionTypeConstrId).toByte + + override def typeId = OptionTypeCode + + override val reprClass: RClass[_] = RClass(classOf[Option[_]]) + + type SBooleanOption = SOption[SBoolean.type] + type SByteOption = SOption[SByte.type] + type SShortOption = SOption[SShort.type] + type SIntOption = SOption[SInt.type] + type SLongOption = SOption[SLong.type] + type SBigIntOption = SOption[SBigInt.type] + type SGroupElementOption = SOption[SGroupElement.type] + type SBoxOption = SOption[SBox.type] + type SAvlTreeOption = SOption[SAvlTree.type] + + /** This descriptors are instantiated once here and then reused. */ + implicit val SByteOption = SOption(SByte) + implicit val SByteArrayOption = SOption(SByteArray) + implicit val SShortOption = SOption(SShort) + implicit val SIntOption = SOption(SInt) + implicit val SLongOption = SOption(SLong) + implicit val SBigIntOption = SOption(SBigInt) + implicit val SBooleanOption = SOption(SBoolean) + implicit val SAvlTreeOption = SOption(SAvlTree) + implicit val SGroupElementOption = SOption(SGroupElement) + implicit val SSigmaPropOption = SOption(SSigmaProp) + implicit val SBoxOption = SOption(SBox) + + def apply[T <: SType](implicit elemType: T, ov: Overloaded1): SOption[T] = SOption(elemType) +} + + +/** Base class for descriptors of `Coll[T]` ErgoTree type for some elemType T. */ +trait SCollection[T <: SType] extends SProduct with SGenericType { + def elemType: T + override type WrappedType = Coll[T#WrappedType] +} + +/** Descriptor of `Coll[T]` ErgoTree type for some elemType T. */ +case class SCollectionType[T <: SType](elemType: T) extends SCollection[T] { + override val typeCode: TypeCode = SCollectionType.CollectionTypeCode + override def typeParams: Seq[STypeParam] = SCollectionType.typeParams + override def toString = s"Coll[$elemType]" + override def toTermString = s"Coll[${elemType.toTermString}]" +} + +object SCollectionType { + /** Code of `Coll[_]` type constructor. */ + val CollectionTypeConstrId = 1 + + /** Type code for `Coll[T] for some T` type used in TypeSerializer. */ + val CollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * CollectionTypeConstrId).toByte + + /** Code of `Coll[Coll[_]]` type constructor. */ + val NestedCollectionTypeConstrId = 2 + + /** Type code for `Coll[Coll[T]] for some T` type used in TypeSerializer. */ + val NestedCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * NestedCollectionTypeConstrId).toByte + + /** Array of generic type parameters reused in all SCollectionType instances. */ + val typeParams: Seq[STypeParam] = Array(SType.paramIV) +} + +object SCollection extends STypeCompanion { + override val reprClass: RClass[_] = RClass(classOf[Coll[_]]) + override def typeId = SCollectionType.CollectionTypeCode + + /** Costructs a collection type with the given type of elements. */ + implicit def typeCollection[V <: SType](implicit tV: V): SCollection[V] = SCollection[V](tV) + + /** Helper descriptors reused across different method descriptors. */ + def tIV = SType.tIV + def tOV = SType.tOV + + /** This descriptors are instantiated once here and then reused. */ + val ThisType = SCollection(tIV) + val tOVColl = SCollection(tOV) + val tPredicate = SFunc(tIV, SBoolean) + + /** Helper constructors. */ + def apply[T <: SType](elemType: T): SCollection[T] = SCollectionType(elemType) + def apply[T <: SType](implicit elemType: T, ov: Overloaded1): SCollection[T] = SCollectionType(elemType) + + type SBooleanArray = SCollection[SBoolean.type] + type SByteArray = SCollection[SByte.type] + type SShortArray = SCollection[SShort.type] + type SIntArray = SCollection[SInt.type] + type SLongArray = SCollection[SLong.type] + type SBigIntArray = SCollection[SBigInt.type] + type SGroupElementArray = SCollection[SGroupElement.type] + type SBoxArray = SCollection[SBox.type] + type SAvlTreeArray = SCollection[SAvlTree.type] + + /** This descriptors are instantiated once here and then reused. */ + val SBooleanArray = SCollection(SBoolean) + val SByteArray = SCollection(SByte) + val SByteArray2 = SCollection(SCollection(SByte)) + val SShortArray = SCollection(SShort) + val SIntArray = SCollection(SInt) + val SLongArray = SCollection(SLong) + val SBigIntArray = SCollection(SBigInt) + val SGroupElementArray = SCollection(SGroupElement) + val SSigmaPropArray = SCollection(SSigmaProp) + val SBoxArray = SCollection(SBox) + val SAvlTreeArray = SCollection(SAvlTree) + val SHeaderArray = SCollection(SHeader) +} + +/** Type descriptor of tuple type. */ +case class STuple(items: IndexedSeq[SType]) extends SCollection[SAny.type] { + override val typeCode = STuple.TupleTypeCode + + override def elemType: SAny.type = SAny + + override val typeParams = Nil + + override def toTermString = s"(${items.map(_.toTermString).mkString(",")})" + override def toString = s"(${items.mkString(",")})" +} + +object STuple extends STypeCompanion { + /** Code of `(_, T) for some embeddable T` type constructor. */ + val Pair1TypeConstrId = 5 + /** Type code for `(E, T) for some embeddable T` type used in TypeSerializer. */ + val Pair1TypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * Pair1TypeConstrId).toByte + + /** Code of `(T, _) for some embeddable T` type constructor. */ + val Pair2TypeConstrId = 6 + /** Type code for `(T, E) for some embeddable T` type used in TypeSerializer. */ + val Pair2TypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * Pair2TypeConstrId).toByte + val TripleTypeCode: TypeCode = Pair2TypeCode + + /** Type constructor code of symmetric pair `(T, T)` for some embeddable T. */ + val PairSymmetricTypeConstrId = 7 + /** Type code of symmetric pair `(T, T)` for some embeddable T. */ + val PairSymmetricTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * PairSymmetricTypeConstrId).toByte + val QuadrupleTypeCode: TypeCode = PairSymmetricTypeCode + + /** Type code of generic tuple type. */ + val TupleTypeCode = ((SPrimType.MaxPrimTypeCode + 1) * 8).toByte + + override def typeId = TupleTypeCode + + override val reprClass: RClass[_] = RClass(classOf[Product2[_,_]]) + + /** Helper factory method. */ + def apply(items: SType*): STuple = STuple(items.toArray) + +} + +/** Type descriptor of `Box` type of ErgoTree. */ +case object SBox extends SProduct with SPredefType with SMonoType { + override type WrappedType = Box + override val typeCode: TypeCode = 99: Byte + override val reprClass: RClass[_] = RClass(classOf[Box]) + override def typeId = typeCode + implicit def typeBox: SBox.type = this +} + +/** Type descriptor of `AvlTree` type of ErgoTree. */ +case object SAvlTree extends SProduct with SPredefType with SMonoType { + override type WrappedType = AvlTree + override val typeCode: TypeCode = 100: Byte + override val reprClass: RClass[_] = RClass(classOf[AvlTree]) + override def typeId = typeCode + implicit def typeAvlTree: SAvlTree.type = this + + import SOption._ + lazy val TCollOptionCollByte = SCollection(SByteArrayOption) + lazy val CollKeyValue = SCollection(STuple(SByteArray, SByteArray)) +} + +/** Type descriptor of `Context` type of ErgoTree. */ +case object SContext extends SProduct with SPredefType with SMonoType { + override type WrappedType = Context + override val typeCode: TypeCode = 101: Byte + override def reprClass: RClass[_] = RClass(classOf[Context]) + override def typeId = typeCode +} + +/** Type descriptor of `Header` type of ErgoTree. */ +case object SHeader extends SProduct with SPredefType with SMonoType { + override type WrappedType = Header + override val typeCode: TypeCode = 104: Byte + override val reprClass: RClass[_] = RClass(classOf[Header]) + override def typeId = typeCode +} + +/** Type descriptor of `PreHeader` type of ErgoTree. */ +case object SPreHeader extends SProduct with SPredefType with SMonoType { + override type WrappedType = PreHeader + override val typeCode: TypeCode = 105: Byte + override val reprClass: RClass[_] = RClass(classOf[PreHeader]) + override def typeId = typeCode +} + +/** This type is introduced to unify handling of global and non-global (i.e. methods) operations. + * It unifies implementation of global operation with implementation of methods and avoids code + * duplication (following DRY principle https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). + * The WrappedType is `sigma.SigmaDslBuilder`, which is an interface implemented by + * the singleton sigmastate.eval.CostingSigmaDslBuilder + * + * The Constant(...) tree node of this type are not allowed, as well as using it in register and + * context variables (aka ContextExtension) + * + * When new methods are added to this type via a soft-fork, they will be serialized as part + * of ErgoTree using MethodCallSerializer, where SGlobal.typeCode will be used. + * + * @see sigmastate.lang.SigmaPredef + * */ +case object SGlobal extends SProduct with SPredefType with SMonoType { + override type WrappedType = SigmaDslBuilder + override val typeCode: TypeCode = 106: Byte + override val reprClass: RClass[_] = RClass(classOf[SigmaDslBuilder]) + override def typeId = typeCode +} + diff --git a/core/shared/src/main/scala/sigma/ast/STypeParam.scala b/core/shared/src/main/scala/sigma/ast/STypeParam.scala new file mode 100644 index 0000000000..56d89d01f8 --- /dev/null +++ b/core/shared/src/main/scala/sigma/ast/STypeParam.scala @@ -0,0 +1,26 @@ +package sigma.ast + +/** Represents a type parameter in a type system. + * + * @param ident The identifier for this type parameter. + * @param upperBound The upper bound of this type parameter, if exists. + * @param lowerBound The lower bound of this type parameter, if exists. + * @note Type parameters with bounds are currently not supported. + */ +case class STypeParam( + ident: STypeVar, + upperBound: Option[SType] = None, + lowerBound: Option[SType] = None) { + assert(upperBound.isEmpty && lowerBound.isEmpty, s"Type parameters with bounds are not supported, but found $this") + + override def toString = ident.toString + upperBound.fold("")(u => s" <: $u") + lowerBound.fold("")(l => s" >: $l") +} + +object STypeParam { + /** Enables implicit conversion from [[STypeVar]] to [[STypeParam]]. + * + * @param id The type variable to convert. + * @return A type parameter constructed from the provided type variable. + */ + implicit def typeIdentToTypeParam(id: STypeVar): STypeParam = STypeParam(id) +} \ No newline at end of file diff --git a/core/shared/src/main/scala/sigma/ast/TypeCodes.scala b/core/shared/src/main/scala/sigma/ast/TypeCodes.scala new file mode 100644 index 0000000000..1b7b6121d6 --- /dev/null +++ b/core/shared/src/main/scala/sigma/ast/TypeCodes.scala @@ -0,0 +1,36 @@ +package sigma.ast + +import supertagged.TaggedType + +/** Encoding of types for serialization. */ +object TypeCodes { + object TypeCode extends TaggedType[Byte] + type TypeCode = TypeCode.Type + + /** Decoding of types depends on the first byte and in general is a recursive procedure + * consuming some number of bytes from Reader. + * All data types are recognized by the first byte falling in the region [FirstDataType .. LastDataType] */ + val FirstDataType: TypeCode = TypeCode @@ 1.toByte + + val LastDataType : TypeCode = TypeCode @@ 111.toByte + + /** SFunc types occupy remaining space of byte values [FirstFuncType .. 255] */ + val FirstFuncType: TypeCode = TypeCode @@ (LastDataType + 1).toByte + + val LastFuncType : TypeCode = TypeCode @@ 255.toByte + + /** We use optimized encoding of constant values to save space in serialization. + * Since Box registers are stored as Constant nodes we save 1 byte for each register. + * This is due to convention that Value.opCode falling in [1..LastDataType] region is a constant. + * Thus, we can just decode an instance of SType and then decode data using DataSerializer. + * + * Decoding of constants depends on the first byte and in general is a recursive procedure + * consuming some number of bytes from Reader. + * */ + val ConstantCode: Byte = 0.toByte + + /** The last constant code is equal to FirstFuncType which represent generic function type. + * We use this single code to represent all functional constants, since we don't have enough space in single byte. + * Subsequent bytes have to be read from Reader in order to decode the type of the function and the corresponding data. */ + val LastConstantCode: Byte = (TypeCodes.LastDataType + 1).toByte +} diff --git a/core/shared/src/main/scala/sigma/ast/package.scala b/core/shared/src/main/scala/sigma/ast/package.scala new file mode 100644 index 0000000000..63a2cfbcba --- /dev/null +++ b/core/shared/src/main/scala/sigma/ast/package.scala @@ -0,0 +1,162 @@ +package sigma + +import sigma.kiama.rewriting.Rewriter.{everywherebu, rewrite, rule} + +package object ast { + + /** Type alias for a substitution of type variables with their corresponding types. */ + type STypeSubst = Map[STypeVar, SType] + + /** Immutable and sharable empty substitution. */ + val EmptySubst = Map.empty[STypeVar, SType] + + /** Performs pairwise type unification making sure each type variable is equally + * substituted in all items. */ + def unifyTypeLists(items1: Seq[SType], items2: Seq[SType]): Option[STypeSubst] = { + // unify items pairwise independently + val itemsUni = (items1, items2).zipped.map((t1, t2) => unifyTypes(t1, t2)) + if (itemsUni.forall(_.isDefined)) { + // merge substitutions making sure the same id is equally substituted in all items + val merged = itemsUni.foldLeft(EmptySubst)((acc, subst) => { + var res = acc + for ( (id, t) <- subst.get ) { + if (res.contains(id) && res(id) != t) return None + res = res + (id -> t) + } + res + }) + Some(merged) + } else + None + } + + private val unifiedWithoutSubst = Some(EmptySubst) + + /** Finds a substitution `subst` of type variables such that unifyTypes(applySubst(t1, subst), t2) shouldBe Some(emptySubst) */ + def unifyTypes(t1: SType, t2: SType): Option[STypeSubst] = (t1, t2) match { + case (_ @ STypeVar(n1), _ @ STypeVar(n2)) => + if (n1 == n2) unifiedWithoutSubst else None + case (id1 @ STypeVar(_), _) => + Some(Map(id1 -> t2)) + case (e1: SCollectionType[_], e2: SCollectionType[_]) => + unifyTypes(e1.elemType, e2.elemType) + case (e1: SCollectionType[_], _: STuple) => + unifyTypes(e1.elemType, SAny) + case (e1: SOption[_], e2: SOption[_]) => + unifyTypes(e1.elemType, e2.elemType) + case (e1: STuple, e2: STuple) if e1.items.length == e2.items.length => + unifyTypeLists(e1.items, e2.items) + case (e1: SFunc, e2: SFunc) if e1.tDom.length == e2.tDom.length => + unifyTypeLists(e1.tDom :+ e1.tRange, e2.tDom :+ e2.tRange) + case (STypeApply(name1, args1), STypeApply(name2, args2)) + if name1 == name2 && args1.length == args2.length => + unifyTypeLists(args1, args2) + case (SBoolean, SSigmaProp) => // it is necessary for implicit conversion in Coll(bool, prop, bool) + unifiedWithoutSubst + case (SPrimType(e1), SPrimType(e2)) if e1 == e2 => + unifiedWithoutSubst + case (SAny, _) => + unifiedWithoutSubst + case _ => None + } + + /** Applies a type substitution to a given type. + * + * @param tpe the type to apply the substitution to + * @param subst the type substitution to apply + * @return the type after applying the substitution + */ + def applySubst(tpe: SType, subst: STypeSubst): SType = tpe match { + case SFunc(args, res, tparams) => + val remainingVars = tparams.filterNot { p => subst.contains(p.ident) } + SFunc(args.map(applySubst(_, subst)), applySubst(res, subst), remainingVars) + case _ => + val substRule = rule[Any] { + case id: STypeVar if subst.contains(id) => subst(id) + } + rewrite(everywherebu(substRule))(tpe) + } + + /** Computes the most general type given two types. + * + * @param t1 the first type + * @param t2 the second type + * @return the most general type if it exists, otherwise None + */ + def msgType(t1: SType, t2: SType): Option[SType] = unifyTypes(t1, t2) match { + case Some(_) => Some(t1) + case None => unifyTypes(t2, t1).map(_ => t2) + } + + /** Most Specific Generalized (MSG) type of ts. + * Currently just the type of the first element as long as all the elements have the same type. */ + def msgTypeOf(ts: Seq[SType]): Option[SType] = { + if (ts.isEmpty) None + else { + var res: SType = ts.head + for ( t <- ts.iterator.drop(1) ) { + msgType(t, res) match { + case Some(msg) => res = msg //assign new + case None => return None + } + } + Some(res) + } + } + + implicit class STypeOps(val tpe: SType) extends AnyVal { + def isCollectionLike: Boolean = tpe.isInstanceOf[SCollection[_]] + + def isCollection: Boolean = tpe.isInstanceOf[SCollectionType[_]] + + def isOption: Boolean = tpe.isInstanceOf[SOption[_]] + + def isBox: Boolean = tpe.isInstanceOf[SBox.type] + + def isGroupElement: Boolean = tpe.isInstanceOf[SGroupElement.type] + + def isSigmaProp: Boolean = tpe.isInstanceOf[SSigmaProp.type] + + def isAvlTree: Boolean = tpe.isInstanceOf[SAvlTree.type] + + def isFunc: Boolean = tpe.isInstanceOf[SFunc] + + def isTuple: Boolean = tpe.isInstanceOf[STuple] + + /** 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] + + def asTuple: STuple = tpe.asInstanceOf[STuple] + + def asOption[T <: SType]: SOption[T] = tpe.asInstanceOf[SOption[T]] + + def whenFunc[T](action: SFunc => Unit) = if (tpe.isInstanceOf[SFunc]) action(tpe.asFunc) + + def asCollection[T <: SType] = tpe.asInstanceOf[SCollection[T]] + + /** Applies a type substitution to this type. + * + * @param subst the type substitution to apply + * @return the type after applying the substitution + */ + def withSubstTypes(subst: Map[STypeVar, SType]): SType = + if (subst.isEmpty) tpe + else + applySubst(tpe, subst) + } +} diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoContext.scala b/core/shared/src/main/scala/sigma/crypto/CryptoContext.scala similarity index 85% rename from interpreter/shared/src/main/scala/sigmastate/crypto/CryptoContext.scala rename to core/shared/src/main/scala/sigma/crypto/CryptoContext.scala index c0190ba093..988a8e9283 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoContext.scala +++ b/core/shared/src/main/scala/sigma/crypto/CryptoContext.scala @@ -1,4 +1,4 @@ -package sigmastate.crypto +package sigma.crypto import java.math.BigInteger @@ -31,3 +31,8 @@ abstract class CryptoContext { def generator: Ecp } +object CryptoContext { + /** Default context for cryptographic operations. */ + val default: CryptoContext = Platform.createContext() +} + diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoFacade.scala b/core/shared/src/main/scala/sigma/crypto/CryptoFacade.scala similarity index 99% rename from interpreter/shared/src/main/scala/sigmastate/crypto/CryptoFacade.scala rename to core/shared/src/main/scala/sigma/crypto/CryptoFacade.scala index 3ce337d695..683ec3f1b7 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoFacade.scala +++ b/core/shared/src/main/scala/sigma/crypto/CryptoFacade.scala @@ -1,4 +1,4 @@ -package sigmastate.crypto +package sigma.crypto import java.math.BigInteger diff --git a/core/shared/src/main/scala/sigma/crypto/package.scala b/core/shared/src/main/scala/sigma/crypto/package.scala new file mode 100644 index 0000000000..cc2598f62a --- /dev/null +++ b/core/shared/src/main/scala/sigma/crypto/package.scala @@ -0,0 +1,27 @@ +package sigma + +package object crypto { + /** Length of hash function used in the signature scheme. Blake2b hash function is used. */ + val hashLengthBits = 256 + + /** Length of hash in bytes. */ + val hashLength: Int = hashLengthBits / 8 + + /** Number of bytes to represent any group element as byte array */ + val groupSize: Int = 256 / 8 //32 bytes + + /** Instance of Elliptic Curve descriptor. */ + type Curve = Platform.Curve + + /** Instance of Elliptic Curve point. */ + type Ecp = Platform.Ecp + + /** Instance of Elliptic Curve field element. */ + type ECFieldElem = Platform.ECFieldElem + + /** A cryptographically strong random number generator. */ + type SecureRandom = Platform.SecureRandom + + /** Type of group elements used in the signature scheme. */ + type EcPointType = Ecp +} diff --git a/interpreter/shared/src/main/scala/sigmastate/AvlTreeData.scala b/core/shared/src/main/scala/sigma/data/AvlTreeData.scala similarity index 85% rename from interpreter/shared/src/main/scala/sigmastate/AvlTreeData.scala rename to core/shared/src/main/scala/sigma/data/AvlTreeData.scala index 83f9b756c7..bddb0e5c44 100644 --- a/interpreter/shared/src/main/scala/sigmastate/AvlTreeData.scala +++ b/core/shared/src/main/scala/sigma/data/AvlTreeData.scala @@ -1,9 +1,7 @@ -package sigmastate +package sigma.data -import sigmastate.crypto.CryptoConstants -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigma.{Coll, Colls} +import sigma.serialization.{CoreByteReader, CoreByteWriter, CoreSerializer} +import sigma.{Coll, Colls, crypto} case class AvlTreeFlags(insertAllowed: Boolean, updateAllowed: Boolean, removeAllowed: Boolean) { @@ -56,7 +54,7 @@ case class AvlTreeData(digest: Coll[Byte], valueLengthOpt: Option[Int] = None) object AvlTreeData { - val DigestSize: Int = CryptoConstants.hashLength + 1 //please read class comments above for details + val DigestSize: Int = crypto.hashLength + 1 //please read class comments above for details val TreeDataSize = DigestSize + 3 + 4 + 4 val dummy = new AvlTreeData( @@ -67,12 +65,12 @@ object AvlTreeData { /** Create [[AvlTreeData]] with the given digest and all operations enabled. */ def avlTreeFromDigest(digest: Coll[Byte]): AvlTreeData = { val flags = AvlTreeFlags(insertAllowed = true, updateAllowed = true, removeAllowed = true) - AvlTreeData(digest, flags, CryptoConstants.hashLength) + AvlTreeData(digest, flags, crypto.hashLength) } - object serializer extends SigmaSerializer[AvlTreeData, AvlTreeData] { + object serializer extends CoreSerializer[AvlTreeData, AvlTreeData] { - override def serialize(data: AvlTreeData, w: SigmaByteWriter): Unit = { + override def serialize(data: AvlTreeData, w: CoreByteWriter): Unit = { val tf = AvlTreeFlags.serializeFlags(data.treeFlags) w.putBytes(data.digest.toArray) .putUByte(tf) @@ -80,7 +78,7 @@ object AvlTreeData { .putOption(data.valueLengthOpt)(_.putUInt(_)) } - override def parse(r: SigmaByteReader): AvlTreeData = { + override def parse(r: CoreByteReader): AvlTreeData = { val digest = r.getBytes(DigestSize) val tf = AvlTreeFlags(r.getByte()) val keyLength = r.getUInt().toInt diff --git a/core/shared/src/main/scala/sigma/data/CAnyValue.scala b/core/shared/src/main/scala/sigma/data/CAnyValue.scala new file mode 100644 index 0000000000..9c10dfbbe2 --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/CAnyValue.scala @@ -0,0 +1,18 @@ +package sigma.data + +import sigma.AnyValue +import sigma.data.OverloadHack.Overloaded1 + +import scala.annotation.unused + +/** Default implementation of AnyValue interface. */ +case class CAnyValue[A](value: A, tVal: RType[Any]) extends AnyValue { + def tA: RType[A] = tVal.asInstanceOf[RType[A]] + + override def toString = s"TestValue($value)" +} + +object CAnyValue { + def apply[A](value: A)(implicit t: RType[A], @unused o: Overloaded1): CAnyValue[A] = + new CAnyValue(value, t.asInstanceOf[RType[Any]]) +} \ No newline at end of file diff --git a/core/shared/src/main/scala/sigma/data/CAvlTree.scala b/core/shared/src/main/scala/sigma/data/CAvlTree.scala new file mode 100644 index 0000000000..a37e5096bb --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/CAvlTree.scala @@ -0,0 +1,36 @@ +package sigma.data + +import sigma.{AvlTree, Coll} + +/** A default implementation of [[AvlTree]] interface. + * + * @see [[AvlTree]] for detailed descriptions + */ +case class CAvlTree(treeData: AvlTreeData) extends AvlTree with WrapperOf[AvlTreeData] { + override def wrappedValue: AvlTreeData = treeData + + override def keyLength: Int = treeData.keyLength + + override def enabledOperations = treeData.treeFlags.serializeToByte + + override def isInsertAllowed: Boolean = treeData.treeFlags.insertAllowed + + override def isUpdateAllowed: Boolean = treeData.treeFlags.updateAllowed + + override def isRemoveAllowed: Boolean = treeData.treeFlags.removeAllowed + + override def updateOperations(newOperations: Byte): AvlTree = { + val td = treeData.copy(treeFlags = AvlTreeFlags(newOperations)) + this.copy(treeData = td) + } + + override def valueLengthOpt: Option[Int] = treeData.valueLengthOpt + + override def digest: Coll[Byte] = treeData.digest + + override def updateDigest(newDigest: Coll[Byte]): AvlTree = { + val td = treeData.copy(digest = newDigest) + this.copy(treeData = td) + } +} + diff --git a/core/shared/src/main/scala/sigma/data/CBigInt.scala b/core/shared/src/main/scala/sigma/data/CBigInt.scala new file mode 100644 index 0000000000..bbf1a85e46 --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/CBigInt.scala @@ -0,0 +1,52 @@ +package sigma.data + +import sigma.util.Extensions.BigIntegerOps +import sigma.{BigInt, Coll, Colls} + +import java.math.BigInteger + +/** A default implementation of [[BigInt]] interface. + * + * @see [[BigInt]] for detailed descriptions + */ +case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with WrapperOf[BigInteger] { + + override def toByte: Byte = wrappedValue.toByteExact + + override def toShort: Short = wrappedValue.toShortExact + + override def toInt: Int = wrappedValue.toIntExact + + override def toLong: Long = wrappedValue.toLongExact + + override def toBytes: Coll[Byte] = Colls.fromArray(wrappedValue.toByteArray) + + override def toAbs: BigInt = CBigInt(wrappedValue.abs()) + + override def compareTo(that: BigInt): Int = + wrappedValue.compareTo(that.asInstanceOf[CBigInt].wrappedValue) + + override def signum: Int = wrappedValue.signum() + + override def add(that: BigInt): BigInt = CBigInt(wrappedValue.add(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) + + override def subtract(that: BigInt): BigInt = CBigInt(wrappedValue.subtract(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) + + override def multiply(that: BigInt): BigInt = CBigInt(wrappedValue.multiply(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) + + override def divide(that: BigInt): BigInt = CBigInt(wrappedValue.divide(that.asInstanceOf[CBigInt].wrappedValue)) + + override def mod(m: BigInt): BigInt = CBigInt(wrappedValue.mod(m.asInstanceOf[CBigInt].wrappedValue)) + + override def remainder(that: BigInt): BigInt = CBigInt(wrappedValue.remainder(that.asInstanceOf[CBigInt].wrappedValue)) + + override def min(that: BigInt): BigInt = CBigInt(wrappedValue.min(that.asInstanceOf[CBigInt].wrappedValue)) + + override def max(that: BigInt): BigInt = CBigInt(wrappedValue.max(that.asInstanceOf[CBigInt].wrappedValue)) + + override def negate(): BigInt = CBigInt(wrappedValue.negate().to256BitValueExact) + + override def and(that: BigInt): BigInt = CBigInt(wrappedValue.and(that.asInstanceOf[CBigInt].wrappedValue)) + + override def or(that: BigInt): BigInt = CBigInt(wrappedValue.or(that.asInstanceOf[CBigInt].wrappedValue)) +} diff --git a/core/shared/src/main/scala/sigma/data/CGroupElement.scala b/core/shared/src/main/scala/sigma/data/CGroupElement.scala new file mode 100644 index 0000000000..ed4849f0d7 --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/CGroupElement.scala @@ -0,0 +1,29 @@ +package sigma.data + +import sigma.crypto.{CryptoFacade, Ecp} +import sigma.serialization.GroupElementSerializer +import sigma.util.Extensions.EcpOps +import sigma.{BigInt, Coll, Colls, GroupElement} + +/** A default implementation of [[GroupElement]] interface. + * + * @see [[GroupElement]] for detailed descriptions + */ +case class CGroupElement(override val wrappedValue: Ecp) extends GroupElement with WrapperOf[Ecp] { + + override def toString: String = s"GroupElement(${wrappedValue.showECPoint})" + + override def getEncoded: Coll[Byte] = + Colls.fromArray(GroupElementSerializer.toBytes(wrappedValue)) + + override def isIdentity: Boolean = CryptoFacade.isInfinityPoint(wrappedValue) + + override def exp(k: BigInt): GroupElement = + CGroupElement(CryptoFacade.exponentiatePoint(wrappedValue, k.asInstanceOf[CBigInt].wrappedValue)) + + override def multiply(that: GroupElement): GroupElement = + CGroupElement(CryptoFacade.multiplyPoints(wrappedValue, that.asInstanceOf[CGroupElement].wrappedValue)) + + override def negate: GroupElement = + CGroupElement(CryptoFacade.negatePoint(wrappedValue)) +} diff --git a/core/shared/src/main/scala/sigma/data/CSigmaProp.scala b/core/shared/src/main/scala/sigma/data/CSigmaProp.scala new file mode 100644 index 0000000000..d8a472424c --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/CSigmaProp.scala @@ -0,0 +1,61 @@ +package sigma.data + +import sigma.ast.SSigmaProp +import sigma.serialization.CoreSerializer +import sigma.util.Extensions.SigmaBooleanOps +import sigma.{Coll, Colls, GroupElement, SigmaProp} + +/** A default implementation of [[SigmaProp]] interface. + * + * @see [[SigmaProp]] for detailed descriptions + */ +case class CSigmaProp(sigmaTree: SigmaBoolean) extends SigmaProp with WrapperOf[SigmaBoolean] { + override def wrappedValue: SigmaBoolean = sigmaTree + + // TODO refactor: remove this (it shouldn't be used in interpreter) + override def isValid: Boolean = sigmaTree match { + case p: TrivialProp => p.condition + case _ => sys.error(s"Method CostingSigmaProp.isValid is not defined for $sigmaTree") + } + + override def propBytes: Coll[Byte] = { + // in order to have comparisons like `box.propositionBytes == pk.propBytes` we need to make sure + // the same serialization method is used in both cases + // TODO v6.0: add `pk.propBytes(version)` (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/903) + val w = CoreSerializer.startWriter() + w.put(0) // ErgoTree.header + w.putType(SSigmaProp) + SigmaBoolean.serializer.serialize(wrappedValue, w) + Colls.fromArray(w.toBytes) + } + + override def &&(other: SigmaProp): SigmaProp = other match { + case other: CSigmaProp => + CSigmaProp(CAND.normalized(Array(sigmaTree, other.sigmaTree))) + } + + override def ||(other: SigmaProp): SigmaProp = other match { + case other: CSigmaProp => + CSigmaProp(COR.normalized(Array(sigmaTree, other.sigmaTree))) + } + + override def toString: String = s"SigmaProp(${wrappedValue.showToString})" +} + +object CSigmaProp { + /** Create trivial sigma proposition with given boolean value. */ + def apply(b: Boolean): CSigmaProp = + CSigmaProp(TrivialProp(b)) + + /** Create SigmaProp value with underlying ProveDlog proposition. */ + def withProveDlog(ge: GroupElement) = + CSigmaProp(ProveDlog(ge.asInstanceOf[CGroupElement].wrappedValue)) + + /** Create SigmaProp value with underlying ProveDHTuple proposition. */ + def withProveDHTuple(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement) = + CSigmaProp(ProveDHTuple( + g.asInstanceOf[CGroupElement].wrappedValue, + h.asInstanceOf[CGroupElement].wrappedValue, + u.asInstanceOf[CGroupElement].wrappedValue, + v.asInstanceOf[CGroupElement].wrappedValue)) +} diff --git a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala index a5bb543e1b..2413f7f427 100644 --- a/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala +++ b/core/shared/src/main/scala/sigma/data/CollsOverArrays.scala @@ -45,7 +45,7 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil // in v5.0 and above this fixes the ClassCastException problem safeConcatArrays_v5(toArray, other.toArray)(tA.classTag) } else { - CollectionUtil.concatArrays(toArray, other.toArray) + CollectionUtil.concatArrays_v4(toArray, other.toArray) } builder.fromArray(result) } diff --git a/core/shared/src/main/scala/sigma/data/Iso.scala b/core/shared/src/main/scala/sigma/data/Iso.scala new file mode 100644 index 0000000000..9b90d63bb0 --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/Iso.scala @@ -0,0 +1,52 @@ +package sigma.data + +import scorex.util.encode.Base16 +import sigma.Extensions.CollBytesOps +import sigma.{Coll, Colls} + +/** Type-class of isomorphisms between types. + * Isomorphism between two types `A` and `B` essentially say that both types + * represents the same information (entity) but in a different way. + *

+ * The information is not lost so that both are true: + * 1) a == from(to(a)) + * 2) b == to(from(b)) + *

+ * It is used to define type-full conversions: + * - different conversions between Java and Scala data types. + * - conversion between Ergo representations and generated API representations + */ +abstract class Iso[A, B] { + def to(a: A): B + def from(b: B): A + def andThen[C](iso: Iso[B,C]): Iso[A,C] = ComposeIso(iso, this) + def inverse: Iso[B, A] = InverseIso(this) +} +final case class InverseIso[A,B](iso: Iso[A,B]) extends Iso[B,A] { + override def to(a: B): A = iso.from(a) + override def from(b: A): B = iso.to(b) +} +final case class ComposeIso[A, B, C](iso2: Iso[B, C], iso1: Iso[A, B]) extends Iso[A, C] { + def from(c: C): A = iso1.from(iso2.from(c)) + def to(a: A): C = iso2.to(iso1.to(a)) +} + +object Iso { + implicit def identityIso[A]: Iso[A, A] = new Iso[A, A] { + override def to(a: A): A = a + override def from(b: A): A = b + } + + implicit def inverseIso[A, B](implicit iso: Iso[A, B]): Iso[B, A] = InverseIso[A, B](iso) + + val isoStringToArray: Iso[String, Array[Byte]] = new Iso[String, Array[Byte]] { + override def to(x: String): Array[Byte] = Base16.decode(x).get + override def from(x: Array[Byte]): String = Base16.encode(x) + } + + val isoStringToColl: Iso[String, Coll[Byte]] = new Iso[String, Coll[Byte]] { + override def to(x: String): Coll[Byte] = Colls.fromArray(Base16.decode(x).get) + override def from(x: Coll[Byte]): String = x.toHex + } + +} \ No newline at end of file diff --git a/core/shared/src/main/scala/sigma/data/RType.scala b/core/shared/src/main/scala/sigma/data/RType.scala index 281e032aa4..54402f1254 100644 --- a/core/shared/src/main/scala/sigma/data/RType.scala +++ b/core/shared/src/main/scala/sigma/data/RType.scala @@ -48,12 +48,6 @@ case class PrimitiveType[A]( override def name: String = classTag.toString() } -case class StringType() extends RType[String] { - override def classTag: ClassTag[String] = ClassTag[String](classOf[String]) - - override def name: String = "String" -} - /** Descriptor of descriptor to represent type of RType[_] instances. * This describes any possible descriptor, disregarding its underlying type. * Thus the underlying type is assumed to be Any. */ diff --git a/core/shared/src/main/scala/sigma/data/SigmaBoolean.scala b/core/shared/src/main/scala/sigma/data/SigmaBoolean.scala new file mode 100644 index 0000000000..681a7d40d2 --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/SigmaBoolean.scala @@ -0,0 +1,257 @@ +package sigma.data + +import debox.cfor +import sigma.crypto.EcPointType +import sigma.data.SigmaPropCodes.{AndCode, AtLeastCode, OrCode, ProveDiffieHellmanTupleCode, ProveDlogCode, SPCode} +import sigma.data.TrivialProp.{FalseProp, TrueProp} +import sigma.serialization.{CoreByteReader, CoreByteWriter, CoreSerializer, GroupElementSerializer, ProveDHTupleSerializer, ProveDlogSerializer} +import sigma.util.safeNewArray + +import scala.collection.mutable.ArrayBuffer + +/** Algebraic data type of sigma proposition expressions. + * Values of this type are used as values of SigmaProp type of SigmaScript and SigmaDsl + */ +sealed trait SigmaBoolean { + /** Unique id of the node class used in serialization of SigmaBoolean. */ + val opCode: SPCode + + /** Size of the proposition tree (number of nodes). */ + def size: Int +} + +object SigmaBoolean { + /** Compute total size of the trees in the collection of children. */ + def totalSize(children: Seq[SigmaBoolean]): Int = { + var res = 0 + val len = children.length + cfor(0)(_ < len, _ + 1) { i => + res += children(i).size + } + res + } + + /** HOTSPOT: don't beautify this code */ + object serializer extends CoreSerializer[SigmaBoolean, SigmaBoolean] { + val dhtSerializer = ProveDHTupleSerializer(ProveDHTuple.apply) + + val dlogSerializer = ProveDlogSerializer(ProveDlog.apply) + + override def serialize(data: SigmaBoolean, w: CoreByteWriter): Unit = { + w.put(data.opCode) + data match { + case dlog: ProveDlog => dlogSerializer.serialize(dlog, w) + case dht: ProveDHTuple => dhtSerializer.serialize(dht, w) + case _: TrivialProp => // besides opCode no additional bytes + case and: CAND => + val nChildren = and.children.length + w.putUShort(nChildren) + cfor(0)(_ < nChildren, _ + 1) { i => + val c = and.children(i) + serializer.serialize(c, w) + } + case or: COR => + val nChildren = or.children.length + w.putUShort(nChildren) + cfor(0)(_ < nChildren, _ + 1) { i => + val c = or.children(i) + serializer.serialize(c, w) + } + case th: CTHRESHOLD => + w.putUShort(th.k) + val nChildren = th.children.length + w.putUShort(nChildren) + cfor(0)(_ < nChildren, _ + 1) { i => + val c = th.children(i) + serializer.serialize(c, w) + } + } + } + + override def parse(r: CoreByteReader): SigmaBoolean = { + val depth = r.level + r.level = depth + 1 + val opCode = r.getByte() + val res = opCode match { + case FalseProp.opCode => FalseProp + case TrueProp.opCode => TrueProp + case ProveDlogCode => dlogSerializer.parse(r) + case ProveDiffieHellmanTupleCode => dhtSerializer.parse(r) + case AndCode => + val n = r.getUShort() + val children = safeNewArray[SigmaBoolean](n) + cfor(0)(_ < n, _ + 1) { i => + children(i) = serializer.parse(r) + } + CAND(children) + case OrCode => + val n = r.getUShort() + val children = safeNewArray[SigmaBoolean](n) + cfor(0)(_ < n, _ + 1) { i => + children(i) = serializer.parse(r) + } + COR(children) + case AtLeastCode => + val k = r.getUShort() + val n = r.getUShort() + val children = safeNewArray[SigmaBoolean](n) + cfor(0)(_ < n, _ + 1) { i => + children(i) = serializer.parse(r) + } + CTHRESHOLD(k, children) + } + r.level = r.level - 1 + res + } + } +} + +/** + * Basic trait for inner nodes of crypto-trees, so AND/OR/THRESHOLD sigma-protocol connectives + */ +trait SigmaConjecture extends SigmaBoolean { + def children: Seq[SigmaBoolean] +} + +/** + * Basic trait for leafs of crypto-trees, such as + * [[sigmastate.crypto.DLogProtocol.ProveDlog]] and [[sigmastate.crypto.ProveDHTuple]] + * instances. + * It plays the same role as [[SigmaConjecture]]. It used in prover to distinguish leafs from + * other nodes and have logic common to leaves regardless of the concrete leaf type. + */ +trait SigmaLeaf extends SigmaBoolean + +/** Construct a new SigmaBoolean value representing public key of discrete logarithm signature protocol. */ +case class ProveDlog(value: EcPointType) extends SigmaLeaf { + override def size: Int = 1 + override val opCode : SPCode = SigmaPropCodes.ProveDlogCode + + /** Serialized bytes of the elliptic curve point (using GroupElementSerializer). */ + lazy val pkBytes: Array[Byte] = GroupElementSerializer.toBytes(value) +} + +/** Construct a new SigmaProp value representing public key of Diffie Hellman signature protocol. + * Common input: (g,h,u,v) */ +case class ProveDHTuple(gv: EcPointType, hv: EcPointType, uv: EcPointType, vv: EcPointType) + extends SigmaLeaf { + override val opCode: SPCode = SigmaPropCodes.ProveDiffieHellmanTupleCode + override def size: Int = 4 // one node for each EcPoint + lazy val g = gv + lazy val h = hv + lazy val u = uv + lazy val v = vv +} + +/** + * AND conjunction for sigma propositions + */ +case class CAND(override val children: Seq[SigmaBoolean]) extends SigmaConjecture { + /** The same code is used for AND operation, but they belong to different type hierarchies. */ + override val opCode: SPCode = SigmaPropCodes.AndCode + override val size: Int = SigmaBoolean.totalSize(children) + 1 +} + +object CAND { + import TrivialProp._ + + /** Connects the given sigma propositions into CAND proposition performing + * partial evaluation when some of them are trivial propositioins. + * + * @param items propositions to combine into CAND + * @return CAND, TrueProp, FalseProp or even one of the items depending on partial evaluation + */ + def normalized(items: Seq[SigmaBoolean]): SigmaBoolean = { + require(items.nonEmpty) + val res = new ArrayBuffer[SigmaBoolean]() + val nItems = items.length + cfor(0)(_ < nItems, _ + 1) { i => + val x = items(i) + x match { + case FalseProp => return FalseProp + case TrueProp => // skip + case _ => res += x + } + } + if (res.isEmpty) TrueProp + else if (res.length == 1) res(0) + else CAND(res.toSeq) + } +} + +/** + * OR disjunction for sigma propositions + */ +case class COR(children: Seq[SigmaBoolean]) extends SigmaConjecture { + /** The same code is also used for OR operation, but they belong to different type hierarchies. */ + override val opCode: SPCode = SigmaPropCodes.OrCode + override val size: Int = SigmaBoolean.totalSize(children) + 1 +} + +object COR { + import TrivialProp._ + + /** Connects the given sigma propositions into COR proposition performing + * partial evaluation when some of them are trivial propositioins. + * + * @param items propositions to combine into COR + * @return COR, TrueProp, FalseProp or even one of the items depending on partial evaluation + */ + def normalized(items: Seq[SigmaBoolean]): SigmaBoolean = { + require(items.nonEmpty) + val res = new ArrayBuffer[SigmaBoolean]() + val nItems = items.length + cfor(0)(_ < nItems, _ + 1) { i => + val x = items(i) + x match { + case FalseProp => // skip + case TrueProp => return TrueProp + case _ => res += x + } + } + if (res.isEmpty) FalseProp + else if (res.length == 1) res(0) + else COR(res.toSeq) + } +} + +/** + * THRESHOLD connector for sigma propositions + */ +case class CTHRESHOLD(k: Int, children: Seq[SigmaBoolean]) extends SigmaConjecture { + // Our polynomial arithmetic can take only byte inputs + require(k >= 0 && k <= children.length && children.length <= 255) + + override val opCode: SPCode = SigmaPropCodes.AtLeastCode + override val size: Int = SigmaBoolean.totalSize(children) + 1 +} + + +/** Represents boolean values (true/false) in SigmaBoolean tree. + * Participates in evaluation of CAND, COR, THRESHOLD connectives over SigmaBoolean values. + * See CAND.normalized, COR.normalized and AtLeast.reduce. */ +abstract class TrivialProp(val condition: Boolean) extends SigmaBoolean with Product1[Boolean] { + override def _1: Boolean = condition + override def canEqual(that: Any): Boolean = that != null && that.isInstanceOf[TrivialProp] +} +object TrivialProp { + // NOTE: the corresponding unapply is missing because any implementation (even using Nullable) + // will lead to Boolean boxing, which we want to avoid + // So, instead of `case TrivialProp(b) => ... b ...` use more efficient + // `case p: TrivialProp => ... p.condition ... + + def apply(b: Boolean): TrivialProp = if (b) TrueProp else FalseProp + + val FalseProp = new TrivialProp(false) { + override val opCode: SPCode = SigmaPropCodes.TrivialPropFalseCode + override def size: Int = 1 + override def toString = "FalseProp" + } + val TrueProp = new TrivialProp(true) { + override val opCode: SPCode = SigmaPropCodes.TrivialPropTrueCode + override def size: Int = 1 + override def toString = "TrueProp" + } +} + + diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaConstants.scala b/core/shared/src/main/scala/sigma/data/SigmaConstants.scala similarity index 94% rename from interpreter/shared/src/main/scala/org/ergoplatform/SigmaConstants.scala rename to core/shared/src/main/scala/sigma/data/SigmaConstants.scala index 74dc98ade9..7198d5dc09 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaConstants.scala +++ b/core/shared/src/main/scala/sigma/data/SigmaConstants.scala @@ -1,7 +1,6 @@ -package org.ergoplatform +package sigma.data import sigma.util.CollectionUtil.TraversableOps // used in Scala 2.11 -import sigmastate.crypto.CryptoConstants /** Descriptor of a constant which represents some size value. * @tparam T type of the constant value @@ -42,7 +41,8 @@ object SigmaConstants { "Max length of Box.propositionBytes collection") { } - object MaxBoxSizeWithoutRefs extends SizeConstant[Int](MaxBoxSize.value - (CryptoConstants.hashLength + 2/*size of Short*/), 6, + object MaxBoxSizeWithoutRefs extends SizeConstant[Int]( + MaxBoxSize.value - (32/*CryptoConstants.hashLength*/ + 2/*size of Short*/), 6, "Box size should not be greater than provided value") { } diff --git a/core/shared/src/main/scala/sigma/data/SigmaPropCodes.scala b/core/shared/src/main/scala/sigma/data/SigmaPropCodes.scala new file mode 100644 index 0000000000..fafd24ffdd --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/SigmaPropCodes.scala @@ -0,0 +1,23 @@ +package sigma.data + +import sigma.ast.TypeCodes.LastConstantCode +import supertagged.TaggedType + +/** Opcodes of sigma proposition nodes. + * + * @see SigmaBoolean.opCode + */ +object SigmaPropCodes { + object SPCode extends TaggedType[Byte] + type SPCode = SPCode.Type + private def newOpCode(shift: Short): SPCode = SPCode @@ (LastConstantCode + shift).toByte + + val AndCode : SPCode = newOpCode(38) + val OrCode : SPCode = newOpCode(39) + val AtLeastCode : SPCode = newOpCode(40) + val ProveDlogCode : SPCode = newOpCode(93) + val ProveDiffieHellmanTupleCode: SPCode = newOpCode(94) + val TrivialPropFalseCode : SPCode = newOpCode(98) + val TrivialPropTrueCode : SPCode = newOpCode(99) +} + diff --git a/core/shared/src/main/scala/sigma/data/WrapperOf.scala b/core/shared/src/main/scala/sigma/data/WrapperOf.scala new file mode 100644 index 0000000000..7a6a98c217 --- /dev/null +++ b/core/shared/src/main/scala/sigma/data/WrapperOf.scala @@ -0,0 +1,7 @@ +package sigma.data + +/** Interface implmented by wrappers to provide access to the underlying wrapped value. */ +trait WrapperOf[T] { + /** The data value wrapped by this wrapper. */ + def wrappedValue: T +} diff --git a/core/shared/src/main/scala/sigma/data/package.scala b/core/shared/src/main/scala/sigma/data/package.scala index 8c34f7e9b9..c5a35f7b5f 100644 --- a/core/shared/src/main/scala/sigma/data/package.scala +++ b/core/shared/src/main/scala/sigma/data/package.scala @@ -1,5 +1,7 @@ package sigma +import supertagged.TaggedType + import scala.annotation.nowarn import scala.reflect.classTag @@ -10,9 +12,11 @@ package object data { */ @nowarn private def rtypeToClassTag = ??? + val StringClassTag = classTag[String] val BigIntClassTag = classTag[BigInt] val GroupElementClassTag = classTag[GroupElement] val SigmaPropClassTag = classTag[SigmaProp] + val SigmaBooleanClassTag = classTag[SigmaBoolean] val AvlTreeClassTag = classTag[AvlTree] val BoxClassTag = classTag[Box] val ContextClassTag = classTag[Context] @@ -38,4 +42,19 @@ package object data { */ def emptyDBufferOfInt: debox.Buffer[Int] = debox.Buffer.unsafe(EmptyArrayOfInt) + /** Constructor of tuple value with more than 2 items. + * Such long tuples are represented as Coll[Any]. + * This representaion of tuples is different from representation of pairs (x, y), + * where Tuple2 type is used instead of Coll. */ + def TupleColl(items: Any*): Coll[Any] = Colls.fromItems(items: _*)(sigma.AnyType) + + type KeyValueColl = Coll[(Coll[Byte], Coll[Byte])] + + trait BaseDigestColl extends TaggedType[Coll[Byte]] + + object Digest32Coll extends BaseDigestColl + + type Digest32Coll = Digest32Coll.Type + + implicit val Digest32CollRType: RType[data.Digest32Coll] = RType[Coll[Byte]].asInstanceOf[RType[data.Digest32Coll]] } diff --git a/core/shared/src/main/scala/sigma/package.scala b/core/shared/src/main/scala/sigma/package.scala index f7f2321096..89b883f52d 100644 --- a/core/shared/src/main/scala/sigma/package.scala +++ b/core/shared/src/main/scala/sigma/package.scala @@ -23,11 +23,14 @@ package object sigma { implicit val UnitType : RType[Unit] = PrimitiveType[Unit](ClassTag.Unit, Array[Unit]()(ClassTag.Unit)) - implicit val StringType : RType[String] = sigma.data.StringType() + implicit val StringType : RType[String] = GeneralType(StringClassTag) - implicit val BigIntRType: RType[BigInt] = GeneralType(BigIntClassTag) + implicit val BigIntRType : RType[BigInt] = GeneralType(BigIntClassTag) implicit val GroupElementRType: RType[GroupElement] = GeneralType(GroupElementClassTag) - implicit val SigmaPropRType: RType[SigmaProp] = GeneralType(SigmaPropClassTag) + implicit val SigmaPropRType : RType[SigmaProp] = GeneralType(SigmaPropClassTag) + implicit val SigmaBooleanRType: RType[SigmaBoolean] = GeneralType(SigmaBooleanClassTag) + + implicit val AvlTreeRType: RType[AvlTree] = GeneralType(AvlTreeClassTag) implicit val BoxRType: RType[Box] = GeneralType(BoxClassTag) diff --git a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala index cdab5d5be7..028e68bf72 100644 --- a/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala +++ b/core/shared/src/main/scala/sigma/reflection/ReflectionData.scala @@ -1,6 +1,7 @@ package sigma.reflection import sigma._ +import sigma.ast.{SCollectionType, SOption, STuple, SType} import sigma.data.RType import scala.collection.compat.immutable.ArraySeq @@ -8,9 +9,15 @@ import scala.collection.mutable import scala.collection.immutable /** Reflection metadata and global dictionaries to access it. - * For each class of this module that needs reflection metadata, - * we register a class entry with the necessary information. + * Such metadata is only used on JS platform to support reflection-like interfaces of + * RClass, RMethod, RConstructor. These interfaces implemented on JVM using Java + * reflection. + * + * For each class that needs reflection metadata, we register a class entry using + * `registerClassEntry` method with the necessary information such as constructors and + * methods. * Only information that is needed at runtime is registered. + * @see mkConstructor, mkMethod */ object ReflectionData { /** Descriptors of classes. */ @@ -174,14 +181,6 @@ object ReflectionData { mkMethod(clazz, "updateOperations", Array[Class[_]](classOf[Byte])) { (obj, args) => obj.asInstanceOf[AvlTree].updateOperations(args(0).asInstanceOf[Byte]) }, - mkMethod(clazz, "getMany", Array[Class[_]](classOf[Coll[_]], classOf[Coll[_]])) { (obj, args) => - obj.asInstanceOf[AvlTree].getMany(args(0).asInstanceOf[Coll[Coll[Byte]]], - args(1).asInstanceOf[Coll[Byte]]) - }, - mkMethod(clazz, "update", Array[Class[_]](classOf[Coll[_]], classOf[Coll[_]])) { (obj, args) => - obj.asInstanceOf[AvlTree].update(args(0).asInstanceOf[Coll[(Coll[Byte], Coll[Byte])]], - args(1).asInstanceOf[Coll[Byte]]) - }, mkMethod(clazz, "keyLength", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[AvlTree].keyLength }, @@ -194,27 +193,12 @@ object ReflectionData { mkMethod(clazz, "digest", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[AvlTree].digest }, - mkMethod(clazz, "insert", Array[Class[_]](classOf[Coll[_]], classOf[Coll[_]])) { (obj, args) => - obj.asInstanceOf[AvlTree].insert(args(0).asInstanceOf[Coll[(Coll[Byte], Coll[Byte])]], - args(1).asInstanceOf[Coll[Byte]]) - }, mkMethod(clazz, "isRemoveAllowed", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[AvlTree].isRemoveAllowed }, mkMethod(clazz, "valueLengthOpt", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[AvlTree].valueLengthOpt }, - mkMethod(clazz, "get", Array[Class[_]](classOf[Coll[_]], classOf[Coll[_]])) { (obj, args) => - obj.asInstanceOf[AvlTree].get(args(0).asInstanceOf[Coll[Byte]], - args(1).asInstanceOf[Coll[Byte]]) - }, - mkMethod(clazz, "remove", Array[Class[_]](classOf[Coll[_]], classOf[Coll[_]])) { (obj, args) => - obj.asInstanceOf[AvlTree].remove(args(0).asInstanceOf[Coll[Coll[Byte]]], args(1).asInstanceOf[Coll[Byte]]) - }, - mkMethod(clazz, "contains", Array[Class[_]](classOf[Coll[_]], classOf[Coll[_]])) { (obj, args) => - obj.asInstanceOf[AvlTree].contains(args(0).asInstanceOf[Coll[Byte]], - args(1).asInstanceOf[Coll[Byte]]) - }, mkMethod(clazz, "isUpdateAllowed", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[AvlTree].isUpdateAllowed }, @@ -463,4 +447,28 @@ object ReflectionData { ) ) } + + registerClassEntry(classOf[SCollectionType[_]], + constructors = Array( + mkConstructor(Array(classOf[SType])) { args => + new SCollectionType(args(0).asInstanceOf[SType]) + } + ) + ) + + registerClassEntry(classOf[SOption[_]], + constructors = Array( + mkConstructor(Array(classOf[SType])) { args => + new SOption(args(0).asInstanceOf[SType]) + } + ) + ) + + registerClassEntry(classOf[STuple], + constructors = Array( + mkConstructor(Array(classOf[IndexedSeq[_]])) { args => + new STuple(args(0).asInstanceOf[IndexedSeq[SType]]) + } + ) + ) } diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/SigmaByteReader.scala b/core/shared/src/main/scala/sigma/serialization/CoreByteReader.scala similarity index 66% rename from interpreter/shared/src/main/scala/sigmastate/utils/SigmaByteReader.scala rename to core/shared/src/main/scala/sigma/serialization/CoreByteReader.scala index 00f900c741..34ed74ac6a 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utils/SigmaByteReader.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreByteReader.scala @@ -1,14 +1,9 @@ -package sigmastate.utils +package sigma.serialization -import org.ergoplatform.validation.ValidationRules.CheckPositionLimit import scorex.util.Extensions._ import scorex.util.serialization.Reader -import sigmastate.SType -import sigmastate.Values.{SValue, Value} -import sigmastate.serialization._ -import sigma.util.safeNewArray -import debox.cfor -import sigmastate.exceptions.DeserializeCallDepthExceeded +import sigma.ast.SType +import sigma.validation.ValidationRules.CheckPositionLimit /** Reader used in the concrete implementations of [[SigmaSerializer]]. * It decorates the given reader, delegates most of the methods to it, but also adds new @@ -16,16 +11,13 @@ import sigmastate.exceptions.DeserializeCallDepthExceeded * * @param r the underlying reader this reader reads from * @param constantStore the store of constants which is used to resolve - * [[sigmastate.Values.ConstantPlaceholder]] + * [[sigma.ast.ConstantPlaceholder]] * @param resolvePlaceholdersToConstants if true then resolved constants will be * substituted in the tree instead of the placeholder. * @param maxTreeDepth limit on the tree depth (recursive invocations) * of the deserializer */ -class SigmaByteReader(val r: Reader, - var constantStore: ConstantStore, - var resolvePlaceholdersToConstants: Boolean, - val maxTreeDepth: Int = SigmaSerializer.MaxTreeDepth) +class CoreByteReader(val r: Reader, val maxTreeDepth: Int = CoreSerializer.MaxTreeDepth) extends Reader { /** Checks that the current reader position is <= positionLimit. @@ -34,17 +26,10 @@ class SigmaByteReader(val r: Reader, * other than that both v4.x and v5.x will work and fail at the same time, so the * change is consensus-safe. */ - @inline private def checkPositionLimit(): Unit = { + @inline protected def checkPositionLimit(): Unit = { CheckPositionLimit(position, positionLimit) } - /** 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 @inline @@ -116,15 +101,6 @@ class SigmaByteReader(val r: Reader, r.getBytes(bytesToRead) } - /** Returns all bytes of the underlying ByteBuffer. */ - private[sigmastate] def getAllBufferBytes: Array[Byte] = { - val savedPos = position - position = 0 - val res = getBytesUnsafe(remaining) - position = savedPos - res - } - @inline override def getBits(size: Int): Array[Boolean] = { checkPositionLimit() r.getBits(size) @@ -149,7 +125,6 @@ class SigmaByteReader(val r: Reader, } @inline def getType(): SType = TypeSerializer.deserialize(this) - @inline def getValue(): SValue = ValueSerializer.deserialize(this) private var lvl: Int = 0 @inline def level: Int = lvl @@ -159,44 +134,24 @@ class SigmaByteReader(val r: Reader, lvl = v } - /** Read sequence of values from this reader. - * It first reads the number of values and then reads each value using `getValue` method. - * - * @return a sequence of zero of more values read - */ - @inline def getValues(): IndexedSeq[SValue] = { - val size = getUIntExact - if (size == 0) Value.EmptySeq // quick short-cut when there is nothing to read - else { - val xs = safeNewArray[SValue](size) - cfor(0)(_ < size, _ + 1) { i => - xs(i) = getValue() - } - xs - } - } - private var positionLmt: Int = r.position + r.remaining @inline final def positionLimit: Int = positionLmt @inline final def positionLimit_=(v: Int): Unit = { positionLmt = v } - private var _complexity: Int = 0 - /** Helper property which is used to accumulate complexity during parsing. */ - @inline final def complexity: Int = _complexity - @inline final def complexity_=(v: Int): Unit = { - _complexity = v - } - - @inline final def addComplexity(delta: Int): Unit = { - _complexity += delta - } - private var _wasDeserialize: Boolean = false /** Helper property which is used to track deserialization operations during parsing. */ @inline final def wasDeserialize: Boolean = _wasDeserialize @inline final def wasDeserialize_=(v: Boolean): Unit = { _wasDeserialize = v } + + private var _wasUsingBlockchainContext: Boolean = false + /** Helper property which is used to track operations using the blockchain context during parsing. */ + @inline final def wasUsingBlockchainContext: Boolean = _wasUsingBlockchainContext + + @inline final def wasUsingBlockchainContext_=(v: Boolean): Unit = { + _wasUsingBlockchainContext = v + } } diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/SigmaByteWriter.scala b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala similarity index 77% rename from interpreter/shared/src/main/scala/sigmastate/utils/SigmaByteWriter.scala rename to core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala index 4c3ef03610..aa4255449c 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utils/SigmaByteWriter.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreByteWriter.scala @@ -1,15 +1,14 @@ -package sigmastate.utils +package sigma.serialization -import scorex.util.serialization.{VLQByteBufferWriter, Writer} import scorex.util.serialization.Writer.Aux -import sigmastate.{ArgInfo, SType} -import sigmastate.Values.{Value, SValue} -import sigmastate.serialization.{TypeSerializer, ValueSerializer, ConstantStore} - -class SigmaByteWriter(val w: Writer, - val constantExtractionStore: Option[ConstantStore]) extends Writer { - import SigmaByteWriter._ - import ValueSerializer._ +import scorex.util.serialization.{VLQByteBufferWriter, Writer} +import sigma.ast.SType +import sigma.serialization.CoreByteWriter.{Bits, DataInfo, U, Vlq, ZigZag} + +/** Implementation of [[Writer]] provided by `sigma-core` module. + * @param w destination [[Writer]] to which all the call got delegated. + */ +class CoreByteWriter(val w: Writer) extends Writer { type CH = w.CH @inline override def length(): Int = w.length() @@ -22,7 +21,6 @@ class SigmaByteWriter(val w: Writer, @inline def put(x: Byte): this.type = { w.put(x); this } @inline def put(x: Byte, info: DataInfo[Byte]): this.type = { - ValueSerializer.addArgInfo(info) w.put(x); this } @@ -30,49 +28,41 @@ class SigmaByteWriter(val w: Writer, super.putUByte(x) } def putUByte(x: Int, info: DataInfo[U[Byte]]): this.type = { - ValueSerializer.addArgInfo(info) super.putUByte(x) } @inline def putBoolean(x: Boolean): this.type = { w.putBoolean(x); this } @inline def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { - ValueSerializer.addArgInfo(info) w.putBoolean(x); this } @inline def putShort(x: Short): this.type = { w.putShort(x); this } @inline def putShort(x: Short, info: DataInfo[Short]): this.type = { - ValueSerializer.addArgInfo(info) w.putShort(x); this } @inline def putUShort(x: Int): this.type = { w.putUShort(x); this } @inline def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { - ValueSerializer.addArgInfo(info) w.putUShort(x); this } @inline def putInt(x: Int): this.type = { w.putInt(x); this } @inline def putInt(x: Int, info: DataInfo[Int]): this.type = { - ValueSerializer.addArgInfo(info) w.putInt(x); this } @inline def putUInt(x: Long): this.type = { w.putUInt(x); this } @inline def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { - ValueSerializer.addArgInfo(info) w.putUInt(x); this } @inline def putLong(x: Long): this.type = { w.putLong(x); this } @inline def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { - ValueSerializer.addArgInfo(info) w.putLong(x); this } @inline def putULong(x: Long): this.type = { w.putULong(x); this } @inline def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { - ValueSerializer.addArgInfo(info) w.putULong(x); this } @@ -83,7 +73,6 @@ class SigmaByteWriter(val w: Writer, } @inline def putBytes(xs: Array[Byte]): this.type = { w.putBytes(xs); this } @inline def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { - ValueSerializer.addArgInfo(info) w.putBytes(xs); this } @@ -97,7 +86,6 @@ class SigmaByteWriter(val w: Writer, @inline def putBits(xs: Array[Boolean]): this.type = { w.putBits(xs); this } @inline def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { - ValueSerializer.addArgInfo(info) w.putBits(xs); this } @@ -118,30 +106,12 @@ class SigmaByteWriter(val w: Writer, @inline def putType[T <: SType](x: T): this.type = { TypeSerializer.serialize(x, this); this } @inline def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { - ValueSerializer.addArgInfo(info) TypeSerializer.serialize(x, this); this } - @inline def putValue[T <: SType](x: Value[T]): this.type = { ValueSerializer.serialize(x, this); this } - @inline def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = { - ValueSerializer.addArgInfo(info) - ValueSerializer.serialize(x, this); this - } - @inline def putValues[T <: SType](xs: Seq[Value[T]]): this.type = { - putUInt(xs.length) - xs.foreach(putValue(_)) - this - } - @inline def putValues[T <: SType](xs: Seq[Value[T]], info: DataInfo[Seq[SValue]], itemInfo: DataInfo[SValue]): this.type = { - putUInt(xs.length, valuesLengthInfo) - foreach("\\#items", xs) { x => - putValue(x, itemInfo) - } - this - } } -object SigmaByteWriter { +object CoreByteWriter { import scala.language.implicitConversions /** Format descriptor type family. */ @@ -171,10 +141,6 @@ object SigmaByteWriter { */ trait Bits - implicit case object ValueFmt extends FormatDescriptor[SValue] { - override def size: String = "[1, *]" - override def toString: String = "Expr" - } implicit case object TypeFmt extends FormatDescriptor[SType] { override def size: String = "[1, *]" override def toString: String = "Type" @@ -253,21 +219,26 @@ object SigmaByteWriter { implicit def toUVlqFmt[T](implicit fmt: FormatDescriptor[U[T]]): FormatDescriptor[Vlq[U[T]]] = UVlqFmt(fmt) implicit def toZigZagVlqFmt[T](implicit fmt: FormatDescriptor[ZigZag[T]]): FormatDescriptor[Vlq[ZigZag[T]]] = ZigZagVlqFmt(fmt) implicit def toSeqFmt[T](implicit fmt: FormatDescriptor[T]): FormatDescriptor[Seq[T]] = SeqFmt(fmt) - case class DataInfo[T](info: ArgInfo, format: FormatDescriptor[T]) - def bitsInfo(name: String, desc: String = ""): DataInfo[Bits] = DataInfo(ArgInfo(name, desc), BitsFmt) - def maxBitsInfo(name: String, maxBits: Int, desc: String = ""): DataInfo[Bits] = DataInfo(ArgInfo(name, desc), MaxBitsFmt(maxBits)) + /** Meta information which can be attached to each argument of SMethod. + * + * @param name name of the argument + * @param description argument description. */ + case class ArgInfo(name: String, description: String) - val valuesLengthInfo: DataInfo[Vlq[U[Int]]] = ArgInfo("\\#items", "number of items in the collection") + case class DataInfo[T](info: ArgInfo, format: FormatDescriptor[T]) + + object DataInfo { + implicit def argnfoToDataInfo[T](arg: ArgInfo)(implicit fmt: FormatDescriptor[T]): DataInfo[T] = DataInfo(arg, fmt) - def valuesItemInfo(info: DataInfo[Seq[SValue]]): DataInfo[SValue] = { - val itemFmt = info.format.asInstanceOf[SeqFmt[SValue]].fmt - DataInfo(ArgInfo(info.info.name+"_i", s"i-th item in the ${info.info.description}"), itemFmt) + // TODO refactor: remove this conversion and make it explicit + /** Helper conversion */ + implicit def nameToDataInfo[T](name: String) + (implicit fmt: FormatDescriptor[T]): DataInfo[T] = ArgInfo(name, "") } - implicit def argInfoToDataInfo[T](arg: ArgInfo)(implicit fmt: FormatDescriptor[T]): DataInfo[T] = DataInfo(arg, fmt) + def bitsInfo(name: String, desc: String = ""): DataInfo[Bits] = DataInfo(ArgInfo(name, desc), BitsFmt) + def maxBitsInfo(name: String, maxBits: Int, desc: String = ""): DataInfo[Bits] = DataInfo(ArgInfo(name, desc), MaxBitsFmt(maxBits)) - // TODO refactor: remove this conversion and make it explicit - /**Helper conversion */ - implicit def nameToDataInfo[T](name: String)(implicit fmt: FormatDescriptor[T]): DataInfo[T] = ArgInfo(name, "") + val valuesLengthInfo: DataInfo[Vlq[U[Int]]] = ArgInfo("\\#items", "number of items in the collection") } diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/DataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala similarity index 77% rename from interpreter/shared/src/main/scala/sigmastate/serialization/DataSerializer.scala rename to core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala index 65fb2cc0c7..479b199da5 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/DataSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala @@ -1,28 +1,24 @@ -package sigmastate.serialization +package sigma.serialization + +import debox.cfor +import sigma.ast._ +import sigma.data._ +import sigma.util.Extensions.{CoreAvlTreeOps, BigIntOps, GroupElementOps, SigmaPropOps} +import sigma.validation.ValidationRules.CheckSerializableTypeCode +import sigma.{Evaluation, _} import java.math.BigInteger import java.nio.charset.StandardCharsets - -import org.ergoplatform.ErgoBox -import org.ergoplatform.validation.ValidationRules.CheckSerializableTypeCode -import sigma.data.RType -import sigmastate.Values.SigmaBoolean -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate._ -import sigmastate.eval.{Evaluation, _} -import sigma._ -import debox.cfor -import sigmastate.exceptions.SerializerException import scala.collection.mutable /** This works in tandem with ConstantSerializer, if you change one make sure to check the other.*/ -object DataSerializer { +class CoreDataSerializer { /** Use type descriptor `tpe` to deconstruct type structure and recursively serialize subcomponents. * Primitive types are leaves of the type tree, and they are served as basis of recursion. * The data value `v` is expected to conform to the type described by `tpe`. */ - def serialize[T <: SType](v: T#WrappedType, tpe: T, w: SigmaByteWriter): Unit = tpe match { + def serialize[T <: SType](v: T#WrappedType, tpe: T, w: CoreByteWriter): Unit = tpe match { case SUnit => // don't need to save anything case SBoolean => w.putBoolean(v.asInstanceOf[Boolean]) case SByte => w.put(v.asInstanceOf[Byte]) @@ -34,19 +30,16 @@ object DataSerializer { w.putUInt(bytes.length) w.putBytes(bytes) case SBigInt => - val data = SigmaDsl.toBigInteger(v.asInstanceOf[BigInt]).toByteArray + val data = v.asInstanceOf[BigInt].toBigInteger.toByteArray w.putUShort(data.length) w.putBytes(data) case SGroupElement => - GroupElementSerializer.serialize(groupElementToECPoint(v.asInstanceOf[GroupElement]), w) + GroupElementSerializer.serialize(v.asInstanceOf[GroupElement].toECPoint, w) case SSigmaProp => val p = v.asInstanceOf[SigmaProp] - SigmaBoolean.serializer.serialize(sigmaPropToSigmaBoolean(p), w) - case SBox => - val b = v.asInstanceOf[Box] - ErgoBox.sigmaSerializer.serialize(boxToErgoBox(b), w) + SigmaBoolean.serializer.serialize(p.toSigmaBoolean, w) case SAvlTree => - AvlTreeData.serializer.serialize(avlTreeToAvlTreeData(v.asInstanceOf[AvlTree]), w) + AvlTreeData.serializer.serialize(v.asInstanceOf[AvlTree].toAvlTreeData, w) case tColl: SCollectionType[a] => val coll = v.asInstanceOf[tColl.WrappedType] w.putUShort(coll.length) @@ -86,7 +79,7 @@ object DataSerializer { * The data structure depth is limited by r.maxTreeDepth which is * SigmaSerializer.MaxTreeDepth by default. */ - def deserialize[T <: SType](tpe: T, r: SigmaByteReader): T#WrappedType = { + def deserialize[T <: SType](tpe: T, r: CoreByteReader): T#WrappedType = { val depth = r.level r.level = depth + 1 val res = (tpe match { @@ -109,15 +102,13 @@ object DataSerializer { throw new SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") } val valueBytes = r.getBytes(size) - SigmaDsl.BigInt(new BigInteger(valueBytes)) + CBigInt(new BigInteger(valueBytes)) case SGroupElement => - SigmaDsl.GroupElement(GroupElementSerializer.parse(r)) + CGroupElement(GroupElementSerializer.parse(r)) case SSigmaProp => - SigmaDsl.SigmaProp(SigmaBoolean.serializer.parse(r)) - case SBox => - SigmaDsl.Box(ErgoBox.sigmaSerializer.parse(r)) + CSigmaProp(SigmaBoolean.serializer.parse(r)) case SAvlTree => - SigmaDsl.avlTree(AvlTreeData.serializer.parse(r)) + CAvlTree(AvlTreeData.serializer.parse(r)) case tColl: SCollectionType[a] => val len = r.getUShort() deserializeColl(len, tColl.elemType, r) @@ -135,7 +126,7 @@ object DataSerializer { res } - def deserializeColl[T <: SType](len: Int, tpeElem: T, r: SigmaByteReader): Coll[T#WrappedType] = + def deserializeColl[T <: SType](len: Int, tpeElem: T, r: CoreByteReader): Coll[T#WrappedType] = tpeElem match { case SBoolean => Colls.fromArray(r.getBits(len)).asInstanceOf[Coll[T#WrappedType]] @@ -160,3 +151,5 @@ object DataSerializer { Colls.fromArray(b.result()) } } + +object CoreDataSerializer extends CoreDataSerializer \ No newline at end of file diff --git a/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala new file mode 100644 index 0000000000..938d3f22c1 --- /dev/null +++ b/core/shared/src/main/scala/sigma/serialization/CoreSerializer.scala @@ -0,0 +1,74 @@ +package sigma.serialization + +import scorex.util.ByteArrayBuilder +import scorex.util.serialization.{Serializer, VLQByteBufferReader, VLQByteBufferWriter} +import sigma.data.SigmaConstants + +import java.nio.ByteBuffer + +/** Implementation of [[Serializer]] provided by `sigma-core` module. */ +abstract class CoreSerializer[TFamily, T <: TFamily] extends Serializer[TFamily, T, CoreByteReader, CoreByteWriter] { + + def error(msg: String) = throw SerializerException(msg, None) + + /** Serializes the given 'obj' to a new array of bytes using this serializer. */ + final def toBytes(obj: T): Array[Byte] = { + val w = CoreSerializer.startWriter() + serialize(obj, w) + w.toBytes + } + + /** Deserializes `bytes` to an object of this [[TFamily]] using this serializer. + * The actual class of the returned object is expected to be descendant of [[TFamily]]. + */ + final def fromBytes(bytes: Array[Byte]): TFamily = { + parse(CoreSerializer.startReader(bytes)) + } +} + +object CoreSerializer { + /** Max length of Box.propositionBytes collection */ + val MaxPropositionSize: Int = SigmaConstants.MaxPropositionBytes.value + + /** Max tree depth should not be greater then provided value */ + val MaxTreeDepth: Int = SigmaConstants.MaxTreeDepth.value + + /** Helper function to be use in serializers. + * Starting position is marked and then used to compute number of consumed bytes. + * val r = Serializer.startReader(bytes, pos) + * val obj = r.getValue() + * obj -> r.consumed + */ + def startReader(bytes: Array[Byte], + pos: Int = 0): CoreByteReader = { + val buf = ByteBuffer.wrap(bytes) + buf.position(pos) + val r = new CoreByteReader( + new VLQByteBufferReader(buf), + maxTreeDepth = MaxTreeDepth + ).mark() + r + } + + /** Helper function to be use in serializers. */ + def startReader(bytes: Array[Byte]): CoreByteReader = { + val buf = ByteBuffer.wrap(bytes) + val r = new CoreByteReader(new VLQByteBufferReader(buf), + maxTreeDepth = MaxTreeDepth).mark() + r + } + + /** Helper function to be use in serializers. + * val w = Serializer.startWriter() + * w.putLong(l) + * val res = w.toBytes + * res */ + def startWriter(): CoreByteWriter = { + val b = new ByteArrayBuilder() + val wi = new VLQByteBufferWriter(b) + val w = new CoreByteWriter(wi) + w + } + +} + diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/GroupElementSerializer.scala b/core/shared/src/main/scala/sigma/serialization/GroupElementSerializer.scala similarity index 65% rename from interpreter/shared/src/main/scala/sigmastate/serialization/GroupElementSerializer.scala rename to core/shared/src/main/scala/sigma/serialization/GroupElementSerializer.scala index e54c41e426..4fd826ace1 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/GroupElementSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/GroupElementSerializer.scala @@ -1,10 +1,7 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.crypto.CryptoConstants -import sigmastate.crypto.CryptoFacade -import CryptoConstants.EcPointType +import sigma.crypto.{CryptoContext, CryptoFacade, EcPointType} import sigma.util.safeNewArray -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} /** * A serializer which encodes group elements, so elliptic curve points in our case, to bytes, and decodes points @@ -15,13 +12,12 @@ import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} * Special case is infinity point, which is encoded by 33 zeroes. * Thus elliptic curve point is always encoded with 33 bytes. */ -object GroupElementSerializer extends SigmaSerializer[EcPointType, EcPointType] { +object GroupElementSerializer extends CoreSerializer[EcPointType, EcPointType] { - private val curve = CryptoConstants.dlogGroup - private val encodingSize = 1 + CryptoConstants.groupSize + private val encodingSize = 1 + sigma.crypto.groupSize private lazy val identityPointEncoding = Array.fill(encodingSize)(0: Byte) - override def serialize(point: EcPointType, w: SigmaByteWriter): Unit = { + override def serialize(point: EcPointType, w: CoreByteWriter): Unit = { val bytes = if (CryptoFacade.isInfinityPoint(point)) { identityPointEncoding } else { @@ -36,15 +32,13 @@ object GroupElementSerializer extends SigmaSerializer[EcPointType, EcPointType] w.putBytes(bytes) } - override def parse(r: SigmaByteReader): EcPointType = { + override def parse(r: CoreByteReader): EcPointType = { val encoded = r.getBytes(encodingSize) if (encoded(0) != 0) { - curve.ctx.decodePoint(encoded) + CryptoContext.default.decodePoint(encoded) } else { - curve.identity + CryptoContext.default.infinity // identity point of multiplicative group } } - def parse(bytes: Array[Byte]): EcPointType = parse(SigmaSerializer.startReader(bytes)) - } diff --git a/core/shared/src/main/scala/sigma/serialization/ProveDHTupleSerializer.scala b/core/shared/src/main/scala/sigma/serialization/ProveDHTupleSerializer.scala new file mode 100644 index 0000000000..3211e90b76 --- /dev/null +++ b/core/shared/src/main/scala/sigma/serialization/ProveDHTupleSerializer.scala @@ -0,0 +1,25 @@ +package sigma.serialization + +import sigma.crypto.EcPointType +import sigma.data.ProveDHTuple + +case class ProveDHTupleSerializer( + cons: (EcPointType, EcPointType, EcPointType, EcPointType) => ProveDHTuple + ) extends CoreSerializer[ProveDHTuple, ProveDHTuple] { + + override def serialize(obj: ProveDHTuple, w: CoreByteWriter): Unit = { + GroupElementSerializer.serialize(obj.gv, w) + GroupElementSerializer.serialize(obj.hv, w) + GroupElementSerializer.serialize(obj.uv, w) + GroupElementSerializer.serialize(obj.vv, w) + } + + override def parse(r: CoreByteReader) = { + val gv = GroupElementSerializer.parse(r) + val hv = GroupElementSerializer.parse(r) + val uv = GroupElementSerializer.parse(r) + val vv = GroupElementSerializer.parse(r) + cons(gv, hv, uv, vv) + } +} + diff --git a/core/shared/src/main/scala/sigma/serialization/ProveDlogSerializer.scala b/core/shared/src/main/scala/sigma/serialization/ProveDlogSerializer.scala new file mode 100644 index 0000000000..af849e8eed --- /dev/null +++ b/core/shared/src/main/scala/sigma/serialization/ProveDlogSerializer.scala @@ -0,0 +1,20 @@ +package sigma.serialization + +import sigma.crypto.EcPointType +import sigma.data.ProveDlog + +case class ProveDlogSerializer(cons: EcPointType => ProveDlog) + extends CoreSerializer[ProveDlog, ProveDlog] { + + override def serialize(obj: ProveDlog, w: CoreByteWriter): Unit = + GroupElementSerializer.serialize(obj.value, w) + + override def parse(r: CoreByteReader) = { + val res = GroupElementSerializer.parse(r) + cons(res) + } +} + + + + diff --git a/core/shared/src/main/scala/sigma/serialization/SerializerException.scala b/core/shared/src/main/scala/sigma/serialization/SerializerException.scala new file mode 100644 index 0000000000..7373bf5ce1 --- /dev/null +++ b/core/shared/src/main/scala/sigma/serialization/SerializerException.scala @@ -0,0 +1,39 @@ +package sigma.serialization + +import sigma.SigmaException + +import scala.collection.compat.immutable.ArraySeq + +/** Exception thrown during serialization. + * + * @param message the error message + * @param cause an optional cause for the exception + */ +case class SerializerException( + override val message: String, + override val cause: Option[Throwable] = None, + override val args: Seq[Any] = ArraySeq.empty +) + extends SigmaException(message, cause, args) + +/** Thrown by TypeSerializer when type prefix <= 0. */ +final class InvalidTypePrefix(message: String, cause: Option[Throwable] = None) + extends SerializerException(message, cause) + +/** Thrown when the current reader position > positionLimit which is set in the Reader. + * @see [[org.ergoplatform.validation.ValidationRules.CheckPositionLimit]] + */ +final class ReaderPositionLimitExceeded( + message: String, + val position: Int, + val positionLimit: Int, + cause: Option[Throwable] = None) + extends SerializerException(message, cause) + +/** Thrown when the current depth level > maxDepthLevel which is set in the Reader. */ +final class DeserializeCallDepthExceeded(message: String, cause: Option[Throwable] = None) + extends SerializerException(message, cause) + +/** Thrown by [[org.ergoplatform.validation.ValidationRules.CheckValidOpCode]] validation rule. */ +final class InvalidOpCode(message: String, cause: Option[Throwable] = None) + extends SerializerException(message, cause) diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/TypeSerializer.scala b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala similarity index 90% rename from interpreter/shared/src/main/scala/sigmastate/serialization/TypeSerializer.scala rename to core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala index 4dc9335ff3..9c84de0944 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/TypeSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala @@ -1,29 +1,24 @@ -package sigmastate.serialization +package sigma.serialization -import java.nio.charset.StandardCharsets -import org.ergoplatform.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckTypeCode} -import sigmastate._ -import sigma.util.safeNewArray -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} import debox.cfor -import sigmastate.exceptions.InvalidTypePrefix - -/** Serialization of types according to specification in TypeSerialization.md. */ -object TypeSerializer { - - import sigmastate.SCollectionType._ +import sigma.ast.SCollectionType.{CollectionTypeCode, NestedCollectionTypeCode} +import sigma.ast._ +import sigma.serialization.{CoreByteReader, CoreByteWriter, InvalidTypePrefix} +import sigma.util.safeNewArray +import sigma.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckTypeCode} - /** The list of embeddable types, i.e. types 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 single byte. */ - val embeddableIdToType = Array[SType](null, SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp) +import java.nio.charset.StandardCharsets +/** Serialization of types according to specification in TypeSerialization.md. */ +class TypeSerializer { + import TypeSerializer._ def getEmbeddableType(code: Int): SType = { CheckPrimitiveTypeCode(code.toByte) embeddableIdToType(code) } - def serialize(tpe: SType, w: SigmaByteWriter): Unit = tpe match { + def serialize(tpe: SType, w: CoreByteWriter): Unit = tpe match { case p: SEmbeddable => w.put(p.typeCode) case SString => w.put(SString.typeCode) case SAny => w.put(SAny.typeCode) @@ -114,9 +109,9 @@ object TypeSerializer { } } - def deserialize(r: SigmaByteReader): SType = deserialize(r, 0) + def deserialize(r: CoreByteReader): SType = deserialize(r, 0) - private def deserialize(r: SigmaByteReader, depth: Int): SType = { + private def deserialize(r: CoreByteReader, depth: Int): SType = { val c = r.getUByte() if (c <= 0) throw new InvalidTypePrefix(s"Cannot deserialize type prefix $c. Unexpected buffer $r with bytes ${r.getBytes(r.remaining)}") @@ -202,13 +197,13 @@ object TypeSerializer { tpe } - private def getArgType(r: SigmaByteReader, primId: Int, depth: Int) = + private def getArgType(r: CoreByteReader, primId: Int, depth: Int) = if (primId == 0) deserialize(r, depth + 1) else getEmbeddableType(primId) - private def serializeTuple(t: STuple, w: SigmaByteWriter) = { + private def serializeTuple(t: STuple, w: CoreByteWriter) = { assert(t.items.length <= 255) w.put(STuple.TupleTypeCode) w.putUByte(t.items.length) @@ -217,3 +212,9 @@ object TypeSerializer { } } +object TypeSerializer extends TypeSerializer { + /** The list of embeddable types, i.e. types 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 single byte. */ + val embeddableIdToType = Array[SType](null, SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp) + +} \ No newline at end of file diff --git a/core/shared/src/main/scala/sigma/util/CollectionUtil.scala b/core/shared/src/main/scala/sigma/util/CollectionUtil.scala index d6bcd69767..ba506dec09 100644 --- a/core/shared/src/main/scala/sigma/util/CollectionUtil.scala +++ b/core/shared/src/main/scala/sigma/util/CollectionUtil.scala @@ -8,7 +8,7 @@ import scala.collection.compat._ object CollectionUtil { /** @deprecated shouldn't be used other than for backwards compatibility with v3.x, v4.x. */ - def concatArrays[T](xs: Array[T], ys: Array[T]): Array[T] = { + def concatArrays_v4[T](xs: Array[T], ys: Array[T]): Array[T] = { val len = xs.length + ys.length val result = (xs match { case _: Array[AnyRef] => new Array[AnyRef](len) // creates an array with invalid type descriptor (i.e. when T == Tuple2) @@ -31,7 +31,7 @@ object CollectionUtil { * This method takes ClassTag to create proper resulting array. * Can be used in v5.0 and above. */ - def concatArrays_v5[T:ClassTag](arr1: Array[T], arr2: Array[T]): Array[T] = { + def concatArrays[T:ClassTag](arr1: Array[T], arr2: Array[T]): Array[T] = { val l1 = arr1.length val l2 = arr2.length val length: Int = l1 + l2 @@ -41,6 +41,13 @@ object CollectionUtil { result } + /** Casts the array of `A`s into array of `B`s, where B is a superclass of A. */ + def castArray[A, B >: A : ClassTag](array: Array[A]): Array[B] = { + val result: Array[B] = new Array[B](array.length) + System.arraycopy(array, 0, result, 0, array.length) + result + } + /** Computes the deep hash code for the given array. * * This method calculates the hash code based on the array's elements and type, taking nested arrays diff --git a/core/shared/src/main/scala/sigma/util/Extensions.scala b/core/shared/src/main/scala/sigma/util/Extensions.scala index 1d38173e1f..624b3f5d6b 100644 --- a/core/shared/src/main/scala/sigma/util/Extensions.scala +++ b/core/shared/src/main/scala/sigma/util/Extensions.scala @@ -1,5 +1,10 @@ package sigma.util +import debox.{cfor, Buffer => DBuffer} +import sigma.crypto.{CryptoFacade, Ecp} +import sigma.data._ +import sigma.{AvlTree, GroupElement, SigmaProp} + import java.math.BigInteger import java.nio.ByteBuffer @@ -211,6 +216,14 @@ object Extensions { else throw new ArithmeticException("BigInteger out of 256 bit range"); } + + /** Converts `x` to [[sigma.BigInt]] */ + def toBigInt: sigma.BigInt = CBigInt(x) + } + + implicit class BigIntOps(val x: sigma.BigInt) extends AnyVal { + /** Converts `x` to [[java.math.BigInteger]] */ + def toBigInteger: BigInteger = x.asInstanceOf[CBigInt].wrappedValue } implicit class OptionOps[T](val opt: Option[T]) extends AnyVal { @@ -269,4 +282,66 @@ object Extensions { self } } + + implicit class EcpOps(val source: Ecp) extends AnyVal { + /** Extracts [[sigma.GroupElement]] from the Ecp instance. */ + def toGroupElement: GroupElement = CGroupElement(source) + + /** Shortened String representation of `source` GroupElement. */ + def showECPoint: String = { + if (source.toGroupElement.isIdentity) { + "IDENTITY" + } + else { + CryptoFacade.showPoint(source) + } + } + } + + implicit class GroupElementOps(val source: GroupElement) extends AnyVal { + def toECPoint: Ecp = source.asInstanceOf[CGroupElement].wrappedValue + /** Shortened String representation of `source` GroupElement. */ + def showToString: String = toECPoint.showECPoint + } + + implicit class DBufferOps[A](val buf: DBuffer[A]) extends AnyVal { + /** Sum all values in `buf` using the given Numeric. */ + def sumAll(implicit n: Numeric[A]): A = { + val limit = buf.length + var result: A = n.zero + cfor(0)(_ < limit, _ + 1) { i => + result = n.plus(result, buf.elems(i)) + } + result + } + } + + implicit class SigmaBooleanOps(val sb: SigmaBoolean) extends AnyVal { + /** Wraps SigmaBoolean into SigmaProp. */ + def toSigmaProp: SigmaProp = CSigmaProp(sb) + + /** Human readable string representation of the proposition. */ + def showToString: String = sb match { + case ProveDlog(v) => + s"ProveDlog(${v.showECPoint})" + case ProveDHTuple(gv, hv, uv, vv) => + s"ProveDHTuple(${gv.showECPoint}, ${hv.showECPoint}, ${uv.showECPoint}, ${vv.showECPoint})" + case _ => sb.toString + } + } + + implicit class SigmaPropOps(val sb: SigmaProp) extends AnyVal { + /** Extracts [[sigma.SigmaBoolean]] from the SigmaProp instance. */ + def toSigmaBoolean: SigmaBoolean = sb.asInstanceOf[CSigmaProp].wrappedValue + } + + implicit class AvlTreeDataOps(val treeData: AvlTreeData) extends AnyVal { + /** Wrap [[sigma.AvlTreeData]] to [[sigma.AvlTree]]. */ + def toAvlTree: AvlTree= CAvlTree(treeData) + } + + implicit class CoreAvlTreeOps(val tree: AvlTree) extends AnyVal { + /** Extracts [[sigma.AvlTreeData]] from the AvlTree instance. */ + def toAvlTreeData: AvlTreeData = tree.asInstanceOf[CAvlTree].wrappedValue + } } diff --git a/core/shared/src/main/scala/sigma/util/package.scala b/core/shared/src/main/scala/sigma/util/package.scala index 87b3b6a805..a2a5a73472 100644 --- a/core/shared/src/main/scala/sigma/util/package.scala +++ b/core/shared/src/main/scala/sigma/util/package.scala @@ -25,6 +25,6 @@ package object util { final def safeConcatArrays_v5[A](arr1: Array[A], arr2: Array[A]) (implicit tA: ClassTag[A]): Array[A] = { checkLength[A](arr1.length + arr2.length) - CollectionUtil.concatArrays_v5(arr1, arr2) + CollectionUtil.concatArrays(arr1, arr2) } } diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/RuleStatus.scala b/core/shared/src/main/scala/sigma/validation/RuleStatus.scala similarity index 97% rename from interpreter/shared/src/main/scala/org/ergoplatform/validation/RuleStatus.scala rename to core/shared/src/main/scala/sigma/validation/RuleStatus.scala index 789016abfb..3d0451b240 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/RuleStatus.scala +++ b/core/shared/src/main/scala/sigma/validation/RuleStatus.scala @@ -1,6 +1,4 @@ -package org.ergoplatform.validation - -import java.util +package sigma.validation /** Base trait for rule status information. */ sealed trait RuleStatus { diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettings.scala b/core/shared/src/main/scala/sigma/validation/SigmaValidationSettings.scala similarity index 99% rename from interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettings.scala rename to core/shared/src/main/scala/sigma/validation/SigmaValidationSettings.scala index e59575a272..51fb97c8ad 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettings.scala +++ b/core/shared/src/main/scala/sigma/validation/SigmaValidationSettings.scala @@ -1,4 +1,4 @@ -package org.ergoplatform.validation +package sigma.validation /** * Configuration of validation. Each `ValidationRule` instance should be diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SoftForkChecker.scala b/core/shared/src/main/scala/sigma/validation/SoftForkChecker.scala similarity index 98% rename from interpreter/shared/src/main/scala/org/ergoplatform/validation/SoftForkChecker.scala rename to core/shared/src/main/scala/sigma/validation/SoftForkChecker.scala index 3af05c6f6d..e26926ae9b 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SoftForkChecker.scala +++ b/core/shared/src/main/scala/sigma/validation/SoftForkChecker.scala @@ -1,4 +1,4 @@ -package org.ergoplatform.validation +package sigma.validation /** Interface implemented by objects capable of checking soft-fork conditions. */ trait SoftForkChecker { diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/core/shared/src/main/scala/sigma/validation/ValidationRules.scala similarity index 62% rename from interpreter/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala rename to core/shared/src/main/scala/sigma/validation/ValidationRules.scala index 442faed890..249e2fce0a 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/core/shared/src/main/scala/sigma/validation/ValidationRules.scala @@ -1,23 +1,23 @@ -package org.ergoplatform.validation +package sigma.validation +import sigma.SigmaException +import sigma.ast.{SGlobal, SOption, TypeCodes} +import sigma.serialization.{ReaderPositionLimitExceeded, SerializerException} import sigma.util.Extensions.toUByte -import sigmastate.Values.{ErgoTree, SValue} -import sigmastate._ -import sigmastate.exceptions.{InterpreterException, InvalidOpCode, ReaderPositionLimitExceeded, SerializerException, SigmaException} -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.serialization.TypeSerializer.embeddableIdToType -import sigmastate.serialization.{OpCodes, ValueSerializer} -import sigmastate.utxo.DeserializeContext +import sigma.serialization.TypeSerializer.embeddableIdToType /** Base class for different validation rules registered in ValidationRules.currentSettings. * Each rule is identified by `id` and have a description. * Validation logic is implemented by `apply` methods of derived classes. */ -case class ValidationRule( +abstract case class ValidationRule( id: Short, description: String ) extends SoftForkChecker { - + /** Validation settings defining the status of this rule. + * @see checkRule() + */ + protected def settings: SigmaValidationSettings /** Whether the status of this rule was checked on first invocation. */ private var _checked: Boolean = false @@ -28,7 +28,7 @@ case class ValidationRule( */ @inline protected final def checkRule(): Unit = { if (!_checked) { - if (ValidationRules.currentSettings.getStatus(this.id).isEmpty) { + if (settings.getStatus(this.id).isEmpty) { throw new SigmaException(s"ValidationRule $this not found in validation settings") } _checked = true // prevent this check on every call (only first call is checked) @@ -77,76 +77,11 @@ object ValidationRules { /** The id of the first validation rule. Can be used as the beginning of the rules id range. */ val FirstRuleId = 1000.toShort - object CheckDeserializedScriptType extends ValidationRule(FirstRuleId, - "Deserialized script should have expected type") { - final def apply[T](d: DeserializeContext[_], script: SValue): Unit = { - checkRule() - if (d.tpe != script.tpe) { - throwValidationException( - new InterpreterException(s"Failed context deserialization of $d: \n" + - s"expected deserialized script to have type ${d.tpe}; got ${script.tpe}"), - Array[Any](d, script)) - } - } - } - - object CheckDeserializedScriptIsSigmaProp extends ValidationRule(1001, - "Deserialized script should have SigmaProp type") { - /** @param root candidate node before it is added as a root of ErgoTree */ - final def apply[T](root: SValue): Unit = { - checkRule() - if (!root.tpe.isSigmaProp) { - throwValidationException( - new SerializerException(s"Failed deserialization, expected deserialized script to have type SigmaProp; got ${root.tpe}"), - Array[Any](root)) - } - } - } - - object CheckValidOpCode extends ValidationRule(1002, - "Check the opcode is supported by registered serializer or is added via soft-fork") - with SoftForkWhenCodeAdded { - final def apply[T](ser: ValueSerializer[_], opCode: OpCode): Unit = { - checkRule() - if (ser == null || ser.opCode != opCode) { - throwValidationException( - new InvalidOpCode(s"Cannot find serializer for Value with opCode = LastConstantCode + ${toUByte(opCode) - OpCodes.LastConstantCode}"), - Array(opCode)) - } - } - } - - /** Not used since v5.0.1. */ - object CheckIsSupportedIndexExpression extends ValidationRule(1003, - "Check the index expression for accessing collection element is supported.") - - /** Not used since v5.0.3 */ - object CheckCostFunc extends ValidationRule(1004, - "Cost function should contain only operations from specified list.") - - object CheckCalcFunc extends ValidationRule(1005, - "If SigmaProp.isProven method calls exists in the given function,\n then it is the last operation") - - /** This rule is not use in v5.x, keep the commented code below as a precise - * documentation of its semantics. - */ - object CheckTupleType extends ValidationRule(1006, - "Supported tuple type.") with SoftForkWhenReplaced { -// final def apply[Ctx <: IRContext, T](ctx: Ctx)(e: ctx.Elem[_]): Unit = { -// checkRule() -// val condition = e match { -// case _: ctx.PairElem[_,_] => true -// case _ => false -// } -// if (!condition) { -// throwValidationException(new SigmaException(s"Invalid tuple type $e"), Array[ctx.Elem[_]](e)) -// } -// } - } - object CheckPrimitiveTypeCode extends ValidationRule(1007, "Check the primitive type code is supported or is added via soft-fork") with SoftForkWhenCodeAdded { + override protected lazy val settings: SigmaValidationSettings = coreSettings + final def apply[T](code: Byte): Unit = { checkRule() val ucode = toUByte(code) @@ -161,6 +96,8 @@ object ValidationRules { object CheckTypeCode extends ValidationRule(1008, "Check the non-primitive type code is supported or is added via soft-fork") with SoftForkWhenCodeAdded { + override protected lazy val settings: SigmaValidationSettings = coreSettings + final def apply[T](typeCode: Byte): Unit = { checkRule() val ucode = toUByte(typeCode) @@ -175,6 +112,7 @@ object ValidationRules { object CheckSerializableTypeCode extends ValidationRule(1009, "Check the data values of the type (given by type code) can be serialized") with SoftForkWhenReplaced { + override protected lazy val settings: SigmaValidationSettings = coreSettings /** Creates an exception which is used as a cause when throwing a ValidationException. */ def throwValidationException(typeCode: Byte): Nothing = { @@ -186,7 +124,7 @@ object ValidationRules { final def apply[T](typeCode: Byte): Unit = { checkRule() val ucode = toUByte(typeCode) - if (typeCode == SOption.OptionTypeCode || ucode > toUByte(OpCodes.LastDataType)) { + if (typeCode == SOption.OptionTypeCode || ucode > toUByte(TypeCodes.LastDataType)) { // the `typeCode == SOption.OptionTypeCode` condition is added in v5.0 and we // throw ValidationException for Option type as well in order to be able to // interpret it as soft-fork condition. @@ -203,6 +141,8 @@ object ValidationRules { object CheckTypeWithMethods extends ValidationRule(1010, "Check the type (given by type code) supports methods") with SoftForkWhenCodeAdded { + override protected lazy val settings: SigmaValidationSettings = coreSettings + final def apply[T](typeCode: Byte, cond: Boolean): Unit = { checkRule() val ucode = toUByte(typeCode) @@ -214,53 +154,13 @@ object ValidationRules { } } - object CheckAndGetMethod extends ValidationRule(1011, - "Check the type has the declared method.") { - final def apply[T](objType: STypeCompanion, methodId: Byte): SMethod = { - checkRule() - val methodOpt = objType.getMethodById(methodId) - if (methodOpt.isDefined) methodOpt.get - else { - throwValidationException( - new SerializerException(s"The method with code $methodId doesn't declared in the type $objType."), - Array[Any](objType, methodId)) - } - } - - override def isSoftFork(vs: SigmaValidationSettings, - ruleId: Short, - status: RuleStatus, - args: Seq[Any]): Boolean = (status, args) match { - case (ChangedRule(newValue), Seq(objType: STypeCompanion, methodId: Byte)) => - val key = Array(objType.typeId, methodId) - newValue.grouped(2).exists(java.util.Arrays.equals(_, key)) - case _ => false - } - } - - object CheckHeaderSizeBit extends ValidationRule(1012, - "For version greater then 0, size bit should be set.") with SoftForkWhenReplaced { - final def apply(header: Byte): Unit = { - checkRule() - val version = ErgoTree.getVersion(header) - if (version != 0 && !ErgoTree.hasSize(header)) { - throwValidationException( - new SigmaException(s"Invalid ErgoTreeHeader $header, size bit is expected for version $version"), - Array(header)) - } - } - } - - /** Not used since v5.0.3 */ - object CheckCostFuncOperation extends ValidationRule(1013, - "Check the opcode is allowed in cost function") - /** Introduced in v5.0, this rule it is used in creation of ValidationExceptions, which * in turn can be checked for soft-fork condition using `this.isSoftFork`. Thus, this * rule can be replaced with a new rule and the limit can be increased. */ object CheckPositionLimit extends ValidationRule(1014, "Check that the Reader has not exceeded the position limit.") with SoftForkWhenReplaced { + override protected lazy val settings: SigmaValidationSettings = coreSettings /** Wraps the given cause into [[ValidationException]] and throws it. */ def throwValidationException(cause: ReaderPositionLimitExceeded): Nothing = { @@ -283,27 +183,12 @@ object ValidationRules { } } - /** Not used since v5.0.1 */ - object CheckLoopLevelInCostFunction extends ValidationRule(1015, - "Check that loop level is not exceeded.") - - val ruleSpecs: Seq[ValidationRule] = Seq( - CheckDeserializedScriptType, - CheckDeserializedScriptIsSigmaProp, - CheckValidOpCode, - CheckIsSupportedIndexExpression, - CheckCostFunc, - CheckCalcFunc, - CheckTupleType, + private val ruleSpecs: Seq[ValidationRule] = Seq( CheckPrimitiveTypeCode, CheckTypeCode, CheckSerializableTypeCode, CheckTypeWithMethods, - CheckAndGetMethod, - CheckHeaderSizeBit, - CheckCostFuncOperation, - CheckPositionLimit, - CheckLoopLevelInCostFunction + CheckPositionLimit ) /** Validation settings that correspond to the current version of the ErgoScript implementation. @@ -312,7 +197,7 @@ object ValidationRules { * This is immutable data structure, it can be augmented with RuleStates from block extension * sections of the blockchain, but that augmentation is only available in stateful context. */ - val currentSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ + val coreSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ val map = ruleSpecs.map(r => r.id -> (r, EnabledRule)).toMap assert(map.size == ruleSpecs.size, s"Duplicate ruleIds ${ruleSpecs.groupBy(_.id).filter(g => g._2.length > 1)}") map diff --git a/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala b/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala index 24b03f9d88..f273568193 100644 --- a/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala +++ b/core/shared/src/test/scala/sigma/util/CollectionUtilTests.scala @@ -21,18 +21,18 @@ class CollectionUtilTests extends BaseTests { test("concatArrays") { val xs = Array[Byte](1,2,3) val ys = Array[Byte](4,5,6) - val zs = concatArrays(xs, ys) + val zs = concatArrays_v4(xs, ys) assertResult(Array[Byte](1, 2, 3, 4, 5, 6))(zs) val pairs = xs.zip(ys) // this reproduces the problem which takes place in v3.x, v4.x (ErgoTree v0, v1) - an[Throwable] should be thrownBy(concatArrays(pairs, pairs)) + an[Throwable] should be thrownBy(concatArrays_v4(pairs, pairs)) // and this is the fix in v5.0 - concatArrays_v5(pairs, pairs) shouldBe Array((1, 4), (2, 5), (3, 6), (1, 4), (2, 5), (3, 6)) + concatArrays(pairs, pairs) shouldBe Array((1, 4), (2, 5), (3, 6), (1, 4), (2, 5), (3, 6)) val xOpts = xs.map(Option(_)) - concatArrays_v5(xOpts, xOpts) shouldBe Array(Some(1), Some(2), Some(3), Some(1), Some(2), Some(3)) + concatArrays(xOpts, xOpts) shouldBe Array(Some(1), Some(2), Some(3), Some(1), Some(2), Some(3)) } def joinSeqs(l: Seq[Int], r: Seq[Int]) = diff --git a/data/js/src/main/scala/org/ergoplatform/js/Address.scala b/data/js/src/main/scala/org/ergoplatform/js/Address.scala new file mode 100644 index 0000000000..0978db5494 --- /dev/null +++ b/data/js/src/main/scala/org/ergoplatform/js/Address.scala @@ -0,0 +1,208 @@ +package org.ergoplatform.js + +import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix +import org.ergoplatform.ErgoAddressEncoder +import scorex.util.encode.Base58 +import sigma.ast.js.ErgoTree +import sigma.js.{GroupElement, SigmaProp} + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.{UndefOr, undefined} +import scala.util.{Failure, Success} + +/** An exported JavaScript class wrapping the Scala `ErgoAddress` type. */ +@JSExportTopLevel("Address") +abstract class Address extends js.Object { + def ergoAddress: org.ergoplatform.ErgoAddress + + private lazy val _addressBytes: Array[Byte] = { + ErgoAddressEncoder(ergoAddress.networkPrefix).toBytes(ergoAddress) + } + + /** Serialize this address to bytes. + * @see ErgoAddressEncoder.toBytes() + */ + def addressBytes(): Array[Byte] = _addressBytes + + /** Address type code used to differentiate between pay-to-public-key, pay-to-script, + * pay-to-script-hash addresses. + * + * @see [[P2PKAddress]], [[P2SAddress]], [[P2SHAddress]] + */ + def addressTypePrefix(): Byte = ergoAddress.addressTypePrefix + + /** First byte is used to encode network type and address type. + * + * @see ErgoAddressEncoder + */ + private def headByte: Byte = _addressBytes(0) + + /** @return true if this address from Ergo mainnet. */ + def isMainnet(): Boolean = Address.isMainnet(headByte) + + /** @return true if this address has Pay-To-Public-Key type. */ + def isP2PK(): Boolean = ergoAddress.isInstanceOf[org.ergoplatform.P2PKAddress] + + /** @return underlying {@link P2PKAddress}. + * @throws IllegalArgumentException if this instance is not P2PK address + */ + def asP2PK(): P2PKAddress = { + require(isP2PK(), s"This instance $this is not P2PKAddress") + new P2PKAddress(ergoAddress.asInstanceOf[org.ergoplatform.P2PKAddress]) + } + + /** @return true if this address has Pay-To-Script type. */ + def isP2S(): Boolean = ergoAddress.isInstanceOf[org.ergoplatform.Pay2SAddress] + + /** @return underlying {@link P2SAddress}. + * @throws IllegalArgumentException if this instance is not P2S address + */ + def asP2S(): P2SAddress = { + require(isP2S(), s"This instance $this is not P2SAddress") + new P2SAddress(ergoAddress.asInstanceOf[org.ergoplatform.Pay2SAddress]) + } + + /** @return true if this address has Pay-To-Script-Hash type. */ + def isP2SH(): Boolean = ergoAddress.isInstanceOf[org.ergoplatform.Pay2SHAddress] + + /** @return underlying {@link P2SHAddress}. + * @throws IllegalArgumentException if this instance is not P2SH address + */ + def asP2SH(): P2SHAddress = { + require(isP2SH(), s"This instance $this is not P2SHAddress") + new P2SHAddress(ergoAddress.asInstanceOf[org.ergoplatform.Pay2SHAddress]) + } + + /** Extracts a [[sigma.js.SigmaProp]] from this address of the underlying ErgoTree if of + * specific form. + * @see ErgoTree.toSigmaBooleanOpt() + */ + def toSigmaPropOpt(): UndefOr[sigma.js.SigmaProp] = { + ergoAddress.script.toSigmaBooleanOpt match { + case Some(sb) => new SigmaProp(sb) + case _ => undefined + } + } + + /** ErgoTree which corresponds to the address (depending on the address type). + * + * @see [[P2PKAddress]], [[P2SAddress]], [[P2SHAddress]] + */ + def toErgoTree(): ErgoTree = new ErgoTree(ergoAddress.script) + + /** @return this addresses ErgoTree's proposition bytes. Use this to store this address + * on Box registers. + */ + def toPropositionBytes(): Array[Byte] = ergoAddress.script.bytes + + /** Converts the given [[Address]] to Base58 string. */ + override def toString() = ergoAddress.toString +} + +/** An exported JavaScript object providing utility methods for working with Address instances. */ +@JSExportTopLevel("Address$") +object Address extends js.Object { + /** Creates JS wrapper over given [[ErgoAddress]]. */ + def fromErgoAddress(ergoAddress: org.ergoplatform.ErgoAddress): Address = { + ergoAddress match { + case p2pk: org.ergoplatform.P2PKAddress => + new P2PKAddress(p2pk) + case p2s: org.ergoplatform.Pay2SAddress => + new P2SAddress(p2s) + case p2sh: org.ergoplatform.Pay2SHAddress => + new P2SHAddress(p2sh) + } + } + + /** @return true if this address from Ergo mainnet. */ + private def isMainnet(headByte: Byte): Boolean = headByte < ErgoAddressEncoder.TestnetNetworkPrefix + + private def getNetworkType(headByte: Byte): NetworkPrefix = { + if (isMainnet(headByte)) ErgoAddressEncoder.MainnetNetworkPrefix else ErgoAddressEncoder.TestnetNetworkPrefix + } + + /** Deserializes an ErgoTree instance from an address string. + * + * @param base58String a Base58 string representing the serialized ErgoTree + */ + def fromString(base58String: String): Address = { + Base58.decode(base58String) match { + case Success(bytes) => + val headByte = bytes(0) + val encoder = ErgoAddressEncoder(getNetworkType(headByte)) + encoder.fromBytes(bytes, base58String) match { + case Success(ergoAddress) => + Address.fromErgoAddress(ergoAddress) + case Failure(t) => + throw new RuntimeException( + "Invalid address encoding, expected base58 string: " + base58String, t) + } + case Failure(t) => + throw new RuntimeException( + "Invalid address encoding, expected base58 string: " + base58String, t) + } + } + + /** Creates an `Address` instance from an `ErgoTree` and a network prefix. + * + * @param ergoTree The `ErgoTree` instance to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `ErgoTree` and network prefix. + */ + def fromErgoTree(ergoTree: ErgoTree, networkPrefix: NetworkPrefix): Address = { + val encoder = ErgoAddressEncoder(networkPrefix) + val ergoAddress = encoder.fromProposition(ergoTree.tree).get + Address.fromErgoAddress(ergoAddress) + } + + /** + * Creates an `Address` from a `SigmaProp` and a network prefix. + * + * @param sigmaProp The `SigmaProp` to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `SigmaProp` and network prefix. + */ + def fromSigmaProp( + sigmaProp: SigmaProp, + networkPrefix: NetworkPrefix): Address = { + val ergoTree = sigma.ast.ErgoTree.fromSigmaBoolean(sigmaProp.sigmaBoolean) + fromErgoTree(new ErgoTree(ergoTree), networkPrefix) + } + + /** Creates address from given ergovalue containing an ErgoTree proposition bytes. + * Use this to convert a box register containing an ErgoTree into its address. + * + * @param networkPrefix mainnet or testnet network + * @param propositionBytes ErgoTree proposition bytes + */ + def fromPropositionBytes(networkPrefix: NetworkPrefix, propositionBytes: Array[Byte]): Address = { + fromErgoTree(ErgoTree.fromBytes(propositionBytes), networkPrefix) + } + +} + +/** An exported JavaScript class wrapping the Scala `P2PKAddress` type. */ +@JSExportTopLevel("P2PKAddress") +class P2PKAddress( + override val ergoAddress: org.ergoplatform.P2PKAddress +) extends Address { + + /** Converts this address to the underlying ProveDlog sigma proposition wrapped in SigmaProp. */ + def toSigmaProp(): sigma.js.SigmaProp = new SigmaProp(ergoAddress.pubkey) + + /** Extract the underlying [[sigma.js.GroupElement]] of this address. */ + def getPublicKeyGE(): sigma.js.GroupElement = new GroupElement(ergoAddress.pubkey.value) +} + +/** An exported JavaScript class wrapping the Scala `P2SAddress` type. */ +@JSExportTopLevel("P2SAddress") +class P2SAddress( + override val ergoAddress: org.ergoplatform.Pay2SAddress +) extends Address + +/** An exported JavaScript class wrapping the Scala `P2SHAddress` type. */ +@JSExportTopLevel("P2SHAddress") +class P2SHAddress( + override val ergoAddress: org.ergoplatform.Pay2SHAddress +) extends Address diff --git a/interpreter/js/src/main/scala/sigmastate/Platform.scala b/data/js/src/main/scala/sigma/Platform.scala similarity index 80% rename from interpreter/js/src/main/scala/sigmastate/Platform.scala rename to data/js/src/main/scala/sigma/Platform.scala index bff11b53a8..29c761c3f1 100644 --- a/interpreter/js/src/main/scala/sigmastate/Platform.scala +++ b/data/js/src/main/scala/sigma/Platform.scala @@ -1,13 +1,8 @@ -package sigmastate +package sigma import org.ergoplatform.ErgoBox -import sigma.data.Nullable -import sigma.VersionContext -import sigmastate.Values.{Constant, FalseLeaf, SigmaBoolean, TrueLeaf} -import sigmastate.eval.{Evaluation, SigmaDsl} -import sigmastate.lang.SigmaBuilder -import sigma.Coll -import sigma.{AnyValue, AvlTree, GroupElement, SigmaProp} +import sigma.ast._ +import sigma.data._ import java.math.BigInteger @@ -16,7 +11,7 @@ object Platform { * Uses scalan.Nullable instead of scala.Option to avoid allocation on consensus hot path. * This method is part of consensus and is used in [[SubstConstants]] operation. */ - private [sigmastate] def liftToConstant(obj: Any, builder: SigmaBuilder): Nullable[Constant[SType]] = { + private[sigma] def liftToConstant(obj: Any, builder: SigmaBuilder): Nullable[Constant[SType]] = { import builder._ obj match { case arr: Array[Boolean] => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) @@ -24,14 +19,14 @@ object Platform { case arr: Array[Short] => Nullable(mkCollectionConstant[SShort.type](arr, SShort)) case arr: Array[Int] => Nullable(mkCollectionConstant[SInt.type](arr, SInt)) case arr: Array[Long] => Nullable(mkCollectionConstant[SLong.type](arr, SLong)) - case arr: Array[BigInteger] => Nullable(mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt(_)), SBigInt)) + case arr: Array[BigInteger] => Nullable(mkCollectionConstant[SBigInt.type](arr.map[BigInt](n => CBigInt(n)), SBigInt)) case arr: Array[String] => Nullable(mkCollectionConstant[SString.type](arr, SString)) case v: AnyValue => val tpe = Evaluation.rtypeToSType(v.tVal) Nullable(mkConstant[tpe.type](v.value.asInstanceOf[tpe.WrappedType], tpe)) case v: Int => Nullable(mkConstant[SInt.type](v, SInt)) case v: Long => Nullable(mkConstant[SLong.type](v, SLong)) - case v: BigInteger => Nullable(mkConstant[SBigInt.type](SigmaDsl.BigInt(v), SBigInt)) + case v: BigInteger => Nullable(mkConstant[SBigInt.type](CBigInt(v), SBigInt)) case n: sigma.BigInt => Nullable(mkConstant[SBigInt.type](n, SBigInt)) case ge: GroupElement => Nullable(mkConstant[SGroupElement.type](ge, SGroupElement)) case b: Boolean => Nullable(if (b) TrueLeaf else FalseLeaf) @@ -43,7 +38,7 @@ object Platform { // ErgoBox cannot be passed as argument as it is never valid value during evaluation. // Thus we can use activation-based versioning and fix this code when v5.0 is activated. case b: ErgoBox => - Nullable(mkConstant[SBox.type](SigmaDsl.Box(b), SBox)) // fixed in v5.0 + Nullable(mkConstant[SBox.type](CBox(b), SBox)) // fixed in v5.0 // this case is added in v5.0 and it can be useful when the box value comes from a // register or a context variable is passed to SubstConstants. @@ -52,9 +47,9 @@ object Platform { Nullable(mkConstant[SBox.type](b, SBox)) else Nullable.None // return the same result as in v4.x when there was no this case - case avl: AvlTreeData => Nullable(mkConstant[SAvlTree.type](SigmaDsl.avlTree(avl), SAvlTree)) + case avl: AvlTreeData => Nullable(mkConstant[SAvlTree.type](CAvlTree(avl), SAvlTree)) case avl: AvlTree => Nullable(mkConstant[SAvlTree.type](avl, SAvlTree)) - case sb: SigmaBoolean => Nullable(mkConstant[SSigmaProp.type](SigmaDsl.SigmaProp(sb), SSigmaProp)) + case sb: SigmaBoolean => Nullable(mkConstant[SSigmaProp.type](CSigmaProp(sb), SSigmaProp)) case p: SigmaProp => Nullable(mkConstant[SSigmaProp.type](p, SSigmaProp)) case coll: Coll[a] => val tpeItem = Evaluation.rtypeToSType(coll.tItem) diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ErgoTree.scala b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala similarity index 70% rename from sdk/js/src/main/scala/org/ergoplatform/sdk/js/ErgoTree.scala rename to data/js/src/main/scala/sigma/ast/js/ErgoTree.scala index 8469545d95..73b8fbfb00 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ErgoTree.scala +++ b/data/js/src/main/scala/sigma/ast/js/ErgoTree.scala @@ -1,21 +1,28 @@ -package org.ergoplatform.sdk.js +package sigma.ast.js -import sigmastate.Values +import sigma.ast +import sigma.js.Value import scala.scalajs.js import scala.scalajs.js.JSConverters.JSRichIterableOnce import scala.scalajs.js.annotation.JSExportTopLevel -/** An exported JavaScript class wrapping the Scala `Values.ErgoTree` type. */ +/** An exported JavaScript class wrapping the Scala `ErgoTree` type. */ @JSExportTopLevel("ErgoTree") -class ErgoTree(tree: Values.ErgoTree) extends js.Object { +class ErgoTree(val tree: ast.ErgoTree) extends js.Object { + /** Root of the contract which is a valid expression of `SigmaProp` type. */ + val root: Expr = new Expr(tree.root.toOption.get) + /** The first byte of serialized byte array which determines interpretation of the rest of the array. */ def header(): Byte = tree.header + /** Version of this tree (== BlockVersion - 1). */ def version(): Byte = tree.version + /** @return true, if constant segregation bit is set in the header. */ def isConstantSegregation(): Boolean = tree.isConstantSegregation + /** @return true, if size bit is set in the header. */ def hasSize(): Boolean = tree.hasSize /** Serializes the ErgoTree instance to an array of bytes. */ @@ -35,13 +42,13 @@ class ErgoTree(tree: Values.ErgoTree) extends js.Object { /** Returns segregated constants of this tree as [[js.Array]]. */ def constants(): js.Array[Value] = { val constants = tree.constants - val values = constants.map(Isos.isoValueToConstant.from) + val values = constants.map(isoValueToConstant.from) values.toJSArray } } /** An exported JavaScript object providing utility methods for working with ErgoTree instances. */ -@JSExportTopLevel("ErgoTreeObj") +@JSExportTopLevel("ErgoTree$") object ErgoTree extends js.Object { /** Deserializes an ErgoTree instance from a hexadecimal string. @@ -49,13 +56,13 @@ object ErgoTree extends js.Object { * @param hex a hexadecimal string representing the serialized ErgoTree */ def fromHex(hex: String): ErgoTree = - new ErgoTree(Values.ErgoTree.fromHex(hex)) + new ErgoTree(ast.ErgoTree.fromHex(hex)) /** Deserializes an ErgoTree instance from an array of bytes. * * @param bytes an array of bytes representing the serialized ErgoTree */ def fromBytes(bytes: Array[Byte]): ErgoTree = { - new ErgoTree(Values.ErgoTree.fromBytes(bytes)) + new ErgoTree(ast.ErgoTree.fromBytes(bytes)) } } diff --git a/data/js/src/main/scala/sigma/ast/js/Expr.scala b/data/js/src/main/scala/sigma/ast/js/Expr.scala new file mode 100644 index 0000000000..54a78f4025 --- /dev/null +++ b/data/js/src/main/scala/sigma/ast/js/Expr.scala @@ -0,0 +1,30 @@ +package sigma.ast.js + +import sigma.ast.syntax.SValue +import sigma.js.JsWrapper +import sigma.serialization.ValueSerializer + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array + +/** Represents a node of ErgoTree. + * An exported JavaScript class wrapping the Scala [[sigma.ast.Value]]. + */ +@JSExportTopLevel("Expr") +class Expr(override val wrappedValue: SValue) extends JsWrapper[SValue]{ + /** Serialize this expression using sigma serializer [[ValueSerializer]]. */ + def toBytes: Int8Array = { + val bytes = ValueSerializer.serialize(wrappedValue) + Int8Array.of(bytes:_*) + } +} + +@JSExportTopLevel("Expr$") +object Expr extends js.Object { + /** Deserialize an expression from bytes using sigma serializer [[ValueSerializer]]. */ + def fromBytes(bytes: Int8Array): Expr = { + val value = ValueSerializer.deserialize(bytes.toArray) + new Expr(value) + } +} diff --git a/data/js/src/main/scala/sigma/ast/js/package.scala b/data/js/src/main/scala/sigma/ast/js/package.scala new file mode 100644 index 0000000000..7af2a48ab0 --- /dev/null +++ b/data/js/src/main/scala/sigma/ast/js/package.scala @@ -0,0 +1,19 @@ +package sigma.ast + +import sigma.Evaluation +import sigma.data.Iso +import sigma.js.{Type, Value} + +package object js { + /** Conversion between `Value` and `Constant[SType]`. */ + implicit val isoValueToConstant: Iso[Value, Constant[SType]] = new Iso[Value, Constant[SType]] { + override def to(x: Value): Constant[SType] = + Constant(x.runtimeData.asInstanceOf[SType#WrappedType], Evaluation.rtypeToSType(x.tpe.rtype)) + + override def from(x: Constant[SType]): Value = { + val rtype = Evaluation.stypeToRType(x.tpe) + val jsvalue = Value.fromRuntimeData(x.value, rtype) + new Value(jsvalue, new Type(rtype)) + } + } +} diff --git a/interpreter/jvm/src/main/scala/sigmastate/Platform.scala b/data/jvm/src/main/scala/sigma/Platform.scala similarity index 89% rename from interpreter/jvm/src/main/scala/sigmastate/Platform.scala rename to data/jvm/src/main/scala/sigma/Platform.scala index fa44f9a16f..bd39d75ab9 100644 --- a/interpreter/jvm/src/main/scala/sigmastate/Platform.scala +++ b/data/jvm/src/main/scala/sigma/Platform.scala @@ -1,13 +1,9 @@ -package sigmastate +package sigma import org.ergoplatform.ErgoBox -import sigma.data.Nullable -import sigma.VersionContext -import sigmastate.Values.{Constant, FalseLeaf, SigmaBoolean, TrueLeaf} -import sigmastate.eval.{Evaluation, SigmaDsl} -import sigmastate.lang.SigmaBuilder -import sigma.Coll -import sigma.{AvlTree, GroupElement, SigmaProp} +import sigma.ast._ +import sigma.data.{AvlTreeData, Nullable, SigmaBoolean} +import sigma.eval.SigmaDsl import java.math.BigInteger @@ -17,7 +13,7 @@ object Platform { * Uses scalan.Nullable instead of scala.Option to avoid allocation on consensus hot path. * This method is part of consensus and is used in [[SubstConstants]] operation. */ - private [sigmastate] def liftToConstant(obj: Any, builder: SigmaBuilder): Nullable[Constant[SType]] = { + private[sigma] def liftToConstant(obj: Any, builder: SigmaBuilder): Nullable[Constant[SType]] = { import builder._ obj match { case arr: Array[Boolean] => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean)) diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala similarity index 92% rename from interpreter/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala rename to data/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala index 5813ff99e7..59eb8df5c5 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoAddress.scala @@ -1,16 +1,18 @@ package org.ergoplatform -import scorex.utils.Ints import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.encode.Base58 -import sigmastate.Values._ -import sigmastate._ -import sigmastate.crypto.DLogProtocol.{ProveDlog, ProveDlogProp} -import sigmastate.exceptions.SigmaException -import sigmastate.serialization._ -import sigmastate.utxo.{DeserializeContext, Slice} -import sigma.Coll +import scorex.utils.Ints +import sigma.ast.{DeserializeContext, SInt, SSigmaProp, Slice} +import sigma.data.{CSigmaProp, ProveDlog} +import sigma.serialization.GroupElementSerializer +import sigma.{Coll, SigmaException, VersionContext} +import sigma.ast.ErgoTree.{ZeroHeader, setVersionBits} +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.serialization._ +import sigma.util.CollectionUtil import scala.util.Try @@ -163,7 +165,7 @@ class Pay2SHAddress(val scriptHash: Array[Byte])(implicit val encoder: ErgoAddre ByteArrayConstant(scriptHash) ) val scriptIsCorrect = DeserializeContext(scriptId, SSigmaProp) - ErgoTree.withoutSegregation(SigmaAnd(hashEquals.toSigmaProp, scriptIsCorrect)) + ErgoTree.withoutSegregation(ZeroHeader, SigmaAnd(hashEquals.toSigmaProp, scriptIsCorrect)) } override def equals(obj: Any): Boolean = obj match { @@ -265,12 +267,17 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { /** This value is be used implicitly in the methods below. */ implicit private def ergoAddressEncoder: ErgoAddressEncoder = this + /** Converts the given [[ErgoAddress]] to array of bytes. */ + def toBytes(address: ErgoAddress): Array[Byte] = { + val prefixByte = (networkPrefix + address.addressTypePrefix).toByte + val withNetworkByte = prefixByte +: address.contentBytes + val checksum = hash256(withNetworkByte).take(ChecksumLength) + CollectionUtil.concatArrays(withNetworkByte,checksum) + } + /** Converts the given [[ErgoAddress]] to Base58 string. */ def toString(address: ErgoAddress): String = { - val withNetworkByte = (networkPrefix + address.addressTypePrefix).toByte +: address.contentBytes - - val checksum = hash256(withNetworkByte).take(ChecksumLength) - Base58.encode(withNetworkByte ++ checksum) + Base58.encode(toBytes(address)) } /** Returns true if the given `addrHeadByte` is a header byte of a testnet address, false otherwise. */ @@ -281,6 +288,11 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { /** Converts the given Base58 string to [[ErgoAddress]] or an error packed in Try. */ def fromString(addrBase58Str: String): Try[ErgoAddress] = Base58.decode(addrBase58Str).flatMap { bytes => + fromBytes(bytes, addrBase58Str) + } + + /** Converts the given Base58 string to [[ErgoAddress]] or an error packed in Try. */ + private [ergoplatform] def fromBytes(bytes: Array[Byte], addrBase58Str: String): Try[ErgoAddress] = { Try { val headByte = bytes.head networkPrefix match { @@ -338,7 +350,7 @@ case class ErgoAddressEncoder(networkPrefix: NetworkPrefix) { */ def fromProposition(proposition: ErgoTree): Try[ErgoAddress] = Try { proposition.root match { - case Right(SigmaPropConstant(ProveDlogProp(d))) => P2PKAddress(d) + case Right(SigmaPropConstant(CSigmaProp(d: ProveDlog))) => P2PKAddress(d) case Right(IsPay2SHAddress(scriptHash)) => new Pay2SHAddress(scriptHash.toArray) case Right(b: Value[SSigmaProp.type]@unchecked) if b.tpe == SSigmaProp => Pay2SAddress(proposition) case Left(unparsedErgoTree) => diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoBox.scala similarity index 90% rename from interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala rename to data/shared/src/main/scala/org/ergoplatform/ErgoBox.scala index 670d51d123..4c240267c4 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoBox.scala @@ -1,21 +1,19 @@ package org.ergoplatform import org.ergoplatform.ErgoBox.{AdditionalRegisters, Token} -import org.ergoplatform.settings.ErgoAlgos import scorex.crypto.authds.ADKey import scorex.crypto.hash.Blake2b256 import scorex.util._ +import scorex.util.encode.Base16 import scorex.utils.{Ints, Shorts} -import sigmastate.SCollection.SByteArray -import sigmastate.SType.AnyOps -import sigmastate.Values._ -import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.eval._ -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.ExtractCreationInfo -import sigma.{Colls, _} +import sigma.Extensions.ArrayOps +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType.AnyOps +import sigma.data.{Digest32Coll, SigmaConstants} +import sigma.ast._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} +import sigma._ +import sigma.util.CollectionUtil /** * Box (aka coin, or an unspent output) is a basic concept of a UTXO-based cryptocurrency. In Bitcoin, such an object @@ -77,7 +75,7 @@ class ErgoBox private ( override def get(identifier: RegisterId): Option[Value[SType]] = { identifier match { case ReferenceRegId => - val tupleVal = (creationHeight, Helpers.concatArrays(transactionId.toBytes, Shorts.toByteArray(index)).toColl) + val tupleVal = (creationHeight, CollectionUtil.concatArrays(transactionId.toBytes, Shorts.toByteArray(index)).toColl) Some(Constant(tupleVal.asWrappedType, SReferenceRegType)) case _ => super.get(identifier) } @@ -106,8 +104,8 @@ class ErgoBox private ( def toCandidate: ErgoBoxCandidate = new ErgoBoxCandidate(value, ergoTree, creationHeight, additionalTokens, additionalRegisters) - override def toString: String = s"ErgoBox(${ErgoAlgos.encode(id)},$value,$ergoTree," + - s"tokens: (${additionalTokens.map(t => ErgoAlgos.encode(t._1) + ":" + t._2)}), $transactionId, " + + override def toString: String = s"ErgoBox(${Base16.encode(id)},$value,$ergoTree," + + s"tokens: (${additionalTokens.map(t => Base16.encode(t._1.toArray) + ":" + t._2)}), $transactionId, " + s"$index, $additionalRegisters, $creationHeight)" } @@ -184,9 +182,9 @@ object ErgoBox { .ensuring(_ == mandatoryRegisters.last.number + 1) val allRegisters: Seq[RegisterId] = - Helpers.concatArrays[RegisterId]( - Helpers.castArray(_mandatoryRegisters): Array[RegisterId], - Helpers.castArray(_nonMandatoryRegisters): Array[RegisterId]).ensuring(_.length == maxRegisters) + CollectionUtil.concatArrays[RegisterId]( + CollectionUtil.castArray(_mandatoryRegisters): Array[RegisterId], + CollectionUtil.castArray(_nonMandatoryRegisters): Array[RegisterId]).ensuring(_.length == maxRegisters) val mandatoryRegistersCount: Byte = mandatoryRegisters.size.toByte val nonMandatoryRegistersCount: Byte = nonMandatoryRegisters.size.toByte diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxAssets.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoBoxAssets.scala similarity index 100% rename from interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxAssets.scala rename to data/shared/src/main/scala/org/ergoplatform/ErgoBoxAssets.scala diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala similarity index 94% rename from interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala rename to data/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala index a167b95143..ba306159d4 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala @@ -2,19 +2,18 @@ package org.ergoplatform import debox.cfor import org.ergoplatform.ErgoBox._ -import org.ergoplatform.settings.ErgoAlgos +import scorex.util.encode.Base16 import scorex.util.{ModifierId, bytesToId} -import sigma.Extensions.CollOps +import sigma.Extensions.{ArrayOps, CollOps} +import sigma.ast.{ErgoTree, SType} +import sigma.ast.SType.AnyOps +import sigma.data.Digest32Coll import sigma.util.safeNewArray import sigma.{Coll, Colls} -import sigmastate.SType.AnyOps -import sigmastate.Values._ -import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.eval._ -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} import scala.collection.{immutable, mutable} import scala.runtime.ScalaRunTime @@ -97,7 +96,7 @@ class ErgoBoxCandidate(val value: Long, } override def toString: String = s"ErgoBoxCandidate($value, $ergoTree," + - s"tokens: (${additionalTokens.map(t => ErgoAlgos.encode(t._1) + ":" + t._2).toArray.mkString(", ")}), " + + s"tokens: (${additionalTokens.map(t => Base16.encode(t._1.toArray) + ":" + t._2).toArray.mkString(", ")}), " + s"$additionalRegisters, creationHeight: $creationHeight)" /** Additional tokens stored in the box, merged into a Map. @@ -216,7 +215,7 @@ object ErgoBoxCandidate { } else { val tokenIdSize = TokenId.size // optimization: access the value once cfor(0)(_ < nTokens, _ + 1) { i => - tokenIds(i) = Digest32Coll @@@ Colls.fromArray(r.getBytes(tokenIdSize)) // READ + tokenIds(i) = Digest32Coll @@ Colls.fromArray(r.getBytes(tokenIdSize)) // READ tokenAmounts(i) = r.getULong() // READ } } diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala b/data/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala similarity index 96% rename from interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala rename to data/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala index fcfdbc922b..eb36d60317 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala +++ b/data/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala @@ -6,13 +6,12 @@ import scorex.crypto.authds.ADKey import scorex.crypto.hash.Blake2b256 import scorex.util._ import sigma.Colls +import sigma.ast.syntax.ErgoBoxCandidateRType +import sigma.data.Digest32Coll +import sigma.eval.Extensions.EvalIterableOps +import sigma.interpreter.ProverResult import sigma.util.safeNewArray -import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.eval._ -import sigmastate.interpreter.ProverResult -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} import scala.util.Try diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/Input.scala b/data/shared/src/main/scala/org/ergoplatform/Input.scala similarity index 80% rename from interpreter/shared/src/main/scala/org/ergoplatform/Input.scala rename to data/shared/src/main/scala/org/ergoplatform/Input.scala index e0b4830797..4d15532ad2 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/Input.scala +++ b/data/shared/src/main/scala/org/ergoplatform/Input.scala @@ -1,13 +1,11 @@ package org.ergoplatform -import java.util - import org.ergoplatform.ErgoBox.BoxId -import org.ergoplatform.settings.ErgoAlgos import scorex.crypto.authds.ADKey -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} +import scorex.util.encode.Base16 +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} +import sigma.util.CollectionUtil /** * Inputs, that are used to enrich script context, but won't be spent by the transaction @@ -15,14 +13,14 @@ import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} * @param boxId - id of a box to add into context (should be in UTXO) */ case class DataInput(boxId: BoxId) { - override def toString: String = s"DataInput(${ErgoAlgos.encode(boxId)})" + override def toString: String = s"DataInput(${Base16.encode(boxId)})" override def equals(obj: Any): Boolean = obj match { case x: DataInput => java.util.Arrays.equals(boxId, x.boxId) case _ => false } - override def hashCode(): Int = Helpers.deepHashCode(boxId) + override def hashCode(): Int = CollectionUtil.deepHashCode(boxId) } /** @@ -43,7 +41,7 @@ class UnsignedInput(val boxId: BoxId, val extension: ContextExtension) { case _ => false } - override def hashCode(): Int = Helpers.deepHashCode(boxId) + override def hashCode(): Int = CollectionUtil.deepHashCode(boxId) /** * Input, that should be signed by prover and verified by verifier. @@ -60,7 +58,7 @@ class UnsignedInput(val boxId: BoxId, val extension: ContextExtension) { */ case class Input(override val boxId: BoxId, spendingProof: ProverResult) extends UnsignedInput(boxId, spendingProof.extension) { - override def toString: String = s"Input(${ErgoAlgos.encode(boxId)},$spendingProof)" + override def toString: String = s"Input(${Base16.encode(boxId)},$spendingProof)" } object Input { diff --git a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala new file mode 100644 index 0000000000..9d4de47a99 --- /dev/null +++ b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -0,0 +1,189 @@ +package org.ergoplatform.validation + +import sigma.SigmaException +import sigma.ast.{DeserializeContext, ErgoTree, MethodsContainer, SMethod} +import sigma.ast.TypeCodes.LastConstantCode +import sigma.serialization.{InvalidOpCode, SerializerException} +import sigma.util.Extensions.toUByte +import sigma.validation.ValidationRules._ +import sigma.validation._ +import sigma.ast.ErgoTree.HeaderType +import sigma.ast.syntax._ +import sigma.exceptions.InterpreterException +import sigma.serialization.ValueCodes.OpCode +import sigma.serialization.ValueSerializer + +/** All validation rules which are used to check soft-forkable conditions. Each validation + * rule throws a [[org.ergoplatform.validation.ValidationException]]. Each + * ValidationException can be caught and handled with respect to + * [[SigmaValidationSettings]], which can be changed by miners via voting. + * Thus, the behavior of the rules can be overridden without breaking consensus. + */ +object ValidationRules { + + object CheckDeserializedScriptType extends ValidationRule(FirstRuleId, + "Deserialized script should have expected type") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + + final def apply[T](d: DeserializeContext[_], script: SValue): Unit = { + checkRule() + if (d.tpe != script.tpe) { + throwValidationException( + new InterpreterException(s"Failed context deserialization of $d: \n" + + s"expected deserialized script to have type ${d.tpe}; got ${script.tpe}"), + Array[Any](d, script)) + } + } + } + + object CheckDeserializedScriptIsSigmaProp extends ValidationRule(1001, + "Deserialized script should have SigmaProp type") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + + /** @param root candidate node before it is added as a root of ErgoTree */ + final def apply[T](root: SValue): Unit = { + checkRule() + if (!root.tpe.isSigmaProp) { + throwValidationException( + new SerializerException(s"Failed deserialization, expected deserialized script to have type SigmaProp; got ${root.tpe}"), + Array[Any](root)) + } + } + } + + object CheckValidOpCode extends ValidationRule(1002, + "Check the opcode is supported by registered serializer or is added via soft-fork") + with SoftForkWhenCodeAdded { + override protected lazy val settings: SigmaValidationSettings = currentSettings + + final def apply[T](ser: ValueSerializer[_], opCode: OpCode): Unit = { + checkRule() + if (ser == null || ser.opCode != opCode) { + throwValidationException( + new InvalidOpCode(s"Cannot find serializer for Value with opCode = LastConstantCode + ${toUByte(opCode) - LastConstantCode}"), + Array(opCode)) + } + } + } + + /** Not used since v5.0.1. */ + object CheckIsSupportedIndexExpression extends ValidationRule(1003, + "Check the index expression for accessing collection element is supported.") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + } + + /** Not used since v5.0.3 */ + object CheckCostFunc extends ValidationRule(1004, + "Cost function should contain only operations from specified list.") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + } + + object CheckCalcFunc extends ValidationRule(1005, + "If SigmaProp.isProven method calls exists in the given function,\n then it is the last operation") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + } + + /** This rule is not use in v5.x, keep the commented code below as a precise + * documentation of its semantics. + */ + object CheckTupleType extends ValidationRule(1006, + "Supported tuple type.") with SoftForkWhenReplaced { + override protected lazy val settings: SigmaValidationSettings = currentSettings + +// final def apply[Ctx <: IRContext, T](ctx: Ctx)(e: ctx.Elem[_]): Unit = { +// checkRule() +// val condition = e match { +// case _: ctx.PairElem[_,_] => true +// case _ => false +// } +// if (!condition) { +// throwValidationException(new SigmaException(s"Invalid tuple type $e"), Array[ctx.Elem[_]](e)) +// } +// } + } + + object CheckAndGetMethod extends ValidationRule(1011, + "Check the type has the declared method.") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + + final def apply[T](objType: MethodsContainer, methodId: Byte): SMethod = { + checkRule() + val methodOpt = objType.getMethodById(methodId) + if (methodOpt.isDefined) methodOpt.get + else { + throwValidationException( + new SerializerException(s"The method with code $methodId doesn't declared in the type $objType."), + Array[Any](objType, methodId)) + } + } + + override def isSoftFork(vs: SigmaValidationSettings, + ruleId: Short, + status: RuleStatus, + args: Seq[Any]): Boolean = (status, args) match { + case (ChangedRule(newValue), Seq(objType: MethodsContainer, methodId: Byte)) => + val key = Array(objType.ownerType.typeId, methodId) + newValue.grouped(2).exists(java.util.Arrays.equals(_, key)) + case _ => false + } + } + + object CheckHeaderSizeBit extends ValidationRule(1012, + "For version greater then 0, size bit should be set.") with SoftForkWhenReplaced { + override protected lazy val settings: SigmaValidationSettings = currentSettings + + final def apply(header: HeaderType): Unit = { + checkRule() + val version = ErgoTree.getVersion(header) + if (version != 0 && !ErgoTree.hasSize(header)) { + throwValidationException( + new SigmaException(s"Invalid ErgoTreeHeader $header, size bit is expected for version $version"), + Array(header)) + } + } + } + + /** Not used since v5.0.3 */ + object CheckCostFuncOperation extends ValidationRule(1013, + "Check the opcode is allowed in cost function") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + } + + /** Not used since v5.0.1 */ + object CheckLoopLevelInCostFunction extends ValidationRule(1015, + "Check that loop level is not exceeded.") { + override protected lazy val settings: SigmaValidationSettings = currentSettings + } + + val ruleSpecs: Seq[ValidationRule] = Seq( + CheckDeserializedScriptType, + CheckDeserializedScriptIsSigmaProp, + CheckValidOpCode, + CheckIsSupportedIndexExpression, + CheckCostFunc, + CheckCalcFunc, + CheckTupleType, + CheckPrimitiveTypeCode, + CheckTypeCode, + CheckSerializableTypeCode, + CheckTypeWithMethods, + CheckAndGetMethod, + CheckHeaderSizeBit, + CheckCostFuncOperation, + CheckPositionLimit, + CheckLoopLevelInCostFunction + ) + + /** Validation settings that correspond to the current version of the ErgoScript implementation. + * Different version of the code will have a different set of rules here. + * This variable is globally available and can be use wherever checking of the rules is necessary. + * This is immutable data structure, it can be augmented with RuleStates from block extension + * sections of the blockchain, but that augmentation is only available in stateful context. + */ + val currentSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ + val map = ruleSpecs.map(r => r.id -> (r, EnabledRule)).toMap + assert(map.size == ruleSpecs.size, s"Duplicate ruleIds ${ruleSpecs.groupBy(_.id).filter(g => g._2.length > 1)}") + map + }) + +} diff --git a/data/shared/src/main/scala/sigma/SigmaDataReflection.scala b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala new file mode 100644 index 0000000000..48939b1460 --- /dev/null +++ b/data/shared/src/main/scala/sigma/SigmaDataReflection.scala @@ -0,0 +1,639 @@ +package sigma + +import org.ergoplatform.ErgoBox.RegisterId +import sigma.ast.SCollection.{SBooleanArray, SByteArray, SIntArray} +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.data.KeyValueColl +import sigma.eval.ErgoTreeEvaluator +import sigma.reflection.ReflectionData.registerClassEntry +import sigma.reflection.{ReflectionData, mkConstructor, mkMethod} +import sigma.serialization.ValueCodes.OpCode + +/** Reflection metadata for `interpreter` module. + * Such metadata is only used on JS platform to support reflection-like interfaces of + * RClass, RMethod, RConstructor. These interfaces implemented on JVM using Java + * reflection. + * + * For each class of this module that needs reflection metadata, + * we register a class entry with the necessary information. + * Only information that is needed at runtime is registered. + */ +object SigmaDataReflection { + val reflection = ReflectionData + + registerClassEntry(classOf[AND], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new AND(args(0).asInstanceOf[Value[SBooleanArray]]) + } + ) + ) + + registerClassEntry(classOf[ArithOp[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Byte])) { args => + new ArithOp(args(0).asInstanceOf[SValue], args(1).asInstanceOf[SValue], args(2).asInstanceOf[OpCode]) + } + ) + ) + + registerClassEntry(classOf[AtLeast], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new AtLeast(args(0).asInstanceOf[IntValue], args(1).asInstanceOf[CollectionValue[SSigmaProp.type]]) + } + ) + ) + + registerClassEntry(classOf[BinAnd], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new BinAnd(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[BoolValue]) + } + ) + ) + + registerClassEntry(classOf[BinOr], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new BinOr(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[BoolValue]) + } + ) + ) + + registerClassEntry(classOf[BinXor], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new BinXor(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[BoolValue]) + } + ) + ) + + registerClassEntry(classOf[BoolToSigmaProp], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new BoolToSigmaProp(args(0).asInstanceOf[BoolValue]) + } + ) + ) + + registerClassEntry(classOf[ByteArrayToBigInt], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new ByteArrayToBigInt(args(0).asInstanceOf[Value[SByteArray]]) + } + ) + ) + + registerClassEntry(classOf[CalcBlake2b256], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new CalcBlake2b256(args(0).asInstanceOf[Value[SByteArray]]) + } + ) + ) + + registerClassEntry(classOf[CalcSha256], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new CalcSha256(args(0).asInstanceOf[Value[SByteArray]]) + } + ) + ) + + registerClassEntry(classOf[CreateProveDHTuple], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => + new CreateProveDHTuple(args(0).asInstanceOf[GroupElementValue], + args(1).asInstanceOf[GroupElementValue], + args(2).asInstanceOf[GroupElementValue], + args(3).asInstanceOf[GroupElementValue]) + } + ) + ) + + registerClassEntry(classOf[Downcast[_,_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[SNumericType])) { args => + new Downcast(args(0).asInstanceOf[Value[SNumericType]], args(1).asInstanceOf[SNumericType]) + } + ) + ) + + registerClassEntry(classOf[EQ[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new EQ(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[Exponentiate], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new Exponentiate(args(0).asInstanceOf[GroupElementValue], args(1).asInstanceOf[BigIntValue]) + } + ) + ) + + registerClassEntry(classOf[GE[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new GE(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[GT[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new GT(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[If[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => + new If(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[SAnyValue], args(2).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[LE[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new LE(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[LT[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new LT(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[LogicalNot], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new LogicalNot(args(0).asInstanceOf[BoolValue]) + } + ) + ) + + registerClassEntry(classOf[MultiplyGroup], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new MultiplyGroup(args(0).asInstanceOf[GroupElementValue], args(1).asInstanceOf[GroupElementValue]) + } + ) + ) + + registerClassEntry(classOf[NEQ[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new NEQ(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[Negation[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new Negation(args(0).asInstanceOf[SAnyValue]) + } + ) + ) + + registerClassEntry(classOf[OR], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new OR(args(0).asInstanceOf[Value[SBooleanArray]]) + } + ) + ) + + { val clazz = SAvlTreeMethods.getClass + registerClassEntry(clazz, + methods = Map( + mkMethod(clazz, "update_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].update_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[KeyValueColl], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "contains_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].contains_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[Coll[Byte]], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "get_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].get_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[Coll[Byte]], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "getMany_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].getMany_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[Coll[Coll[Byte]]], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "remove_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].remove_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[Coll[Coll[Byte]]], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "insert_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SAvlTreeMethods.type].insert_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[AvlTree], + args(2).asInstanceOf[KeyValueColl], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + } + ) + ) + } + + { val clazz = SCollectionMethods.getClass + registerClassEntry(clazz, + methods = Map( + mkMethod(clazz, "zip_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].zip_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], + args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "getOrElse_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Int], classOf[java.lang.Object], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].getOrElse_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], + args(2).asInstanceOf[Int], + args(3).asInstanceOf[Any])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "patch_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Int], classOf[Coll[_]], classOf[Int], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].patch_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], + args(2).asInstanceOf[Int], + args(3).asInstanceOf[Coll[Any]], + args(4).asInstanceOf[Int])(args(5).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "map_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].map_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], + args(2).asInstanceOf[Any => Any])(args(3).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "updated_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Int], classOf[java.lang.Object], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].updated_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], + args(2).asInstanceOf[Int], + args(3))(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "indexOf_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[java.lang.Object], classOf[Int], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].indexOf_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], args(2), args(3).asInstanceOf[Int])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "updateMany_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].updateMany_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], + args(2).asInstanceOf[Coll[Int]], + args(3).asInstanceOf[Coll[Any]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "indices_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].indices_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator]) + }, + mkMethod(clazz, "flatMap_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SCollectionMethods.type].flatMap_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Any => Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) + } + ) + ) + } + + { val clazz = SGlobalMethods.getClass + registerClassEntry(clazz, + methods = Map( + mkMethod(clazz, "xor_eval", Array[Class[_]](classOf[MethodCall], classOf[SigmaDslBuilder], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => + obj.asInstanceOf[SGlobalMethods.type].xor_eval(args(0).asInstanceOf[MethodCall], + args(1).asInstanceOf[SigmaDslBuilder], + args(2).asInstanceOf[Coll[Byte]], + args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) + } + ) + ) + } + + registerClassEntry(classOf[SigmaAnd], + constructors = Array( + mkConstructor(Array(classOf[Seq[_]])) { args => + new SigmaAnd(args(0).asInstanceOf[Seq[SigmaPropValue]]) + } + ) + ) + + registerClassEntry(classOf[SigmaOr], + constructors = Array( + mkConstructor(Array(classOf[Seq[_]])) { args => + new SigmaOr(args(0).asInstanceOf[Seq[SigmaPropValue]]) + } + ) + ) + + registerClassEntry(classOf[SubstConstants[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => + new SubstConstants(args(0).asInstanceOf[Value[SByteArray]], + args(1).asInstanceOf[Value[SIntArray]], + args(2).asInstanceOf[CollectionValue[SType]]) + } + ) + ) + + registerClassEntry(classOf[Upcast[_,_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[SNumericType])) { args => + new Upcast(args(0).asInstanceOf[Value[SNumericType]], args(1).asInstanceOf[SNumericType]) + } + ) + ) + + registerClassEntry(classOf[BlockValue], + constructors = Array( + mkConstructor(Array(classOf[IndexedSeq[_]], classOf[Value[_]])) { args => + new BlockValue(args(0).asInstanceOf[IndexedSeq[BlockItem]], args(1).asInstanceOf[SValue]) + } + ) + ) + + registerClassEntry(classOf[ConcreteCollection[_]], + constructors = Array( + mkConstructor(Array(classOf[Seq[_]], classOf[SType])) { args => + new ConcreteCollection(args(0).asInstanceOf[Seq[SValue]], args(1).asInstanceOf[SType]) + } + ) + ) + + registerClassEntry(classOf[FuncValue], + constructors = Array( + mkConstructor(Array(classOf[IndexedSeq[_]], classOf[Value[_]])) { args => + new FuncValue(args(0).asInstanceOf[IndexedSeq[(Int, SType)]], args(1).asInstanceOf[SValue]) + } + ) + ) + + registerClassEntry(classOf[Tuple], + constructors = Array( + mkConstructor(Array(classOf[IndexedSeq[_]])) { args => + new Tuple(args(0).asInstanceOf[IndexedSeq[SValue]]) + } + ) + ) + + registerClassEntry(classOf[ValDef], + constructors = Array( + mkConstructor(Array(classOf[Int], classOf[Seq[_]], classOf[Value[_]])) { args => + new ValDef(args(0).asInstanceOf[Int], args(1).asInstanceOf[Seq[STypeVar]], args(2).asInstanceOf[SValue]) + } + ) + ) + + registerClassEntry(classOf[Apply], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[IndexedSeq[_]])) { args => + new Apply(args(0).asInstanceOf[SValue], args(1).asInstanceOf[IndexedSeq[SValue]]) + } + ) + ) + + registerClassEntry(classOf[ApplyTypes], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Seq[_]])) { args => + new ApplyTypes(args(0).asInstanceOf[SValue], args(1).asInstanceOf[Seq[SType]]) + } + ) + ) + + registerClassEntry(classOf[Block], + constructors = Array( + mkConstructor(Array(classOf[Seq[_]], classOf[Value[_]])) { args => + new Block(args(0).asInstanceOf[Seq[Val]], args(1).asInstanceOf[SValue]) + } + ) + ) + + registerClassEntry(classOf[Lambda], + constructors = Array( + mkConstructor(Array(classOf[Seq[_]], classOf[IndexedSeq[_]], classOf[SType], classOf[Option[_]])) { args => + new Lambda(args(0).asInstanceOf[Seq[STypeParam]], + args(1).asInstanceOf[IndexedSeq[(String, SType)]], + args(2).asInstanceOf[SType], + args(3).asInstanceOf[Option[SValue]]) + } + ) + ) + + registerClassEntry(classOf[MethodCall], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[SMethod], classOf[IndexedSeq[_]], classOf[scala.collection.immutable.Map[_,_]])) { args => + new MethodCall(args(0).asInstanceOf[SValue], + args(1).asInstanceOf[SMethod], + args(2).asInstanceOf[IndexedSeq[SValue]], + args(3).asInstanceOf[Map[STypeVar,SType]]) + } + ) + ) + + registerClassEntry(classOf[MethodCallLike], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[java.lang.String], classOf[IndexedSeq[_]], classOf[SType])) { args => + new MethodCallLike(args(0).asInstanceOf[SValue], + args(1).asInstanceOf[String], + args(2).asInstanceOf[IndexedSeq[SValue]], + args(3).asInstanceOf[SType]) + } + ) + ) + + registerClassEntry(classOf[Select], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[java.lang.String], classOf[Option[_]])) { args => + new Select(args(0).asInstanceOf[SValue], args(1).asInstanceOf[String], args(2).asInstanceOf[Option[SType]]) + } + ) + ) + + registerClassEntry(classOf[ValNode], + constructors = Array( + mkConstructor(Array(classOf[java.lang.String], classOf[SType], classOf[Value[_]])) { args => + new ValNode(args(0).asInstanceOf[String], args(1).asInstanceOf[SType], args(2).asInstanceOf[SValue]) + } + ) + ) + + registerClassEntry(classOf[Append[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new Append(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[CollectionValue[SType]]) + } + ) + ) + + registerClassEntry(classOf[ByIndex[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Option[_]])) { args => + new ByIndex(args(0).asInstanceOf[CollectionValue[SType]], + args(1).asInstanceOf[IntValue], args(2).asInstanceOf[Option[SValue]]) + } + ) + ) + + registerClassEntry(classOf[Exists[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new Exists(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) + } + ) + ) + + registerClassEntry(classOf[ExtractAmount], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new ExtractAmount(args(0).asInstanceOf[BoxValue]) + } + ) + ) + + registerClassEntry(classOf[ExtractBytesWithNoRef], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new ExtractBytesWithNoRef(args(0).asInstanceOf[BoxValue]) + } + ) + ) + + registerClassEntry(classOf[ExtractCreationInfo], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new ExtractCreationInfo(args(0).asInstanceOf[BoxValue]) + } + ) + ) + + registerClassEntry(classOf[ExtractId], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new ExtractId(args(0).asInstanceOf[BoxValue]) + } + ) + ) + + registerClassEntry(classOf[ExtractRegisterAs[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[org.ergoplatform.ErgoBox.RegisterId], classOf[SOption[_]])) { args => + new ExtractRegisterAs(args(0).asInstanceOf[BoxValue], args(1).asInstanceOf[RegisterId], args(2).asInstanceOf[SOption[SAny.type]]) + } + ) + ) + + registerClassEntry(classOf[ExtractScriptBytes], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new ExtractScriptBytes(args(0).asInstanceOf[BoxValue]) + } + ) + ) + + registerClassEntry(classOf[Filter[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new Filter(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) + } + ) + ) + + registerClassEntry(classOf[Fold[_,_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => + new Fold(args(0).asInstanceOf[CollectionValue[SType]], + args(1).asInstanceOf[SValue], args(2).asInstanceOf[Value[SFunc]]) + } + ) + ) + + registerClassEntry(classOf[ForAll[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new ForAll(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) + } + ) + ) + + registerClassEntry(classOf[MapCollection[_,_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new MapCollection(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) + } + ) + ) + + registerClassEntry(classOf[OptionGet[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new OptionGet(args(0).asInstanceOf[Value[SOption[SType]]]) + } + ) + ) + + registerClassEntry(classOf[OptionGetOrElse[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => + new OptionGetOrElse(args(0).asInstanceOf[Value[SOption[SType]]], args(1).asInstanceOf[SValue]) + } + ) + ) + + registerClassEntry(classOf[OptionIsDefined[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new OptionIsDefined(args(0).asInstanceOf[Value[SOption[SType]]]) + } + ) + ) + + registerClassEntry(classOf[SelectField], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Byte])) { args => + new SelectField(args(0).asInstanceOf[Value[STuple]], args(1).asInstanceOf[Byte]) + } + ) + ) + + registerClassEntry(classOf[SigmaPropBytes], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new SigmaPropBytes(args(0).asInstanceOf[SigmaPropValue]) + } + ) + ) + + registerClassEntry(classOf[SizeOf[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]])) { args => + new SizeOf(args(0).asInstanceOf[CollectionValue[SType]]) + } + ) + ) + + registerClassEntry(classOf[Slice[_]], + constructors = Array( + mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => + new Slice(args(0).asInstanceOf[CollectionValue[SType]], + args(1).asInstanceOf[IntValue], args(2).asInstanceOf[IntValue]) + } + ) + ) +} diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/CostItem.scala b/data/shared/src/main/scala/sigma/ast/CostItem.scala similarity index 71% rename from interpreter/shared/src/main/scala/sigmastate/interpreter/CostItem.scala rename to data/shared/src/main/scala/sigma/ast/CostItem.scala index 27b50779cf..c69c81daa3 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/CostItem.scala +++ b/data/shared/src/main/scala/sigma/ast/CostItem.scala @@ -1,16 +1,12 @@ -package sigmastate.interpreter +package sigma.ast -import sigmastate.{FixedCost, JitCost, PerItemCost, SMethod, SType, TypeBasedCost} -import sigmastate.Values.{FixedCostValueCompanion, PerItemCostValueCompanion, ValueCompanion} -import sigmastate.lang.Terms.MethodCall - -/** An item in the cost accumulation trace of a [[sigmastate.Values.ErgoTree]] evaluation. */ +/** An item in the cost accumulation trace of a [[sigma.ast.ErgoTree]] evaluation. */ abstract class CostItem { def opName: String def cost: JitCost } -/** An item in the cost accumulation trace of a [[sigmastate.Values.ErgoTree]] evaluation. +/** An item in the cost accumulation trace of a [[sigma.ast.ErgoTree]] evaluation. * Represents cost of simple operation. * Used for debugging, testing and profiling of costing. * @param opDesc descriptor of the ErgoTree operation @@ -29,13 +25,13 @@ object FixedCostItem { } } -/** An item in the cost accumulation trace of a [[sigmastate.Values.ErgoTree]] evaluation. +/** An item in the cost accumulation trace of a [[sigma.ast.ErgoTree]] evaluation. * Represents cost of an operation which depends on type (e.g. type of arguments). * Used for debugging, testing and profiling of costing. * @param opDesc descriptor of the ErgoTree operation * @param costKind type based cost descriptor added to accumulator * @param tpe concrete type on this the operation is executed - * @see [[sigmastate.LE]], [[sigmastate.GT]] + * @see [[LE]], [[GT]] */ case class TypeBasedCostItem( opDesc: OperationDesc, @@ -60,7 +56,7 @@ object TypeBasedCostItem { } } -/** An item in the cost accumulation trace of a [[sigmastate.Values.ErgoTree]] evaluation. +/** An item in the cost accumulation trace of a [[sigma.ast.ErgoTree]] evaluation. * Represents cost of a sequence of operation. * Used for debugging, testing and profiling of costing. * @@ -80,15 +76,4 @@ object SeqCostItem { SeqCostItem(companion.opDesc, companion.costKind, nItems) } -/** An item in the cost accumulation trace of a [[sigmastate.Values.ErgoTree]] evaluation. - * Represents cost of MethodCall operation. - * Used for debugging, testing and profiling of costing. - * - * @param items cost details obtained as part of MethodCall evaluation - */ -case class MethodCallCostItem(items: CostDetails) extends CostItem { - override def opName: String = MethodCall.typeName - override def cost: JitCost = items.cost -} - diff --git a/interpreter/shared/src/main/scala/sigmastate/CostKind.scala b/data/shared/src/main/scala/sigma/ast/CostKind.scala similarity index 96% rename from interpreter/shared/src/main/scala/sigmastate/CostKind.scala rename to data/shared/src/main/scala/sigma/ast/CostKind.scala index 0730bb4ec0..1fda6018ec 100644 --- a/interpreter/shared/src/main/scala/sigmastate/CostKind.scala +++ b/data/shared/src/main/scala/sigma/ast/CostKind.scala @@ -1,9 +1,9 @@ -package sigmastate +package sigma.ast import scala.runtime.Statics /** Cost descriptor of a single operation, usually associated with - * [[sigmastate.interpreter.OperationDesc]]. + * [[OperationDesc]]. */ sealed abstract class CostKind diff --git a/data/shared/src/main/scala/sigma/ast/ErgoTree.scala b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala new file mode 100644 index 0000000000..d6ed6118dc --- /dev/null +++ b/data/shared/src/main/scala/sigma/ast/ErgoTree.scala @@ -0,0 +1,414 @@ +package sigma.ast + +import scorex.util.encode.Base16 +import sigma.VersionContext +import sigma.ast.ErgoTree.{HeaderType, substConstants} +import sigma.ast.syntax._ +import sigma.data.{CSigmaProp, SigmaBoolean} +import sigma.kiama.rewriting.Rewriter.{everywherebu, strategy} +import sigma.validation.ValidationException +import sigma.ast.syntax.ValueOps +import sigma.eval.Extensions.SigmaBooleanOps +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer +import sigma.serialization.{ConstantStore, ErgoTreeSerializer, SigmaSerializer, ValueSerializer} +import supertagged.TaggedType + +import java.util.Objects +import scala.collection.mutable + +/** This is alternative representation of ErgoTree expression when it cannot be parsed + * due to `error`. This is used by the nodes running old versions of code to recognize + * soft-fork conditions and skip validation of box propositions which are unparsable. */ +case class UnparsedErgoTree(bytes: mutable.WrappedArray[Byte], error: ValidationException) + +/** The root of ErgoScript IR. Serialized instances of this class are self sufficient and can be passed around. + * ErgoTreeSerializer defines top-level serialization format of the scripts. + * The interpretation of the byte array depend on the first `header` byte, which uses VLQ encoding up to 30 bits. + * Currently we define meaning for only first byte, which may be extended in future versions. + * 7 6 5 4 3 2 1 0 + * ------------------------- + * | | | | | | | | | + * ------------------------- + * Bit 7 == 1 if the header contains more than 1 byte (default == 0) + * Bit 6 - reserved for GZIP compression (should be 0) + * Bit 5 == 1 - reserved for context dependent costing (should be = 0) + * Bit 4 == 1 if constant segregation is used for this ErgoTree (default = 0) + * (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/264) + * Bit 3 == 1 if size of the whole tree is serialized after the header byte (default = 0) + * Bits 2-0 - language version (current version == 0) + * + * Currently we don't specify interpretation for the second and other bytes of the header. + * We reserve the possibility to extend header by using Bit 7 == 1 and chain additional bytes as in VLQ. + * Once the new bytes are required, a new version of the language should be created and implemented. + * That new language will give an interpretation for the new bytes. + * + * Consistency between fields is ensured by private constructor and factory methods in `ErgoTree` object. + * For performance reasons, ErgoTreeSerializer can be configured to perform additional constant segregation. + * In such a case after deserialization there may be more constants segregated. This is done for example to + * support caching optimization described in #264 mentioned above. + * + * The default behavior of ErgoTreeSerializer is to preserve original structure of ErgoTree and check + * consistency. In case of any inconsistency the serializer throws exception. + * + * @param header the first byte of serialized byte array which determines interpretation of the rest of the array + * @param constants If isConstantSegregation == true contains the constants for which there may be + * ConstantPlaceholders in the tree. + * If isConstantSegregation == false this array should be empty and any placeholder in + * the tree will lead to exception. + * @param root On the right side it has valid expression of `SigmaProp` type. Or alternatively, + * on the left side, it has unparsed bytes along with the ValidationException, + * which caused the deserializer to fail. + * `Right(tree)` if isConstantSegregation == true contains ConstantPlaceholder + * instead of some Constant nodes. Otherwise, it may not contain placeholders. + * It is possible to have both constants and placeholders in the tree, but for every placeholder + * there should be a constant in `constants` array. + * @param propositionBytes original bytes of this tree from which it has been deserialized. + * If null then the bytes are not provided, and will be lazily generated when `bytes` + * method is called. + * These bytes are obtained in two ways: + * 1) in the ErgoTreeSerializer from Reader + * 2) in the alternative constructor using ErgoTreeSerializer.serializeErgoTree + * @param givenDeserialize optional flag, which contains information about presence of + * deserialization operations in the tree. If it is None, the information is not + * available. If Some(true) then there are deserialization operations, otherwise + * the tree doesn't contain deserialization and is eligible + * for optimized execution. + * ErgoTreeSerializer parsing method computes the value of + * this flag and provides it to the constructor. + */ +case class ErgoTree private[sigma]( + header: HeaderType, + constants: IndexedSeq[Constant[SType]], + root: Either[UnparsedErgoTree, SigmaPropValue], + private val propositionBytes: Array[Byte], + private val givenDeserialize: Option[Boolean], + private val givenIsUsingBlockchainContext: Option[Boolean] +) { + def this( + header: HeaderType, + constants: IndexedSeq[Constant[SType]], + root: Either[UnparsedErgoTree, SigmaPropValue]) = + this( + header, constants, root, + propositionBytes = DefaultSerializer.serializeErgoTree( + ErgoTree(header, constants, root, null, None, None) + ), + givenDeserialize = None, + givenIsUsingBlockchainContext = None + ) + + require(isConstantSegregation || constants.isEmpty) + + require(version == 0 || hasSize, s"For newer version the size bit is required: $this") + + /** Then it throws the error from UnparsedErgoTree. + * It does so on every usage of `proposition` because the lazy value remains uninitialized. + */ + @deprecated("Use toProposition instead", "v2.1") + lazy val proposition: SigmaPropValue = toProposition(isConstantSegregation) + + /** Version of this tree (== BlockVersion - 1). */ + @inline final def version: Byte = ErgoTree.getVersion(header) + + @inline final def isRightParsed: Boolean = root.isRight + + /** @return true, if constant segregation bit is set in the header. */ + @inline final def isConstantSegregation: Boolean = ErgoTree.isConstantSegregation(header) + + /** @return true, if size bit is set in the header. */ + @inline final def hasSize: Boolean = ErgoTree.hasSize(header) + + private[sigma] var _bytes: Array[Byte] = propositionBytes + + /** Serialized bytes of this tree. */ + final def bytes: Array[Byte] = { + if (_bytes == null) { + _bytes = DefaultSerializer.serializeErgoTree(this) + } + _bytes + } + + /** Hexadecimal encoded string of ErgoTree.bytes. */ + final def bytesHex: String = Base16.encode(bytes) + + private[sigma] var _hasDeserialize: Option[Boolean] = givenDeserialize + + /** Returns true if the tree contains at least one deserialization operation, + * false otherwise. + */ + lazy val hasDeserialize: Boolean = { + if (_hasDeserialize.isEmpty) { + _hasDeserialize = Some(root match { + case Right(p) => Value.hasDeserialize(p) + case _ => false + }) + } + _hasDeserialize.get + } + + private[sigma] var _isUsingBlockchainContext: Option[Boolean] = givenIsUsingBlockchainContext + + /** Returns true if the tree depends on the blockchain context. + */ + lazy val isUsingBlockchainContext: Boolean = { + if (_isUsingBlockchainContext.isEmpty) { + _isUsingBlockchainContext = Some(root match { + case Right(p) => Value.isUsingBlockchainContext(p) + case _ => false + }) + } + _isUsingBlockchainContext.get + } + + /** Serialized proposition expression of SigmaProp type with + * ConstantPlaceholder nodes not replaced by Constant nodes. + */ + lazy val template: Array[Byte] = { + val r = SigmaSerializer.startReader(bytes) + DefaultSerializer.deserializeHeaderWithTreeBytes(r)._4 + } + + /** Base16 encoding of `template` bytes. */ + def templateHex: String = Base16.encode(template) + + /** Get proposition expression from this contract. + * When root.isRight then + * if replaceConstants == false this is the same as `root.right.get`. + * Otherwise, it is equivalent to `root.right.get` where all placeholders are replaced by Constants. + * When root.isLeft then + * throws the error from UnparsedErgoTree. + * It does so on every usage of `proposition` because the lazy value remains uninitialized. + */ + def toProposition(replaceConstants: Boolean): SigmaPropValue = root match { + case Right(tree) => + val prop = if (replaceConstants) + substConstants(tree, constants).asSigmaProp + else + tree + prop + case Left(UnparsedErgoTree(_, error)) => + throw error + } + + /** This method attempts to convert the current instance into a `SigmaBoolean`. + * It does so by first converting the instance to a `SigmaPropValue` using the + * `toProposition` method, and then checks if the resulting expression is a + * `SigmaPropConstant`. If it is, the method extracts the `SigmaBoolean` from the + * `SigmaPropConstant`. Otherwise, the method returns `None`. + * + * @note This method relies on constant segregation flag in the header to determine the + * behavior of `toProposition` method. + * + * @return `Some(SigmaBoolean)` if conversion is successful, `None` otherwise. + */ + def toSigmaBooleanOpt: Option[SigmaBoolean] = { + val prop = this.toProposition(this.isConstantSegregation) + prop match { + case SigmaPropConstant(p) => Some(p.asInstanceOf[CSigmaProp].wrappedValue) + case _ => None + } + } + + /** The default equality of case class is overridden to exclude `complexity`. */ + override def canEqual(that: Any): Boolean = that.isInstanceOf[ErgoTree] + + override def hashCode(): Int = header * 31 + Objects.hash(constants, root) + + override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || + ((obj.asInstanceOf[AnyRef] != null) && (obj match { + case other: ErgoTree => + other.header == header && other.constants == constants && other.root == root + case _ => false + })) +} + +object ErgoTree { + /** Represents information in ErgoTree header. */ + object HeaderType extends TaggedType[Byte] + + type HeaderType = HeaderType.Type + + /** Current version of ErgoTree serialization format (aka bite-code language version) */ + val VersionFlag: Byte = VersionContext.MaxSupportedScriptVersion + + /** Header flag to indicate that constant segregation should be applied. */ + val ConstantSegregationFlag: Byte = 0x10 + + /** Header flag to indicate that whole size of ErgoTree should be saved before tree content. */ + val SizeFlag: Byte = 0x08 + + /** Header mask to extract version bits. */ + val VersionMask: Byte = 0x07 + + /** Header with all the flags set to 0 and version 0. */ + val ZeroHeader: HeaderType = HeaderType @@ 0.toByte + + /** Default value of ErgoTree.header byte */ + val DefaultHeader: HeaderType = ZeroHeader //HeaderType @@ (VersionFlag | SizeFlag).toByte + + /** Default header with constant segregation enabled. */ + val ConstantSegregationHeader: HeaderType = HeaderType @@ (DefaultHeader | ConstantSegregationFlag).toByte + + /** @return true if the constant segregation flag is set to 1 in the given header byte. */ + @inline final def isConstantSegregation(header: HeaderType): Boolean = (header & ConstantSegregationFlag) != 0 + + /** @return true if the size flag is set to 1 in the given header byte. */ + @inline final def hasSize(header: HeaderType): Boolean = (header & SizeFlag) != 0 + + /** @return a value of the version bits from the given header byte. */ + @inline final def getVersion(header: HeaderType): Byte = (header & VersionMask).toByte + + /** Update the version bits of the given header byte with the given version value, + * leaving all other bits unchanged. + */ + @inline final def setVersionBits(header: HeaderType, version: Byte): HeaderType = { + require(version < 8, s"ErgoTree.version should be < 8: $version") + val h = header & (~VersionMask) // clear version bits + HeaderType @@ (h | version).toByte + } + + /** Sets the required bit in the given header: + * - The SizeFlag is set if version > 0 + */ + @inline final def setRequiredBits(header: HeaderType): HeaderType = { + if (getVersion(header) > 0) { + // set SizeFlag if version is greater then 0 (see require() in ErgoTree constructor) + HeaderType @@ (header | ErgoTree.SizeFlag).toByte + } else + header + } + + /** Sets the ConstantSegregationFlag in the given header */ + @inline final def setConstantSegregation(header: HeaderType): HeaderType = { + HeaderType @@ (header | ConstantSegregationFlag).toByte + } + + /** Sets the ConstantSegregationFlag in the given header */ + @inline final def setSizeBit(header: HeaderType): HeaderType = { + HeaderType @@ (header | SizeFlag).toByte + } + + /** Creates valid header byte with the given version. + * The SizeFlag is set if version > 0 */ + @inline final def defaultHeaderWithVersion(version: Byte): HeaderType = { + headerWithVersion(DefaultHeader, version) + } + + /** Creates valid header byte with the given version. + * The SizeFlag is set if version > 0 */ + @inline final def headerWithVersion(header: HeaderType, version: Byte): HeaderType = { + // take the header and embedd the given version in it + val h = setVersionBits(header, version) + setRequiredBits(h) + } + + /** Substitute [[ConstantPlaceholder]] nodes in the given expression with the constants + * taken from the given collection. + * + * @param root expression to transform + * @param constants collection of constants to replace placeholders + * @return new expression without placeholders + */ + def substConstants(root: SValue, constants: IndexedSeq[Constant[SType]]): SValue = { + val store = new ConstantStore(constants) + val substRule = strategy[Any] { + case ph: ConstantPlaceholder[_] => + Some(store.get(ph.id)) + case _ => None + } + everywherebu(substRule)(root).fold(root)(_.asInstanceOf[SValue]) + } + + /** Create an ErgoTree with the given parameters. */ + def apply( + header: HeaderType, + constants: IndexedSeq[Constant[SType]], + root: SigmaPropValue): ErgoTree = { + new ErgoTree(setRequiredBits(header), constants, Right(root)) + } + + val EmptyConstants: IndexedSeq[Constant[SType]] = Array[Constant[SType]]() + + /** Create new ErgoTree for the given proposition using default header. + * If the property is not a simple constant, then constant segregation is performed. + */ + def fromProposition(prop: SigmaPropValue): ErgoTree = { + fromProposition(ErgoTree.ZeroHeader, prop) + } + + /** Create new ErgoTree for the given proposition using the given header flags. + * If the property is not a simple constant, then constant segregation is performed. + */ + def fromProposition(header: HeaderType, prop: SigmaPropValue): ErgoTree = { + prop match { + case SigmaPropConstant(_) => withoutSegregation(header, prop) + case _ => withSegregation(header, prop) + } + } + + /** Create new ErgoTree for the given sigma proposition using default header and + * without performing constant segregation. + */ + def fromSigmaBoolean(pk: SigmaBoolean): ErgoTree = { + withoutSegregation(ZeroHeader, pk.toSigmaPropValue) + } + + /** Create new ErgoTree for the given sigma proposition using the given header flags + * and without performing constant segregation. + */ + def fromSigmaBoolean(header: HeaderType, pk: SigmaBoolean): ErgoTree = { + withoutSegregation(header, pk.toSigmaPropValue) + } + + /** Create new ErgoTree for the given proposition using the given header flags and + * without performing constant segregation. + */ + def withoutSegregation(header: HeaderType, root: SigmaPropValue): ErgoTree = + ErgoTree(setRequiredBits(header), EmptyConstants, root) + + /** Build ErgoTree via serialization of the value with ConstantSegregationHeader, constants segregated + * from the tree and ConstantPlaceholders referring to the segregated constants. + * + * This method uses single traverse of the tree to: + * 1) find and segregate all constants; + * 2) replace constants with ConstantPlaceholders in the `tree`; + * 3) write the `tree` to the Writer's buffer obtaining `treeBytes`; + * 4) deserialize `tree` with ConstantPlaceholders. + * + * @param headerFlags additional header flags to combine with + * ConstantSegregationHeader flag. + * @param prop expression to be transformed into ErgoTree + * */ + def withSegregation(header: HeaderType, prop: SigmaPropValue): ErgoTree = { + val constantStore = new ConstantStore() + val w = SigmaSerializer.startWriter(constantStore) + // serialize value and segregate constants into constantStore + ValueSerializer.serialize(prop, w) + val extractedConstants = constantStore.getAll + val r = SigmaSerializer.startReader(w.toBytes) + r.constantStore = new ConstantStore(extractedConstants) + // deserialize value with placeholders + val valueWithPlaceholders = ValueSerializer.deserialize(r).asSigmaProp + new ErgoTree( + header = setRequiredBits(setConstantSegregation(header)), + constants = extractedConstants, + root = Right(valueWithPlaceholders)) + } + + /** Deserializes an ErgoTree instance from a hexadecimal string. + * + * @param hex a hexadecimal string representing the serialized ErgoTree + */ + def fromHex(hex: String): ErgoTree = { + val bytes = Base16.decode(hex).get + fromBytes(bytes) + } + + /** Deserializes an ErgoTree instance from an array of bytes. + * + * @param bytes an array of bytes representing the serialized ErgoTree + */ + def fromBytes(bytes: Array[Byte]): ErgoTree = { + ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes) + } +} diff --git a/interpreter/shared/src/main/scala/sigmastate/JitCost.scala b/data/shared/src/main/scala/sigma/ast/JitCost.scala similarity index 87% rename from interpreter/shared/src/main/scala/sigmastate/JitCost.scala rename to data/shared/src/main/scala/sigma/ast/JitCost.scala index dd6d57fe9f..0b84dc21e2 100644 --- a/interpreter/shared/src/main/scala/sigmastate/JitCost.scala +++ b/data/shared/src/main/scala/sigma/ast/JitCost.scala @@ -1,11 +1,12 @@ -package sigmastate +package sigma.ast /** Represents cost estimation computed by JITC interpreter. * The JITC costs use 10x more accurate scale comparing to block cost values. * * @see toBlockCost */ -case class JitCost private[sigmastate](private[sigmastate] val value: Int) extends AnyVal { +// TODO make 'value` private[sigma] after code moved from sigmastate package +case class JitCost private[sigma](val value: Int) extends AnyVal { /** Adds two cost values. */ def +(y: JitCost): JitCost = new JitCost(java7.compat.Math.addExact(value, y.value)) diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/OperationDesc.scala b/data/shared/src/main/scala/sigma/ast/OperationDesc.scala similarity index 93% rename from interpreter/shared/src/main/scala/sigmastate/interpreter/OperationDesc.scala rename to data/shared/src/main/scala/sigma/ast/OperationDesc.scala index ca7fc9b217..06ba3391a8 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/OperationDesc.scala +++ b/data/shared/src/main/scala/sigma/ast/OperationDesc.scala @@ -1,7 +1,4 @@ -package sigmastate.interpreter - -import sigmastate.{CostKind, SMethod} -import sigmastate.Values.ValueCompanion +package sigma.ast /** Each costable operation is described in one of the following ways: * 1) using [[ValueCompanion]] - operation with separate node class diff --git a/interpreter/shared/src/main/scala/sigmastate/Operations.scala b/data/shared/src/main/scala/sigma/ast/Operations.scala similarity index 99% rename from interpreter/shared/src/main/scala/sigmastate/Operations.scala rename to data/shared/src/main/scala/sigma/ast/Operations.scala index 51ae277757..44804de84e 100644 --- a/interpreter/shared/src/main/scala/sigmastate/Operations.scala +++ b/data/shared/src/main/scala/sigma/ast/Operations.scala @@ -1,7 +1,7 @@ -package sigmastate +package sigma.ast -import sigmastate.lang.SigmaPredef.PredefinedFuncRegistry -import sigmastate.lang.StdSigmaBuilder +import sigma.serialization.CoreByteWriter.ArgInfo +import SigmaPredef.PredefinedFuncRegistry /** WARNING: This file is generated by GenInfoObjects tool. * Don't edit it directly, use the tool instead to regenerate. diff --git a/data/shared/src/main/scala/sigma/ast/SMethod.scala b/data/shared/src/main/scala/sigma/ast/SMethod.scala new file mode 100644 index 0000000000..2d306d8948 --- /dev/null +++ b/data/shared/src/main/scala/sigma/ast/SMethod.scala @@ -0,0 +1,311 @@ +package sigma.ast + +import debox.cfor +import sigma.ast.SMethod.{InvokeDescBuilder, MethodCostFunc} +import sigma.ast.syntax._ +import sigma.data.RType +import sigma.eval.{CostDetails, ErgoTreeEvaluator, GivenCost, TracedCost} +import sigma.reflection.{RClass, RMethod} +import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.validation.ValidationRules.CheckTypeWithMethods +import sigma.{Coll, Evaluation} + +import scala.collection.compat.immutable.ArraySeq +import scala.reflect.ClassTag + +/** Meta information which can be attached to SMethod. + * @param opDesc optional operation descriptor + * @param description human readable description of the method + * @param args one item for each argument */ +case class OperationInfo(opDesc: Option[ValueCompanion], description: String, args: Seq[ArgInfo]) { + def isFrontendOnly: Boolean = opDesc.isEmpty + def opTypeName: String = opDesc.map(_.typeName).getOrElse("(FRONTEND ONLY)") +} + +object OperationInfo { + /** Convenience factory method. */ + def apply(opDesc: ValueCompanion, description: String, args: Seq[ArgInfo]): OperationInfo = + OperationInfo(Some(opDesc), description, args) +} + +/** Meta information connecting SMethod with ErgoTree. + * The optional builder is used by front-end ErgoScript compiler to replace method calls + * with ErgoTree nodes. In many cases [[SMethod.MethodCallIrBuilder]] builder is used. + * However there are specific cases where more complex builders are used, see for example + * usage of `withIRInfo` in the declaration of [[SCollection.GetOrElseMethod]]. + * @param irBuilder optional method call recognizer and ErgoTree node builder. + * When the partial function is defined on a tuple + * (builder, obj, m, args, subst) it transforms it to a new ErgoTree + * node, which is then used in the resuting ErgoTree coming out of + * the ErgoScript compiler. + * @param javaMethod Java [[Method]] which should be used to evaluate + * [[sigmastate.lang.Terms.MethodCall]] node of ErgoTree. + * @param invokeDescsBuilder optional builder of additional type descriptors (see extraDescriptors) + */ +case class MethodIRInfo( + irBuilder: Option[PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue]], + javaMethod: Option[RMethod], + invokeDescsBuilder: Option[InvokeDescBuilder] +) + +/** Represents method descriptor. + * + * @param objType type or type constructor descriptor + * @param name method name + * @param stype method signature type, + * where `stype.tDom`` - argument type and + * `stype.tRange` - method result type. + * @param methodId method code, it should be unique among methods of the same objType. + * @param costKind cost descriptor for this method + * @param irInfo meta information connecting SMethod with ErgoTree (see [[MethodIRInfo]]) + * @param docInfo optional human readable method description data + * @param costFunc optional specification of how the cost should be computed for the + * given method call (See ErgoTreeEvaluator.calcCost method). + */ +case class SMethod( + objType: MethodsContainer, + name: String, + stype: SFunc, + methodId: Byte, + costKind: CostKind, + irInfo: MethodIRInfo, + docInfo: Option[OperationInfo], + costFunc: Option[MethodCostFunc]) { + + /** Operation descriptor of this method. */ + lazy val opDesc = MethodDesc(this) + + /** Finds and keeps the [[RMethod]] instance which corresponds to this method descriptor. + * The lazy value is forced only if irInfo.javaMethod == None + */ + lazy val javaMethod: RMethod = { + irInfo.javaMethod.getOrElse { + val paramTypes = stype.tDom.drop(1).map(t => t match { + case _: STypeVar => classOf[AnyRef] + case _: SFunc => classOf[_ => _] + case _ => Evaluation.stypeToRType(t).classTag.runtimeClass + }).toArray + val m = objType.ownerType.reprClass.getMethod(name, paramTypes:_*) + m + } + } + + /** Additional type descriptors, which are necessary to perform invocation of Method + * associated with this instance. + * @see MethodCall.eval + */ + lazy val extraDescriptors: Seq[RType[_]] = { + irInfo.invokeDescsBuilder match { + case Some(builder) => + builder(stype).map(Evaluation.stypeToRType) + case None => + ArraySeq.empty[RType[_]] + } + } + + /** Invoke this method on the given object with the arguments. + * This is used for methods with FixedCost costKind. */ + def invokeFixed(obj: Any, args: Array[Any])(implicit E: ErgoTreeEvaluator): Any = { + javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) + } + + // TODO optimize: avoid lookup when this SMethod is created via `specializeFor` + /** Return generic template of this method. */ + @inline final def genericMethod: SMethod = { + objType.getMethodById(methodId).get + } + + /** Returns refection [[RMethod]] which must be invoked to evaluate this method. + * The method is resolved by its name using `name + "_eval"` naming convention. + * @see `map_eval`, `flatMap_eval` and other `*_eval` methods. + * @hotspot don't beautify the code */ + lazy val evalMethod: RMethod = { + val argTypes = stype.tDom + val nArgs = argTypes.length + val paramTypes = new Array[Class[_]](nArgs + 2) + paramTypes(0) = classOf[MethodCall] + cfor(0)(_ < nArgs, _ + 1) { i => + paramTypes(i + 1) = argTypes(i) match { + case _: STypeVar => classOf[AnyRef] + case _: SFunc => classOf[_ => _] + case _: SCollectionType[_] => classOf[Coll[_]] + case _: SOption[_] => classOf[Option[_]] + case t => + Evaluation.stypeToRType(t).classTag.runtimeClass + } + } + paramTypes(paramTypes.length - 1) = classOf[ErgoTreeEvaluator] + + val methodName = name + "_eval" + val m = try { + objType.thisRClass.getMethod(methodName, paramTypes:_*) + } + catch { case e: NoSuchMethodException => + throw new RuntimeException(s"Cannot find eval method def $methodName(${Seq(paramTypes:_*)})", e) + } + m + } + + /** Create a new instance with the given stype. */ + def withSType(newSType: SFunc): SMethod = copy(stype = newSType) + + /** Create a new instance with the given cost function. */ + def withCost(costFunc: MethodCostFunc): SMethod = copy(costFunc = Some(costFunc)) + + /** Create a new instance in which the `stype` field transformed using + * the given substitution. */ + def withConcreteTypes(subst: Map[STypeVar, SType]): SMethod = + withSType(stype.withSubstTypes(subst).asFunc) + + /** Name of a language operation represented by this method. */ + def opName = objType.getClass.getSimpleName + "." + name + + /** Specializes this instance by creating a new [[SMethod]] instance where signature + * is specialized with respect to the given object and args types. It is used in + * [[sigmastate.serialization.MethodCallSerializer]] `parse` method, so it is part of + * consensus protocol. + * + * @param objTpe specific type of method receiver (aka object) + * @param args specific types of method arguments + * @return new instance of method descriptor with specialized signature + * @consensus + */ + def specializeFor(objTpe: SType, args: Seq[SType]): SMethod = { + unifyTypeLists(stype.tDom, objTpe +: args) match { + case Some(subst) if subst.nonEmpty => + withConcreteTypes(subst) + case _ => this + } + } + + /** Create a new instance with the given [[OperationInfo]] parameters. */ + def withInfo(opDesc: ValueCompanion, desc: String, args: ArgInfo*): SMethod = { + this.copy(docInfo = Some(OperationInfo(opDesc, desc, ArgInfo("this", "this instance") +: args.toSeq))) + } + + /** Create a new instance with the given [[OperationInfo]] parameters. + * NOTE: opDesc parameter is not defined and falls back to None. + */ + def withInfo(desc: String, args: ArgInfo*): SMethod = { + this.copy(docInfo = Some(OperationInfo(None, desc, ArgInfo("this", "this instance") +: args.toSeq))) + } + + /** Create a new instance with the given IR builder (aka MethodCall rewriter) parameter. */ + def withIRInfo( + irBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue], + javaMethod: RMethod = null, + invokeHandler: InvokeDescBuilder = null): SMethod = { + this.copy(irInfo = MethodIRInfo(Some(irBuilder), Option(javaMethod), Option(invokeHandler))) + } + + /** Lookup [[ArgInfo]] for the given argName or throw an exception. */ + def argInfo(argName: String): ArgInfo = + docInfo.get.args.find(_.name == argName).get +} + + +object SMethod { + /** Type of functions used to assign cost to method call nodes. + * For a function `f: (mc, obj, args) => cost` it is called before the evaluation of + * the `mc` node with the given `obj` as method receiver and `args` as method + * arguments. + */ + abstract class MethodCostFunc extends Function4[ErgoTreeEvaluator, MethodCall, Any, Array[Any], CostDetails] { + /** + * The function returns an estimated cost of evaluation BEFORE actual evaluation of + * the method. For this reason [[MethodCostFunc]] is not used for higher-order + * operations like `Coll.map`, `Coll.filter` etc. + */ + def apply(E: ErgoTreeEvaluator, mc: MethodCall, obj: Any, args: Array[Any]): CostDetails + } + + /** Returns a cost function which always returs the given cost. */ + def givenCost(costKind: FixedCost): MethodCostFunc = new MethodCostFunc { + override def apply(E: ErgoTreeEvaluator, + mc: MethodCall, + obj: Any, args: Array[Any]): CostDetails = { + if (E.settings.costTracingEnabled) + TracedCost(Array(FixedCostItem(MethodDesc(mc.method), costKind))) + else + GivenCost(costKind.cost) + } + } + + /** Returns a cost function which expects `obj` to be of `Coll[T]` type and + * uses its length to compute SeqCostItem */ + def perItemCost(costKind: PerItemCost): MethodCostFunc = new MethodCostFunc { + override def apply(E: ErgoTreeEvaluator, + mc: MethodCall, + obj: Any, args: Array[Any]): CostDetails = obj match { + case coll: Coll[a] => + if (E.settings.costTracingEnabled) { + val desc = MethodDesc(mc.method) + TracedCost(Array(SeqCostItem(desc, costKind, coll.length))) + } + else + GivenCost(costKind.cost(coll.length)) + case _ => + sys.error( + s"Invalid object $obj of method call $mc: Coll type is expected") + } + } + + /** Some runtime methods (like Coll.map, Coll.flatMap) require additional RType descriptors. + * The builder can extract those descriptors from the given type of the method signature. + */ + type InvokeDescBuilder = SFunc => Seq[SType] + + /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. + * @param methodName the name of the method to lookup + * @param cT the class where to search the methodName + * @param cA1 the class of the method argument + */ + def javaMethodOf[T, A1](methodName: String) + (implicit cT: ClassTag[T], cA1: ClassTag[A1]): RMethod = + RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass) + + /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. + * @param methodName the name of the method to lookup + * @param cT the class where to search the methodName + * @param cA1 the class of the method's first argument + * @param cA2 the class of the method's second argument + */ + def javaMethodOf[T, A1, A2] + (methodName: String) + (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod = + RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass) + + /** Default fallback method call recognizer which builds MethodCall ErgoTree nodes. */ + val MethodCallIrBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue] = { + case (builder, obj, method, args, tparamSubst) => + builder.mkMethodCall(obj, method, args.toIndexedSeq, tparamSubst) + } + + /** Convenience factory method. */ + def apply(objType: MethodsContainer, name: String, stype: SFunc, + methodId: Byte, + costKind: CostKind): SMethod = { + SMethod( + objType, name, stype, methodId, costKind, + MethodIRInfo(None, None, None), None, None) + } + + + /** Looks up [[SMethod]] instance for the given type and method ids. + * + * @param typeId id of a type which can contain methods + * @param methodId id of a method of the type given by `typeId` + * @return an instance of [[SMethod]] which may contain generic type variables in the + * signature (see SMethod.stype). As a result `specializeFor` is called by + * deserializer to obtain monomorphic method descriptor. + * @consensus this is method is used in [[sigmastate.serialization.MethodCallSerializer]] + * `parse` method and hence it is part of consensus protocol + */ + def fromIds(typeId: Byte, methodId: Byte): SMethod = { + CheckTypeWithMethods(typeId, MethodsContainer.contains(typeId)) + val container = MethodsContainer(typeId) + val method = container.methodById(methodId) + method + } +} + diff --git a/interpreter/shared/src/main/scala/sigmastate/lang/SigmaBuilder.scala b/data/shared/src/main/scala/sigma/ast/SigmaBuilder.scala similarity index 98% rename from interpreter/shared/src/main/scala/sigmastate/lang/SigmaBuilder.scala rename to data/shared/src/main/scala/sigma/ast/SigmaBuilder.scala index 8d87d8ffce..cafba5f6b6 100644 --- a/interpreter/shared/src/main/scala/sigmastate/lang/SigmaBuilder.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaBuilder.scala @@ -1,20 +1,16 @@ -package sigmastate.lang +package sigma.ast import debox.cfor import org.ergoplatform.ErgoBox.RegisterId +import sigma.ast.Constraints._ +import sigma.ast.SCollection.{SByteArray, SIntArray} +import sigma.ast.SOption.SIntOption +import sigma.ast.syntax._ import sigma.data.Nullable -import sigma.{AnyValue, Coll, Colls, Environment} -import sigmastate.SCollection.{SByteArray, SIntArray} -import sigmastate.SOption.SIntOption -import sigmastate.Values._ -import sigmastate._ -import sigmastate.eval._ -import sigmastate.exceptions.ConstraintFailed -import sigmastate.lang.Constraints._ -import sigmastate.lang.Terms.{STypeSubst, _} -import sigmastate.serialization.OpCodes -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.utxo._ +import sigma.exceptions.ConstraintFailed +import sigma.serialization.OpCodes +import sigma.serialization.ValueCodes.OpCode +import sigma.{AnyValue, Coll, Colls, Environment, Evaluation, Platform} import scala.util.DynamicVariable diff --git a/interpreter/shared/src/main/scala/sigmastate/lang/SigmaPredef.scala b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala similarity index 97% rename from interpreter/shared/src/main/scala/sigmastate/lang/SigmaPredef.scala rename to data/shared/src/main/scala/sigma/ast/SigmaPredef.scala index 5b1d3dc2ca..5cdbdedaa8 100644 --- a/interpreter/shared/src/main/scala/sigmastate/lang/SigmaPredef.scala +++ b/data/shared/src/main/scala/sigma/ast/SigmaPredef.scala @@ -1,17 +1,16 @@ -package sigmastate.lang +package sigma.ast import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix import org.ergoplatform.{ErgoAddressEncoder, P2PKAddress} +import scorex.util.encode.{Base16, Base58, Base64} +import sigma.ast.SCollection.{SByteArray, SIntArray} +import sigma.ast.SOption.SIntOption +import sigma.ast.SigmaPropConstant +import sigma.ast.syntax._ import sigma.data.Nullable -import scorex.util.encode.{Base64, Base58, Base16} -import sigmastate.SCollection.{SIntArray, SByteArray} -import sigmastate.SOption._ -import sigmastate.Values.{StringConstant, SValue, IntValue, SigmaPropConstant, Value, ByteArrayConstant, SigmaPropValue, ValueCompanion, Constant, EvaluatedValue, ConstantPlaceholder, BoolValue} -import sigmastate._ -import sigmastate.lang.Terms._ -import sigmastate.exceptions.InvalidArguments -import sigmastate.serialization.ValueSerializer -import sigmastate.utxo.{DeserializeRegister, SelectField, DeserializeContext, GetVar} +import sigma.exceptions.InvalidArguments +import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.serialization.ValueSerializer object SigmaPredef { @@ -44,7 +43,7 @@ object SigmaPredef { import builder._ /** Type variable used in the signatures of global functions below. */ - import SType.{tT, tO, tR, tL, paramR, paramT, tK} + import SType.{paramR, paramT, tK, tL, tO, tR, tT} private val undefined: IrBuilderFunc = PartialFunction.empty[(SValue, Seq[SValue]), SValue] @@ -152,7 +151,7 @@ object SigmaPredef { { case (_, Seq(arg: EvaluatedValue[SString.type]@unchecked)) => ErgoAddressEncoder(networkPrefix).fromString(arg.value).get match { case a: P2PKAddress => SigmaPropConstant(a.pubkey) - case a@_ => sys.error(s"unsupported address $a") + case a => sys.error(s"unsupported address $a") } }), OperationInfo(Constant, "", diff --git a/interpreter/shared/src/main/scala/sigmastate/lang/SourceContext.scala b/data/shared/src/main/scala/sigma/ast/SourceContext.scala similarity index 97% rename from interpreter/shared/src/main/scala/sigmastate/lang/SourceContext.scala rename to data/shared/src/main/scala/sigma/ast/SourceContext.scala index 2c814bdbdb..f9441e1a52 100644 --- a/interpreter/shared/src/main/scala/sigmastate/lang/SourceContext.scala +++ b/data/shared/src/main/scala/sigma/ast/SourceContext.scala @@ -1,4 +1,4 @@ -package sigmastate.lang +package sigma.ast import fastparse.Parsed.Failure diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala new file mode 100644 index 0000000000..b637acf792 --- /dev/null +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -0,0 +1,1508 @@ +package sigma.ast + +import org.ergoplatform._ +import org.ergoplatform.validation._ +import sigma._ +import sigma.ast.SCollection.{SBooleanArray, SBoxArray, SByteArray, SByteArray2, SHeaderArray} +import sigma.ast.SMethod.{MethodCallIrBuilder, MethodCostFunc, javaMethodOf} +import sigma.ast.SType.TypeCode +import sigma.ast.syntax.{SValue, ValueOps} +import sigma.data.OverloadHack.Overloaded1 +import sigma.data.{DataValueComparer, KeyValueColl, Nullable, RType, SigmaConstants} +import sigma.eval.{CostDetails, ErgoTreeEvaluator, TracedCost} +import sigma.reflection.RClass +import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.utils.SparseArrayContainer + +import scala.annotation.unused +import scala.language.implicitConversions + +/** Base type for all companions of AST nodes of sigma lang. */ +trait SigmaNodeCompanion + +/** Defines recognizer method which allows the derived object to be used in patterns + * to recognize method descriptors by method name. + * @see SCollecton + */ +trait MethodByNameUnapply extends MethodsContainer { + def unapply(methodName: String): Option[SMethod] = methods.find(_.name == methodName) +} + +/** Base trait for all method containers (which store methods and properties) */ +sealed trait MethodsContainer { + /** Type for which this container defines methods. */ + def ownerType: STypeCompanion + + override def toString: String = + getClass.getSimpleName.stripSuffix("$") // e.g. SInt, SCollection, etc + + /** Represents class of `this`. */ + lazy val thisRClass: RClass[_] = RClass(this.getClass) + def typeId: Byte = ownerType.typeId + def typeName: String = ownerType.typeName + + /** Returns -1 if `method` is not found. */ + def methodIndex(name: String): Int = methods.indexWhere(_.name == name) + + /** Returns true if this type has a method with the given name. */ + def hasMethod(name: String): Boolean = methodIndex(name) != -1 + + /** This method should be overriden in derived classes to add new methods in addition to inherited. + * Typical override: `super.getMethods() ++ Seq(m1, m2, m3)` + */ + protected def getMethods(): Seq[SMethod] = Nil + + /** Returns all the methods of this type. */ + lazy val methods: Seq[SMethod] = { + val ms = getMethods().toArray + assert(ms.map(_.name).distinct.length == ms.length, s"Duplicate method names in $this") + ms.groupBy(_.objType).foreach { case (comp, ms) => + assert(ms.map(_.methodId).distinct.length == ms.length, s"Duplicate method ids in $comp: $ms") + } + ms + } + private lazy val _methodsMap: Map[Byte, Map[Byte, SMethod]] = methods + .groupBy(_.objType.typeId) + .map { case (typeId, ms) => (typeId -> ms.map(m => m.methodId -> m).toMap) } + + /** Lookup method by its id in this type. */ + @inline def getMethodById(methodId: Byte): Option[SMethod] = + _methodsMap.get(typeId) match { + case Some(ms) => ms.get(methodId) + case None => None + } + + /** Lookup method in this type by method's id or throw ValidationException. + * This method can be used in trySoftForkable section to either obtain valid method + * or catch ValidatioinException which can be checked for soft-fork condition. + * It delegate to getMethodById to lookup method. + * + * @see getMethodById + */ + def methodById(methodId: Byte): SMethod = { + ValidationRules.CheckAndGetMethod(this, methodId) + } + + /** Finds a method descriptor [[SMethod]] for the given name. */ + def method(methodName: String): Option[SMethod] = methods.find(_.name == methodName) + + /** Looks up the method descriptor by the method name. */ + def getMethodByName(name: String): SMethod = methods.find(_.name == name).get + +} +object MethodsContainer { + private val containers = new SparseArrayContainer[MethodsContainer](Array( + SByteMethods, + SShortMethods, + SIntMethods, + SLongMethods, + SBigIntMethods, + SBooleanMethods, + SStringMethods, + SGroupElementMethods, + SSigmaPropMethods, + SBoxMethods, + SAvlTreeMethods, + SHeaderMethods, + SPreHeaderMethods, + SGlobalMethods, + SContextMethods, + SCollectionMethods, + SOptionMethods, + STupleMethods, + SUnitMethods, + SAnyMethods + ).map(m => (m.typeId, m))) + + def contains(typeId: TypeCode): Boolean = containers.contains(typeId) + + def apply(typeId: TypeCode): MethodsContainer = containers(typeId) + + /** Finds the method of the give type. + * + * @param tpe type of the object for which the method is looked up + * @param methodName name of the method + * @return method descriptor or None if not found + */ + def getMethod(tpe: SType, methodName: String): Option[SMethod] = tpe match { + case tup: STuple => + STupleMethods.getTupleMethod(tup, methodName) + case _ => + containers.get(tpe.typeCode).flatMap(_.method(methodName)) + } +} + +trait MonoTypeMethods extends MethodsContainer { + def ownerType: SMonoType + /** Helper method to create method descriptors for properties (i.e. methods without args). */ + protected def propertyCall( + name: String, + tpeRes: SType, + id: Byte, + costKind: CostKind): SMethod = + SMethod(this, name, SFunc(this.ownerType, tpeRes), id, costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, "") + + /** Helper method to create method descriptors for properties (i.e. methods without args). */ + protected def property( + name: String, + tpeRes: SType, + id: Byte, + valueCompanion: ValueCompanion): SMethod = + SMethod(this, name, SFunc(this.ownerType, tpeRes), id, valueCompanion.costKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(valueCompanion, "") +} + +trait SNumericTypeMethods extends MonoTypeMethods { + import SNumericTypeMethods.tNum + protected override def getMethods(): Seq[SMethod] = { + super.getMethods() ++ SNumericTypeMethods.methods.map { + m => m.copy(stype = applySubst(m.stype, Map(tNum -> this.ownerType)).asFunc) + } + } +} + +object SNumericTypeMethods extends MethodsContainer { + /** Type for which this container defines methods. */ + override def ownerType: STypeCompanion = SNumericType + + /** Type variable used in generic signatures of method descriptors. */ + val tNum = STypeVar("TNum") + + /** Cost function which is assigned for numeric cast MethodCall nodes in ErgoTree. + * It is called as part of MethodCall.eval method. */ + val costOfNumericCast: MethodCostFunc = new MethodCostFunc { + override def apply( + E: ErgoTreeEvaluator, + mc: MethodCall, + obj: Any, + args: Array[Any]): CostDetails = { + val targetTpe = mc.method.stype.tRange + val cast = getNumericCast(mc.obj.tpe, mc.method.name, targetTpe).get + val costKind = if (cast == Downcast) Downcast.costKind else Upcast.costKind + TracedCost(Array(TypeBasedCostItem(MethodDesc(mc.method), costKind, targetTpe))) + } + } + + /** The following SMethod instances are descriptors of methods available on all numeric + * types. + * + * @see `val methods` below + * */ + val ToByteMethod : SMethod = SMethod(this, "toByte", SFunc(tNum, SByte), 1, null) + .withCost(costOfNumericCast) + .withInfo(PropertyCall, "Converts this numeric value to \\lst{Byte}, throwing exception if overflow.") + + val ToShortMethod : SMethod = SMethod(this, "toShort", SFunc(tNum, SShort), 2, null) + .withCost(costOfNumericCast) + .withInfo(PropertyCall, "Converts this numeric value to \\lst{Short}, throwing exception if overflow.") + + val ToIntMethod : SMethod = SMethod(this, "toInt", SFunc(tNum, SInt), 3, null) + .withCost(costOfNumericCast) + .withInfo(PropertyCall, "Converts this numeric value to \\lst{Int}, throwing exception if overflow.") + + val ToLongMethod : SMethod = SMethod(this, "toLong", SFunc(tNum, SLong), 4, null) + .withCost(costOfNumericCast) + .withInfo(PropertyCall, "Converts this numeric value to \\lst{Long}, throwing exception if overflow.") + + val ToBigIntMethod: SMethod = SMethod(this, "toBigInt", SFunc(tNum, SBigInt), 5, null) + .withCost(costOfNumericCast) + .withInfo(PropertyCall, "Converts this numeric value to \\lst{BigInt}") + + /** Cost of: 1) creating Byte collection from a numeric value */ + val ToBytes_CostKind = FixedCost(JitCost(5)) + + val ToBytesMethod: SMethod = SMethod( + this, "toBytes", SFunc(tNum, SByteArray), 6, ToBytes_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric value in a collection of bytes. + | For example, the \lst{Int} value \lst{0x12131415} would yield the + | collection of bytes \lst{[0x12, 0x13, 0x14, 0x15]}. + """.stripMargin) + + /** Cost of: 1) creating Boolean collection (one bool for each bit) from a numeric + * value. */ + val ToBits_CostKind = FixedCost(JitCost(5)) + + val ToBitsMethod: SMethod = SMethod( + this, "toBits", SFunc(tNum, SBooleanArray), 7, ToBits_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ Returns a big-endian representation of this numeric in a collection of Booleans. + | Each boolean corresponds to one bit. + """.stripMargin) + + protected override def getMethods: Seq[SMethod] = Array( + ToByteMethod, // see Downcast + ToShortMethod, // see Downcast + ToIntMethod, // see Downcast + ToLongMethod, // see Downcast + ToBigIntMethod, // see Downcast + ToBytesMethod, + ToBitsMethod + ) + + /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ + val castMethods: Array[String] = + Array(ToByteMethod, ToShortMethod, ToIntMethod, ToLongMethod, ToBigIntMethod) + .map(_.name) + + /** Checks the given name is numeric type cast method (like toByte, toInt, etc.). */ + def isCastMethod(name: String): Boolean = castMethods.contains(name) + + /** Convert the given method to a cast operation from fromTpe to resTpe. */ + def getNumericCast( + fromTpe: SType, + methodName: String, + resTpe: SType): Option[NumericCastCompanion] = (fromTpe, resTpe) match { + case (from: SNumericType, to: SNumericType) if isCastMethod(methodName) => + val op = if (to > from) Upcast else Downcast + Some(op) + case _ => None // the method in not numeric type cast + } + +} + +/** Methods of ErgoTree type `Boolean`. */ +case object SBooleanMethods extends MonoTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SBoolean + + val ToByte = "toByte" + protected override def getMethods() = super.getMethods() + /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + ++ Seq( + SMethod(this, ToByte, SFunc(this, SByte), 1) + .withInfo(PropertyCall, "Convert true to 1 and false to 0"), + ) + */ +} + +/** Methods of ErgoTree type `Byte`. */ +case object SByteMethods extends SNumericTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SByte +} + +/** Methods of ErgoTree type `Short`. */ +case object SShortMethods extends SNumericTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = ast.SShort +} + +/** Descriptor of ErgoTree type `Int`. */ +case object SIntMethods extends SNumericTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SInt +} + +/** Descriptor of ErgoTree type `Long`. */ +case object SLongMethods extends SNumericTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SLong +} + +/** Methods of BigInt type. Implemented using [[java.math.BigInteger]]. */ +case object SBigIntMethods extends SNumericTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SBigInt + + /** The following `modQ` methods are not fully implemented in v4.x and this descriptors. + * This descritors are remain here in the code and are waiting for full implementation + * is upcoming soft-forks at which point the cost parameters should be calculated and + * changed. + */ + val ModQMethod = SMethod(this, "modQ", SFunc(this.ownerType, SBigInt), 1, FixedCost(JitCost(1))) + .withInfo(ModQ, "Returns this \\lst{mod} Q, i.e. remainder of division by Q, where Q is an order of the cryprographic group.") + val PlusModQMethod = SMethod(this, "plusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 2, FixedCost(JitCost(1))) + .withInfo(ModQArithOp.PlusModQ, "Adds this number with \\lst{other} by module Q.", ArgInfo("other", "Number to add to this.")) + val MinusModQMethod = SMethod(this, "minusModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 3, FixedCost(JitCost(1))) + .withInfo(ModQArithOp.MinusModQ, "Subtracts \\lst{other} number from this by module Q.", ArgInfo("other", "Number to subtract from this.")) + val MultModQMethod = SMethod(this, "multModQ", SFunc(IndexedSeq(this.ownerType, SBigInt), SBigInt), 4, FixedCost(JitCost(1))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) + + protected override def getMethods() = super.getMethods() ++ Seq( +// ModQMethod, +// PlusModQMethod, +// MinusModQMethod, + // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + // MultModQMethod, + ) +} + +/** Methods of type `String`. */ +case object SStringMethods extends MonoTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SString +} + +/** Methods of type `GroupElement`. */ +case object SGroupElementMethods extends MonoTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SGroupElement + + /** Cost of: 1) serializing EcPointType to bytes 2) packing them in Coll. */ + val GetEncodedCostKind = FixedCost(JitCost(250)) + + /** The following SMethod instances are descriptors of methods defined in `GroupElement` type. */ + lazy val GetEncodedMethod: SMethod = SMethod( + this, "getEncoded", SFunc(Array(this.ownerType), SByteArray), 2, GetEncodedCostKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, "Get an encoding of the point value.") + + lazy val ExponentiateMethod: SMethod = SMethod( + this, "exp", SFunc(Array(this.ownerType, SBigInt), this.ownerType), 3, Exponentiate.costKind) + .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")) + + lazy val MultiplyMethod: SMethod = SMethod( + this, "multiply", SFunc(Array(this.ownerType, SGroupElement), this.ownerType), 4, MultiplyGroup.costKind) + .withIRInfo({ case (builder, obj, _, Seq(arg), _) => + builder.mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) + }) + .withInfo(MultiplyGroup, "Group operation.", ArgInfo("other", "other element of the group")) + + /** Cost of: 1) calling EcPoint.negate 2) wrapping in GroupElement. */ + val Negate_CostKind = FixedCost(JitCost(45)) + + lazy val NegateMethod: SMethod = SMethod( + this, "negate", SFunc(this.ownerType, this.ownerType), 5, Negate_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, "Inverse element of the group.") + + protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( + /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + SMethod(this, "isIdentity", SFunc(this, SBoolean), 1) + .withInfo(PropertyCall, "Checks if this value is identity element of the eliptic curve group."), + */ + GetEncodedMethod, + ExponentiateMethod, + MultiplyMethod, + NegateMethod + ) +} + +/** Methods of type `SigmaProp` which represent sigma-protocol propositions. */ +case object SSigmaPropMethods extends MonoTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType: SMonoType = SSigmaProp + + /** The maximum size of SigmaProp value in serialized byte array representation. */ + val MaxSizeInBytes: Long = SigmaConstants.MaxSigmaPropSizeInBytes.value + + val PropBytes = "propBytes" + val IsProven = "isProven" + lazy val PropBytesMethod = SMethod( + this, PropBytes, SFunc(this.ownerType, SByteArray), 1, SigmaPropBytes.costKind) + .withInfo(SigmaPropBytes, "Serialized bytes of this sigma proposition taken as ErgoTree.") + + lazy val IsProvenMethod = SMethod(this, IsProven, SFunc(this.ownerType, SBoolean), 2, null) + .withInfo(// available only at frontend of ErgoScript + "Verify that sigma proposition is proven.") + + protected override def getMethods() = super.getMethods() ++ Seq( + PropBytesMethod, IsProvenMethod + ) +} + +/** Any other type is implicitly subtype of this type. */ +case object SAnyMethods extends MonoTypeMethods { + override def ownerType: SMonoType = SAny +} + +/** The type with single inhabitant value `()` */ +case object SUnitMethods extends MonoTypeMethods { + /** Type for which this container defines methods. */ + override def ownerType = SUnit +} + +object SOptionMethods extends MethodsContainer { + /** Type for which this container defines methods. */ + override def ownerType: STypeCompanion = SOption + + /** Code of `Option[_]` type constructor. */ + val OptionTypeConstrId = 3 + /** Type code for `Option[T] for some T` type used in TypeSerializer. */ + val OptionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionTypeConstrId).toByte + /** Code of `Option[Coll[_]]` type constructor. */ + val OptionCollectionTypeConstrId = 4 + /** Type code for `Option[Coll[T]] for some T` type used in TypeSerializer. */ + val OptionCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionCollectionTypeConstrId).toByte + + val IsDefined = "isDefined" + val Get = "get" + val GetOrElse = "getOrElse" + + import SType.{paramR, paramT, tR, tT} + + /** Type descriptor of `this` argument used in the methods below. */ + val ThisType = SOption(tT) + + /** The following SMethod instances are descriptors of methods defined in `Option` type. */ + val IsDefinedMethod = SMethod( + this, IsDefined, SFunc(ThisType, SBoolean), 2, OptionIsDefined.costKind) + .withIRInfo({ + case (builder, obj, _, args, _) if args.isEmpty => builder.mkOptionIsDefined(obj.asValue[SOption[SType]]) + }) + .withInfo(OptionIsDefined, + "Returns \\lst{true} if the option is an instance of \\lst{Some}, \\lst{false} otherwise.") + + val GetMethod = SMethod(this, Get, SFunc(ThisType, tT), 3, OptionGet.costKind) + .withIRInfo({ + case (builder, obj, _, args, _) if args.isEmpty => builder.mkOptionGet(obj.asValue[SOption[SType]]) + }) + .withInfo(OptionGet, + """Returns the option's value. The option must be nonempty. Throws exception if the option is empty.""") + + lazy val GetOrElseMethod = SMethod( + this, GetOrElse, SFunc(Array(ThisType, tT), tT, Array[STypeParam](tT)), 4, OptionGetOrElse.costKind) + .withIRInfo(irBuilder = { + case (builder, obj, _, Seq(d), _) => builder.mkOptionGetOrElse(obj.asValue[SOption[SType]], d) + }) + .withInfo(OptionGetOrElse, + """Returns the option's value if the option is nonempty, otherwise + |return the result of evaluating \lst{default}. + """.stripMargin, ArgInfo("default", "the default value")) + +// TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 +// val FoldMethod = SMethod( +// this, Fold, SFunc(Array(ThisType, tR, SFunc(tT, tR)), tR, Array[STypeParam](tT, tR)), 5, FixedCost(JitCost(1))) +// .withInfo(MethodCall, +// """Returns the result of applying \lst{f} to this option's +// | value if the option is nonempty. Otherwise, evaluates +// | expression \lst{ifEmpty}. +// | This is equivalent to \lst{option map f getOrElse ifEmpty}. +// """.stripMargin, +// ArgInfo("ifEmpty", "the expression to evaluate if empty"), +// ArgInfo("f", "the function to apply if nonempty")) + + val MapMethod = SMethod(this, "map", + SFunc(Array(ThisType, SFunc(tT, tR)), SOption(tR), Array(paramT, paramR)), 7, FixedCost(JitCost(20))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """Returns a \lst{Some} containing the result of applying \lst{f} to this option's + | value if this option is nonempty. + | Otherwise return \lst{None}. + """.stripMargin, ArgInfo("f", "the function to apply")) + + val FilterMethod = SMethod(this, "filter", + SFunc(Array(ThisType, SFunc(tT, SBoolean)), ThisType, Array(paramT)), 8, FixedCost(JitCost(20))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """Returns this option if it is nonempty and applying the predicate \lst{p} to + | this option's value returns true. Otherwise, return \lst{None}. + """.stripMargin, ArgInfo("p", "the predicate used for testing")) + + override protected def getMethods(): Seq[SMethod] = super.getMethods() ++ + Seq( + IsDefinedMethod, + GetMethod, + GetOrElseMethod, + /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + FoldMethod, + */ + MapMethod, + FilterMethod + ) + + /** Creates a descriptor of `Option[T]` type for the given element type `T`. */ + def apply[T <: SType](implicit elemType: T, @unused ov: Overloaded1): SOption[T] = SOption(elemType) +} + +object SCollectionMethods extends MethodsContainer with MethodByNameUnapply { + import SType.{paramIV, paramIVSeq, paramOV} + + /** Type for which this container defines methods. */ + override def ownerType: STypeCompanion = SCollection + + /** Helper descriptors reused across different method descriptors. */ + def tIV = SType.tIV + def tOV = SType.tOV + + /** This descriptors are instantiated once here and then reused. */ + val ThisType = SCollection(tIV) + val tOVColl = SCollection(tOV) + val tPredicate = SFunc(tIV, SBoolean) + + /** The following SMethod instances are descriptors of methods defined in `Coll` type. */ + val SizeMethod = SMethod(this, "size", SFunc(ThisType, SInt), 1, SizeOf.costKind) + .withInfo(SizeOf, "The size of the collection in elements.") + + val GetOrElseMethod = SMethod( + this, "getOrElse", SFunc(Array(ThisType, SInt, tIV), tIV, paramIVSeq), 2, DynamicCost) + .withIRInfo({ case (builder, obj, _, Seq(index, defaultValue), _) => + val index1 = index.asValue[SInt.type] + val defaultValue1 = defaultValue.asValue[SType] + builder.mkByIndex(obj.asValue[SCollection[SType]], index1, Some(defaultValue1)) + }) + .withInfo(ByIndex, "Return the element of collection if \\lst{index} is in range \\lst{0 .. size-1}", + ArgInfo("index", "index of the element of this collection"), + ArgInfo("default", "value to return when \\lst{index} is out of range")) + + /** Implements evaluation of Coll.getOrElse method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def getOrElse_eval[A](mc: MethodCall, xs: Coll[A], i: Int, default: A)(implicit E: ErgoTreeEvaluator): A = { + E.addCost(ByIndex.costKind, mc.method.opDesc) + // the following lines should be semantically the same as in ByIndex.eval + Value.checkType(mc.args.last.tpe, default) + xs.getOrElse(i, default) + } + + val MapMethod = SMethod(this, "map", + SFunc(Array(ThisType, SFunc(tIV, tOV)), tOVColl, Array(paramIV, paramOV)), 3, MapCollection.costKind) + .withIRInfo({ + case (builder, obj, _, Seq(mapper), _) => builder.mkMapCollection(obj.asValue[SCollection[SType]], mapper.asFunc) + }) + .withInfo(MapCollection, + """ Builds a new collection by applying a function to all elements of this collection. + | Returns a new collection of type \lst{Coll[B]} resulting from applying the given function + | \lst{f} to each element of this collection and collecting the results. + """.stripMargin, + ArgInfo("f", "the function to apply to each element")) + + /** Implements evaluation of Coll.map method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def map_eval[A,B](mc: MethodCall, xs: Coll[A], f: A => B)(implicit E: ErgoTreeEvaluator): Coll[B] = { + val tpeB = mc.tpe.asInstanceOf[SCollection[SType]].elemType + val tB = Evaluation.stypeToRType(tpeB).asInstanceOf[RType[B]] + E.addSeqCostNoOp(MapCollection.costKind, xs.length, mc.method.opDesc) + xs.map(f)(tB) + } + + val ExistsMethod = SMethod(this, "exists", + SFunc(Array(ThisType, tPredicate), SBoolean, paramIVSeq), 4, Exists.costKind) + .withIRInfo({ + case (builder, obj, _, Seq(c), _) => builder.mkExists(obj.asValue[SCollection[SType]], c.asFunc) + }) + .withInfo(Exists, + """Tests whether a predicate holds for at least one element of this collection. + |Returns \lst{true} if the given predicate \lst{p} is satisfied by at least one element of this collection, otherwise \lst{false} + """.stripMargin, + ArgInfo("p", "the predicate used to test elements")) + + val FoldMethod = SMethod( + this, "fold", + SFunc(Array(ThisType, tOV, SFunc(Array(tOV, tIV), tOV)), tOV, Array(paramIV, paramOV)), + 5, Fold.costKind) + .withIRInfo({ + case (builder, obj, _, Seq(z, op), _) => builder.mkFold(obj.asValue[SCollection[SType]], z, op.asFunc) + }) + .withInfo(Fold, "Applies a binary operator to a start value and all elements of this collection, going left to right.", + ArgInfo("zero", "a starting value"), + ArgInfo("op", "the binary operator")) + + val ForallMethod = SMethod(this, "forall", + SFunc(Array(ThisType, tPredicate), SBoolean, paramIVSeq), 6, ForAll.costKind) + .withIRInfo({ + case (builder, obj, _, Seq(c), _) => builder.mkForAll(obj.asValue[SCollection[SType]], c.asFunc) + }) + .withInfo(ForAll, + """Tests whether a predicate holds for all elements of this collection. + |Returns \lst{true} if this collection is empty or the given predicate \lst{p} + |holds for all elements of this collection, otherwise \lst{false}. + """.stripMargin, + ArgInfo("p", "the predicate used to test elements")) + + val SliceMethod = SMethod(this, "slice", + SFunc(Array(ThisType, SInt, SInt), ThisType, paramIVSeq), 7, Slice.costKind) + .withIRInfo({ + case (builder, obj, _, Seq(from, until), _) => + builder.mkSlice(obj.asCollection[SType], from.asIntValue, until.asIntValue) + }) + .withInfo(Slice, + """Selects an interval of elements. The returned collection is made up + | of all elements \lst{x} which satisfy the invariant: + | \lst{ + | from <= indexOf(x) < until + | } + """.stripMargin, + ArgInfo("from", "the lowest index to include from this collection"), + ArgInfo("until", "the lowest index to EXCLUDE from this collection")) + + val FilterMethod = SMethod(this, "filter", + SFunc(Array(ThisType, tPredicate), ThisType, paramIVSeq), 8, Filter.costKind) + .withIRInfo({ + case (builder, obj, _, Seq(l), _) => builder.mkFilter(obj.asValue[SCollection[SType]], l.asFunc) + }) + .withInfo(Filter, + """Selects all elements of this collection which satisfy a predicate. + | Returns a new collection consisting of all elements of this collection that satisfy the given + | predicate \lst{p}. The order of the elements is preserved. + """.stripMargin, + ArgInfo("p", "the predicate used to test elements.")) + + val AppendMethod = SMethod(this, "append", + SFunc(Array(ThisType, ThisType), ThisType, paramIVSeq), 9, Append.costKind) + .withIRInfo({ + case (builder, obj, _, Seq(xs), _) => + builder.mkAppend(obj.asCollection[SType], xs.asCollection[SType]) + }) + .withInfo(Append, "Puts the elements of other collection after the elements of this collection (concatenation of 2 collections)", + ArgInfo("other", "the collection to append at the end of this")) + + val ApplyMethod = SMethod(this, "apply", + SFunc(Array(ThisType, SInt), tIV, Array[STypeParam](tIV)), 10, ByIndex.costKind) + .withInfo(ByIndex, + """The element at given index. + | Indices start at \lst{0}; \lst{xs.apply(0)} is the first element of collection \lst{xs}. + | Note the indexing syntax \lst{xs(i)} is a shorthand for \lst{xs.apply(i)}. + | Returns the element at the given index. + | Throws an exception if \lst{i < 0} or \lst{length <= i} + """.stripMargin, ArgInfo("i", "the index")) + + /** Cost of creating a collection of indices */ + val IndicesMethod_CostKind = PerItemCost( + baseCost = JitCost(20), perChunkCost = JitCost(2), chunkSize = 16) + + val IndicesMethod = SMethod( + this, "indices", SFunc(ThisType, SCollection(SInt)), 14, IndicesMethod_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """Produces the range of all indices of this collection as a new collection + | containing [0 .. length-1] values. + """.stripMargin) + + /** Implements evaluation of Coll.indices method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def indices_eval[A, B](mc: MethodCall, xs: Coll[A]) + (implicit E: ErgoTreeEvaluator): Coll[Int] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.indices + } + } + /** BaseCost: + * 1) base cost of Coll.flatMap + * PerChunkCost: + * 1) cost of Coll.flatMap (per item) + * 2) new collection is allocated for each item + * 3) each collection is then appended to the resulting collection */ + val FlatMapMethod_CostKind = PerItemCost( + baseCost = JitCost(60), perChunkCost = JitCost(10), chunkSize = 8) + + val FlatMapMethod = SMethod(this, "flatMap", + SFunc(Array(ThisType, SFunc(tIV, tOVColl)), tOVColl, Array(paramIV, paramOV)), + 15, FlatMapMethod_CostKind) + .withIRInfo( + MethodCallIrBuilder, + javaMethodOf[Coll[_], Function1[_,_], RType[_]]("flatMap"), + { mtype => Array(mtype.tRange.asCollection[SType].elemType) }) + .withInfo(MethodCall, + """ Builds a new collection by applying a function to all elements of this collection + | and using the elements of the resulting collections. + | Function \lst{f} is constrained to be of the form \lst{x => x.someProperty}, otherwise + | it is illegal. + | Returns a new collection of type \lst{Coll[B]} resulting from applying the given collection-valued function + | \lst{f} to each element of this collection and concatenating the results. + """.stripMargin, ArgInfo("f", "the function to apply to each element.")) + + /** We assume all flatMap body patterns have similar executon cost. */ + final val CheckFlatmapBody_Info = OperationCostInfo( + PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(20), chunkSize = 1), + NamedDesc("CheckFlatmapBody")) + + + /** This patterns recognize all expressions, which are allowed as lambda body + * of flatMap. Other bodies are rejected with throwing exception. + */ + val flatMap_BodyPatterns = Array[PartialFunction[SValue, Int]]( + { case MethodCall(ValUse(id, tpe), m, args, _) if args.isEmpty => id }, + { case ExtractScriptBytes(ValUse(id, _)) => id }, + { case ExtractId(ValUse(id, _)) => id }, + { case SigmaPropBytes(ValUse(id, _)) => id }, + { case ExtractBytes(ValUse(id, _)) => id }, + { case ExtractBytesWithNoRef(ValUse(id, _)) => id } + ) + + /** Check the given expression is valid body of flatMap argument lambda. + * @param varId id of lambda variable (see [[FuncValue]].args) + * @param expr expression with is expected to use varId in ValUse node. + * @return true if the body is allowed + */ + def isValidPropertyAccess(varId: Int, expr: SValue) + (implicit E: ErgoTreeEvaluator): Boolean = { + var found = false + // NOTE: the cost depends on the position of the pattern since + // we are checking until the first matching pattern found. + E.addSeqCost(CheckFlatmapBody_Info) { () => + // the loop is bounded because flatMap_BodyPatterns is fixed + var i = 0 + val nPatterns = flatMap_BodyPatterns.length + while (i < nPatterns && !found) { + val p = flatMap_BodyPatterns(i) + found = p.lift(expr) match { + case Some(id) => id == varId // `id` in the pattern is equal to lambda `varId` + case None => false + } + i += 1 + } + i // how many patterns checked + } + found + } + + /** Operation descriptor for matching `flatMap` method calls with valid lambdas. */ + final val MatchSingleArgMethodCall_Info = OperationCostInfo( + FixedCost(JitCost(30)), NamedDesc("MatchSingleArgMethodCall")) + + /** Recognizer of `flatMap` method calls with valid lambdas. */ + object IsSingleArgMethodCall { + def unapply(mc:MethodCall) + (implicit E: ErgoTreeEvaluator): Nullable[(Int, SValue)] = { + var res: Nullable[(Int, SValue)] = Nullable.None + E.addFixedCost(MatchSingleArgMethodCall_Info) { + res = mc match { + case MethodCall(_, m, Seq(FuncValue(args, body)), _) if args.length == 1 => + val id = args(0)._1 + Nullable((id, body)) + case _ => + Nullable.None + } + } + res + } + } + + /** Checks that the given [[MethodCall]] operation is valid flatMap. */ + def checkValidFlatmap(mc: MethodCall)(implicit E: ErgoTreeEvaluator) = { + mc match { + case IsSingleArgMethodCall(varId, lambdaBody) + if isValidPropertyAccess(varId, lambdaBody) => + // ok, do nothing + case _ => + throwInvalidFlatmap(mc) + } + } + + def throwInvalidFlatmap(mc: MethodCall) = { + sys.error( + s"Unsupported lambda in flatMap: allowed usage `xs.flatMap(x => x.property)`: $mc") + } + + /** Implements evaluation of Coll.flatMap method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def flatMap_eval[A, B](mc: MethodCall, xs: Coll[A], f: A => Coll[B]) + (implicit E: ErgoTreeEvaluator): Coll[B] = { + val m = mc.method + var res: Coll[B] = null + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], m.opDesc) { () => + val tpeB = mc.tpe.asInstanceOf[SCollection[SType]].elemType + val tB = Evaluation.stypeToRType(tpeB).asInstanceOf[RType[B]] + res = xs.flatMap(f)(tB) + res.length + } + res + } + + val PatchMethod = SMethod(this, "patch", + SFunc(Array(ThisType, SInt, ThisType, SInt), ThisType, paramIVSeq), + 19, PerItemCost(baseCost = JitCost(30), perChunkCost = JitCost(2), chunkSize = 10)) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + "Produces a new Coll where a slice of elements in this Coll is replaced by another Coll.") + + /** Implements evaluation of Coll.patch method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def patch_eval[A](mc: MethodCall, xs: Coll[A], from: Int, patch: Coll[A], replaced: Int) + (implicit E: ErgoTreeEvaluator): Coll[A] = { + val m = mc.method + val nItems = xs.length + patch.length + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], nItems, m.opDesc) { () => + xs.patch(from, patch, replaced) + } + } + + val UpdatedMethod = SMethod(this, "updated", + SFunc(Array(ThisType, SInt, tIV), ThisType, paramIVSeq), + 20, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(1), chunkSize = 10)) + .withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Int, Any]("updated")) + .withInfo(MethodCall, + "A copy of this Coll with one single replaced element.") + + /** Implements evaluation of Coll.updated method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def updated_eval[A](mc: MethodCall, coll: Coll[A], index: Int, elem: A) + (implicit E: ErgoTreeEvaluator): Coll[A] = { + val m = mc.method + val costKind = m.costKind.asInstanceOf[PerItemCost] + E.addSeqCost(costKind, coll.length, m.opDesc) { () => + coll.updated(index, elem) + } + } + + val UpdateManyMethod = SMethod(this, "updateMany", + SFunc(Array(ThisType, SCollection(SInt), ThisType), ThisType, paramIVSeq), + 21, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(2), chunkSize = 10)) + .withIRInfo(MethodCallIrBuilder).withInfo(MethodCall, "") + + /** Implements evaluation of Coll.updateMany method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def updateMany_eval[A](mc: MethodCall, coll: Coll[A], indexes: Coll[Int], values: Coll[A]) + (implicit E: ErgoTreeEvaluator): Coll[A] = { + val costKind = mc.method.costKind.asInstanceOf[PerItemCost] + E.addSeqCost(costKind, coll.length, mc.method.opDesc) { () => + coll.updateMany(indexes, values) + } + } + + val IndexOfMethod = SMethod(this, "indexOf", + SFunc(Array(ThisType, tIV, SInt), SInt, paramIVSeq), + 26, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(10), chunkSize = 2)) + .withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Any, Int]("indexOf")) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.indexOf method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def indexOf_eval[A](mc: MethodCall, xs: Coll[A], elem: A, from: Int) + (implicit E: ErgoTreeEvaluator): Int = { + val costKind = mc.method.costKind.asInstanceOf[PerItemCost] + var res: Int = -1 + E.addSeqCost(costKind, mc.method.opDesc) { () => + // this loop is bounded because MaxArrayLength limit is enforced + val len = xs.length + val start = math.max(from, 0) + var i = start + var different = true + while (i < len && different) { + different = !DataValueComparer.equalDataValues(xs(i), elem) + i += 1 + } + if (!different) + res = i - 1 + i - start // return number of performed iterations + } + res + } + + /** Cost descriptor of Coll.zip operation. */ + val Zip_CostKind = PerItemCost( + baseCost = JitCost(10), perChunkCost = JitCost(1), chunkSize = 10) + + val ZipMethod = SMethod(this, "zip", + SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), + 29, Zip_CostKind) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, "") + + /** Implements evaluation of Coll.zip method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def zip_eval[A, B](mc: MethodCall, xs: Coll[A], ys: Coll[B]) + (implicit E: ErgoTreeEvaluator): Coll[(A,B)] = { + val m = mc.method + E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => + xs.zip(ys) + } + } + + /** This method should be overriden in derived classes to add new methods in addition to inherited. + * Typical override: `super.getMethods() ++ Seq(m1, m2, m3)` + */ + override protected def getMethods(): Seq[SMethod] = super.getMethods() ++ + Seq( + SizeMethod, + GetOrElseMethod, + MapMethod, + ExistsMethod, + FoldMethod, + ForallMethod, + SliceMethod, + FilterMethod, + AppendMethod, + ApplyMethod, + IndicesMethod, + FlatMapMethod, + PatchMethod, + UpdatedMethod, + UpdateManyMethod, + IndexOfMethod, + ZipMethod + ) +} + +object STupleMethods extends MethodsContainer { + /** Type for which this container defines methods. */ + override def ownerType: STypeCompanion = STuple + + private val MaxTupleLength: Int = SigmaConstants.MaxTupleLength.value + + private val componentNames = Array.tabulate(MaxTupleLength) { i => s"_${i + 1}" } + + /** A list of Coll methods inherited from Coll type and available as method of tuple. */ + lazy val colMethods: Seq[SMethod] = { + val subst = Map(SType.tIV -> SAny) + // TODO: implement other methods + val activeMethods = Set(1.toByte /*Coll.size*/, 10.toByte /*Coll.apply*/) + SCollectionMethods.methods.filter(m => activeMethods.contains(m.methodId)).map { m => + m.copy(stype = applySubst(m.stype, subst).asFunc) + } + } + + def getTupleMethod(tup: STuple, name: String): Option[SMethod] = { + colMethods.find(_.name == name).orElse { + val iComponent = componentNames.lastIndexOf(name, end = tup.items.length - 1) + if (iComponent == -1) None + else + Some(SMethod( + STupleMethods, name, SFunc(tup, tup.items(iComponent)), + (iComponent + 1).toByte, SelectField.costKind)) + } + } + + override protected def getMethods(): Seq[SMethod] = super.getMethods() +} + +/** Type descriptor of `Box` type of ErgoTree. */ +case object SBoxMethods extends MonoTypeMethods { + import ErgoBox._ + import SType.{paramT, tT} + + override def ownerType: SMonoType = SBox + + /** Defined once here and then reused in SMethod descriptors. */ + lazy val GetRegFuncType = SFunc(Array(SBox), SOption(tT), Array(paramT)) + + /** Creates a descriptor for the given register method. (i.e. R1, R2, etc) */ + def registers(idOfs: Int): Seq[SMethod] = { + allRegisters.map { i => + i match { + case r: MandatoryRegisterId => + SMethod(this, s"R${i.asIndex}", + GetRegFuncType, (idOfs + i.asIndex + 1).toByte, ExtractRegisterAs.costKind) + .withInfo(ExtractRegisterAs, r.purpose) + case _ => + SMethod(this, s"R${i.asIndex}", + GetRegFuncType, (idOfs + i.asIndex + 1).toByte, ExtractRegisterAs.costKind) + .withInfo(ExtractRegisterAs, "Non-mandatory register") + } + } + } + + val PropositionBytes = "propositionBytes" + val Value = "value" + val Id = "id" + val Bytes = "bytes" + val BytesWithoutRef = "bytesWithoutRef" + val CreationInfo = "creationInfo" + val GetReg = "getReg" + + // should be lazy, otherwise lead to initialization error + lazy val ValueMethod = SMethod( + this, Value, SFunc(SBox, SLong), 1, ExtractAmount.costKind) + .withInfo(ExtractAmount, + "Mandatory: Monetary value, in Ergo tokens (NanoErg unit of measure)") + + lazy val PropositionBytesMethod = SMethod( + this, PropositionBytes, SFunc(SBox, SByteArray), 2, ExtractScriptBytes.costKind) + .withInfo(ExtractScriptBytes, + "Serialized bytes of guarding script, which should be evaluated to true in order to\n" + + " open this box. (aka spend it in a transaction)") + + lazy val BytesMethod = SMethod( + this, Bytes, SFunc(SBox, SByteArray), 3, ExtractBytes.costKind) + .withInfo(ExtractBytes, "Serialized bytes of this box's content, including proposition bytes.") + + lazy val BytesWithoutRefMethod = SMethod( + this, BytesWithoutRef, SFunc(SBox, SByteArray), 4, ExtractBytesWithNoRef.costKind) + .withInfo(ExtractBytesWithNoRef, + "Serialized bytes of this box's content, excluding transactionId and index of output.") + + lazy val IdMethod = SMethod(this, Id, SFunc(SBox, SByteArray), 5, ExtractId.costKind) + .withInfo(ExtractId, + "Blake2b256 hash of this box's content, basically equals to \\lst{blake2b256(bytes)}") + + lazy val creationInfoMethod = SMethod( + this, CreationInfo, ExtractCreationInfo.OpType, 6, ExtractCreationInfo.costKind) + .withInfo(ExtractCreationInfo, + """ If \lst{tx} is a transaction which generated this box, then \lst{creationInfo._1} + | is a height of the tx's block. The \lst{creationInfo._2} is a serialized transaction + | identifier followed by box index in the transaction outputs. + """.stripMargin ) // see ExtractCreationInfo + + lazy val getRegMethod = SMethod(this, "getReg", + SFunc(Array(SBox, SInt), SOption(tT), Array(paramT)), 7, ExtractRegisterAs.costKind) + .withInfo(ExtractRegisterAs, + """ Extracts register by id and type. + | Type param \lst{T} expected type of the register. + | Returns \lst{Some(value)} if the register is defined and has given type and \lst{None} otherwise + """.stripMargin, + ArgInfo("regId", "zero-based identifier of the register.")) + + lazy val tokensMethod = SMethod( + this, "tokens", SFunc(SBox, ErgoBox.STokensRegType), 8, FixedCost(JitCost(15))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, "Secondary tokens") + + + // should be lazy to solve recursive initialization + protected override def getMethods() = super.getMethods() ++ Array( + ValueMethod, // see ExtractAmount + PropositionBytesMethod, // see ExtractScriptBytes + BytesMethod, // see ExtractBytes + BytesWithoutRefMethod, // see ExtractBytesWithNoRef + IdMethod, // see ExtractId + creationInfoMethod, + getRegMethod, + tokensMethod + ) ++ registers(8) +} + +/** Type descriptor of `AvlTree` type of ErgoTree. */ +case object SAvlTreeMethods extends MonoTypeMethods { + import SOption._ + + override def ownerType: SMonoType = SAvlTree + + lazy val TCollOptionCollByte = SCollection(SByteArrayOption) + lazy val CollKeyValue = SCollection(STuple(SByteArray, SByteArray)) + + lazy val digestMethod = SMethod(this, "digest", SFunc(SAvlTree, SByteArray), 1, FixedCost(JitCost(15))) + .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) + + /** Cost descriptor of `digest` method. */ + lazy val digest_Info = { + val m = digestMethod + OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) + } + + lazy val enabledOperationsMethod = SMethod( + this, "enabledOperations", SFunc(SAvlTree, SByte), 2, FixedCost(JitCost(15))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ Flags of enabled operations packed in single byte. + | \lst{isInsertAllowed == (enabledOperations & 0x01) != 0}\newline + | \lst{isUpdateAllowed == (enabledOperations & 0x02) != 0}\newline + | \lst{isRemoveAllowed == (enabledOperations & 0x04) != 0} + """.stripMargin) + + lazy val keyLengthMethod = SMethod( + this, "keyLength", SFunc(SAvlTree, SInt), 3, FixedCost(JitCost(15))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ + | + """.stripMargin) + + lazy val valueLengthOptMethod = SMethod( + this, "valueLengthOpt", SFunc(SAvlTree, SIntOption), 4, FixedCost(JitCost(15))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ + | + """.stripMargin) + + lazy val isInsertAllowedMethod = SMethod( + this, "isInsertAllowed", SFunc(SAvlTree, SBoolean), 5, FixedCost(JitCost(15))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ + | + """.stripMargin) + + lazy val isInsertAllowed_Info = { + val m = isInsertAllowedMethod + OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) + } + + lazy val isUpdateAllowedMethod = SMethod( + this, "isUpdateAllowed", SFunc(SAvlTree, SBoolean), 6, FixedCost(JitCost(15))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ + | + """.stripMargin) + + lazy val isUpdateAllowed_Info = { + val m = isUpdateAllowedMethod + OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) + } + + lazy val isRemoveAllowedMethod = SMethod( + this, "isRemoveAllowed", SFunc(SAvlTree, SBoolean), 7, FixedCost(JitCost(15))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(PropertyCall, + """ + | + """.stripMargin) + + lazy val isRemoveAllowed_Info = { + val m = isRemoveAllowedMethod + OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) + } + + lazy val updateOperationsMethod = SMethod(this, "updateOperations", + SFunc(Array(SAvlTree, SByte), SAvlTree), 8, FixedCost(JitCost(45))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | + """.stripMargin) + + lazy val containsMethod = SMethod(this, "contains", + SFunc(Array(SAvlTree, SByteArray, SByteArray), SBoolean), 9, DynamicCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | /** Checks if an entry with key `key` exists in this tree using proof `proof`. + | * Throws exception if proof is incorrect + | + | * @note CAUTION! Does not support multiple keys check, use [[getMany]] instead. + | * Return `true` if a leaf with the key `key` exists + | * Return `false` if leaf with provided key does not exist. + | * @param key a key of an element of this authenticated dictionary. + | * @param proof + | */ + | + """.stripMargin) + + /** The proof may contain keys, labels and values, we don't know for sure how many, + * but we assume the cost is O(proof.length). + * So the following is an approximation of the proof parsing cost. + */ + final val CreateAvlVerifier_Info = OperationCostInfo( + PerItemCost(baseCost = JitCost(110), perChunkCost = JitCost(20), chunkSize = 64), + NamedDesc("CreateAvlVerifier")) + + final val LookupAvlTree_Info = OperationCostInfo( + PerItemCost(baseCost = JitCost(40), perChunkCost = JitCost(10), chunkSize = 1), + NamedDesc("LookupAvlTree")) + + final val InsertIntoAvlTree_Info = OperationCostInfo( + PerItemCost(baseCost = JitCost(40), perChunkCost = JitCost(10), chunkSize = 1), + NamedDesc("InsertIntoAvlTree")) + + final val UpdateAvlTree_Info = OperationCostInfo( + PerItemCost(baseCost = JitCost(120), perChunkCost = JitCost(20), chunkSize = 1), + NamedDesc("UpdateAvlTree")) + + final val RemoveAvlTree_Info = OperationCostInfo( + PerItemCost(baseCost = JitCost(100), perChunkCost = JitCost(15), chunkSize = 1), + NamedDesc("RemoveAvlTree")) + + /** Implements evaluation of AvlTree.contains method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def contains_eval(mc: MethodCall, tree: AvlTree, key: Coll[Byte], proof: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Boolean = { + E.contains_eval(mc, tree, key, proof) + } + + lazy val getMethod = SMethod(this, "get", + SFunc(Array(SAvlTree, SByteArray, SByteArray), SByteArrayOption), 10, DynamicCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | /** Perform a lookup of key `key` in this tree using proof `proof`. + | * Throws exception if proof is incorrect + | * + | * @note CAUTION! Does not support multiple keys check, use [[getMany]] instead. + | * Return Some(bytes) of leaf with key `key` if it exists + | * Return None if leaf with provided key does not exist. + | * @param key a key of an element of this authenticated dictionary. + | * @param proof + | */ + | + """.stripMargin) + + /** Implements evaluation of AvlTree.get method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def get_eval(mc: MethodCall, tree: AvlTree, key: Coll[Byte], proof: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Option[Coll[Byte]] = { + E.get_eval(mc, tree, key, proof) + } + + lazy val getManyMethod = SMethod(this, "getMany", + SFunc(Array(SAvlTree, SByteArray2, SByteArray), TCollOptionCollByte), 11, DynamicCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | /** Perform a lookup of many keys `keys` in this tree using proof `proof`. + | * + | * @note CAUTION! Keys must be ordered the same way they were in lookup before proof was generated. + | * For each key return Some(bytes) of leaf if it exists and None if is doesn't. + | * @param keys keys of elements of this authenticated dictionary. + | * @param proof + | */ + | + """.stripMargin) + + /** Implements evaluation of AvlTree.getMany method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def getMany_eval(mc: MethodCall, tree: AvlTree, keys: Coll[Coll[Byte]], proof: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Coll[Option[Coll[Byte]]] = { + E.getMany_eval(mc, tree, keys, proof) + } + + lazy val insertMethod = SMethod(this, "insert", + SFunc(Array(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 12, DynamicCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | /** Perform insertions of key-value entries into this tree using proof `proof`. + | * Throws exception if proof is incorrect + | * + | * @note CAUTION! Pairs must be ordered the same way they were in insert ops before proof was generated. + | * Return Some(newTree) if successful + | * Return None if operations were not performed. + | * @param operations collection of key-value pairs to insert in this authenticated dictionary. + | * @param proof + | */ + | + """.stripMargin) + + /** Implements evaluation of AvlTree.insert method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def insert_eval(mc: MethodCall, tree: AvlTree, entries: KeyValueColl, proof: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Option[AvlTree] = { + E.insert_eval(mc, tree, entries, proof) + } + + lazy val updateMethod = SMethod(this, "update", + SFunc(Array(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 13, DynamicCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | /** Perform updates of key-value entries into this tree using proof `proof`. + | * Throws exception if proof is incorrect + | * + | * @note CAUTION! Pairs must be ordered the same way they were in update ops before proof was generated. + | * Return Some(newTree) if successful + | * Return None if operations were not performed. + | * @param operations collection of key-value pairs to update in this authenticated dictionary. + | * @param proof + | */ + | + """.stripMargin) + + /** Implements evaluation of AvlTree.update method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def update_eval(mc: MethodCall, tree: AvlTree, + operations: KeyValueColl, proof: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Option[AvlTree] = { + E.update_eval(mc, tree, operations, proof) + } + + lazy val removeMethod = SMethod(this, "remove", + SFunc(Array(SAvlTree, SByteArray2, SByteArray), SAvlTreeOption), 14, DynamicCost) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | /** Perform removal of entries into this tree using proof `proof`. + | * Throws exception if proof is incorrect + | * Return Some(newTree) if successful + | * Return None if operations were not performed. + | * + | * @note CAUTION! Keys must be ordered the same way they were in remove ops before proof was generated. + | * @param operations collection of keys to remove from this authenticated dictionary. + | * @param proof + | */ + | + """.stripMargin) + + /** Implements evaluation of AvlTree.remove method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod + */ + def remove_eval(mc: MethodCall, tree: AvlTree, + operations: Coll[Coll[Byte]], proof: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Option[AvlTree] = { + E.remove_eval(mc, tree, operations, proof) + } + + lazy val updateDigestMethod = SMethod(this, "updateDigest", + SFunc(Array(SAvlTree, SByteArray), SAvlTree), 15, FixedCost(JitCost(40))) + .withIRInfo(MethodCallIrBuilder) + .withInfo(MethodCall, + """ + | + """.stripMargin) + + lazy val updateDigest_Info = { + val m = updateDigestMethod + OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) + } + + protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( + digestMethod, + enabledOperationsMethod, + keyLengthMethod, + valueLengthOptMethod, + isInsertAllowedMethod, + isUpdateAllowedMethod, + isRemoveAllowedMethod, + updateOperationsMethod, + containsMethod, + getMethod, + getManyMethod, + insertMethod, + updateMethod, + removeMethod, + updateDigestMethod + ) +} + +/** Type descriptor of `Context` type of ErgoTree. */ +case object SContextMethods extends MonoTypeMethods { + override def ownerType: SMonoType = SContext + + /** Arguments on context operation such as getVar, DeserializeContext etc. + * This value can be reused where necessary to avoid allocations. */ + val ContextFuncDom: IndexedSeq[SType] = Array(SContext, SByte) + + import SType.{paramT, tT} + + lazy val dataInputsMethod = propertyCall("dataInputs", SBoxArray, 1, FixedCost(JitCost(15))) + lazy val headersMethod = propertyCall("headers", SHeaderArray, 2, FixedCost(JitCost(15))) + lazy val preHeaderMethod = propertyCall("preHeader", SPreHeader, 3, FixedCost(JitCost(15))) + 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 = propertyCall("selfBoxIndex", SInt, 8, FixedCost(JitCost(20))) + lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) + lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) + lazy val getVarMethod = SMethod( + this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind) + .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", + ArgInfo("varId", "\\lst{Byte} identifier of context variable")) + + protected override def getMethods() = super.getMethods() ++ Seq( + dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, + selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod + ) + + /** Names of methods which provide blockchain context. + * This value can be reused where necessary to avoid allocations. */ + val BlockchainContextMethodNames: IndexedSeq[String] = Array( + headersMethod.name, preHeaderMethod.name, heightMethod.name, + lastBlockUtxoRootHashMethod.name, minerPubKeyMethod.name + ) +} + +/** Type descriptor of `Header` type of ErgoTree. */ +case object SHeaderMethods extends MonoTypeMethods { + override def ownerType: SMonoType = SHeader + + lazy val idMethod = propertyCall("id", SByteArray, 1, FixedCost(JitCost(10))) + lazy val versionMethod = propertyCall("version", SByte, 2, FixedCost(JitCost(10))) + lazy val parentIdMethod = propertyCall("parentId", SByteArray, 3, FixedCost(JitCost(10))) + lazy val ADProofsRootMethod = propertyCall("ADProofsRoot", SByteArray, 4, FixedCost(JitCost(10))) + lazy val stateRootMethod = propertyCall("stateRoot", SAvlTree, 5, FixedCost(JitCost(10))) + lazy val transactionsRootMethod = propertyCall("transactionsRoot", SByteArray, 6, FixedCost(JitCost(10))) + lazy val timestampMethod = propertyCall("timestamp", SLong, 7, FixedCost(JitCost(10))) + lazy val nBitsMethod = propertyCall("nBits", SLong, 8, FixedCost(JitCost(10))) + lazy val heightMethod = propertyCall("height", SInt, 9, FixedCost(JitCost(10))) + lazy val extensionRootMethod = propertyCall("extensionRoot", SByteArray, 10, FixedCost(JitCost(10))) + lazy val minerPkMethod = propertyCall("minerPk", SGroupElement, 11, FixedCost(JitCost(10))) + lazy val powOnetimePkMethod = propertyCall("powOnetimePk", SGroupElement, 12, FixedCost(JitCost(10))) + lazy val powNonceMethod = propertyCall("powNonce", SByteArray, 13, FixedCost(JitCost(10))) + lazy val powDistanceMethod = propertyCall("powDistance", SBigInt, 14, FixedCost(JitCost(10))) + lazy val votesMethod = propertyCall("votes", SByteArray, 15, FixedCost(JitCost(10))) + + protected override def getMethods() = super.getMethods() ++ Seq( + idMethod, versionMethod, parentIdMethod, ADProofsRootMethod, stateRootMethod, transactionsRootMethod, + timestampMethod, nBitsMethod, heightMethod, extensionRootMethod, minerPkMethod, powOnetimePkMethod, + powNonceMethod, powDistanceMethod, votesMethod + ) +} + +/** Type descriptor of `PreHeader` type of ErgoTree. */ +case object SPreHeaderMethods extends MonoTypeMethods { + override def ownerType: SMonoType = SPreHeader + + lazy val versionMethod = propertyCall("version", SByte, 1, FixedCost(JitCost(10))) + lazy val parentIdMethod = propertyCall("parentId", SByteArray, 2, FixedCost(JitCost(10))) + lazy val timestampMethod = propertyCall("timestamp", SLong, 3, FixedCost(JitCost(10))) + lazy val nBitsMethod = propertyCall("nBits", SLong, 4, FixedCost(JitCost(10))) + lazy val heightMethod = propertyCall("height", SInt, 5, FixedCost(JitCost(10))) + lazy val minerPkMethod = propertyCall("minerPk", SGroupElement, 6, FixedCost(JitCost(10))) + lazy val votesMethod = propertyCall("votes", SByteArray, 7, FixedCost(JitCost(10))) + + protected override def getMethods() = super.getMethods() ++ Seq( + versionMethod, parentIdMethod, timestampMethod, nBitsMethod, heightMethod, minerPkMethod, votesMethod + ) +} + +/** This type is introduced to unify handling of global and non-global (i.e. methods) operations. + * It unifies implementation of global operation with implementation of methods and avoids code + * duplication (following DRY principle https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). + * The WrappedType is `sigma.SigmaDslBuilder`, which is an interface implemented by + * the singleton sigmastate.eval.CostingSigmaDslBuilder + * + * The Constant(...) tree node of this type are not allowed, as well as using it in register and + * context variables (aka ContextExtension) + * + * When new methods are added to this type via a soft-fork, they will be serialized as part + * of ErgoTree using MethodCallSerializer, where SGlobal.typeCode will be used. + * + * @see sigmastate.lang.SigmaPredef + * */ +case object SGlobalMethods extends MonoTypeMethods { + override def ownerType: SMonoType = SGlobal + + lazy val groupGeneratorMethod = SMethod( + this, "groupGenerator", SFunc(SGlobal, SGroupElement), 1, GroupGenerator.costKind) + .withIRInfo({ case (builder, obj, method, args, tparamSubst) => GroupGenerator }) + .withInfo(GroupGenerator, "") + + lazy val xorMethod = SMethod( + this, "xor", SFunc(Array(SGlobal, SByteArray, SByteArray), SByteArray), 2, Xor.costKind) + .withIRInfo({ + case (_, _, _, Seq(l, r), _) => Xor(l.asByteArray, r.asByteArray) + }) + .withInfo(Xor, "Byte-wise XOR of two collections of bytes", + ArgInfo("left", "left operand"), ArgInfo("right", "right operand")) + + /** Implements evaluation of Global.xor method call ErgoTree node. + * Called via reflection based on naming convention. + * @see SMethod.evalMethod, Xor.eval, Xor.xorWithCosting + */ + def xor_eval(mc: MethodCall, G: SigmaDslBuilder, ls: Coll[Byte], rs: Coll[Byte]) + (implicit E: ErgoTreeEvaluator): Coll[Byte] = { + Xor.xorWithCosting(ls, rs) + } + + protected override def getMethods() = super.getMethods() ++ Seq( + groupGeneratorMethod, + xorMethod + ) +} + diff --git a/data/shared/src/main/scala/sigma/ast/syntax.scala b/data/shared/src/main/scala/sigma/ast/syntax.scala new file mode 100644 index 0000000000..5a257481cb --- /dev/null +++ b/data/shared/src/main/scala/sigma/ast/syntax.scala @@ -0,0 +1,211 @@ +package sigma.ast + +import sigma.ast.SCollection.{SByteArray, SIntArray} +import sigma.data.{AvlTreeData, CSigmaProp, GeneralType, Nullable, RType, SigmaBoolean, TrivialProp} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate} +import sigma.kiama.rewriting.Rewriter.{everywherebu, rewrite, rule} +import StdSigmaBuilder.mkUpcast +import sigma.SigmaDataReflection +import sigma.exceptions.InterpreterException + +import scala.annotation.nowarn +import scala.reflect.classTag + +/** Contains global definitions which define syntactic extensions for working with classes + * of sigma.ast package. + */ +object syntax { + /** Force initialization of reflection. */ + val reflection = SigmaDataReflection + + /** The following type synonyms are used throughout the codebase to simplify type annotations. */ + type SValue = Value[SType] + type BoolValue = Value[SBoolean.type] + type ByteValue = Value[SByte.type] + type ShortValue = Value[SShort.type] + type IntValue = Value[SInt.type] + type LongValue = Value[SLong.type] + type BigIntValue = Value[SBigInt.type] + type BoxValue = Value[SBox.type] + type GroupElementValue = Value[SGroupElement.type] + type SigmaPropValue = Value[SSigmaProp.type] + type AvlTreeValue = Value[SAvlTree.type] + type SAnyValue = Value[SAny.type] + type CollectionValue[T <: SType] = Value[SCollection[T]] + + type BooleanConstant = Constant[SBoolean.type] + type ByteConstant = Constant[SByte.type] + type ShortConstant = Constant[SShort.type] + type IntConstant = Constant[SInt.type] + type LongConstant = Constant[SLong.type] + type StringConstant = Constant[SString.type] + type BigIntConstant = Constant[SBigInt.type] + type BoxConstant = Constant[SBox.type] + type GroupElementConstant = Constant[SGroupElement.type] + type SigmaPropConstant = Constant[SSigmaProp.type] + type AvlTreeConstant = Constant[SAvlTree.type] + type CollectionConstant[T <: SType] = Constant[SCollection[T]] + + /** Sigma proposition with trivial false proposition. */ + val FalseSigmaProp = SigmaPropConstant(CSigmaProp(TrivialProp.FalseProp)) + + /** Sigma proposition with trivial true proposition. */ + val TrueSigmaProp = SigmaPropConstant(CSigmaProp(TrivialProp.TrueProp)) + + /** Methods to work with tree nodes of [[SCollection]] type. */ + implicit class CollectionOps[T <: SType](val coll: Value[SCollection[T]]) extends AnyVal { + /** Returns number of items in the collection expression. */ + def length: Int = matchCase(_.items.length, _.value.length, _.items.length) + + /** Returns a sequence of items in the collection expression. */ + def items: Seq[Value[SType]] = matchCase(_.items, _ => sys.error(s"Cannot get 'items' property of node $coll"), _.items) + + /** Abstracts from details of pattern matching collection expressions. + * Folds over given `coll` structure. + */ + def matchCase[R]( + whenConcrete: ConcreteCollection[T] => R, + whenConstant: CollectionConstant[T] => R, + whenTuple: Tuple => R + ): R = coll match { + case cc: ConcreteCollection[T]@unchecked => whenConcrete(cc) + case const: CollectionConstant[T]@unchecked => whenConstant(const) + case tuple: Tuple => whenTuple(tuple) + case _ => sys.error(s"Unexpected node $coll") + } + } + + /** Methods to work with tree nodes of [[SSigmaProp]] type. */ + implicit class SigmaPropValueOps(val p: Value[SSigmaProp.type]) extends AnyVal { + def propBytes: Value[SByteArray] = SigmaPropBytes(p) + } + + /** Methods to work with tree nodes of [[SSigmaProp]] type. */ + implicit class OptionValueOps[T <: SType](val p: Value[SOption[T]]) extends AnyVal { + def get: Value[T] = OptionGet(p) + + def getOrElse(default: Value[T]): Value[T] = OptionGetOrElse(p, default) + + def isDefined: Value[SBoolean.type] = OptionIsDefined(p) + } + + + def GetVarBoolean(varId: Byte): GetVar[SBoolean.type] = GetVar(varId, SBoolean) + + def GetVarByte(varId: Byte): GetVar[SByte.type] = GetVar(varId, SByte) + + def GetVarShort(varId: Byte): GetVar[SShort.type] = GetVar(varId, SShort) + + def GetVarInt(varId: Byte): GetVar[SInt.type] = GetVar(varId, SInt) + + def GetVarLong(varId: Byte): GetVar[SLong.type] = GetVar(varId, SLong) + + def GetVarBigInt(varId: Byte): GetVar[SBigInt.type] = GetVar(varId, SBigInt) + + def GetVarBox(varId: Byte): GetVar[SBox.type] = GetVar(varId, SBox) + + def GetVarSigmaProp(varId: Byte): GetVar[SSigmaProp.type] = GetVar(varId, SSigmaProp) + + def GetVarByteArray(varId: Byte): GetVar[SCollection[SByte.type]] = GetVar(varId, SByteArray) + + def GetVarIntArray(varId: Byte): GetVar[SCollection[SInt.type]] = GetVar(varId, SIntArray) + + implicit def boolToSigmaProp(b: BoolValue): SigmaPropValue = BoolToSigmaProp(b) + + /** Shadow the implicit from sigma package so it doesn't interfere with the resolution + * of ClassTags below. + */ + @nowarn private def rtypeToClassTag = ??? + + /** RType descriptors for predefined types used in AOTC-based interpreter. */ + implicit val ErgoBoxRType: RType[ErgoBox] = RType.fromClassTag(classTag[ErgoBox]) + + implicit val ErgoBoxCandidateRType: RType[ErgoBoxCandidate] = RType.fromClassTag(classTag[ErgoBoxCandidate]) + + implicit val AvlTreeDataRType: RType[AvlTreeData] = GeneralType(classTag[AvlTreeData]) + + /** Type casting methods for [[Value]] nodes. + * Each `asX` method casts the value to the corresponding `X` type of node. + * No runtime checks are performed, so the caller should ensure that the value has the + * expected type. + * */ + implicit class ValueOps(val v: Value[SType]) extends AnyVal { + def asValue[T <: SType]: Value[T] = v.asInstanceOf[Value[T]] + + def asNumValue: Value[SNumericType] = v.asInstanceOf[Value[SNumericType]] + + def asBoolValue: Value[SBoolean.type] = v.asInstanceOf[Value[SBoolean.type]] + + def asByteValue: Value[SByte.type] = v.asInstanceOf[Value[SByte.type]] + + def asIntValue: Value[SInt.type] = v.asInstanceOf[Value[SInt.type]] + + def asBigInt: Value[SBigInt.type] = v.asInstanceOf[Value[SBigInt.type]] + + def asBox: Value[SBox.type] = v.asInstanceOf[Value[SBox.type]] + + def asGroupElement: Value[SGroupElement.type] = v.asInstanceOf[Value[SGroupElement.type]] + + def asSigmaProp: Value[SSigmaProp.type] = v.asInstanceOf[Value[SSigmaProp.type]] + + def asByteArray: Value[SByteArray] = v.asInstanceOf[Value[SByteArray]] + + def asIntArray: Value[SIntArray] = v.asInstanceOf[Value[SIntArray]] + + def asCollection[T <: SType]: Value[SCollection[T]] = v.asInstanceOf[Value[SCollection[T]]] + + def asOption[T <: SType]: Value[SOption[T]] = v.asInstanceOf[Value[SOption[T]]] + + def asTuple: Value[STuple] = v.asInstanceOf[Value[STuple]] + + def asFunc: Value[SFunc] = v.asInstanceOf[Value[SFunc]] + + /** Upcast the value `v` to the given type checking that `v` if of a numeric type. + * @param targetType type to upcast to + * @return upcasted value + */ + def upcastTo[T <: SNumericType](targetType: T): Value[T] = { + assert(v.tpe.isInstanceOf[SNumericType], + s"Cannot upcast value of type ${v.tpe} to $targetType: only numeric types can be upcasted.") + val tV = v.asValue[SNumericType] + assert(targetType.max(tV.tpe) == targetType, + s"Invalid upcast from $tV to $targetType: target type should be larger than source type.") + if (targetType == tV.tpe) v.asValue[T] + else + mkUpcast(tV, targetType).withSrcCtx(v.sourceContext) + } + + /** Assigns optional [[SourceContext]] to the value `v`. + * The is effectful operation changing state of `v`. + * @return the same instance of `v` with the given source context assigned + */ + def withSrcCtx[T <: SType](sourceContext: Nullable[SourceContext]): Value[T] = { + v.sourceContext = sourceContext + v.asValue[T] + } + + /** + * Sets the source context of the `v` instance only if it's empty (i.e. not set yet). + */ + def withEnsuredSrcCtx[T <: SType](sourceContext: Nullable[SourceContext]): Value[T] = { + if (v.sourceContext.isEmpty) v.sourceContext = sourceContext + v.asValue[T] + } + + /** + * Set source context to all nodes missing source context in the given tree. + * + * @param srcCtx source context to set + * @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[Any] { + case node: SValue if node != null && node.sourceContext.isEmpty => + node.withSrcCtx(srcCtx) + }))(v).asValue[T] + } + } + + /** Helper method to throw errors from Interpreter. */ + def error(msg: String) = throw new InterpreterException(msg) +} diff --git a/interpreter/shared/src/main/scala/sigmastate/utxo/transformers.scala b/data/shared/src/main/scala/sigma/ast/transformers.scala similarity index 94% rename from interpreter/shared/src/main/scala/sigmastate/utxo/transformers.scala rename to data/shared/src/main/scala/sigma/ast/transformers.scala index 3d1ec92872..939da79d98 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utxo/transformers.scala +++ b/data/shared/src/main/scala/sigma/ast/transformers.scala @@ -1,20 +1,16 @@ -package sigmastate.utxo - -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ -import sigmastate.lang.Terms._ -import sigmastate._ -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.serialization.OpCodes +package sigma.ast + import org.ergoplatform.ErgoBox.RegisterId -import sigma.data.RType -import sigmastate.Operations._ -import sigmastate.eval.{Evaluation, SigmaDsl} -import sigmastate.exceptions.InterpreterException -import sigmastate.interpreter.ErgoTreeEvaluator -import sigmastate.interpreter.ErgoTreeEvaluator.{DataEnv, error} -import sigma.Coll -import sigma.{Box, SigmaProp} +import sigma.ast.Operations._ +import sigma.ast.SCollection.SByteArray +import sigma.ast.syntax.SValue +import sigma.data.{CSigmaProp, RType} +import sigma.eval.ErgoTreeEvaluator +import sigma.eval.ErgoTreeEvaluator.DataEnv +import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.serialization.OpCodes +import sigma.serialization.ValueCodes.OpCode +import sigma.{Box, Coll, Evaluation} // TODO refactor: remove this trait as it doesn't have semantic meaning @@ -40,7 +36,7 @@ case class MapCollection[IV <: SType, OV <: SType]( extends Transformer[SCollection[IV], SCollection[OV]] { override def companion = MapCollection override val tpe = SCollection[OV](mapper.tpe.tRange.asInstanceOf[OV]) - override val opType = SCollection.MapMethod.stype.asFunc + override val opType = SCollectionMethods.MapMethod.stype.asFunc protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val inputV = input.evalTo[Coll[Any]](env) val mapperV = mapper.evalTo[Any => Any](env) @@ -64,7 +60,7 @@ case class Append[IV <: SType](input: Value[SCollection[IV]], col2: Value[SColle extends Transformer[SCollection[IV], SCollection[IV]] { override def companion = Append override val tpe = input.tpe - override val opType = SCollection.AppendMethod.stype + override val opType = SCollectionMethods.AppendMethod.stype protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val inputV = input.evalTo[Coll[IV#WrappedType]](env) val col2V = col2.evalTo[Coll[IV#WrappedType]](env) @@ -123,7 +119,7 @@ case class Filter[IV <: SType](input: Value[SCollection[IV]], extends Transformer[SCollection[IV], SCollection[IV]] { override def companion = Filter override def tpe: SCollection[IV] = input.tpe - override val opType = SCollection.FilterMethod.stype + override val opType = SCollectionMethods.FilterMethod.stype protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val inputV = input.evalTo[Coll[Any]](env) val conditionV = condition.evalTo[Any => Boolean](env) @@ -160,7 +156,7 @@ case class Exists[IV <: SType](override val input: Value[SCollection[IV]], override val condition: Value[SFunc]) extends BooleanTransformer[IV] { override def companion = Exists - override val opType = SCollection.ExistsMethod.stype + override val opType = SCollectionMethods.ExistsMethod.stype protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val inputV = input.evalTo[Coll[Any]](env) val conditionV = condition.evalTo[Any => Boolean](env) @@ -187,7 +183,7 @@ case class ForAll[IV <: SType](override val input: Value[SCollection[IV]], override val condition: Value[SFunc]) extends BooleanTransformer[IV] { override def companion = ForAll - override val opType = SCollection.ForallMethod.stype + override val opType = SCollectionMethods.ForallMethod.stype protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val inputV = input.evalTo[Coll[Any]](env) val conditionV = condition.evalTo[Any => Boolean](env) @@ -224,7 +220,7 @@ case class Fold[IV <: SType, OV <: SType](input: Value[SCollection[IV]], extends Transformer[SCollection[IV], OV] { override def companion = Fold implicit override def tpe: OV = zero.tpe - val opType: SFunc = SCollection.FoldMethod.stype + val opType: SFunc = SCollectionMethods.FoldMethod.stype protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val inputV = input.evalTo[Coll[IV#WrappedType]](env) val zeroV = zero.evalTo[OV#WrappedType](env) @@ -239,14 +235,6 @@ object Fold extends ValueCompanion { override def opCode: OpCode = OpCodes.FoldCode override val costKind = PerItemCost( baseCost = JitCost(3), perChunkCost = JitCost(1), chunkSize = 10) - def sum[T <: SNumericType](input: Value[SCollection[T]], varId: Int)(implicit tT: T) = - Fold(input, - Constant(tT.upcast(0.toByte), tT), - FuncValue(Array((varId, STuple(tT, tT))), - Plus( - SelectField(ValUse(varId, STuple(tT, tT)), 1).asNumValue, - SelectField(ValUse(varId, STuple(tT, tT)), 2).asNumValue)) - ) } /** The element of the collection or default value. @@ -264,7 +252,7 @@ case class ByIndex[V <: SType](input: Value[SCollection[V]], extends Transformer[SCollection[V], V] with NotReadyValue[V] { override def companion = ByIndex override val tpe = input.tpe.elemType - override val opType = SCollection.ApplyMethod.stype.asFunc + override val opType = SCollectionMethods.ApplyMethod.stype.asFunc protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val inputV = input.evalTo[Coll[V#WrappedType]](env) val indexV = index.evalTo[Int](env) @@ -301,7 +289,7 @@ case class SelectField(input: Value[STuple], fieldIndex: Byte) case p: Tuple2[_,_] => if (fieldIndex == 1) p._1 else if (fieldIndex == 2) p._2 - else error(s"Unknown fieldIndex $fieldIndex to select from $p: evaluating tree $this") + else sys.error(s"Unknown fieldIndex $fieldIndex to select from $p: evaluating tree $this") case _ => Value.typeError(input, inputV) } @@ -335,8 +323,8 @@ case class SigmaPropBytes(input: Value[SSigmaProp.type]) override def tpe = SByteArray override val opType = SFunc(input.tpe, tpe) protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - val inputV = input.evalTo[SigmaProp](env) - val numNodes = SigmaDsl.toSigmaBoolean(inputV).size + val inputV = input.evalTo[CSigmaProp](env) + val numNodes = inputV.wrappedValue.size addSeqCost(SigmaPropBytes.costKind, numNodes) { () => inputV.propBytes } @@ -551,7 +539,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(SContext.ContextFuncDom, tpe) + override val opType = SFunc(SContextMethods.ContextFuncDom, tpe) } object DeserializeContext extends ValueCompanion { override def opCode: OpCode = OpCodes.DeserializeContextCode @@ -575,7 +563,7 @@ object DeserializeRegister extends ValueCompanion { /** See [[sigma.Context.getVar()]] for detailed description. */ case class GetVar[V <: SType](varId: Byte, override val tpe: SOption[V]) extends NotReadyValue[SOption[V]] { override def companion = GetVar - override val opType = SFunc(SContext.ContextFuncDom, tpe) + override val opType = SFunc(SContextMethods.ContextFuncDom, tpe) protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val t = Evaluation.stypeToRType(tpe.elemType) addCost(GetVar.costKind) diff --git a/interpreter/shared/src/main/scala/sigmastate/trees.scala b/data/shared/src/main/scala/sigma/ast/trees.scala similarity index 90% rename from interpreter/shared/src/main/scala/sigmastate/trees.scala rename to data/shared/src/main/scala/sigma/ast/trees.scala index 2346ccd9e6..38d4565f30 100644 --- a/interpreter/shared/src/main/scala/sigmastate/trees.scala +++ b/data/shared/src/main/scala/sigma/ast/trees.scala @@ -1,158 +1,28 @@ -package sigmastate +package sigma.ast import debox.{cfor, Map => DMap} -import org.ergoplatform.SigmaConstants -import org.ergoplatform.validation.SigmaValidationSettings +import scorex.crypto.hash.{Blake2b256, CryptographicHash32, Sha256} +import sigma.ast.ArithOp.OperationImpl +import sigma.ast.Operations._ +import sigma.ast.SCollection.{SByteArray, SIntArray} +import sigma.ast.SOption.SIntOption +import sigma.ast.syntax._ import sigma.data.ExactIntegral._ import sigma.data.ExactOrdering._ import sigma.data.OverloadHack.Overloaded1 -import sigma.data.{ExactIntegral, ExactOrdering} -import scorex.crypto.hash.{Blake2b256, CryptographicHash32, Sha256} +import sigma.data._ +import sigma.serialization.CoreByteWriter.ArgInfo +import sigma.validation.SigmaValidationSettings import sigma.{Coll, Colls, GroupElement, SigmaProp, VersionContext} -import sigmastate.ArithOp.OperationImpl -import sigmastate.Operations._ -import sigmastate.SCollection.{SByteArray, SIntArray} -import sigmastate.SOption.SIntOption -import sigmastate.Values._ -import sigmastate.eval.Extensions.EvalCollOps -import sigmastate.eval.NumericOps.{BigIntIsExactIntegral, BigIntIsExactOrdering} -import sigmastate.eval.SigmaDsl -import sigmastate.interpreter.ErgoTreeEvaluator -import sigmastate.interpreter.ErgoTreeEvaluator.DataEnv -import sigmastate.serialization.OpCodes._ -import sigmastate.serialization._ -import sigmastate.utxo.{SimpleTransformerCompanion, Transformer} +import NumericOps.{BigIntIsExactIntegral, BigIntIsExactOrdering} +import sigma.eval.ErgoTreeEvaluator.DataEnv +import sigma.eval.Extensions.EvalCollOps +import sigma.eval.{ErgoTreeEvaluator, SigmaDsl} +import sigma.serialization.OpCodes._ +import sigma.serialization.ValueCodes.OpCode +import sigma.serialization._ import scala.collection.mutable -import scala.collection.mutable.ArrayBuffer - -/** - * Basic trait for inner nodes of crypto-trees, so AND/OR/THRESHOLD sigma-protocol connectives - */ -trait SigmaConjecture extends SigmaBoolean { - def children: Seq[SigmaBoolean] -} - -/** - * Basic trait for leafs of crypto-trees, such as - * [[sigmastate.crypto.DLogProtocol.ProveDlog]] and [[sigmastate.crypto.ProveDHTuple]] - * instances. - * It plays the same role as [[SigmaConjecture]]. It used in prover to distinguish leafs from - * other nodes and have logic common to leaves regardless of the concrete leaf type. - */ -trait SigmaLeaf extends SigmaBoolean - - -/** - * AND conjunction for sigma propositions - */ -case class CAND(override val children: Seq[SigmaBoolean]) extends SigmaConjecture { - /** The same code is used for AND operation, but they belong to different type hierarchies. */ - override val opCode: OpCode = OpCodes.AndCode - override val size: Int = SigmaBoolean.totalSize(children) + 1 -} - -object CAND { - import TrivialProp._ - - /** Connects the given sigma propositions into CAND proposition performing - * partial evaluation when some of them are trivial propositioins. - * - * @param items propositions to combine into CAND - * @return CAND, TrueProp, FalseProp or even one of the items depending on partial evaluation - */ - def normalized(items: Seq[SigmaBoolean]): SigmaBoolean = { - require(items.nonEmpty) - val res = new ArrayBuffer[SigmaBoolean]() - val nItems = items.length - cfor(0)(_ < nItems, _ + 1) { i => - val x = items(i) - x match { - case FalseProp => return FalseProp - case TrueProp => // skip - case _ => res += x - } - } - if (res.isEmpty) TrueProp - else if (res.length == 1) res(0) - else CAND(res.toSeq) - } -} - -/** - * OR disjunction for sigma propositions - */ -case class COR(children: Seq[SigmaBoolean]) extends SigmaConjecture { - /** The same code is also used for OR operation, but they belong to different type hierarchies. */ - override val opCode: OpCode = OpCodes.OrCode - override val size: Int = SigmaBoolean.totalSize(children) + 1 -} - -object COR { - import TrivialProp._ - - /** Connects the given sigma propositions into COR proposition performing - * partial evaluation when some of them are trivial propositioins. - * - * @param items propositions to combine into COR - * @return COR, TrueProp, FalseProp or even one of the items depending on partial evaluation - */ - def normalized(items: Seq[SigmaBoolean]): SigmaBoolean = { - require(items.nonEmpty) - val res = new ArrayBuffer[SigmaBoolean]() - val nItems = items.length - cfor(0)(_ < nItems, _ + 1) { i => - val x = items(i) - x match { - case FalseProp => // skip - case TrueProp => return TrueProp - case _ => res += x - } - } - if (res.isEmpty) FalseProp - else if (res.length == 1) res(0) - else COR(res.toSeq) - } -} - -/** - * THRESHOLD connector for sigma propositions - */ -case class CTHRESHOLD(k: Int, children: Seq[SigmaBoolean]) extends SigmaConjecture { - // Our polynomial arithmetic can take only byte inputs - require(k >= 0 && k <= children.length && children.length <= 255) - - override val opCode: OpCode = OpCodes.AtLeastCode - override val size: Int = SigmaBoolean.totalSize(children) + 1 -} - - -/** Represents boolean values (true/false) in SigmaBoolean tree. - * Participates in evaluation of CAND, COR, THRESHOLD connectives over SigmaBoolean values. - * See CAND.normalized, COR.normalized and AtLeast.reduce. */ -abstract class TrivialProp(val condition: Boolean) extends SigmaBoolean with Product1[Boolean] { - override def _1: Boolean = condition - override def canEqual(that: Any): Boolean = that != null && that.isInstanceOf[TrivialProp] -} -object TrivialProp { - // NOTE: the corresponding unapply is missing because any implementation (even using Nullable) - // will lead to Boolean boxing, which we want to avoid - // So, instead of `case TrivialProp(b) => ... b ...` use more efficient - // `case p: TrivialProp => ... p.condition ... - - def apply(b: Boolean): TrivialProp = if (b) TrueProp else FalseProp - - val FalseProp = new TrivialProp(false) { - override val opCode: OpCode = OpCodes.TrivialPropFalseCode - override def size: Int = 1 - override def toString = "FalseProp" - } - val TrueProp = new TrivialProp(true) { - override val opCode: OpCode = OpCodes.TrivialPropTrueCode - override def size: Int = 1 - override def toString = "TrueProp" - } -} /** Embedding of Boolean values to SigmaProp values. As an example, this operation allows boolean experesions * to be used as arguments of `atLeast(..., sigmaProp(boolExpr), ...)` operation. @@ -166,7 +36,7 @@ case class BoolToSigmaProp(value: BoolValue) extends SigmaPropValue { val v = value.evalTo[Any](env) addCost(BoolToSigmaProp.costKind) if (VersionContext.current.isJitActivated) { - SigmaDsl.sigmaProp(v.asInstanceOf[Boolean]) + CSigmaProp(v.asInstanceOf[Boolean]) } else { // before v5.0 is activated we follow the old v4.x semantics to handle cases // when the value is not a boolean. There are historical transactions with such @@ -174,7 +44,7 @@ case class BoolToSigmaProp(value: BoolValue) extends SigmaPropValue { v match { case sp: SigmaProp => sp case _ => - SigmaDsl.sigmaProp(v.asInstanceOf[Boolean]) + CSigmaProp(v.asInstanceOf[Boolean]) } } } @@ -194,7 +64,7 @@ case class CreateProveDlog(value: Value[SGroupElement.type]) extends SigmaPropVa protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { val v = value.evalTo[GroupElement](env) addCost(CreateProveDlog.costKind) - SigmaDsl.proveDlog(v) + CSigmaProp.withProveDlog(v) } } object CreateProveDlog extends FixedCostValueCompanion { @@ -235,7 +105,7 @@ case class CreateProveDHTuple(gv: Value[SGroupElement.type], val u = uv.evalTo[GroupElement](env) val v = vv.evalTo[GroupElement](env) addCost(CreateProveDHTuple.costKind) - SigmaDsl.proveDHTuple(g, h, u, v) + CSigmaProp.withProveDHTuple(g, h, u, v) } } object CreateProveDHTuple extends FixedCostValueCompanion { @@ -468,7 +338,7 @@ object AtLeast extends ValueCompanion { /** HOTSPOT: don't beautify this code */ def reduce(bound: Int, children: Seq[SigmaBoolean]): SigmaBoolean = { - import sigmastate.TrivialProp._ + import sigma.data.TrivialProp._ if (bound <= 0) return TrueProp val nChildren = children.length if (bound > nChildren) return FalseProp @@ -802,7 +672,7 @@ object SubstConstants extends ValueCompanion { /** Transforms serialized bytes of ErgoTree with segregated constants by * replacing constants at given positions with new values. This operation * allow to use serialized scripts as pre-defined templates. - * See [[sigmastate.SubstConstants]] for details. + * See [[SubstConstants]] for details. * * @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1. * @param positions zero based indexes in ErgoTree.constants array which @@ -991,7 +861,7 @@ object ArithOp { } } - private[sigmastate] val operations: DMap[Byte, ArithOpCompanion] = + private[sigma] val operations: DMap[Byte, ArithOpCompanion] = DMap.fromIterable(Seq(Plus, Minus, Multiply, Division, Modulo, Min, Max).map(o => (o.opCode, o))) /** Represents implementation of numeric Arith operations for the given type argTpe. */ @@ -1000,7 +870,7 @@ object ArithOp { val o = _o.asInstanceOf[ExactOrdering[Any]] } - private[sigmastate] val impls: DMap[SType.TypeCode, OperationImpl] = + private[sigma] val impls: DMap[SType.TypeCode, OperationImpl] = DMap.fromIterable(Seq( SByte -> new OperationImpl(ByteIsExactIntegral, ByteIsExactOrdering, SByte), SShort -> new OperationImpl(ShortIsExactIntegral, ShortIsExactOrdering, SShort), @@ -1464,7 +1334,7 @@ case class TreeLookup(tree: Value[SAvlTree.type], key: Value[SByteArray], proof: Value[SByteArray]) extends Quadruple[SAvlTree.type, SByteArray, SByteArray, SOption[SByteArray]] { override def companion = TreeLookup - override def tpe = SOption[SByteArray] + override def tpe = SOption[SByteArray](SByteArray) override lazy val first = tree override lazy val second = key override lazy val third = proof diff --git a/data/shared/src/main/scala/sigma/ast/values.scala b/data/shared/src/main/scala/sigma/ast/values.scala new file mode 100644 index 0000000000..87c661a00a --- /dev/null +++ b/data/shared/src/main/scala/sigma/ast/values.scala @@ -0,0 +1,1530 @@ +package sigma.ast + +import debox.cfor +import sigma.Extensions.ArrayOps +import sigma._ +import sigma.ast.SCollection.SByteArray +import sigma.ast.TypeCodes.ConstantCode +import sigma.ast.syntax._ +import sigma.crypto.{CryptoConstants, EcPointType} +import sigma.data.OverloadHack.Overloaded1 +import sigma.data.{CSigmaDslBuilder, CSigmaProp, Nullable, RType, SigmaBoolean} +import sigma.eval.ErgoTreeEvaluator.DataEnv +import sigma.eval.{ErgoTreeEvaluator, SigmaDsl} +import sigma.exceptions.InterpreterException +import sigma.kiama.rewriting.Rewriter.count +import sigma.serialization.OpCodes._ +import sigma.serialization.ValueCodes.OpCode +import sigma.serialization._ +import sigma.util.CollectionUtil._ +import sigma.util.Extensions._ + +import java.math.BigInteger +import java.util.{Arrays, Objects} +import scala.annotation.unused +import scala.collection.compat.immutable.ArraySeq +import scala.collection.mutable +import scala.language.implicitConversions + +/** Base class for all ErgoTree expression nodes. + * + * @see [[ErgoTree]] + */ +abstract class Value[+S <: SType] extends SigmaNode { + /** The companion node descriptor with opCode, cost and other metadata. */ + def companion: ValueCompanion + + /** Unique id of the node class used in serialization of ErgoTree. */ + def opCode: OpCode = companion.opCode + + /** The type of the value represented by this node. If the value is an operation it is + * the type of operation result. */ + def tpe: S + + /** Every value represents an operation and that operation can be associated with a function type, + * describing functional meaning of the operation, kind of operation signature. + * Thus, we can obtain global operation identifiers by combining Value.opName with Value.opType, + * so that if (v1.opName == v2.opName) && (v1.opType == v2.opType) then v1 and v2 are functionally + * point-wise equivalent. + * This in particular means that if two _different_ ops have the same opType they _should_ have + * different opNames. + * Thus defined op ids are used in a v4.x Cost Model - a table of all existing primitives coupled with + * performance parameters. + * */ + def opType: SFunc + + /** Name of the operation. */ + def opName: String = this.getClass.getSimpleName + + /** Transforms this expression to SigmaProp expression or throws an exception. */ + def toSigmaProp: SigmaPropValue = this match { + case b if b.tpe == SBoolean => BoolToSigmaProp(this.asBoolValue) + case p if p.tpe == SSigmaProp => p.asSigmaProp + case _ => sys.error(s"Expected SBoolean or SSigmaProp typed value, but was: $this") + } + + /** Parser has some source information like line,column in the text. We need to keep it up until RuntimeCosting. + * The way to do this is to add Nullable property to every Value. Since Parser is always using SigmaBuilder + * to create nodes, + * Adding additional (implicit source: SourceContext) parameter to every builder method would pollute its API + * and also doesn't make sence during deserialization, where Builder is also used. + * We can assume some indirect mechanism to pass current source context into every mkXXX method of Builder. + * We can pass it using `scala.util.DynamicVariable` by wrapping each mkXXX call into `withValue { }` calls. + * The same will happen in Typer. + * We can take sourceContext from untyped nodes and use it while creating typed nodes. + * And we can make sourceContext of every Value writeOnce value, i.e. it will be Nullable.Null by default, + * but can be set afterwards, but only once. + * This property will not participate in equality and other operations, so will be invisible for existing code. + * But Builder can use it to set sourceContext if it is present. + */ + private[ast] var _sourceContext: Nullable[SourceContext] = Nullable.None + + def sourceContext: Nullable[SourceContext] = _sourceContext + + def sourceContext_=(srcCtx: Nullable[SourceContext]): Unit = + if (_sourceContext.isEmpty) { + _sourceContext = srcCtx + } else { + sys.error("_sourceContext can be set only once") + } + + /** Defines an evaluation semantics of this tree node (aka Value or expression) in the given data environment. + * Should be implemented by all the ErgoTree nodes (aka operations). + * Thus, the ErgoTree interpreter implementation consists of combined implementations of this method. + * NOTE, this method shouldn't be called directly, instead use `evalTo` method. + * + * @param E Evaluator which defines evaluation context, cost accumulator, settings etc. + * @param env immutable map, which binds variables (given by ids) to the values + * @return the data value which is the result of evaluation + */ + protected def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = + sys.error(s"Should be overriden in ${this.getClass}: $this") + + /** Evaluates this node to the value of the given expected type. + * This method should called from all `eval` implementations. + * + * @tparam T expected type of the resulting value + * @param E Evaluator which defines evaluation context, cost accumulator, settings etc. + * @param env immutable map, which binds variables (given by ids) to the values + * @return the data value which is the result of evaluation + */ + @inline + final def evalTo[T](env: DataEnv)(implicit E: ErgoTreeEvaluator): T = { + if (E.settings.isMeasureOperationTime) E.profiler.onBeforeNode(this) + val v = eval(env) + if (E.settings.isMeasureOperationTime) E.profiler.onAfterNode(this) + v.asInstanceOf[T] + } + + /** Add the cost given by the kind to the accumulator and associate it with this operation + * node. + */ + @inline + final def addCost(costKind: FixedCost)(implicit E: ErgoTreeEvaluator): Unit = { + E.addCost(costKind, this.companion.opDesc) + } + + /** Add the cost given by the descriptor to the accumulator and associate it with this operation + * node. + */ + @inline + final def addCost[R](costKind: TypeBasedCost, tpe: SType) + (block: () => R) + (implicit E: ErgoTreeEvaluator): R = { + E.addTypeBasedCost(costKind, tpe, this.companion.opDesc)(block) + } + + /** Add the cost of a repeated operation to the accumulator and associate it with this + * operation. The number of items (loop iterations) is known in advance (like in + * Coll.map operation) + * + * @param costKind cost descriptor of the operation + * @param nItems number of operations known in advance (before loop execution) + */ + @inline + final def addSeqCostNoOp(costKind: PerItemCost, nItems: Int) + (implicit E: ErgoTreeEvaluator): Unit = { + E.addSeqCostNoOp(costKind, nItems, this.companion.opDesc) + } + + /** Add the cost of a repeated operation to the accumulator and associate it with this + * operation. The number of items (loop iterations) is known in advance (like in + * Coll.map operation) + * + * @param costKind cost descriptor of the operation + * @param nItems number of operations known in advance (before loop execution) + * @param block operation executed under the given cost + * @tparam R result type of the operation + */ + @inline + final def addSeqCost[R](costKind: PerItemCost, nItems: Int) + (block: () => R)(implicit E: ErgoTreeEvaluator): R = { + E.addSeqCost(costKind, nItems, this.companion.opDesc)(block) + } +} + +object Value { + type PropositionCode = Byte + + implicit def liftByte(n: Byte): Value[SByte.type] = ByteConstant(n) + + implicit def liftShort(n: Short): Value[SShort.type] = ShortConstant(n) + + implicit def liftInt(n: Int): Value[SInt.type] = IntConstant(n) + + implicit def liftLong(n: Long): Value[SLong.type] = LongConstant(n) + + implicit def liftByteArray(arr: Array[Byte]): Value[SByteArray] = ByteArrayConstant(arr) + + implicit def liftBigInt(arr: BigInt): Value[SBigInt.type] = BigIntConstant(arr) + + implicit def liftGroupElement(g: GroupElement): Value[SGroupElement.type] = GroupElementConstant(g) + + implicit def liftECPoint(g: EcPointType): Value[SGroupElement.type] = GroupElementConstant(g) + + implicit def liftSigmaProp(g: SigmaProp): Value[SSigmaProp.type] = SigmaPropConstant(g) + + implicit def liftSigmaBoolean(sb: SigmaBoolean): Value[SSigmaProp.type] = SigmaPropConstant(SigmaDsl.SigmaProp(sb)) + + object Typed { + def unapply(v: SValue): Option[(SValue, SType)] = Some((v, v.tpe)) + } + + def notSupportedError(v: Any, opName: String) = + throw new IllegalArgumentException(s"Method $opName is not supported for node $v") + + /** Immutable empty array of values. Can be used to avoid allocation. */ + val EmptyArray = Array.empty[SValue] + + /** Immutable empty Seq of values. Can be used to avoid allocation. */ + val EmptySeq: IndexedSeq[SValue] = EmptyArray + + /** Traverses the given expression tree and counts the number of deserialization + * operations. If it is non-zero, returns true. */ + def hasDeserialize(exp: SValue): Boolean = { + val deserializeNode: PartialFunction[Any, Int] = { + case _: DeserializeContext[_] => 1 + case _: DeserializeRegister[_] => 1 + } + val c = count(deserializeNode)(exp) + c > 0 + } + + /** Traverses the given expression tree and counts the number of operations + * which read the blockchain context. If it is non-zero, returns true. */ + def isUsingBlockchainContext(exp: SValue): Boolean = { + val blockchainContextNode: PartialFunction[Any, Int] = { + case Height => 1 + case LastBlockUtxoRootHash => 1 + case MinerPubkey => 1 + case MethodCall(_, method, _, _) => + method.objType match { + case SContextMethods => + if (SContextMethods.BlockchainContextMethodNames.contains(method.name)) 1 else 0 + case _ => 0 + } + case _ => 0 + } + val c = count(blockchainContextNode)(exp) + c > 0 + } + + def typeError(node: SValue, evalResult: Any) = { + val tpe = node.tpe + throw new InterpreterException( + s"""Invalid type returned by evaluator: + | expression: $node + | expected type: $tpe + | resulting value: $evalResult + """.stripMargin) + } + + def typeError(tpe: SType, evalResult: Any) = { + throw new InterpreterException( + s"""Invalid type returned by evaluator: + | expected type: $tpe + | resulting value: $evalResult + """.stripMargin) + } + + def checkType(node: SValue, evalResult: Any) = { + val tpe = node.tpe + if (!SType.isValueOfType(evalResult, tpe)) + typeError(node, evalResult) + } + + def checkType(tpe: SType, evalResult: Any) = { + if (!SType.isValueOfType(evalResult, tpe)) + typeError(tpe, evalResult) + } +} + +/** Base class for all companion objects which are used as operation descriptors. */ +trait ValueCompanion extends SigmaNodeCompanion { + import ValueCompanion._ + + /** Unique id of the node class used in serialization of ErgoTree. */ + def opCode: OpCode + + /** Returns cost descriptor of this operation. */ + def costKind: CostKind + + override def toString: String = s"${this.getClass.getSimpleName}(${opCode.toUByte})" + + def typeName: String = this.getClass.getSimpleName.replace("$", "") + + def init() { + if (this.opCode != 0 && _allOperations.contains(this.opCode)) + throw sys.error(s"Operation $this already defined") + _allOperations += (this.opCode -> this) + } + + init() + val opDesc = CompanionDesc(this) +} + +object ValueCompanion { + private val _allOperations: mutable.HashMap[Byte, ValueCompanion] = mutable.HashMap.empty + + lazy val allOperations = _allOperations.toMap +} + +/** Should be inherited by companion objects of operations with fixed cost kind. */ +trait FixedCostValueCompanion extends ValueCompanion { + /** Returns cost descriptor of this operation. */ + override def costKind: FixedCost +} + +/** Should be inherited by companion objects of operations with per-item cost kind. */ +trait PerItemCostValueCompanion extends ValueCompanion { + /** Returns cost descriptor of this operation. */ + override def costKind: PerItemCost +} + +/** Base class for ErgoTree nodes which represents a data value which has already been + * evaluated and no further evaluation (aka reduction) is necessary by the interpreter. + * + * @see Constant, ConcreteCollection, Tuple + */ +abstract class EvaluatedValue[+S <: SType] extends Value[S] { + /** The evaluated data value of the corresponding underlying data type. */ + val value: S#WrappedType + + override def opType: SFunc = { + val resType = tpe match { + case ct: SCollection[_] => + SCollection(ct.typeParams.head.ident) + case ft @ SFunc(_, _, _) => + ft.getGenericType + case _ => tpe + } + SFunc(ArraySeq.empty, resType) + } +} + +/** Base class for all constant literals whose data value is already known and never + * changes. + * + * @see ConstantNode + */ +abstract class Constant[+S <: SType] extends EvaluatedValue[S] + +/** ErgoTree node which represents data literals, i.e. data values embedded in an + * expression. + * + * @param value data value of the underlying Scala type + * @param tpe type descriptor of the data value and also the type of the value + * represented by this node. + * @see Constant + */ +case class ConstantNode[S <: SType](value: S#WrappedType, tpe: S) extends Constant[S] { + require(sigma.crypto.Platform.isCorrectType(value, tpe), + s"Invalid type of constant value $value, expected type $tpe") + + override def companion: ValueCompanion = Constant + + override def opCode: OpCode = companion.opCode + + override def opName: String = s"Const" + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(Constant.costKind) + value + } + + override def equals(obj: scala.Any): Boolean = (obj != null) && (this.eq(obj.asInstanceOf[AnyRef]) || (obj match { + case c: Constant[_] => tpe == c.tpe && Objects.deepEquals(value, c.value) + case _ => false + })) + + override def hashCode(): Int = Arrays.deepHashCode(Array(value.asInstanceOf[AnyRef], tpe)) + + override def toString: String = tpe.asInstanceOf[SType] match { + case SGroupElement if value.isInstanceOf[GroupElement] => + s"ConstantNode(${value.asInstanceOf[GroupElement].showToString},$tpe)" + case SGroupElement => + sys.error(s"Invalid value in Constant($value, $tpe)") + case SInt => s"IntConstant($value)" + case SLong => s"LongConstant($value)" + case SBoolean if value == true => "TrueLeaf" + case SBoolean if value == false => "FalseLeaf" + case _ => s"ConstantNode($value,$tpe)" + } +} + +object Constant extends FixedCostValueCompanion { + override def opCode: OpCode = OpCode @@ ConstantCode + + /** Cost of: returning value from Constant node. */ + override val costKind = FixedCost(JitCost(5)) + + /** Immutable empty array, can be used to save allocations in many places. */ + val EmptyArray = Array.empty[Constant[SType]] + + /** Immutable empty IndexedSeq, can be used to save allocations in many places. */ + val EmptySeq: IndexedSeq[Constant[SType]] = Array.empty[Constant[SType]] + + /** Helper factory method. */ + def apply[S <: SType]( + value: S#WrappedType, + tpe: S): Constant[S] = ConstantNode(value, tpe) + + /** Recognizer of Constant tree nodes used in patterns. */ + def unapply[S <: SType](v: EvaluatedValue[S]): Option[(S#WrappedType, S)] = v match { + case ConstantNode(value, tpe) => Some((value, tpe)) + case _ => None + } +} + +/** Placeholder for a constant in ErgoTree. Zero based index in ErgoTree.constants array. */ +case class ConstantPlaceholder[S <: SType]( + id: Int, + override val tpe: S) extends Value[S] { + def opType = SFunc(SInt, tpe) + + override def companion: ValueCompanion = ConstantPlaceholder + + override protected def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + val c = E.constants(id) + addCost(ConstantPlaceholder.costKind) + val res = c.value + Value.checkType(c, res) + res + } +} + +object ConstantPlaceholder extends ValueCompanion { + override def opCode: OpCode = ConstantPlaceholderCode + + /** Cost of: accessing Constant in array by index. */ + override val costKind = FixedCost(JitCost(1)) +} + +trait NotReadyValue[S <: SType] extends Value[S] { +} + +// TODO v6.0: remove these TaggedVariable and TaggedVariableNode (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/584) + +/** Reference a context variable by id. */ +trait TaggedVariable[T <: SType] extends NotReadyValue[T] { + val varId: Byte +} + +case class TaggedVariableNode[T <: SType](varId: Byte, override val tpe: T) + extends TaggedVariable[T] { + override def companion = TaggedVariable + + def opType: SFunc = Value.notSupportedError(this, "opType") +} + +object TaggedVariable extends ValueCompanion { + override def opCode: OpCode = TaggedVariableCode + + override def costKind: CostKind = FixedCost(JitCost(1)) + + def apply[T <: SType](varId: Byte, tpe: T): TaggedVariable[T] = + TaggedVariableNode(varId, tpe) +} + +/** High-level interface to internal representation of Unit constants in ErgoTree. */ +object UnitConstant { + /** ErgoTree node that represent a literal of Unit type. It is global immutable value + * which should be reused wherever necessary to avoid allocations. + */ + val instance = apply() + + /** Constucts a fresh new instance of Unit literal node. */ + def apply() = Constant[SUnit.type]((), SUnit) + + /** Recognizer to pattern match on Unit constant literal nodes (aka Unit constants). */ + def unapply(node: SValue): Boolean = node match { + case ConstantNode(_, SUnit) => true + case _ => false + } +} + +object ByteConstant { + def apply(value: Byte): Constant[SByte.type] = Constant[SByte.type](value, SByte) +} + +object ShortConstant { + def apply(value: Short): Constant[SShort.type] = Constant[SShort.type](value, SShort) +} + +object IntConstant { + def apply(value: Int): Constant[SInt.type] = Constant[SInt.type](value, SInt) + + def unapply(v: SValue): Option[Int] = v match { + case Constant(value: Int, SInt) => Some(value) + case _ => None + } + + def Zero = IntConstant(0) +} + +object LongConstant { + def apply(value: Long): Constant[SLong.type] = Constant[SLong.type](value, SLong) + + def unapply(v: SValue): Option[Long] = v match { + case Constant(value: Long, SLong) => Some(value) + case _ => None + } +} + +object BigIntConstant { + def apply(value: BigInt): Constant[SBigInt.type] = Constant[SBigInt.type](value, SBigInt) + + def apply(value: BigInteger): Constant[SBigInt.type] = Constant[SBigInt.type](SigmaDsl.BigInt(value), SBigInt) + + def apply(value: Long): Constant[SBigInt.type] = Constant[SBigInt.type](SigmaDsl.BigInt(BigInteger.valueOf(value)), SBigInt) +} + +object StringConstant { + def apply(value: String): Constant[SString.type] = Constant[SString.type](value, SString) + + def unapply(v: SValue): Option[String] = v match { + case Constant(value: String, SString) => Some(value) + case _ => None + } +} + +object BoxConstant { + def apply(value: Box): Constant[SBox.type] = Constant[SBox.type](value, SBox) +} + +object GroupElementConstant { + def apply(value: EcPointType): Constant[SGroupElement.type] = apply(SigmaDsl.GroupElement(value)) + + def apply(value: GroupElement): Constant[SGroupElement.type] = Constant[SGroupElement.type](value, SGroupElement) + + def unapply(v: SValue): Option[GroupElement] = v match { + case Constant(value: GroupElement, SGroupElement) => Some(value) + case _ => None + } +} + +object SigmaPropConstant { + def apply(value: SigmaProp): Constant[SSigmaProp.type] = Constant[SSigmaProp.type](value, SSigmaProp) + + def apply(value: SigmaBoolean): Constant[SSigmaProp.type] = Constant[SSigmaProp.type](CSigmaProp(value), SSigmaProp) + + def unapply(v: SValue): Option[SigmaProp] = v match { + case Constant(value: SigmaProp, SSigmaProp) => Some(value) + case _ => None + } +} + +object AvlTreeConstant { + def apply(value: AvlTree): Constant[SAvlTree.type] = Constant[SAvlTree.type](value, SAvlTree) +} + +object PreHeaderConstant { + def apply(value: PreHeader): Constant[SPreHeader.type] = Constant[SPreHeader.type](value, SPreHeader) + + def unapply(v: SValue): Option[PreHeader] = v match { + case Constant(value: PreHeader, SPreHeader) => Some(value) + case _ => None + } +} + +object HeaderConstant { + def apply(value: Header): Constant[SHeader.type] = Constant[SHeader.type](value, SHeader) + + def unapply(v: SValue): Option[Header] = v match { + case Constant(value: Header, SHeader) => Some(value) + case _ => None + } +} + +trait NotReadyValueInt extends NotReadyValue[SInt.type] { + override def tpe = SInt +} + +trait NotReadyValueLong extends NotReadyValue[SLong.type] { + override def tpe = SLong +} + +trait NotReadyValueBigInt extends NotReadyValue[SBigInt.type] { + override def tpe = SBigInt +} + +/** Base type for evaluated tree nodes of Coll type. */ +trait EvaluatedCollection[T <: SType, C <: SCollection[T]] extends EvaluatedValue[C] { + /** Type descriptor of the collection elements. */ + def elementType: T +} + +object CollectionConstant { + def apply[T <: SType]( + value: Coll[T#WrappedType], + elementType: T): Constant[SCollection[T]] = + Constant[SCollection[T]](value, SCollection(elementType)) + + def unapply[T <: SType](node: Value[SCollection[T]]): Option[(Coll[T#WrappedType], T)] = node match { + case c: Constant[SCollection[a]]@unchecked if c.tpe.isCollection => + val v = c.value.asInstanceOf[Coll[T#WrappedType]] + val t = c.tpe.elemType + Some((v, t)) + case _ => None + } +} + +object ByteArrayConstant { + val ByteArrayTypeCode = (SCollectionType.CollectionTypeCode + SByte.typeCode).toByte + + def apply(value: Coll[Byte]): CollectionConstant[SByte.type] = CollectionConstant[SByte.type](value, SByte) + + def apply(value: Array[Byte]): CollectionConstant[SByte.type] = CollectionConstant[SByte.type](value.toColl, SByte) + + def unapply(node: SValue): Option[Coll[Byte]] = node match { + case coll: CollectionConstant[SByte.type]@unchecked => coll match { + case CollectionConstant(arr, SByte) => Some(arr) + case _ => None + } + case _ => None + } +} + +object ShortArrayConstant { + def apply(value: Coll[Short]): CollectionConstant[SShort.type] = CollectionConstant[SShort.type](value, SShort) + + def apply(value: Array[Short]): CollectionConstant[SShort.type] = CollectionConstant[SShort.type](value.toColl, SShort) + + def unapply(node: SValue): Option[Coll[Short]] = node match { + case coll: CollectionConstant[SShort.type]@unchecked => coll match { + case CollectionConstant(arr, SShort) => Some(arr) + case _ => None + } + case _ => None + } +} + +object IntArrayConstant { + def apply(value: Coll[Int]): CollectionConstant[SInt.type] = CollectionConstant[SInt.type](value, SInt) + + def apply(value: Array[Int]): CollectionConstant[SInt.type] = CollectionConstant[SInt.type](value.toColl, SInt) + + def unapply(node: SValue): Option[Coll[Int]] = node match { + case coll: CollectionConstant[SInt.type]@unchecked => coll match { + case CollectionConstant(arr, SInt) => Some(arr) + case _ => None + } + case _ => None + } +} + +object LongArrayConstant { + def apply(value: Coll[Long]): CollectionConstant[SLong.type] = CollectionConstant[SLong.type](value, SLong) + + def apply(value: Array[Long]): CollectionConstant[SLong.type] = CollectionConstant[SLong.type](value.toColl, SLong) + + def unapply(node: SValue): Option[Coll[Long]] = node match { + case coll: CollectionConstant[SLong.type]@unchecked => coll match { + case CollectionConstant(arr, SLong) => Some(arr) + case _ => None + } + case _ => None + } +} + +object BigIntArrayConstant { + def apply(value: Coll[BigInt]): CollectionConstant[SBigInt.type] = CollectionConstant[SBigInt.type](value, SBigInt) + + def apply(value: Array[BigInt]): CollectionConstant[SBigInt.type] = CollectionConstant[SBigInt.type](value.toColl, SBigInt) + + def unapply(node: SValue): Option[Coll[BigInt]] = node match { + case coll: CollectionConstant[SBigInt.type]@unchecked => coll match { + case CollectionConstant(arr, SBigInt) => Some(arr) + case _ => None + } + case _ => None + } +} + +object BoolArrayConstant { + val BoolArrayTypeCode = (SCollectionType.CollectionTypeCode + SBoolean.typeCode).toByte + + def apply(value: Coll[Boolean]): CollectionConstant[SBoolean.type] = CollectionConstant[SBoolean.type](value, SBoolean) + + def apply(value: Array[Boolean]): CollectionConstant[SBoolean.type] = apply(value.toColl) + + def unapply(node: SValue): Option[Coll[Boolean]] = node match { + case coll: CollectionConstant[SBoolean.type]@unchecked => coll match { + case CollectionConstant(arr, SBoolean) => Some(arr) + case _ => None + } + case _ => None + } +} + +trait NotReadyValueByteArray extends NotReadyValue[SByteArray] { + override def tpe = SByteArray +} + +trait NotReadyValueAvlTree extends NotReadyValue[SAvlTree.type] { + override def tpe = SAvlTree +} + +/** ErgoTree node that represents the operation of obtaining the generator of elliptic curve group. + * The generator g of the group is an element of the group such that, when written + * multiplicative form, every element of the group is a power of g. + */ +case object GroupGenerator extends EvaluatedValue[SGroupElement.type] with ValueCompanion { + override def opCode: OpCode = OpCodes.GroupGeneratorCode + + override val costKind = FixedCost(JitCost(10)) + + override def tpe = SGroupElement + + override val value = SigmaDsl.GroupElement(CryptoConstants.dlogGroup.generator) + + override def companion = this + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(costKind) + SigmaDsl.groupGenerator + } +} + +trait NotReadyValueGroupElement extends NotReadyValue[SGroupElement.type] { + override def tpe = SGroupElement +} + +object BooleanConstant { + def fromBoolean(v: Boolean): BooleanConstant = if (v) TrueLeaf else FalseLeaf + + def apply(value: Boolean): BooleanConstant = Constant[SBoolean.type](value, SBoolean) + + def unapply(v: SValue): Option[Boolean] = v match { + case Constant(value: Boolean, SBoolean) => Some(value) + case _ => None + } +} + +/** ErgoTree node which represents `true` literal. */ +object TrueLeaf extends ConstantNode[SBoolean.type](true, SBoolean) with ValueCompanion { + override def companion = this + + override def opCode: OpCode = TrueCode + + override def costKind: FixedCost = Constant.costKind + + override def toString: String = "TrueLeaf" +} + +/** ErgoTree node which represents `false` literal. */ +object FalseLeaf extends ConstantNode[SBoolean.type](false, SBoolean) with ValueCompanion { + override def companion = this + + override def opCode: OpCode = FalseCode + + override def costKind: FixedCost = Constant.costKind + + override def toString: String = "FalseLeaf" +} + +trait NotReadyValueBoolean extends NotReadyValue[SBoolean.type] { + override def tpe = SBoolean +} + +trait NotReadyValueBox extends NotReadyValue[SBox.type] { + def tpe = SBox +} + +/** ErgoTree node which converts a collection of expressions into a tuple of data values + * of different types. Each data value of the resulting collection is obtained by + * evaluating the corresponding expression in `items`. All items may have different + * types. + * + * @param items source collection of expressions + */ +case class Tuple(items: IndexedSeq[Value[SType]]) + extends EvaluatedValue[STuple] // note, this superclass is required as Tuple can be in a register + with EvaluatedCollection[SAny.type, STuple] { + override def companion = Tuple + + override lazy val tpe = STuple(items.map(_.tpe)) + + override def opType: SFunc = ??? + + override def elementType: SAny.type = SAny + + override lazy val value = { + val xs = items.cast[EvaluatedValue[SAny.type]].map(_.value) + implicit val tAny: RType[Any] = sigma.AnyType + Colls.fromArray(xs.toArray) + } + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + // in v5.0 version we support only tuples of 2 elements to be equivalent with v4.x + if (items.length != 2) + syntax.error(s"Invalid tuple $this") + val item0 = items(0) + val x = item0.evalTo[Any](env) + Value.checkType(item0, x) + val item1 = items(1) + val y = item1.evalTo[Any](env) + Value.checkType(item1, y) + val res = (x, y) // special representation for pairs (to pass directly to Coll primitives) + addCost(Tuple.costKind) + res + } +} + +object Tuple extends FixedCostValueCompanion { + override def opCode: OpCode = TupleCode + + /** Cost of: 1) allocating a new tuple (of limited max size) */ + override val costKind = FixedCost(JitCost(15)) + + def apply(items: Value[SType]*): Tuple = Tuple(items.toIndexedSeq) +} + +/** ErgoTree node which converts a collection of expressions into a collection of data + * values. Each data value of the resulting collection is obtained by evaluating the + * corresponding expression in `items`. All items must have the same type. + * + * @param items source collection of expressions + * @param elementType type descriptor of elements in the resulting collection + */ +case class ConcreteCollection[V <: SType](items: Seq[Value[V]], elementType: V) + extends EvaluatedCollection[V, SCollection[V]] { + // TODO uncomment and make sure Ergo works with it, i.e. complex data types are never used for `items`. + // There is nothing wrong in using List, Vector and other fancy types as a concrete representation + // of `items`, but these types have sub-optimal performance (2-3x overhead comparing to WrappedArray) + // which is immediately visible in profile. + // NOTE, the assert below should be commented before production release. + // Is it there for debuging only, basically to catch call stacks where the fancy types may + // occasionally be used. + // assert( + // items.isInstanceOf[mutable.WrappedArray[_]] || + // items.isInstanceOf[ArrayBuffer[_]] || + // items.isInstanceOf[mutable.ArraySeq[_]], + // s"Invalid types of items ${items.getClass}") + + private val isBooleanConstants = elementType == SBoolean && items.forall(_.isInstanceOf[Constant[_]]) + + override def companion = + if (isBooleanConstants) ConcreteCollectionBooleanConstant + else ConcreteCollection + + val tpe = SCollection[V](elementType) + + implicit lazy val tElement: RType[V#WrappedType] = Evaluation.stypeToRType(elementType) + + // TODO refactor: this method is not used and can be removed + lazy val value = { + val xs = items.cast[EvaluatedValue[V]].map(_.value) + Colls.fromArray(xs.toArray(tElement.classTag)) + } + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + val len = items.length + addCost(ConcreteCollection.costKind) + val is = Array.ofDim[V#WrappedType](len)(tElement.classTag) + cfor(0)(_ < len, _ + 1) { i => + val item = items(i) + val itemV = item.evalTo[V#WrappedType](env) + Value.checkType(item, itemV) // necessary because cast to V#WrappedType is erased + is(i) = itemV + } + Colls.fromArray(is) + } +} + +object ConcreteCollection extends FixedCostValueCompanion { + override def opCode: OpCode = ConcreteCollectionCode + + /** Cost of: allocating new collection + * + * @see ConcreteCollection_PerItem */ + override val costKind = FixedCost(JitCost(20)) + + def fromSeq[V <: SType](items: Seq[Value[V]])(implicit tV: V): ConcreteCollection[V] = + ConcreteCollection(items, tV) + + def fromItems[V <: SType](items: Value[V]*)(implicit tV: V): ConcreteCollection[V] = + ConcreteCollection(items, tV) +} + +object ConcreteCollectionBooleanConstant extends ValueCompanion { + override def opCode: OpCode = ConcreteCollectionBooleanConstantCode + + override def costKind = ConcreteCollection.costKind +} + +trait LazyCollection[V <: SType] extends NotReadyValue[SCollection[V]] + +sealed trait BlockItem extends NotReadyValue[SType] { + def id: Int + + def rhs: SValue + + def isValDef: Boolean +} + +object BlockItem { + /** Immutable empty array, can be used to save allocations in many places. */ + val EmptyArray = Array.empty[BlockItem] + + /** Immutable empty IndexedSeq to save allocations in many places. */ + val EmptySeq: IndexedSeq[BlockItem] = EmptyArray +} + +/** IR node for let-bound expressions `let x = rhs` which is ValDef, or `let f[T] = rhs` which is FunDef. + * These nodes are used to represent ErgoTrees after common sub-expression elimination. + * This representation is more compact in serialized form. + * + * @param id unique identifier of the variable in the current scope. */ +case class ValDef( + override val id: Int, + tpeArgs: Seq[STypeVar], + override val rhs: SValue) extends BlockItem { + require(id >= 0, "id must be >= 0") + + override def companion = if (tpeArgs.isEmpty) ValDef else FunDef + + override def tpe: SType = rhs.tpe + + override def isValDef: Boolean = tpeArgs.isEmpty + + /** This is not used as operation, but rather to form a program structure */ + override def opType: SFunc = Value.notSupportedError(this, "opType") +} + +object ValDef extends ValueCompanion { + override def opCode: OpCode = ValDefCode + + override def costKind = Value.notSupportedError(this, "costKind") + + def apply(id: Int, rhs: SValue): ValDef = ValDef(id, Nil, rhs) +} + +object FunDef extends ValueCompanion { + override def opCode: OpCode = FunDefCode + + override def costKind = Value.notSupportedError(this, "costKind") + + def unapply(d: BlockItem): Option[(Int, Seq[STypeVar], SValue)] = d match { + case ValDef(id, targs, rhs) if !d.isValDef => Some((id, targs, rhs)) + case _ => None + } +} + +/** Special node which represents a reference to ValDef it was introduced as result of + * CSE. */ +case class ValUse[T <: SType](valId: Int, tpe: T) extends NotReadyValue[T] { + override def companion = ValUse + + /** This is not used as operation, but rather to form a program structure */ + def opType: SFunc = Value.notSupportedError(this, "opType") + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(ValUse.costKind) + val res = env.getOrElse(valId, syntax.error(s"cannot resolve $this")) + Value.checkType(this, res) + res + } +} + +object ValUse extends FixedCostValueCompanion { + override def opCode: OpCode = ValUseCode + + /** Cost of: 1) Lookup in immutable HashMap by valId: Int 2) alloc of Some(v) */ + override val costKind = FixedCost(JitCost(5)) +} + +/** The order of ValDefs in the block is used to assign ids to ValUse(id) nodes + * For all i: items(i).id == {number of ValDefs preceded in a graph} with respect to topological order. + * Specific topological order doesn't really matter, what is important is to preserve semantic linkage + * between ValUse(id) and ValDef with the corresponding id. + * This convention allow to valid serializing ids because we always serializing and deserializing + * in a fixed well defined order. + */ +case class BlockValue( + items: IndexedSeq[BlockItem], + result: SValue) extends NotReadyValue[SType] { + override def companion = BlockValue + + def tpe: SType = result.tpe + + /** This is not used as operation, but rather to form a program structure */ + def opType: SFunc = Value.notSupportedError(this, "opType") + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + var curEnv = env + val len = items.length + addSeqCostNoOp(BlockValue.costKind, len) + cfor(0)(_ < len, _ + 1) { i => + val vd = items(i).asInstanceOf[ValDef] + val v = vd.rhs.evalTo[Any](curEnv) + Value.checkType(vd, v) + E.addFixedCost(FuncValue.AddToEnvironmentDesc_CostKind, + FuncValue.AddToEnvironmentDesc) { + curEnv = curEnv + (vd.id -> v) + } + } + val res = result.evalTo[Any](curEnv) + Value.checkType(result, res) + res + } +} + +object BlockValue extends ValueCompanion { + override def opCode: OpCode = BlockValueCode + + override val costKind = PerItemCost( + baseCost = JitCost(1), perChunkCost = JitCost(1), chunkSize = 10) +} + +/** + * @param args parameters list, where each parameter has an id and a type. + * @param body expression, which refers function parameters with ValUse. + */ +case class FuncValue( + args: IndexedSeq[(Int, SType)], + body: Value[SType]) extends NotReadyValue[SFunc] { + import FuncValue._ + + override def companion = FuncValue + + lazy val tpe: SFunc = { + val nArgs = args.length + val argTypes = new Array[SType](nArgs) + cfor(0)(_ < nArgs, _ + 1) { i => + argTypes(i) = args(i)._2 + } + SFunc(argTypes, body.tpe) + } + + /** This is not used as operation, but rather to form a program structure */ + override def opType: SFunc = SFunc(ArraySeq.empty, tpe) + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(FuncValue.costKind) + if (args.length == 1) { + val arg0 = args(0) + (vArg: Any) => { + Value.checkType(arg0._2, vArg) + var env1: DataEnv = null + E.addFixedCost(AddToEnvironmentDesc_CostKind, AddToEnvironmentDesc) { + env1 = env + (arg0._1 -> vArg) + } + val res = body.evalTo[Any](env1) + Value.checkType(body, res) + res + } + } else { + syntax.error(s"Function must have 1 argument, but was: $this") + } + } +} + +object FuncValue extends FixedCostValueCompanion { + val AddToEnvironmentDesc = NamedDesc("AddToEnvironment") + + /** Cost of: adding value to evaluator environment */ + val AddToEnvironmentDesc_CostKind = FixedCost(JitCost(5)) + + override def opCode: OpCode = FuncValueCode + + /** Cost of: 1) switch on the number of args 2) allocating a new Scala closure + * Old cost: ("Lambda", "() => (D1) => R", lambdaCost), */ + override val costKind = FixedCost(JitCost(5)) + + def apply(argId: Int, tArg: SType, body: SValue): FuncValue = + FuncValue(IndexedSeq((argId, tArg)), body) +} + +/** Frontend representation of a block of Val definitions. + * { val x = ...; val y = ... } + * This node is not part of ErgoTree and hence have Undefined opCode. */ +case class Block(bindings: Seq[Val], result: SValue) extends Value[SType] { + override def companion = Block + + override def tpe: SType = result.tpe + + /** This is not used as operation, but rather to form a program structure */ + override def opType: SFunc = Value.notSupportedError(this, "opType") +} + +object Block extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") + + /** Create a Block node with a single Val definition. */ + def apply(let: Val, result: SValue)(implicit @unused o1: Overloaded1): Block = + Block(Seq(let), result) +} + +/** IR node to represent explicit Zero Knowledge scope in ErgoTree. + * Compiler checks Zero Knowledge properties and issue error message is case of violations. + * ZK-scoping is optional, it can be used when the user want to ensure Zero Knowledge of + * specific set of operations. + * Usually it will require simple restructuring of the code to make the scope body explicit. + * Invariants checked by the compiler: + * - single ZKProof in ErgoTree in a root position + * - no boolean operations in the body, because otherwise the result may be disclosed + * - all the operations are over SigmaProp values + * + * For motivation and details see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/236 + * */ +case class ZKProofBlock(body: SigmaPropValue) extends BoolValue { + override def companion = ZKProofBlock + + override def tpe = SBoolean + + override def opType: SFunc = ZKProofBlock.OpType +} + +object ZKProofBlock extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") + + val OpType = SFunc(SSigmaProp, SBoolean) +} + +trait Val extends Value[SType] { + val name : String + val givenType: SType + val body : SValue +} + +object Val { + def apply(name: String, body: SValue): Val = ValNode(name, NoType, body) + + def apply( + name: String, + givenType: SType, + body: SValue): Val = ValNode(name, givenType, body) + + def unapply(v: SValue): Option[(String, SType, SValue)] = v match { + case ValNode(name, givenType, body) => Some((name, givenType, body)) + case _ => None + } +} + +case class ValNode( + name: String, + givenType: SType, + body: SValue) extends Val { + override def companion = ValNode + + override def tpe: SType = givenType ?: body.tpe + + /** This is not used as operation, but rather to form a program structure */ + override def opType: SFunc = Value.notSupportedError(this, "opType") +} + +object ValNode extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") +} + +/** Frontend node to select a field from an object. Should be transformed to SelectField */ +case class Select( + obj: Value[SType], + field: String, + resType: Option[SType] = None) extends Value[SType] { + override def companion = Select + + override val tpe: SType = resType.getOrElse(obj.tpe match { + case p: SProduct => + MethodsContainer.getMethod(p, field) match { + case Some(m) => m.stype + case None => NoType + } + case _ => NoType + }) + + override def opType: SFunc = SFunc(obj.tpe, tpe) +} + +object Select extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") +} + +/** Frontend node to represent variable names parsed in a source code. + * Should be resolved during compilation to lambda argument, Val definition or + * compilation environment value. */ +case class Ident(name: String, tpe: SType = NoType) extends Value[SType] { + override def companion = Ident + + override def opType: SFunc = SFunc(ArraySeq.empty, tpe) +} + +object Ident extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") + + def apply(name: String): Ident = Ident(name, NoType) +} + +// TODO refactor: move to sigma.ast + +/** ErgoTree node which represents application of function `func` to the given arguments. + * + * @param func expression which evaluates to a function + * @param args arguments of the function application + */ +case class Apply( + func: Value[SType], + args: IndexedSeq[Value[SType]]) extends Value[SType] { + override def companion = Apply + + override lazy val tpe : SType = func.tpe match { + case SFunc(_, r, _) => r + case tColl: SCollectionType[_] => tColl.elemType + case _ => NoType + } + + override lazy val opType: SFunc = { + val nArgs = args.length + val argTypes = new Array[SType](nArgs + 1) + argTypes(0) = func.tpe + cfor(0)(_ < nArgs, _ + 1) { i => + argTypes(i + 1) = args(i).tpe + } + SFunc(argTypes, tpe) + } + + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(Apply.costKind) + if (args.length == 1) { + val fV = func.evalTo[Any => Any](env) + val argV = args(0).evalTo[Any](env) + fV(argV) + } else { + // zero or more than 1 argument functions are not supported in v4.x, v5.0 + // see `case Terms.Apply(f, Seq(x))` in RuntimeCosting which means other cases are not supported. + syntax.error(s"Function application must have 1 argument, but was: $this") + } + } +} + +object Apply extends FixedCostValueCompanion { + override def opCode: OpCode = OpCodes.FuncApplyCode + + /** Cost of: 1) switch on the number of args 2) Scala method call 3) add args to env + * Old cost: lambdaInvoke == 30 */ + override val costKind = FixedCost(JitCost(30)) +} + +/** Apply types for type parameters of input value. */ +case class ApplyTypes( + input: Value[SType], + tpeArgs: Seq[SType]) extends Value[SType] { node => + override def companion = ApplyTypes + + override lazy val tpe: SType = input.tpe match { + case funcType: SFunc => + val subst = funcType.tpeParams.map(_.ident).zip(tpeArgs).toMap + applySubst(input.tpe, subst) + case _ => input.tpe + } + + /** This is not used as operation, but rather to form a program structure */ + override def opType: SFunc = Value.notSupportedError(this, "opType") +} + +object ApplyTypes extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") +} + +/** Frontend node to represent potential method call in a source code. + * Should be resolved during compilation to MethodCall. + * Cannot be serialized to ErgoTree. */ +case class MethodCallLike( + obj: Value[SType], + name: String, + args: IndexedSeq[Value[SType]], + tpe: SType = NoType) extends Value[SType] { + override def companion = MethodCallLike + + override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) +} + +object MethodCallLike extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") +} + +/** Represents in ErgoTree an invocation of method of the object `obj` with arguments `args`. + * The SMethod instances in STypeCompanions may have type STypeIdent in methods types, + * but valid ErgoTree should have SMethod instances specialized for specific types of + * obj and args using `specializeFor`. + * This means, if we save typeId, methodId, and we save all the arguments, + * we can restore the specialized SMethod instance. + * This work by induction, if we assume all arguments are monomorphic, + * then we can make MethodCall monomorphic. + * Thus, all ErgoTree instances are monomorphic by construction. + * + * @param obj object on which method will be invoked + * @param method method to be invoked + * @param args arguments passed to the method on invocation + * @param typeSubst a map of concrete type for each generic type parameter + */ +case class MethodCall( + obj: Value[SType], + method: SMethod, + args: IndexedSeq[Value[SType]], + typeSubst: Map[STypeVar, SType]) extends Value[SType] { + override def companion = if (args.isEmpty) PropertyCall else MethodCall + + override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) + + override val tpe: SType = method.stype match { + case f: SFunc => f.tRange.withSubstTypes(typeSubst) + case t => t.withSubstTypes(typeSubst) + } + + /** @hotspot don't beautify this code */ + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + val objV = obj.evalTo[Any](env) + addCost(MethodCall.costKind) // MethodCall overhead + method.costKind match { + case fixed: FixedCost => + val extra = method.extraDescriptors + val extraLen = extra.length + val len = args.length + val argsBuf = new Array[Any](len + extraLen) + cfor(0)(_ < len, _ + 1) { i => + argsBuf(i) = args(i).evalTo[Any](env) + } + cfor(0)(_ < extraLen, _ + 1) { i => + argsBuf(len + i) = extra(i) + } + var res: Any = null + E.addFixedCost(fixed, method.opDesc) { + res = method.invokeFixed(objV, argsBuf) + } + res + case _ => + val len = args.length + val argsBuf = new Array[Any](len + 3) + argsBuf(0) = this + argsBuf(1) = objV + cfor(0)(_ < len, _ + 1) { i => + argsBuf(i + 2) = args(i).evalTo[Any](env) + } + argsBuf(argsBuf.length - 1) = E + val evalMethod = method.genericMethod.evalMethod + evalMethod.invoke(method.objType, argsBuf.asInstanceOf[Array[AnyRef]]: _*) + } + } +} + +object MethodCall extends FixedCostValueCompanion { + override def opCode: OpCode = OpCodes.MethodCallCode + + /** Cost of: 1) packing args into Array 2) RMethod.invoke */ + override val costKind = FixedCost(JitCost(4)) + + /** Helper constructor which allows to cast the resulting node to the specified + * [[sigma.ast.Value]] type `T`. + * + * @see [[sigmastate.lang.Terms.MethodCall]] + */ + def typed[T <: SValue]( + obj: Value[SType], + method: SMethod, + args: IndexedSeq[Value[SType]], + typeSubst: Map[STypeVar, SType]): T = { + MethodCall(obj, method, args, typeSubst).asInstanceOf[T] + } +} + +object PropertyCall extends FixedCostValueCompanion { + override def opCode: OpCode = OpCodes.PropertyCallCode + + /** Cost of: 1) packing args into Array 2) RMethod.invoke */ + override val costKind = FixedCost(JitCost(4)) +} + +/** Frontend implementation of lambdas. Should be transformed to FuncValue. */ +case class Lambda( + tpeParams: Seq[STypeParam], + args: IndexedSeq[(String, SType)], + givenResType: SType, + body: Option[Value[SType]]) extends Value[SFunc] { + require(!(tpeParams.nonEmpty && body.nonEmpty), s"Generic function definitions are not supported, but found $this") + + override def companion = Lambda + + override lazy val tpe: SFunc = { + val sRange = givenResType ?: body.fold(NoType: SType)(_.tpe) + SFunc(args.map(_._2), sRange, tpeParams) + } + + /** This is not used as operation, but rather to form a program structure */ + override def opType: SFunc = SFunc(Vector(), tpe) +} + +object Lambda extends ValueCompanion { + override def opCode: OpCode = OpCodes.Undefined + + override def costKind: CostKind = Value.notSupportedError(this, "costKind") + + def apply( + args: IndexedSeq[(String, SType)], + resTpe: SType, + body: Value[SType]): Lambda = + Lambda(Nil, args, resTpe, Some(body)) + + def apply( + args: IndexedSeq[(String, SType)], + resTpe: SType, + body: Option[Value[SType]]): Lambda = + Lambda(Nil, args, resTpe, body) + + def apply( + args: IndexedSeq[(String, SType)], + body: Value[SType]): Lambda = Lambda(Nil, args, NoType, Some(body)) +} + +/** When interpreted evaluates to a ByteArrayConstant built from Context.minerPubkey */ +case object MinerPubkey extends NotReadyValueByteArray with ValueCompanion { + override def opCode: OpCode = OpCodes.MinerPubkeyCode + /** Cost of calling Context.minerPubkey Scala method. */ + override val costKind = FixedCost(JitCost(20)) + override val opType = SFunc(SContext, SCollection.SByteArray) + override def companion = this + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + E.context.minerPubKey + } +} + +/** When interpreted evaluates to a IntConstant built from Context.currentHeight */ +case object Height extends NotReadyValueInt with FixedCostValueCompanion { + override def companion = this + override def opCode: OpCode = OpCodes.HeightCode + /** Cost of: 1) Calling Context.HEIGHT Scala method. */ + override val costKind = FixedCost(JitCost(26)) + override val opType = SFunc(SContext, SInt) + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + E.context.HEIGHT + } +} + +/** When interpreted evaluates to a collection of BoxConstant built from Context.boxesToSpend */ +case object Inputs extends LazyCollection[SBox.type] with FixedCostValueCompanion { + override def companion = this + override def opCode: OpCode = OpCodes.InputsCode + /** Cost of: 1) Calling Context.INPUTS Scala method. */ + override val costKind = FixedCost(JitCost(10)) + override def tpe = SCollection.SBoxArray + override val opType = SFunc(SContext, tpe) + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + E.context.INPUTS + } +} + +/** When interpreted evaluates to a collection of BoxConstant built from Context.spendingTransaction.outputs */ +case object Outputs extends LazyCollection[SBox.type] with FixedCostValueCompanion { + override def companion = this + override def opCode: OpCode = OpCodes.OutputsCode + /** Cost of: 1) Calling Context.OUTPUTS Scala method. */ + override val costKind = FixedCost(JitCost(10)) + override def tpe = SCollection.SBoxArray + override val opType = SFunc(SContext, tpe) + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + E.context.OUTPUTS + } +} + +/** When interpreted evaluates to a AvlTreeConstant built from Context.lastBlockUtxoRoot */ +case object LastBlockUtxoRootHash extends NotReadyValueAvlTree with ValueCompanion { + override def companion = this + override def opCode: OpCode = OpCodes.LastBlockUtxoRootHashCode + + /** Cost of: 1) Calling Context.LastBlockUtxoRootHash Scala method. */ + override val costKind = FixedCost(JitCost(15)) + + override val opType = SFunc(SContext, tpe) + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + E.context.LastBlockUtxoRootHash + } +} + +/** When interpreted evaluates to a BoxConstant built from context.boxesToSpend(context.selfIndex) */ +case object Self extends NotReadyValueBox with FixedCostValueCompanion { + override def companion = this + override def opCode: OpCode = OpCodes.SelfCode + /** Cost of: 1) Calling Context.SELF Scala method. */ + override val costKind = FixedCost(JitCost(10)) + override val opType = SFunc(SContext, SBox) + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + E.context.SELF + } +} + +/** When interpreted evaluates to the singleton instance of [[sigma.Context]]. + * Corresponds to `CONTEXT` variable in ErgoScript which can be used like `CONTEXT.headers`. + */ +case object Context extends NotReadyValue[SContext.type] with ValueCompanion { + override def companion = this + override def opCode: OpCode = OpCodes.ContextCode + + /** Cost of: 1) accessing global Context instance. */ + override val costKind = FixedCost(JitCost(1)) + + override def tpe: SContext.type = SContext + override val opType: SFunc = SFunc(SUnit, SContext) + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + E.context + } +} + +/** When interpreted evaluates to the singleton instance of [[sigma.SigmaDslBuilder]]. + * Corresponds to `Global` variable in ErgoScript which can be used like `Global.groupGenerator`. + */ +case object Global extends NotReadyValue[SGlobal.type] with FixedCostValueCompanion { + override def companion = this + override def opCode: OpCode = OpCodes.GlobalCode + /** Cost of: 1) accessing Global instance. */ + override val costKind = FixedCost(JitCost(5)) + override def tpe: SGlobal.type = SGlobal + override val opType: SFunc = SFunc(SUnit, SGlobal) + protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { + addCost(this.costKind) + CSigmaDslBuilder + } +} + + diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/BcDlogGroup.scala b/data/shared/src/main/scala/sigma/crypto/BcDlogGroup.scala similarity index 98% rename from interpreter/shared/src/main/scala/sigmastate/crypto/BcDlogGroup.scala rename to data/shared/src/main/scala/sigma/crypto/BcDlogGroup.scala index 030b333032..721cd74f5e 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/BcDlogGroup.scala +++ b/data/shared/src/main/scala/sigma/crypto/BcDlogGroup.scala @@ -1,10 +1,7 @@ -package sigmastate.crypto +package sigma.crypto -import java.math.BigInteger -import sigmastate.crypto.BigIntegers import debox.cfor -import sigmastate.crypto.{CryptoContext, CryptoFacade} - +import java.math.BigInteger import scala.collection.mutable diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/BigIntegers.scala b/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala similarity index 98% rename from interpreter/shared/src/main/scala/sigmastate/crypto/BigIntegers.scala rename to data/shared/src/main/scala/sigma/crypto/BigIntegers.scala index 43bded8e09..4465184580 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/BigIntegers.scala +++ b/data/shared/src/main/scala/sigma/crypto/BigIntegers.scala @@ -1,7 +1,6 @@ -package sigmastate.crypto +package sigma.crypto import java.math.BigInteger -import sigmastate.crypto.SecureRandom /** Re-implementation in Scala of select set of utility methods from * org.bouncycastle.util.BigIntegers. diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoConstants.scala b/data/shared/src/main/scala/sigma/crypto/CryptoConstants.scala similarity index 74% rename from interpreter/shared/src/main/scala/sigmastate/crypto/CryptoConstants.scala rename to data/shared/src/main/scala/sigma/crypto/CryptoConstants.scala index 59525bec7e..682ef83076 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoConstants.scala +++ b/data/shared/src/main/scala/sigma/crypto/CryptoConstants.scala @@ -1,12 +1,9 @@ -package sigmastate.crypto +package sigma.crypto import java.math.BigInteger /** Constants used in crypto operations implementation. */ object CryptoConstants { - /** Type of group elements used in the signature scheme. */ - type EcPointType = Ecp - /** Length of encoded group element in bytes. */ val EncodedGroupElementLength: Byte = 33 @@ -14,23 +11,15 @@ object CryptoConstants { val dlogGroup: BcDlogGroup = SecP256K1Group /** Secure random generator used in the signature scheme. */ - val secureRandom: sigmastate.crypto.SecureRandom = dlogGroup.secureRandom + val secureRandom: sigma.crypto.SecureRandom = dlogGroup.secureRandom /** Size of the binary representation of any group element (2 ^ groupSizeBits == ) */ val groupSizeBits: Int = 256 - /** Number of bytes to represent any group element as byte array */ - val groupSize: Int = 256 / 8 //32 bytes /** Group order, i.e. number of elements in the group */ val groupOrder: BigInteger = dlogGroup.order - /** Length of hash function used in the signature scheme. Blake2b hash function is used. */ - val hashLengthBits = 256 - - /** Length of hash in bytes. */ - val hashLength: Int = hashLengthBits / 8 - /** A size of challenge in Sigma protocols, in bits. * If this anything but 192, threshold won't work, because we have polynomials over GF(2^192) and no others. * So DO NOT change the value without implementing polynomials over GF(2^soundnessBits) first diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/DlogGroup.scala b/data/shared/src/main/scala/sigma/crypto/DlogGroup.scala similarity index 99% rename from interpreter/shared/src/main/scala/sigmastate/crypto/DlogGroup.scala rename to data/shared/src/main/scala/sigma/crypto/DlogGroup.scala index 80ab2e372d..5c8c94ddd2 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/DlogGroup.scala +++ b/data/shared/src/main/scala/sigma/crypto/DlogGroup.scala @@ -1,4 +1,4 @@ -package sigmastate.crypto +package sigma.crypto import java.math.BigInteger diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/BigIntegerOps.scala b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala similarity index 93% rename from interpreter/shared/src/main/scala/sigmastate/eval/BigIntegerOps.scala rename to data/shared/src/main/scala/sigma/data/BigIntegerOps.scala index 058676b0fe..168b2f8266 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/BigIntegerOps.scala +++ b/data/shared/src/main/scala/sigma/data/BigIntegerOps.scala @@ -1,10 +1,9 @@ -package sigmastate.eval +package sigma.data -import sigma.data.{ExactOrderingImpl, ExactIntegral} +import sigma._ +import sigma.eval.Extensions.IntExt import scala.math.{Integral, Ordering} -import sigma._ -import sigmastate.eval.Extensions._ object OrderingOps { def apply[T](implicit ord: Ordering[T]) = ord @@ -47,8 +46,8 @@ object NumericOps { def fromInt(x: Int): BigInt = x.toBigInt def toInt(x: BigInt): Int = x.toInt def toLong(x: BigInt): Long = x.toLong - def toFloat(x: BigInt): Float = CostingSigmaDslBuilder.toBigInteger(x).floatValue() - def toDouble(x: BigInt): Double = CostingSigmaDslBuilder.toBigInteger(x).doubleValue() + def toFloat(x: BigInt): Float = CSigmaDslBuilder.toBigInteger(x).floatValue() + def toDouble(x: BigInt): Double = CSigmaDslBuilder.toBigInteger(x).doubleValue() } /** The instance of Integral for BigInt. diff --git a/data/shared/src/main/scala/sigma/data/CBox.scala b/data/shared/src/main/scala/sigma/data/CBox.scala new file mode 100644 index 0000000000..9d350c5c6b --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/CBox.scala @@ -0,0 +1,95 @@ +package sigma.data + +import org.ergoplatform.ErgoBox +import scorex.utils.Ints +import sigma.Evaluation.stypeToRType +import sigma.ast.SCollection.SByteArray +import sigma.ast.syntax._ +import sigma.ast.{ConstantNode, EvaluatedValue, SInt, STuple, SType} +import sigma.data.CBox.regs +import sigma.eval.Extensions.toAnyValue +import sigma.exceptions.InvalidType +import sigma.{AnyValue, Box, Coll, Colls} + +import java.util.Arrays + +/** A default implementation of [[Box]] interface. + * + * @see [[Box]] for detailed descriptions + */ +case class CBox(ebox: ErgoBox) extends Box with WrapperOf[ErgoBox] { + val builder = CSigmaDslBuilder + + val value = ebox.value + lazy val id : Coll[Byte] = Colls.fromArray(ebox.id) + lazy val bytes : Coll[Byte] = Colls.fromArray(ebox.bytes) + lazy val bytesWithoutRef : Coll[Byte] = Colls.fromArray(ebox.bytesWithNoRef) + lazy val propositionBytes: Coll[Byte] = Colls.fromArray(ebox.propositionBytes) + lazy val registers : Coll[AnyValue] = regs(ebox) + + override def wrappedValue: ErgoBox = ebox + + override def getReg[T](i: Int)(implicit tT: RType[T]): Option[T] = { + if (i < 0 || i >= registers.length) return None + val value = registers(i) + if (value != null) { + // once the value is not null it should be of the right type + value match { + case value: CAnyValue[_] if value.value != null && value.tA == tT => + Some(value.value.asInstanceOf[T]) + case _ => + throw new InvalidType(s"Cannot getReg[${tT.name}]($i): invalid type of value $value at id=$i") + } + } else None + } + + override def creationInfo: (Int, Coll[Byte]) = { + this.getReg[(Int, Coll[Byte])](3).get.asInstanceOf[Any] match { + case info: Tuple2[Int, Coll[Byte]]@unchecked => info + case ConstantNode(arr: Array[Any], STuple(IndexedSeq(SInt, SByteArray))) if arr.length == 2 => + (arr(0).asInstanceOf[Int], builder.Colls.fromArray(arr(1).asInstanceOf[Array[Byte]])) + case v => + sys.error(s"Invalid value $v of creationInfo register R3") + } + } + + override def tokens: Coll[(Coll[Byte], Long)] = { + this.getReg[Coll[(Coll[Byte], Long)]](ErgoBox.R2.asIndex).get + } + + override def executeFromRegister[T](regId: Byte) + (implicit cT: RType[T]): T = ??? // TODO implement + + override def hashCode(): Int = Ints.fromByteArray(id.toArray) + + override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj != null && { + obj match { + case obj: Box => Arrays.equals(id.toArray, obj.id.toArray) + case _ => + // this case was missing in v4.x, however has never been a problem + // Thus, v5.0 interpreter will not fail (while v4.x would fail here) + false + } + }) +} + +object CBox { + def regs(ebox: ErgoBox): Coll[AnyValue] = { + val res = new Array[AnyValue](ErgoBox.maxRegisters) + + def checkNotYetDefined(id: Int, newValue: SValue) = + require(res(id) == null, s"register $id is defined more then once: previous value ${res(id)}, new value $newValue") + + for ( (k, v: EvaluatedValue[t]) <- ebox.additionalRegisters ) { + checkNotYetDefined(k.number, v) + res(k.number) = toAnyValue(v.value)(stypeToRType(v.tpe)) + } + for ( r <- ErgoBox.mandatoryRegisters ) { + val regId = r.number + val v = ebox.get(r).get.asInstanceOf[EvaluatedValue[SType]] + checkNotYetDefined(regId, v) + res(regId) = toAnyValue(v.value)(stypeToRType(v.tpe)) + } + Colls.fromArray(res) + } +} \ No newline at end of file diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala new file mode 100644 index 0000000000..3938feacd3 --- /dev/null +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -0,0 +1,206 @@ +package sigma.data + +import debox.cfor +import org.ergoplatform.ErgoBox +import org.ergoplatform.validation.ValidationRules +import scorex.crypto.hash.{Blake2b256, Sha256} +import scorex.utils.Longs +import sigma.ast.{AtLeast, SubstConstants} +import sigma.crypto.{CryptoConstants, EcPointType, Ecp} +import sigma.eval.Extensions.EvalCollOps +import sigma.serialization.{GroupElementSerializer, SigmaSerializer} +import sigma.util.Extensions.BigIntegerOps +import sigma.validation.SigmaValidationSettings +import sigma.{AvlTree, BigInt, Box, Coll, CollBuilder, GroupElement, SigmaDslBuilder, SigmaProp, VersionContext} + +import java.math.BigInteger + +/** A default implementation of [[SigmaDslBuilder]] interface. + * + * @see [[SigmaDslBuilder]] for detailed descriptions + */ +class CSigmaDslBuilder extends SigmaDslBuilder { dsl => + implicit val validationSettings: SigmaValidationSettings = ValidationRules.currentSettings + + override val Colls: CollBuilder = sigma.Colls + + override def BigInt(n: BigInteger): BigInt = CBigInt(n) + + override def toBigInteger(n: BigInt): BigInteger = n.asInstanceOf[CBigInt].wrappedValue + + /** Wraps the given elliptic curve point into GroupElement type. */ + def GroupElement(p: Ecp): GroupElement = p match { + case ept: EcPointType => CGroupElement(ept) + case m => sys.error(s"Point of type ${m.getClass} is not supported") + } + + /** Wraps the given sigma proposition into SigmaDsl value of type SigmaProp. */ + def SigmaProp(sigmaTree: SigmaBoolean): SigmaProp = new CSigmaProp(sigmaTree) + + /** Extract `sigma.data.SigmaBoolean` from DSL's `SigmaProp` type. */ + @inline def toSigmaBoolean(p: SigmaProp): SigmaBoolean = p.asInstanceOf[CSigmaProp].sigmaTree + + /** Extract `sigmastate.AvlTreeData` from DSL's `AvlTree` type. */ + def toAvlTreeData(p: AvlTree): AvlTreeData = p.asInstanceOf[CAvlTree].treeData + + /** Extract `sigmastate.crypto.Ecp` from DSL's `GroupElement` type. */ + def toECPoint(ge: GroupElement): Ecp = ge.asInstanceOf[CGroupElement].wrappedValue + + /** Creates a new AvlTree instance with the given parameters. + * + * @see AvlTreeData for details + */ + override def avlTree( + operationFlags: Byte, + digest: Coll[Byte], + keyLength: Int, + valueLengthOpt: Option[Int]): CAvlTree = { + val treeData = AvlTreeData(digest, AvlTreeFlags(operationFlags), keyLength, valueLengthOpt) + CAvlTree(treeData) + } + + /** Wraps the given tree data into SigmaDsl value of type [[AvlTree]]. */ + def avlTree(treeData: AvlTreeData): AvlTree = { + CAvlTree(treeData) + } + + /** Wraps the given [[ErgoBox]] into SigmaDsl value of type [[Box]]. + * + * @param ebox the value to be wrapped + * @see [[sigmastate.SBox]], [[sigma.Box]] + */ + def Box(ebox: ErgoBox): Box = CBox(ebox) + + /** Extracts [[ErgoBox]] from the given [[Box]] instance. This is inverse to the Box method. */ + def toErgoBox(b: Box): ErgoBox = b.asInstanceOf[CBox].ebox + + /** HOTSPOT: don't beautify this code */ + private def toSigmaTrees(props: Array[SigmaProp]): Array[SigmaBoolean] = { + val len = props.length + val res = new Array[SigmaBoolean](len) + cfor(0)(_ < len, _ + 1) { i => + res(i) = toSigmaBoolean(props(i)) + } + res + } + + @inline private def toEcPointType(ge: GroupElement): EcPointType = + toECPoint(ge).asInstanceOf[EcPointType] + + override def atLeast(bound: Int, props: Coll[SigmaProp]): SigmaProp = { + if (props.length > AtLeast.MaxChildrenCount) + throw new IllegalArgumentException(s"Expected input elements count should not exceed ${AtLeast.MaxChildrenCount}, actual: ${props.length}") + val sigmaTrees = toSigmaTrees(props.toArray) + val tree = AtLeast.reduce(bound, sigmaTrees) + CSigmaProp(tree) + } + + override def allOf(conditions: Coll[Boolean]): Boolean = + conditions.forall(c => c) + + override def anyOf(conditions: Coll[Boolean]): Boolean = + conditions.exists(c => c) + + override def xorOf(conditions: Coll[Boolean]): Boolean = { + if (VersionContext.current.isJitActivated) { + val len = conditions.length + if (len == 0) false + else if (len == 1) conditions(0) + else { + var res = conditions(0) + cfor(1)(_ < len, _ + 1) { i => + res ^= conditions(i) + } + res + } + } else { + // This is buggy version used in v4.x interpreter (for ErgoTrees v0, v1) + conditions.toArray.distinct.length == 2 + } + } + + override def allZK(props: Coll[SigmaProp]): SigmaProp = { + val sigmaTrees = toSigmaTrees(props.toArray) + val tree = CAND.normalized(sigmaTrees) + CSigmaProp(tree) + } + + override def anyZK(props: Coll[SigmaProp]): SigmaProp = { + val sigmaTrees = toSigmaTrees(props.toArray) + val tree = COR.normalized(sigmaTrees) + CSigmaProp(tree) + } + + override def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] = + Colls.xor(l, r) + + override def sigmaProp(b: Boolean): SigmaProp = { + CSigmaProp(TrivialProp(b)) + } + + override def blake2b256(bytes: Coll[Byte]): Coll[Byte] = { + val h = Blake2b256.hash(bytes.toArray) + Colls.fromArray(h) + } + + override def sha256(bytes: Coll[Byte]): Coll[Byte] = { + val h = Sha256.hash(bytes.toArray) + Colls.fromArray(h) + } + + override def byteArrayToBigInt(bytes: Coll[Byte]): BigInt = { + val bi = new BigInteger(bytes.toArray).to256BitValueExact + this.BigInt(bi) + } + + override def longToByteArray(l: Long): Coll[Byte] = + Colls.fromArray(Longs.toByteArray(l)) + + override def byteArrayToLong(bytes: Coll[Byte]): Long = + Longs.fromByteArray(bytes.toArray) + + override def proveDlog(ge: GroupElement): SigmaProp = + CSigmaProp(ProveDlog(toECPoint(ge).asInstanceOf[EcPointType])) + + override def proveDHTuple( + g: GroupElement, + h: GroupElement, + u: GroupElement, + v: GroupElement): SigmaProp = { + val dht = ProveDHTuple(toEcPointType(g), toEcPointType(h), toEcPointType(u), toEcPointType(v)) + CSigmaProp(dht) + } + + private lazy val _generatorElement = this.GroupElement(CryptoConstants.dlogGroup.generator) + + override def groupGenerator: GroupElement = _generatorElement + + /** + * @return the identity of the Dlog group used in ErgoTree + */ + def groupIdentity: GroupElement = { + this.GroupElement(CryptoConstants.dlogGroup.identity) + } + + override def substConstants[T]( + scriptBytes: Coll[Byte], + positions: Coll[Int], + newValues: Coll[T]): Coll[Byte] = { + val constants = try newValues.toArrayOfConstants + catch { + case e: Throwable => + throw new RuntimeException(s"Cannot evaluate substConstants($scriptBytes, $positions, $newValues)", e) + } + val (res, _) = SubstConstants.eval(scriptBytes.toArray, positions.toArray, constants)(validationSettings) + Colls.fromArray(res) + } + + override def decodePoint(encoded: Coll[Byte]): GroupElement = { + val r = SigmaSerializer.startReader(encoded.toArray) + val p = GroupElementSerializer.parse(r) + this.GroupElement(p) + } +} + +/** Default singleton instance of Global object, which implements global ErgoTree functions. */ +object CSigmaDslBuilder extends CSigmaDslBuilder \ No newline at end of file diff --git a/interpreter/shared/src/main/scala/sigmastate/DataValueComparer.scala b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala similarity index 97% rename from interpreter/shared/src/main/scala/sigmastate/DataValueComparer.scala rename to data/shared/src/main/scala/sigma/data/DataValueComparer.scala index 0bba6c0dc4..21ca85012f 100644 --- a/interpreter/shared/src/main/scala/sigmastate/DataValueComparer.scala +++ b/data/shared/src/main/scala/sigma/data/DataValueComparer.scala @@ -1,14 +1,10 @@ -package sigmastate +package sigma.data -import sigma.data.{AVHashMap, CollOverArray, Nullable, PairOfCols, RType} import debox.{cfor, sp} -import sigmastate.Values.SigmaBoolean -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.ProveDHTuple -import sigmastate.eval.SigmaDsl -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.interpreter.{ErgoTreeEvaluator, NamedDesc, OperationCostInfo} import sigma._ +import sigma.ast.{FixedCost, JitCost, NamedDesc, OperationCostInfo, PerItemCost} +import sigma.crypto.EcPointType +import sigma.eval.{ErgoTreeEvaluator, SigmaDsl} /** Implementation of data equality for two arbitrary ErgoTree data types. * @see [[DataValueComparer.equalDataValues]] @@ -279,7 +275,7 @@ object DataValueComparer { val sb2 = r.asInstanceOf[CTHRESHOLD] k == sb2.k && equalSigmaBooleans(children, sb2.children) case _ => - ErgoTreeEvaluator.error( + sys.error( s"Cannot compare SigmaBoolean values $l and $r: unknown type") } } @@ -408,7 +404,7 @@ object DataValueComparer { okEqual = r.isInstanceOf[Unit] case _ => - ErgoTreeEvaluator.error(s"Cannot compare $l and $r: unknown type") + sys.error(s"Cannot compare $l and $r: unknown type") } okEqual } diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/ExactIntegral.scala b/data/shared/src/main/scala/sigma/data/ExactIntegral.scala similarity index 100% rename from interpreter/shared/src/main/scala/sigmastate/eval/ExactIntegral.scala rename to data/shared/src/main/scala/sigma/data/ExactIntegral.scala diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/ExactNumeric.scala b/data/shared/src/main/scala/sigma/data/ExactNumeric.scala similarity index 100% rename from interpreter/shared/src/main/scala/sigmastate/eval/ExactNumeric.scala rename to data/shared/src/main/scala/sigma/data/ExactNumeric.scala diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/ExactOrdering.scala b/data/shared/src/main/scala/sigma/data/ExactOrdering.scala similarity index 90% rename from interpreter/shared/src/main/scala/sigmastate/eval/ExactOrdering.scala rename to data/shared/src/main/scala/sigma/data/ExactOrdering.scala index 7c65b53075..a7b002cb80 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/ExactOrdering.scala +++ b/data/shared/src/main/scala/sigma/data/ExactOrdering.scala @@ -1,6 +1,6 @@ package sigma.data -import scala.math.Numeric.{ByteIsIntegral, LongIsIntegral, ShortIsIntegral, IntIsIntegral} +import scala.math.Numeric.{ByteIsIntegral, IntIsIntegral, LongIsIntegral, ShortIsIntegral} /** Ordering operations to be used with other Exact traits. * All methods are implemented by delegating to the corresponding Ordering instance from diff --git a/data/shared/src/main/scala/sigma/eval/AvlTreeVerifier.scala b/data/shared/src/main/scala/sigma/eval/AvlTreeVerifier.scala new file mode 100644 index 0000000000..bc4a625458 --- /dev/null +++ b/data/shared/src/main/scala/sigma/eval/AvlTreeVerifier.scala @@ -0,0 +1,76 @@ +package sigma.eval + +import scala.util.Try + +/** Represents a verifier of authenticated AVL+ tree created from a proof. + * The verifier holds the tree data parsed from the proof. + */ +trait AvlTreeVerifier { + /** Height of the tree. */ + def treeHeight: Int + + /** Looks up a key in the tree. + * If `key` exists in the tree and the operation succeeds, + * returns `Success(Some(v))`, where v is the value associated with `key`. + * If `key` does not exists in the tree and the operation succeeds, returns Success(None). + * Returns Failure if the operation fails or the proof does not verify. + * After one failure, all subsequent operations with this verifier will fail and digest + * is None. + * + * @param key key to look up + * @return Success(Some(value)), Success(None), or Failure + */ + def performLookup(key: Array[Byte]): Try[Option[Array[Byte]]] + + /** Check the key-value pair has been inserted in the tree. + * If `key` exists in the tree and the operation succeeds, + * returns `Success(Some(value))`, where value is associated with `key`. + * If `key` does not exists in the tree and the operation succeeds, returns Success(None). + * Returns Failure if the operation fails or the proof does not verify. + * After one failure, all subsequent operations with this verifier will fail and digest + * is None. + * + * @param key key to look up + * @param value value to check it was inserted + * @return Success(Some(inserted value)), Success(None), or Failure + */ + def performInsert(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] + + /** Check the key-value pair has been updated in the tree. + * If `key` exists in the tree and the operation succeeds, + * returns `Success(Some(value))`, where value is associated with `key`. + * If `key` does not exists in the tree and the operation succeeds, returns Success(None). + * Returns Failure if the operation fails or the proof does not verify. + * After one failure, all subsequent operations with this verifier will fail and digest + * is None. + * + * @param key key to look up + * @param value value to check it was updated + * @return Success(Some(value)), Success(None), or Failure + */ + def performUpdate(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] + + /** Check the key has been removed in the tree. + * If `key` exists in the tree and the operation succeeds, + * returns `Success(Some(v))`, where v is old value associated with `key`. + * If `key` does not exists in the tree and the operation succeeds, returns Success(None). + * Returns Failure if the operation fails or the proof does not verify. + * After one failure, all subsequent operations with this verifier will fail and digest + * is None. + * + * @param key key to look up + * @return Success(Some(old value)), Success(None), or Failure + */ + def performRemove(key: Array[Byte]): Try[Option[Array[Byte]]] + + /** + * Returns Some(d), where d - the current digest of the authenticated data structure. + * The digest contains the root hash and the root height. + * + * Returns None if the proof verification failed at construction + * or during any of the operations with this verifier. + * + * @return - Some[digest] or None + */ + def digest: Option[Array[Byte]] +} diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/CostDetails.scala b/data/shared/src/main/scala/sigma/eval/CostDetails.scala similarity index 97% rename from interpreter/shared/src/main/scala/sigmastate/interpreter/CostDetails.scala rename to data/shared/src/main/scala/sigma/eval/CostDetails.scala index 6bffb8ff8e..89d522833b 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/CostDetails.scala +++ b/data/shared/src/main/scala/sigma/eval/CostDetails.scala @@ -1,8 +1,7 @@ -package sigmastate.interpreter +package sigma.eval -import sigmastate.JitCost import debox.cfor - +import sigma.ast.{CostItem, JitCost} import scala.collection.compat.immutable.ArraySeq /** Abstract representation of cost results obtained during evaluation. */ diff --git a/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala b/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala new file mode 100644 index 0000000000..52f839354c --- /dev/null +++ b/data/shared/src/main/scala/sigma/eval/ErgoTreeEvaluator.scala @@ -0,0 +1,146 @@ +package sigma.eval + +import sigma.{AvlTree, Coll, Context} +import sigma.ast.{Constant, FixedCost, MethodCall, OperationCostInfo, OperationDesc, PerItemCost, SType, TypeBasedCost} +import sigma.data.KeyValueColl + +abstract class ErgoTreeEvaluator { + /** Adds the given cost to the `coster`. If tracing is enabled, creates a new cost item + * with the given operation. + * + * @param costKind the cost to be added to `coster` for each item + * @param nItems the number of items + * @param opDesc the operation to associate the cost with (when costTracingEnabled) + * @param block operation executed under the given cost + * @tparam R result type of the operation + * @hotspot don't beautify the code + */ + def addSeqCost[R](costKind: PerItemCost, nItems: Int, opDesc: OperationDesc) + (block: () => R): R + + /** Adds the cost to the `coster`. See the other overload for details. */ + def addSeqCost[R](costInfo: OperationCostInfo[PerItemCost], nItems: Int) + (block: () => R): R + + /** Adds the cost to the `coster`. If tracing is enabled, creates a new cost item with + * the given operation descriptor and cost kind. If time measuring is enabled also + * performs profiling. + * + * WARNING: The cost is accumulated AFTER the block is executed. + * Each usage of this method should be accompanied with a proof of why this cannot lead + * to unbounded execution (see all usages). + * + * @param costKind the cost descriptor to be used to compute the cost based on the + * actual number of items returned by the `block` + * @param opDesc the operation to associate the cost with (when costTracingEnabled) + * @param block operation executed under the given cost descriptors, returns the + * actual number of items processed + * @hotspot don't beautify the code + */ + def addSeqCost(costKind: PerItemCost, opDesc: OperationDesc)(block: () => Int): Unit + + /** Adds the cost to the `coster`. See the other overload for details. */ + def addSeqCost(costInfo: OperationCostInfo[PerItemCost])(block: () => Int): Unit + + /** Adds the given cost to the `coster`. If tracing is enabled, associates the cost with + * the given operation. + * + * @param costKind kind of the cost to be added to `coster` + * @param opDesc operation descriptor to associate the cost with (when costTracingEnabled) + */ + def addCost(costKind: FixedCost, opDesc: OperationDesc): Unit + + def addCost(costInfo: OperationCostInfo[FixedCost]): Unit + + /** Adds the given cost to the `coster`. If tracing is enabled, associates the cost with + * the given operation. + * + * @param costKind kind of the cost to be added to `coster` + * @param opDesc the operation descriptor to associate the cost with (when costTracingEnabled) + * @param block operation executed under the given cost + */ + def addFixedCost(costKind: FixedCost, opDesc: OperationDesc)(block: => Unit): Unit + + def addFixedCost(costInfo: OperationCostInfo[FixedCost])(block: => Unit): Unit + + /** Adds the given cost to the `coster`. If tracing is enabled, creates a new cost item + * with the given operation. + * + * @param costKind the cost to be added to `coster` for each item + * @param nItems the number of items + * @param opDesc the operation to associate the cost with (when costTracingEnabled) + */ + def addSeqCostNoOp(costKind: PerItemCost, nItems: Int, opDesc: OperationDesc): Unit + + /** Add the cost given by the cost descriptor and the type to the accumulator and + * associate it with this operation descriptor. + * + * @param costKind descriptor of the cost + * @param tpe specific type for which the cost should be computed by this descriptor + * (see costFunc method) + * @param opDesc operation which is associated with this cost + */ + def addTypeBasedCost[R]( + costKind: TypeBasedCost, + tpe: SType, opDesc: OperationDesc)(block: () => R): R + + /** Settings to be used during evaluation. */ + def settings: EvalSettings + + /** Performs operations profiling and time measurements (if enabled in settings). */ + def profiler: Profiler + + /** Segregated constants from ErgoTree, to lookup them from + * [[ConstantPlaceholder]] evaluation. + */ + def constants: Seq[Constant[SType]] + + /** Represents blockchain data context for ErgoTree evaluation. */ + def context: Context + + /** Create an instance of [[AvlTreeVerifier]] for the given tree and proof. */ + def createTreeVerifier(tree: AvlTree, proof: Coll[Byte]): AvlTreeVerifier + + /** Implements evaluation of AvlTree.contains method call ErgoTree node. */ + def contains_eval( + mc: MethodCall, + tree: AvlTree, + key: Coll[Byte], + proof: Coll[Byte]): Boolean + + /** Implements evaluation of AvlTree.get method call ErgoTree node. */ + def get_eval( + mc: MethodCall, + tree: AvlTree, + key: Coll[Byte], + proof: Coll[Byte]): Option[Coll[Byte]] + + /** Implements evaluation of AvlTree.getMany method call ErgoTree node. */ + def getMany_eval( + mc: MethodCall, + tree: AvlTree, + keys: Coll[Coll[Byte]], + proof: Coll[Byte]): Coll[Option[Coll[Byte]]] + + /** Implements evaluation of AvlTree.insert method call ErgoTree node. */ + def insert_eval( + mc: MethodCall, + tree: AvlTree, + entries: KeyValueColl, + proof: Coll[Byte]): Option[AvlTree] + + /** Implements evaluation of AvlTree.update method call ErgoTree node. */ + def update_eval( + mc: MethodCall, tree: AvlTree, + operations: KeyValueColl, proof: Coll[Byte]): Option[AvlTree] + + /** Implements evaluation of AvlTree.remove method call ErgoTree node. */ + def remove_eval( + mc: MethodCall, tree: AvlTree, + operations: Coll[Coll[Byte]], proof: Coll[Byte]): Option[AvlTree] +} + +object ErgoTreeEvaluator { + /** Immutable data environment used to assign data values to graph nodes. */ + type DataEnv = Map[Int, Any] +} \ No newline at end of file diff --git a/data/shared/src/main/scala/sigma/eval/EvalSettings.scala b/data/shared/src/main/scala/sigma/eval/EvalSettings.scala new file mode 100644 index 0000000000..6567d0cdec --- /dev/null +++ b/data/shared/src/main/scala/sigma/eval/EvalSettings.scala @@ -0,0 +1,83 @@ +package sigma.eval + +import sigma.eval.EvalSettings.EvaluationMode +import supertagged.TaggedType + +/** Configuration parameters of the evaluation run. */ +case class EvalSettings( + /** Used together with [[ErgoTreeEvaluator.profiler]] to measure individual operations timings. */ + isMeasureOperationTime: Boolean, + + /** Used together with [[ErgoTreeEvaluator.profiler]] to measure script timings. */ + isMeasureScriptTime: Boolean, + + /** Used by [[ErgoTreeEvaluator]] to conditionally perform debug mode operations. */ + isDebug: Boolean = false, + + /** Used by [[ErgoTreeEvaluator]] to conditionally emit log messages. */ + isLogEnabled: Boolean = false, + + /** Used by [[ErgoTreeEvaluator]] to conditionally build a trace of added costs. + * + * @see Value.addCost + */ + costTracingEnabled: Boolean = false, + + /** Profiler which, when defined, should be used in [[ErgoTreeEvaluator]] constructor. */ + profilerOpt: Option[Profiler] = None, + + /** Should be set to true, if evaluation is performed as part of test suite. + * In such a case, additional operations may be performed (such as sanity checks). */ + isTestRun: Boolean = false, + + /** If true, then expected test vectors are pretty-printed. */ + printTestVectors: Boolean = false, + + /** When Some(mode) is specified then it defines which version of the Interpreter.verify + * and Interpreter.prove methods should use. + * The default value is None, which means the version is defined by ErgoTree.version + * and Context.activatedScriptVersion. + */ + evaluationMode: Option[EvaluationMode] = None, + + /** Maximum execution cost of a script used by profiler. + * + * @see ErgoTreeEvaluator + */ + scriptCostLimitInEvaluator: Int = 1000000 +) + +object EvalSettings { + /** Enumeration type of evaluation modes of [[Interpreter]]. + * This type can be removed in v5.x releases together with AOT implementation once v5.0 + * protocol is activated. + */ + object EvaluationMode extends TaggedType[Int] { + implicit class EvaluationModeOps(val x: EvaluationMode) extends AnyVal { + def name: String = x match { + case AotEvaluationMode => "AotEvaluationMode" + case JitEvaluationMode => "JitEvaluationMode" + } + + /** Returns true if AOT interpreter should be evaluated. */ + def okEvaluateAot: Boolean = { + x == AotEvaluationMode + } + + /** Returns true if JIT interpreter should be evaluated. */ + def okEvaluateJit: Boolean = { + x == JitEvaluationMode + } + } + } + + type EvaluationMode = EvaluationMode.Type + + /** Evaluation mode when the interpreter is executing using AOT costing implementation + * of v4.x protocol. */ + val AotEvaluationMode: EvaluationMode = EvaluationMode @@ 1 // first bit + + /** Evaluation mode when the interpreter is executing using JIT costing implementation + * of v5.x protocol. */ + val JitEvaluationMode: EvaluationMode = EvaluationMode @@ 2 // second bit +} \ No newline at end of file diff --git a/data/shared/src/main/scala/sigma/eval/Extensions.scala b/data/shared/src/main/scala/sigma/eval/Extensions.scala new file mode 100644 index 0000000000..def9086e02 --- /dev/null +++ b/data/shared/src/main/scala/sigma/eval/Extensions.scala @@ -0,0 +1,75 @@ +package sigma.eval + +import sigma.ast.syntax.SigmaPropValue +import sigma.data.{CAnyValue, CSigmaDslBuilder, Nullable, RType, SigmaBoolean} +import sigma.{BigInt, Coll, Colls, Evaluation, Platform} +import sigma.ast.{Constant, ConstantNode, SBoolean, SCollection, SCollectionType, SType, SigmaPropConstant, SigmaPropIsProven, TransformingSigmaBuilder, Value} + +import java.math.BigInteger + +object Extensions { + implicit class ByteExt(val b: Byte) extends AnyVal { + @inline def toBigInt: BigInt = CSigmaDslBuilder.BigInt(BigInteger.valueOf(b.toLong)) + } + + implicit class ShortExt(val b: Short) extends AnyVal { + @inline def toBigInt: BigInt = CSigmaDslBuilder.BigInt(BigInteger.valueOf(b.toLong)) + } + + implicit class IntExt(val x: Int) extends AnyVal { + /** Convert this value to BigInt. */ + @inline def toBigInt: BigInt = CSigmaDslBuilder.BigInt(BigInteger.valueOf(x.toLong)) + } + + implicit class LongExt(val x: Long) extends AnyVal { + /** Convert this value to BigInt. */ + @inline def toBigInt: BigInt = CSigmaDslBuilder.BigInt(BigInteger.valueOf(x)) + } + + def toAnyValue[A: RType](x: A) = new CAnyValue(x, RType[A].asInstanceOf[RType[Any]]) + + implicit class EvalCollOps[T](val coll: Coll[T]) extends AnyVal { + /** Helper type synonym. */ + type ElemTpe = SType {type WrappedType = T} + + /** Wraps the collection into ConstantNode using collection's element type. */ + def toConstant: Constant[SCollection[ElemTpe]] = { + val elemTpe = Evaluation.rtypeToSType(coll.tItem).asInstanceOf[ElemTpe] + ConstantNode[SCollection[ElemTpe]](coll, SCollectionType(elemTpe)) + } + + /** Transforms this collection into array of constants. + * + * This method have exactly the same semantics on JS and JVM IF `coll.tItem` + * precisely describes the type of elements in `call`. (Which is the case for all + * collections created by ErgoTree interpreter). + * + * However, if it is not the case, then JVM and JS will have different semantics for Byte and Short. + * + * The JVM version preserves v5.0 consensus protocol semantics. + * The JS version is a reasonable approximation of the JVM version. + */ + def toArrayOfConstants: Array[Constant[SType]] = { + val constants = coll.toArray.map { v => + // see ScalaDoc for ensureTypeCarringValue + val valToLift = ensureTypeCarringValue(v, coll.tItem.asInstanceOf[RType[Any]]) + // call platform-specific method to transform the value to a Constant + Platform.liftToConstant(valToLift, TransformingSigmaBuilder) match { + case Nullable(c) => c + case _ => sys.error(s"Cannot liftToConstant($valToLift)") + } + } + constants + } + } + + implicit class SigmaBooleanOps(val sb: SigmaBoolean) extends AnyVal { + def toSigmaPropValue: SigmaPropValue = SigmaPropConstant(sb) + + def isProven: Value[SBoolean.type] = SigmaPropIsProven(SigmaPropConstant(sb)) + } + + implicit class EvalIterableOps[T: RType](seq: Iterable[T]) { + @inline def toColl: Coll[T] = Colls.fromArray[T](seq.toArray(RType[T].classTag)) + } +} diff --git a/data/shared/src/main/scala/sigma/eval/Profiler.scala b/data/shared/src/main/scala/sigma/eval/Profiler.scala new file mode 100644 index 0000000000..ac4c7a299d --- /dev/null +++ b/data/shared/src/main/scala/sigma/eval/Profiler.scala @@ -0,0 +1,28 @@ +package sigma.eval + +import sigma.ast.{CostItem, JitCost} +import sigma.ast.syntax.SValue + +abstract class Profiler { + /** Called from evaluator immediately before the evaluator start recursive evaluation of + * the given node. + */ + def onBeforeNode(node: SValue): Unit + + /** Called from evaluator immediately after the evaluator finishes recursive evaluation + * of the given node. + */ + def onAfterNode(node: SValue): Unit + + /** Add new measurement point to the stats of this profiler. + * + * @param costItem executed cost item + * @param time time spent to execute this cost item + */ + def addCostItem(costItem: CostItem, time: Long): Unit + + /** Adds estimated cost and actual measured time data point to the StatCollection for + * the given script. + */ + def addJitEstimation(script: String, cost: JitCost, actualTimeNano: Long): Unit +} diff --git a/data/shared/src/main/scala/sigma/eval/package.scala b/data/shared/src/main/scala/sigma/eval/package.scala new file mode 100644 index 0000000000..d2c6415fde --- /dev/null +++ b/data/shared/src/main/scala/sigma/eval/package.scala @@ -0,0 +1,30 @@ +package sigma + +import sigma.data.{CAnyValue, CSigmaDslBuilder, OverloadHack, RType} + +package object eval { + /** The primary reference to Global instance of SigmaDsl. + * Besides operations of SigmaDslBuilder class, this instance also contains methods, + * which are not available in Dsl code, and which are not in SigmaDslBuilder interface. + * For example methods like `Box`, `toErgoBox` are available here, but not available in Dsl. + * + * @see SigmaDslBuilder + */ + val SigmaDsl = CSigmaDslBuilder + + /** Encapsulate platform-specific logic of ensuring the value carries its precise type. + * For JVM this is identity function. + * For JS it can transform to AnyValue, if the type is numeric + */ + def ensureTypeCarringValue(v: Any, tT: RType[Any]): Any = + if (Environment.current.isJVM) v + else { // JS + v match { + case _: Byte | _: Short | _: Int => + // this is necessary for JS where Byte, Short, Int have the same runtime class + // and hence we need to pass the type information explicitly + CAnyValue(v)(tT, OverloadHack.overloaded1) + case _ => v + } + } +} diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/CompilerExceptions.scala b/data/shared/src/main/scala/sigma/exceptions/CompilerExceptions.scala similarity index 95% rename from interpreter/shared/src/main/scala/sigmastate/exceptions/CompilerExceptions.scala rename to data/shared/src/main/scala/sigma/exceptions/CompilerExceptions.scala index 8c331766af..8b650256a9 100644 --- a/interpreter/shared/src/main/scala/sigmastate/exceptions/CompilerExceptions.scala +++ b/data/shared/src/main/scala/sigma/exceptions/CompilerExceptions.scala @@ -1,6 +1,7 @@ -package sigmastate.exceptions +package sigma.exceptions -import sigmastate.lang.SourceContext +import sigma.SigmaException +import sigma.ast.SourceContext /** Base class for exceptions thrown by the compiler. * diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/ConstraintFailed.scala b/data/shared/src/main/scala/sigma/exceptions/ConstraintFailed.scala similarity index 66% rename from interpreter/shared/src/main/scala/sigmastate/exceptions/ConstraintFailed.scala rename to data/shared/src/main/scala/sigma/exceptions/ConstraintFailed.scala index bddd2a6951..eb77f8c31b 100644 --- a/interpreter/shared/src/main/scala/sigmastate/exceptions/ConstraintFailed.scala +++ b/data/shared/src/main/scala/sigma/exceptions/ConstraintFailed.scala @@ -1,6 +1,6 @@ -package sigmastate.exceptions +package sigma.exceptions -import sigmastate.lang.SourceContext +import sigma.ast.SourceContext final class ConstraintFailed(message: String, source: Option[SourceContext] = None) extends BuilderException(message, source) diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/Exceptions.scala b/data/shared/src/main/scala/sigma/exceptions/Exceptions.scala similarity index 73% rename from interpreter/shared/src/main/scala/sigmastate/eval/Exceptions.scala rename to data/shared/src/main/scala/sigma/exceptions/Exceptions.scala index 2949fb6815..a1584d8d71 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/Exceptions.scala +++ b/data/shared/src/main/scala/sigma/exceptions/Exceptions.scala @@ -1,4 +1,4 @@ -package sigmastate.eval +package sigma.exceptions final class InvalidType(message: String) extends Exception(message) diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/InvalidArguments.scala b/data/shared/src/main/scala/sigma/exceptions/InvalidArguments.scala similarity index 65% rename from interpreter/shared/src/main/scala/sigmastate/exceptions/InvalidArguments.scala rename to data/shared/src/main/scala/sigma/exceptions/InvalidArguments.scala index b154fdb6d4..38db5ceeec 100644 --- a/interpreter/shared/src/main/scala/sigmastate/exceptions/InvalidArguments.scala +++ b/data/shared/src/main/scala/sigma/exceptions/InvalidArguments.scala @@ -1,6 +1,6 @@ -package sigmastate.exceptions +package sigma.exceptions -import sigmastate.lang.SourceContext +import sigma.ast.SourceContext final class InvalidArguments(message: String, source: Option[SourceContext] = None) extends BinderException(message, source) diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaExceptions.scala b/data/shared/src/main/scala/sigma/exceptions/SigmaExceptions.scala similarity index 61% rename from interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaExceptions.scala rename to data/shared/src/main/scala/sigma/exceptions/SigmaExceptions.scala index 8014fccda9..befb497dde 100644 --- a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaExceptions.scala +++ b/data/shared/src/main/scala/sigma/exceptions/SigmaExceptions.scala @@ -1,24 +1,7 @@ -package sigmastate.exceptions +package sigma.exceptions -import sigmastate.JitCost - -/** Base class for Sigma-related exceptions. - * - * @param message the error message - * @param cause an optional cause for the exception - */ -class SigmaException(val message: String, val cause: Option[Throwable] = None) - extends Exception(message, cause.orNull) - -/** Exception thrown during serialization. - * - * @param message the error message - * @param cause an optional cause for the exception - */ -case class SerializerException( - override val message: String, - override val cause: Option[Throwable] = None) - extends SigmaException(message, cause) +import sigma.SigmaException +import sigma.ast.JitCost /** Exception thrown by [[sigmastate.interpreter.Interpreter]]. * diff --git a/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala new file mode 100644 index 0000000000..e8cdb7d709 --- /dev/null +++ b/data/shared/src/main/scala/sigma/interpreter/ContextExtension.scala @@ -0,0 +1,49 @@ +package sigma.interpreter + +import sigma.ast.{EvaluatedValue, SType} +import sigma.interpreter.ContextExtension.VarBinding +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} + +/** + * User-defined variables to be put into context. + * Each variable is identified by `id: Byte` and can be accessed from a script + * using `getVar[T](id)` operation. + * The value of the variable is represented by [[sigma.ast.Constant]] instance, + * which contains both data value and [[SType]] descriptor. The descriptor is checked + * against the type `T` expected in the script operation. If the types don't match, + * exception is thrown and the box spending (protected by the script) fails. + * + * @param values internal container of the key-value pairs + */ +case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) { + def add(bindings: VarBinding*): ContextExtension = + ContextExtension(values ++ bindings) +} + +object ContextExtension { + /** Immutable instance of empty ContextExtension, which can be shared to avoid + * allocations. */ + val empty = ContextExtension(Map()) + + /** Type of context variable binding. */ + type VarBinding = (Byte, EvaluatedValue[_ <: SType]) + + object serializer extends SigmaSerializer[ContextExtension, ContextExtension] { + override def serialize(obj: ContextExtension, w: SigmaByteWriter): Unit = { + val size = obj.values.size + if (size > Byte.MaxValue) + error(s"Number of ContextExtension values $size exceeds ${Byte.MaxValue}.") + w.putUByte(size) + obj.values.foreach { case (id, v) => w.put(id).putValue(v) } + } + + override def parse(r: SigmaByteReader): ContextExtension = { + val extSize = r.getByte() + if (extSize < 0) + error(s"Negative amount of context extension values: $extSize") + val values = (0 until extSize) + .map(_ => (r.getByte(), r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]])) + ContextExtension(values.toMap) + } + } +} \ No newline at end of file diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverResult.scala b/data/shared/src/main/scala/sigma/interpreter/ProverResult.scala similarity index 92% rename from interpreter/shared/src/main/scala/sigmastate/interpreter/ProverResult.scala rename to data/shared/src/main/scala/sigma/interpreter/ProverResult.scala index 45d6159d8c..f04b972cb5 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverResult.scala +++ b/data/shared/src/main/scala/sigma/interpreter/ProverResult.scala @@ -1,10 +1,7 @@ -package sigmastate.interpreter - -import java.util +package sigma.interpreter import scorex.util.encode.Base16 -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} /** * Proof of correctness of tx spending diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ApplySerializer.scala b/data/shared/src/main/scala/sigma/serialization/ApplySerializer.scala similarity index 70% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ApplySerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ApplySerializer.scala index 89daeb0713..5a76f56298 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ApplySerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ApplySerializer.scala @@ -1,14 +1,14 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.lang.Terms.Apply -import sigmastate.utils.SigmaByteWriter._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast.{SType, Value} +import sigma.ast.syntax._ +import sigma.ast.Apply +import sigma.serialization.CoreByteWriter._ +import SigmaByteWriter._ case class ApplySerializer(cons: (Value[SType], IndexedSeq[Value[SType]]) => Value[SType]) extends ValueSerializer[Apply] { - import sigmastate.Operations.ApplyInfo._ + import sigma.ast.Operations.ApplyInfo._ override def opDesc = Apply val funcInfo: DataInfo[SValue] = funcArg val argsInfo: DataInfo[Seq[SValue]] = argsArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/BlockValueSerializer.scala b/data/shared/src/main/scala/sigma/serialization/BlockValueSerializer.scala similarity index 88% rename from interpreter/shared/src/main/scala/sigmastate/serialization/BlockValueSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/BlockValueSerializer.scala index 6e4056e275..a8a2ac359c 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/BlockValueSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/BlockValueSerializer.scala @@ -1,12 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast._ +import sigma.ast.syntax._ import ValueSerializer._ import sigma.util.safeNewArray -import sigmastate.utils.SigmaByteWriter.{DataInfo, U, Vlq} +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, U, Vlq} +import SigmaByteWriter._ import debox.cfor +import sigma.ast.SType case class BlockValueSerializer(cons: (IndexedSeq[BlockItem], Value[SType]) => Value[SType]) extends ValueSerializer[BlockValue] { diff --git a/data/shared/src/main/scala/sigma/serialization/BoolToSigmaPropSerializer.scala b/data/shared/src/main/scala/sigma/serialization/BoolToSigmaPropSerializer.scala new file mode 100644 index 0000000000..4032157a02 --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/BoolToSigmaPropSerializer.scala @@ -0,0 +1,21 @@ +package sigma.serialization + +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.serialization.CoreByteWriter.DataInfo +import SigmaByteWriter._ + +case class BoolToSigmaPropSerializer(cons: BoolValue => SigmaPropValue) extends ValueSerializer[BoolToSigmaProp] { + import Operations.BoolToSigmaPropInfo._ + override def opDesc = BoolToSigmaProp + val conditionInfo: DataInfo[SValue] = conditionArg + + def serialize(obj: BoolToSigmaProp, w: SigmaByteWriter): Unit = { + w.putValue(obj.value, conditionInfo) + } + + def parse(r: SigmaByteReader): Value[SType] = { + val p = r.getValue().asBoolValue + cons(p) + } +} diff --git a/data/shared/src/main/scala/sigma/serialization/CaseObjectSerialization.scala b/data/shared/src/main/scala/sigma/serialization/CaseObjectSerialization.scala new file mode 100644 index 0000000000..f1a8047922 --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/CaseObjectSerialization.scala @@ -0,0 +1,20 @@ +package sigma.serialization + +import sigma.ast.{Height, LastBlockUtxoRootHash, MinerPubkey, SType, Value, ValueCompanion} + +case class CaseObjectSerialization[V <: Value[SType]](override val opDesc: ValueCompanion, obj: V) + extends ValueSerializer[V] { + + override def serialize(obj: V, w: SigmaByteWriter): Unit = () + + override def parse(r: SigmaByteReader): V = { + opDesc match { + case Height => r.wasUsingBlockchainContext = true + case LastBlockUtxoRootHash => r.wasUsingBlockchainContext = true + case MinerPubkey => r.wasUsingBlockchainContext = true + case _ => + } + + obj + } +} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ConcreteCollectionBooleanConstantSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ConcreteCollectionBooleanConstantSerializer.scala similarity index 89% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ConcreteCollectionBooleanConstantSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ConcreteCollectionBooleanConstantSerializer.scala index 0419fd6e04..6d747f627f 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ConcreteCollectionBooleanConstantSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ConcreteCollectionBooleanConstantSerializer.scala @@ -1,11 +1,11 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.{SCollection, SBoolean, ArgInfo} -import sigmastate.Values._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import SigmaByteWriter._ +import sigma.ast._ +import sigma.ast.syntax._ import sigma.util.safeNewArray import debox.cfor +import sigma.ast.{SBoolean, SCollection} +import sigma.serialization.CoreByteWriter.{ArgInfo, Bits, DataInfo, U, Vlq, maxBitsInfo} case class ConcreteCollectionBooleanConstantSerializer(cons: (IndexedSeq[Value[SBoolean.type]], SBoolean.type) => Value[SCollection[SBoolean.type]]) extends ValueSerializer[ConcreteCollection[SBoolean.type]] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ConcreteCollectionSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ConcreteCollectionSerializer.scala similarity index 86% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ConcreteCollectionSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ConcreteCollectionSerializer.scala index c11452dde8..a415b4a567 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ConcreteCollectionSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ConcreteCollectionSerializer.scala @@ -1,12 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.{SCollection, ArgInfo, SType} -import sigmastate.Values._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast._ +import sigma.ast.syntax._ import ValueSerializer._ import sigma.util.safeNewArray -import sigmastate.utils.SigmaByteWriter.{DataInfo, U, Vlq} +import SigmaByteWriter._ import debox.cfor +import sigma.ast.{SCollection, SType} +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, U, Vlq} case class ConcreteCollectionSerializer(cons: (IndexedSeq[Value[SType]], SType) => Value[SCollection[SType]]) extends ValueSerializer[ConcreteCollection[_ <: SType]] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ConstantPlaceholderSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ConstantPlaceholderSerializer.scala similarity index 80% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ConstantPlaceholderSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ConstantPlaceholderSerializer.scala index 803f17079f..7cda3713a4 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ConstantPlaceholderSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ConstantPlaceholderSerializer.scala @@ -1,12 +1,11 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast.SType +import sigma.ast._ case class ConstantPlaceholderSerializer(cons: (Int, SType) => Value[SType]) extends ValueSerializer[ConstantPlaceholder[SType]] { - import sigmastate.Operations.ConstantPlaceholderInfo._ + import Operations.ConstantPlaceholderInfo._ override def opDesc = ConstantPlaceholder override def serialize(obj: ConstantPlaceholder[SType], w: SigmaByteWriter): Unit = { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ConstantSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ConstantSerializer.scala similarity index 77% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ConstantSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ConstantSerializer.scala index 8e97117467..eccc2d79ca 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ConstantSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ConstantSerializer.scala @@ -1,9 +1,7 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.SType -import sigmastate.Values._ -import sigmastate.lang.SigmaBuilder -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast.{SType, SigmaBuilder} +import sigma.ast._ /** This works in tandem with DataSerializer, if you change one make sure to check the other.*/ case class ConstantSerializer(builder: SigmaBuilder) diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ConstantStore.scala b/data/shared/src/main/scala/sigma/serialization/ConstantStore.scala similarity index 74% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ConstantStore.scala rename to data/shared/src/main/scala/sigma/serialization/ConstantStore.scala index dfa7dbb30a..dd91c9a7b1 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ConstantStore.scala +++ b/data/shared/src/main/scala/sigma/serialization/ConstantStore.scala @@ -1,9 +1,8 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.SType -import sigmastate.Values.{Constant, ConstantNode, ConstantPlaceholder} -import sigmastate.lang.SigmaBuilder +import sigma.ast.{Constant, ConstantNode, ConstantPlaceholder, SigmaBuilder} import debox.Buffer +import sigma.ast.SType /** HOTSPOT: used in deserialization (don't beautify this code) */ class ConstantStore(private val constants: IndexedSeq[Constant[SType]] = Constant.EmptySeq) { @@ -14,7 +13,7 @@ class ConstantStore(private val constants: IndexedSeq[Constant[SType]] = Constan store += c.asInstanceOf[Constant[SType]] val tpe = c.asInstanceOf[ConstantNode[T]].tpe builder.mkConstantPlaceholder[tpe.type](store.length - 1, tpe) - .asInstanceOf[sigmastate.Values.ConstantPlaceholder[T]] + .asInstanceOf[sigma.ast.ConstantPlaceholder[T]] } @inline final def get(index: Int): Constant[SType] = store(index) diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala b/data/shared/src/main/scala/sigma/serialization/CreateAvlTreeSerializer.scala similarity index 74% rename from interpreter/shared/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/CreateAvlTreeSerializer.scala index efd84f38b6..018b63376b 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/CreateAvlTreeSerializer.scala @@ -1,19 +1,20 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.SCollection._ -import sigmastate.SOption.SIntOption -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate._ -import sigmastate.Values._ -import sigmastate.lang.Terms.ValueOps -import sigmastate.utils.SigmaByteWriter.DataInfo +import sigma.ast.SCollection.SByteArray +import sigma.ast.SInt +import sigma.ast.SOption.SIntOption +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.ast.syntax.ValueOps +import SigmaByteWriter._ case class CreateAvlTreeSerializer( cons: (ByteValue, Value[SByteArray], IntValue, Value[SIntOption]) => AvlTreeValue ) extends ValueSerializer[CreateAvlTree] { - import sigmastate.Operations.CreateAvlTreeInfo._ + import Operations.CreateAvlTreeInfo._ override def opDesc = CreateAvlTree val operationFlagsInfo: DataInfo[SValue] = operationFlagsArg val digestInfo: DataInfo[SValue] = digestArg diff --git a/data/shared/src/main/scala/sigma/serialization/CreateProveDlogSerializer.scala b/data/shared/src/main/scala/sigma/serialization/CreateProveDlogSerializer.scala new file mode 100644 index 0000000000..5135cd1e37 --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/CreateProveDlogSerializer.scala @@ -0,0 +1,26 @@ +package sigma.serialization + +import sigma.ast.{CreateProveDlog, SGroupElement} +import sigma.serialization.CoreByteWriter._ +import sigma.ast.Value +import sigma.ast.syntax._ +import sigma.ast.syntax.ValueOps +import SigmaByteWriter._ + +case class CreateProveDlogSerializer(cons: Value[SGroupElement.type] => SigmaPropValue) + extends ValueSerializer[CreateProveDlog] { + import sigma.ast.Operations.CreateProveDlogInfo._ + + override def opDesc = CreateProveDlog + + val valueInfo: DataInfo[SValue] = valueArg + + override def serialize(obj: CreateProveDlog, w: SigmaByteWriter): Unit = { + w.putValue(obj.value, valueInfo) + } + + override def parse(r: SigmaByteReader) = { + val v = r.getValue().asValue[SGroupElement.type] + cons(v) + } +} diff --git a/data/shared/src/main/scala/sigma/serialization/DataSerializer.scala b/data/shared/src/main/scala/sigma/serialization/DataSerializer.scala new file mode 100644 index 0000000000..5f554e96a1 --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/DataSerializer.scala @@ -0,0 +1,41 @@ +package sigma.serialization + +import org.ergoplatform.ErgoBox +import sigma.ast._ +import sigma.data.CBox + +/** This works in tandem with ConstantSerializer, if you change one make sure to check the other.*/ +object DataSerializer extends CoreDataSerializer { + + /** Use type descriptor `tpe` to deconstruct type structure and recursively serialize subcomponents. + * Primitive types are leaves of the type tree, and they are served as basis of recursion. + * The data value `v` is expected to conform to the type described by `tpe`. + */ + override def serialize[T <: SType](v: T#WrappedType, tpe: T, w: CoreByteWriter): Unit = tpe match { + case SBox => + val b = v.asInstanceOf[CBox] + ErgoBox.sigmaSerializer.serialize(b.ebox, w.asInstanceOf[SigmaByteWriter]) + case _ => + super.serialize(v, tpe, w) + } + + /** Reads a data value from Reader. The data value bytes is expected to confirm + * to the type descriptor `tpe`. + * The data structure depth is limited by r.maxTreeDepth which is + * SigmaSerializer.MaxTreeDepth by default. + */ + override def deserialize[T <: SType](tpe: T, r: CoreByteReader): T#WrappedType = { + val res = (tpe match { + case SBox => + val depth = r.level + r.level = depth + 1 + val res = CBox(ErgoBox.sigmaSerializer.parse(r.asInstanceOf[SigmaByteReader])) + r.level = r.level - 1 + res + case t => + super.deserialize(t, r) + }).asInstanceOf[T#WrappedType] + res + } + +} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala similarity index 90% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala index f8c5221945..3d1061d774 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ErgoTreeSerializer.scala @@ -1,19 +1,14 @@ -package sigmastate.serialization +package sigma.serialization -import org.ergoplatform.validation.ValidationRules.{CheckDeserializedScriptIsSigmaProp, CheckHeaderSizeBit, CheckPositionLimit} -import org.ergoplatform.validation.{SigmaValidationSettings, ValidationException} -import sigmastate.{SType} -import sigmastate.Values.{Constant, ErgoTree, UnparsedErgoTree} -import sigmastate.lang.DeserializationSigmaBuilder -import sigmastate.lang.Terms.ValueOps -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.Values.ErgoTree.EmptyConstants +import org.ergoplatform.validation.ValidationRules.{CheckDeserializedScriptIsSigmaProp, CheckHeaderSizeBit} +import sigma.ast.{Constant, DeserializationSigmaBuilder, ErgoTree, SType, SubstConstants, UnparsedErgoTree} +import sigma.ast.syntax.ValueOps +import sigma.ast.ErgoTree.{EmptyConstants, HeaderType} import sigma.util.safeNewArray -import sigmastate.utxo.ComplexityTable import debox.cfor import sigma.VersionContext -import sigmastate.exceptions.{SerializerException, ReaderPositionLimitExceeded} -import java.util +import sigma.validation.{SigmaValidationSettings, ValidationException} +import sigma.validation.ValidationRules.CheckPositionLimit /** * Rationale for soft-forkable ErgoTree serialization. @@ -137,12 +132,10 @@ class ErgoTreeSerializer { deserializeErgoTree(r, maxTreeSizeBytes, true) } - private[sigmastate] def deserializeErgoTree(r: SigmaByteReader, maxTreeSizeBytes: Int, checkType: Boolean): ErgoTree = { + private[sigma] def deserializeErgoTree(r: SigmaByteReader, maxTreeSizeBytes: Int, checkType: Boolean): ErgoTree = { val startPos = r.position val previousPositionLimit = r.positionLimit - val previousComplexity = r.complexity r.positionLimit = r.position + maxTreeSizeBytes - r.complexity = 0 val (h, sizeOpt) = deserializeHeaderAndSize(r) val bodyPos = r.position val tree = try { @@ -155,25 +148,29 @@ class ErgoTreeSerializer { val wasDeserialize_saved = r.wasDeserialize r.wasDeserialize = false + val wasUsingBlockchainContext_saved = r.wasUsingBlockchainContext + r.wasUsingBlockchainContext = false + val root = ValueSerializer.deserialize(r) val hasDeserialize = r.wasDeserialize // == true if there was deserialization node r.wasDeserialize = wasDeserialize_saved + val isUsingBlockchainContext = r.wasUsingBlockchainContext // == true if there was a node using the blockchain context + r.wasUsingBlockchainContext = wasUsingBlockchainContext_saved + if (checkType) { CheckDeserializedScriptIsSigmaProp(root) } r.constantStore = previousConstantStore - val complexity = r.complexity - // now we know the end position of propositionBytes, read them all at once into array val treeSize = r.position - startPos r.position = startPos val propositionBytes = r.getBytes(treeSize) new ErgoTree( - h, cs, Right(root.asSigmaProp), complexity, - propositionBytes, Some(hasDeserialize)) + h, cs, Right(root.asSigmaProp), + propositionBytes, Some(hasDeserialize), Some(isUsingBlockchainContext)) } catch { case e: ReaderPositionLimitExceeded => @@ -187,8 +184,7 @@ class ErgoTreeSerializer { val numBytes = bodyPos - startPos + treeSize r.position = startPos val bytes = r.getBytes(numBytes) - val complexity = ComplexityTable.OpCodeComplexity(Constant.opCode) - new ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, Left(UnparsedErgoTree(bytes, ve)), complexity, bytes, None) + new ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, Left(UnparsedErgoTree(bytes, ve)), bytes, None, None) case None => throw new SerializerException( s"Cannot handle ValidationException, ErgoTree serialized without size bit.", Some(ve)) @@ -196,14 +192,13 @@ class ErgoTreeSerializer { } finally { r.positionLimit = previousPositionLimit - r.complexity = previousComplexity } tree } /** Deserialize `header` and optional `size` slots only. */ - private def deserializeHeaderAndSize(r: SigmaByteReader): (Byte, Option[Int]) = { - val header = r.getByte() + private def deserializeHeaderAndSize(r: SigmaByteReader): (HeaderType, Option[Int]) = { + val header = HeaderType @@ r.getByte() CheckHeaderSizeBit(header) val sizeOpt = if (ErgoTree.hasSize(header)) { val size = r.getUInt().toInt @@ -230,7 +225,7 @@ class ErgoTreeSerializer { /** Deserialize constants section only. * HOTSPOT: don't beautify this code */ - private def deserializeConstants(header: Byte, r: SigmaByteReader): IndexedSeq[Constant[SType]] = { + private def deserializeConstants(header: HeaderType, r: SigmaByteReader): IndexedSeq[Constant[SType]] = { val constants: IndexedSeq[Constant[SType]] = if (ErgoTree.isConstantSegregation(header)) { val nConsts = r.getUInt().toInt @@ -254,7 +249,7 @@ class ErgoTreeSerializer { } /** Deserialize header and constant sections, but output the rest of the bytes as separate array. */ - def deserializeHeaderWithTreeBytes(r: SigmaByteReader): (Byte, Option[Int], IndexedSeq[Constant[SType]], Array[Byte]) = { + def deserializeHeaderWithTreeBytes(r: SigmaByteReader): (HeaderType, Option[Int], IndexedSeq[Constant[SType]], Array[Byte]) = { val (header, sizeOpt) = deserializeHeaderAndSize(r) val constants = deserializeConstants(header, r) val treeBytes = r.getBytes(r.remaining) @@ -271,7 +266,7 @@ class ErgoTreeSerializer { * positions(r(i)) == i whenever r(i) != -1. When r(i) == -1 then backreference * is not defined (which means the constant with the index `i` is not substituted. */ - private[sigmastate] def getPositionsBackref(positions: Array[Int], positionsRange: Int): Array[Int] = { + private[sigma] def getPositionsBackref(positions: Array[Int], positionsRange: Int): Array[Int] = { // allocate array of back references: forall i: positionsBackref(i) is index in `positions` val positionsBackref = safeNewArray[Int](positionsRange) // mark all positions are not assigned @@ -290,7 +285,7 @@ class ErgoTreeSerializer { /** Transforms serialized bytes of ErgoTree with segregated constants by * replacing constants at given positions with new values. This operation * allow to use serialized scripts as pre-defined templates. - * See [[sigmastate.SubstConstants]] for details. + * See [[SubstConstants]] for details. * * @param scriptBytes serialized ErgoTree with ConstantSegregationFlag set to 1. * @param positions zero based indexes in ErgoTree.constants array which diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/FuncValueSerializer.scala b/data/shared/src/main/scala/sigma/serialization/FuncValueSerializer.scala similarity index 89% rename from interpreter/shared/src/main/scala/sigmastate/serialization/FuncValueSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/FuncValueSerializer.scala index d9f9ea752d..7f9b8cc927 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/FuncValueSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/FuncValueSerializer.scala @@ -1,12 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast._ +import sigma.ast.syntax._ import ValueSerializer._ import sigma.util.safeNewArray -import sigmastate.utils.SigmaByteWriter.{DataInfo, U, Vlq} +import SigmaByteWriter._ import debox.cfor +import sigma.ast.SType +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, U, Vlq} case class FuncValueSerializer(cons: (IndexedSeq[(Int, SType)], Value[SType]) => Value[SType]) extends ValueSerializer[FuncValue] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/GetVarSerializer.scala b/data/shared/src/main/scala/sigma/serialization/GetVarSerializer.scala similarity index 68% rename from interpreter/shared/src/main/scala/sigmastate/serialization/GetVarSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/GetVarSerializer.scala index cd9e9872c4..c3618ac0bf 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/GetVarSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/GetVarSerializer.scala @@ -1,14 +1,11 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.GetVar +import sigma.ast._ +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} case class GetVarSerializer(cons: (Byte, SType) => Value[SOption[SType]]) extends ValueSerializer[GetVar[_ <: SType]] { - import sigmastate.Operations.GetVarInfo._ + import Operations.GetVarInfo._ override def opDesc = GetVar val typeInfo: DataInfo[SType] = ArgInfo("type", "expected type of context variable") val varIdInfo: DataInfo[Byte] = varIdArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/LogicalNotSerializer.scala b/data/shared/src/main/scala/sigma/serialization/LogicalNotSerializer.scala similarity index 56% rename from interpreter/shared/src/main/scala/sigmastate/serialization/LogicalNotSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/LogicalNotSerializer.scala index 667e8a699f..0130d58d83 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/LogicalNotSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/LogicalNotSerializer.scala @@ -1,11 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.LogicalNot -import sigmastate.Operations.LogicalNotInfo.inputArg -import sigmastate.Values.{BoolValue, SValue} -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast.LogicalNot +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.Operations.LogicalNotInfo.inputArg +import sigma.ast.syntax._ +import SigmaByteWriter._ case class LogicalNotSerializer(cons: BoolValue => BoolValue) extends ValueSerializer[LogicalNot] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala b/data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala similarity index 81% rename from interpreter/shared/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala index e474d8a7c2..319a4284e2 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala @@ -1,14 +1,12 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.lang.Terms.STypeSubst -import sigmastate.lang.Terms.MethodCall +import sigma.ast.syntax._ +import sigma.ast.{MethodCall, SContextMethods, SMethod, SType, STypeSubst, Value, ValueCompanion} import sigma.util.safeNewArray -import sigmastate.utils.SigmaByteWriter.{DataInfo, valuesItemInfo} -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.ComplexityTable +import SigmaByteWriter._ import debox.cfor +import sigma.ast.SContextMethods.BlockchainContextMethodNames +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[SType]], STypeSubst) => Value[SType]) extends ValueSerializer[MethodCall] { @@ -27,8 +25,6 @@ case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[S w.putValues(mc.args, argsInfo, argsItemInfo) } - override def getComplexity: Int = 0 // because we add it explicitly in parse below - /** The SMethod instances in STypeCompanions may have type STypeIdent in methods types, * but a valid ErgoTree should have SMethod instances specialized for specific types * of `obj` and `args` using `specializeFor`. @@ -47,8 +43,6 @@ case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[S val obj = r.getValue() val args = r.getValues() val method = SMethod.fromIds(typeId, methodId) - val complexity = ComplexityTable.MethodCallComplexity.getOrElse((typeId, methodId), ComplexityTable.MinimalComplexity) - r.addComplexity(complexity) val nArgs = args.length val types: Seq[SType] = @@ -62,6 +56,11 @@ case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[S } val specMethod = method.specializeFor(obj.tpe, types) + + var isUsingBlockchainContext = specMethod.objType == SContextMethods && + BlockchainContextMethodNames.contains(method.name) + r.wasUsingBlockchainContext ||= isUsingBlockchainContext + cons(obj, specMethod, args, Map.empty) } } diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ModQArithOpSerializer.scala similarity index 71% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ModQArithOpSerializer.scala index 765aaeb7b4..181c3f7c5e 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ModQArithOpSerializer.scala @@ -1,10 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{BigIntValue, Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.{ModQArithOpCompanion, SType, ModQArithOp} +import sigma.ast.{ModQArithOp, ModQArithOpCompanion, SType} +import sigma.ast.syntax.{BigIntValue, SValue, ValueOps} +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.Value +import SigmaByteWriter._ // TODO v6.0: make sure it is covered with tests (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/327) case class ModQArithOpSerializer(override val opDesc: ModQArithOpCompanion, cons: (BigIntValue, BigIntValue) => BigIntValue) diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ModQSerializer.scala similarity index 68% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ModQSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ModQSerializer.scala index 335c12c1af..346f1ef6e2 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ModQSerializer.scala @@ -1,9 +1,9 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.Value -import sigmastate.lang.Terms._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.{ModQ, SType} +import sigma.ast.{ModQ, SType} +import sigma.ast.Value +import SigmaByteWriter._ +import sigma.ast.syntax.ValueOps // TODO v6.0: make sure it is covered with tests (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/327) object ModQSerializer extends ValueSerializer[ModQ] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/OneArgumentOperationSerializer.scala b/data/shared/src/main/scala/sigma/serialization/OneArgumentOperationSerializer.scala similarity index 60% rename from interpreter/shared/src/main/scala/sigmastate/serialization/OneArgumentOperationSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/OneArgumentOperationSerializer.scala index fd3fd8c2ea..15b5d89cb5 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/OneArgumentOperationSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/OneArgumentOperationSerializer.scala @@ -1,10 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.{OneArgumentOperation, OneArgumentOperationCompanion, SType} +import sigma.ast.{OneArgumentOperation, OneArgumentOperationCompanion, SType} +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.Value +import sigma.ast.syntax._ +import SigmaByteWriter._ case class OneArgumentOperationSerializer[T <: SType](opDesc: OneArgumentOperationCompanion, cons: Value[T] => SValue) extends ValueSerializer[OneArgumentOperation[T, SType]] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/OpCodes.scala b/data/shared/src/main/scala/sigma/serialization/OpCodes.scala similarity index 81% rename from interpreter/shared/src/main/scala/sigmastate/serialization/OpCodes.scala rename to data/shared/src/main/scala/sigma/serialization/OpCodes.scala index 74ec9bd58e..70050d00ba 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/OpCodes.scala +++ b/data/shared/src/main/scala/sigma/serialization/OpCodes.scala @@ -1,37 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.serialization.OpCodes.OpCode +import sigma.ast.TypeCodes.LastConstantCode import supertagged.TaggedType -/** Encoding of types for serialization. */ -trait TypeCodes { - /** Decoding of types depends on the first byte and in general is a recursive procedure - * consuming some number of bytes from Reader. - * All data types are recognized by the first byte falling in the region [FirstDataType .. LastDataType] */ - val FirstDataType: OpCode = OpCode @@ 1.toByte - val LastDataType : OpCode = OpCode @@ 111.toByte - - /** SFunc types occupy remaining space of byte values [FirstFuncType .. 255] */ - val FirstFuncType: OpCode = OpCode @@ (LastDataType + 1).toByte - val LastFuncType : OpCode = OpCode @@ 255.toByte -} - /** Encoding of values for serialization. */ -trait ValueCodes extends TypeCodes { - /** We use optimized encoding of constant values to save space in serialization. - * Since Box registers are stored as Constant nodes we save 1 byte for each register. - * This is due to convention that Value.opCode falling in [1..LastDataType] region is a constant. - * Thus, we can just decode an instance of SType and then decode data using DataSerializer. - * - * Decoding of constants depends on the first byte and in general is a recursive procedure - * consuming some number of bytes from Reader. - * */ - val ConstantCode: OpCode = OpCode @@ 0.toByte - - /** The last constant code is equal to FirstFuncType which represent generic function type. - * We use this single code to represent all functional constants, since we don't have enough space in single byte. - * Subsequent bytes have to be read from Reader in order to decode the type of the function and the corresponding data. */ - val LastConstantCode: OpCode = OpCode @@ (LastDataType + 1).toByte +object ValueCodes { + object OpCode extends TaggedType[Byte] + type OpCode = OpCode.Type + } /** The set of all possible IR graph nodes can be split in two subsets: @@ -52,10 +28,8 @@ trait ValueCodes extends TypeCodes { * 1) For validation rule CheckValidOpCode we use OpCodes range, so we use single byte encoding. * 2) For CheckCostFuncOperation we use 1-511 range and extended encoding (see docs) */ -object OpCodes extends ValueCodes { - - object OpCode extends TaggedType[Byte] - type OpCode = OpCode.Type +object OpCodes { + import ValueCodes._ private def newOpCode(shift: Short): OpCode = OpCode @@ (LastConstantCode + shift).toByte diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/OptionGetOrElseSerializer.scala b/data/shared/src/main/scala/sigma/serialization/OptionGetOrElseSerializer.scala similarity index 66% rename from interpreter/shared/src/main/scala/sigmastate/serialization/OptionGetOrElseSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/OptionGetOrElseSerializer.scala index bc0fe694e6..ef00ad48ff 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/OptionGetOrElseSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/OptionGetOrElseSerializer.scala @@ -1,15 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.OptionGetOrElse +import sigma.ast._ +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.syntax._ +import SigmaByteWriter._ case class OptionGetOrElseSerializer(cons: (Value[SOption[SType]], Value[SType]) => Value[SType]) extends ValueSerializer[OptionGetOrElse[_ <: SType]] { - import sigmastate.Operations.OptionGetOrElseInfo._ + import Operations.OptionGetOrElseInfo._ override def opDesc = OptionGetOrElse val thisInfo: DataInfo[SValue] = thisArg val defaultInfo: DataInfo[SValue] = defaultArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/PropertyCallSerializer.scala b/data/shared/src/main/scala/sigma/serialization/PropertyCallSerializer.scala similarity index 60% rename from interpreter/shared/src/main/scala/sigmastate/serialization/PropertyCallSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/PropertyCallSerializer.scala index 0c4b15aaf5..10411e21ce 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/PropertyCallSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/PropertyCallSerializer.scala @@ -1,18 +1,14 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.lang.Terms -import sigmastate.lang.Terms.STypeSubst -import sigmastate.lang.Terms.{MethodCall, PropertyCall} -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteWriter, SigmaByteReader} -import sigmastate.utxo.ComplexityTable +import sigma.ast.{EmptySubst, SType, STypeSubst} +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} +import sigma.ast._ +import sigma.ast.syntax.SValue +import SigmaByteWriter._ case class PropertyCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[SType]], STypeSubst) => Value[SType]) extends ValueSerializer[MethodCall] { override def opDesc: ValueCompanion = PropertyCall - override def getComplexity: Int = 0 // because we add it explicitly in parse below val typeCodeInfo: DataInfo[Byte] = ArgInfo("typeCode", "type of the method (see Table~\\ref{table:predeftypes})") val methodCodeInfo: DataInfo[Byte] = ArgInfo("methodCode", "a code of the property") val objInfo: DataInfo[SValue] = ArgInfo("obj", "receiver object of this property call") @@ -28,9 +24,7 @@ case class PropertyCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value val methodId = r.getByte() val obj = r.getValue() val method = SMethod.fromIds(typeId, methodId) - val complexity = ComplexityTable.MethodCallComplexity.getOrElse((typeId, methodId), ComplexityTable.MinimalComplexity) - r.addComplexity(complexity) val specMethod = method.specializeFor(obj.tpe, SType.EmptySeq) - cons(obj, specMethod, Value.EmptySeq, Terms.EmptySubst) + cons(obj, specMethod, Value.EmptySeq, EmptySubst) } } diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/SelectFieldSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SelectFieldSerializer.scala similarity index 64% rename from interpreter/shared/src/main/scala/sigmastate/serialization/SelectFieldSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/SelectFieldSerializer.scala index 6abf691f8d..95e692c85a 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/SelectFieldSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SelectFieldSerializer.scala @@ -1,13 +1,12 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Operations.SelectFieldInfo -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.SelectField -import sigmastate.{STuple, SType} +import sigma.ast.Operations.SelectFieldInfo +import sigma.ast.{SelectField, Value} +import sigma.ast.syntax._ import SelectFieldInfo._ -import sigmastate.utils.SigmaByteWriter.DataInfo +import sigma.ast.{STuple, SType} +import sigma.serialization.CoreByteWriter.DataInfo +import SigmaByteWriter._ case class SelectFieldSerializer(cons: (Value[STuple], Byte) => Value[SType]) extends ValueSerializer[SelectField] { override def opDesc = SelectField diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaByteReader.scala b/data/shared/src/main/scala/sigma/serialization/SigmaByteReader.scala new file mode 100644 index 0000000000..8cbeeb1cc1 --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/SigmaByteReader.scala @@ -0,0 +1,65 @@ +package sigma.serialization + +import debox.cfor +import scorex.util.serialization.Reader +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.util.safeNewArray + +/** Reader used in the concrete implementations of [[SigmaSerializer]]. + * It decorates the given reader, delegates most of the methods to it, but also adds new + * methods. + * + * @param r the underlying reader this reader reads from + * @param constantStore the store of constants which is used to resolve + * [[sigma.ast.ConstantPlaceholder]] + * @param resolvePlaceholdersToConstants if true then resolved constants will be + * substituted in the tree instead of the placeholder. + * @param maxTreeDepth limit on the tree depth (recursive invocations) + * of the deserializer + */ +class SigmaByteReader(override val r: Reader, + var constantStore: ConstantStore, + var resolvePlaceholdersToConstants: Boolean, + override val maxTreeDepth: Int = SigmaSerializer.MaxTreeDepth) + extends CoreByteReader(r, maxTreeDepth) { + + /** 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 + + + /** Returns all bytes of the underlying ByteBuffer. */ + private[sigma] def getAllBufferBytes: Array[Byte] = { + val savedPos = position + position = 0 + val res = getBytesUnsafe(remaining) + position = savedPos + res + } + + @inline def getValue(): SValue = ValueSerializer.deserialize(this) + + /** Read sequence of values from this reader. + * It first reads the number of values and then reads each value using `getValue` method. + * + * @return a sequence of zero of more values read + */ + @inline def getValues(): IndexedSeq[SValue] = { + val size = getUIntExact + if (size == 0) Value.EmptySeq // quick short-cut when there is nothing to read + else { + val xs = safeNewArray[SValue](size) + cfor(0)(_ < size, _ + 1) { i => + xs(i) = getValue() + } + xs + } + } + +} diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala new file mode 100644 index 0000000000..35d5e0c9b9 --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/SigmaByteWriter.scala @@ -0,0 +1,104 @@ +package sigma.serialization + +import scorex.util.serialization.Writer +import sigma.ast.syntax._ +import sigma.ast._ +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, FormatDescriptor, SeqFmt} + +class SigmaByteWriter(override val w: Writer, + val constantExtractionStore: Option[ConstantStore]) + extends CoreByteWriter(w) { + import CoreByteWriter._ + import ValueSerializer._ + + override def put(x: Byte, info: DataInfo[Byte]): this.type = { + ValueSerializer.addArgInfo(info) + w.put(x); this + } + + override def putUByte(x: Int, info: DataInfo[U[Byte]]): this.type = { + ValueSerializer.addArgInfo(info) + super.putUByte(x) + } + + @inline override def putBoolean(x: Boolean, info: DataInfo[Boolean]): this.type = { + ValueSerializer.addArgInfo(info) + w.putBoolean(x); this + } + + @inline override def putShort(x: Short, info: DataInfo[Short]): this.type = { + ValueSerializer.addArgInfo(info) + w.putShort(x); this + } + + @inline override def putUShort(x: Int, info: DataInfo[Vlq[U[Short]]]): this.type = { + ValueSerializer.addArgInfo(info) + w.putUShort(x); this + } + + @inline override def putInt(x: Int, info: DataInfo[Int]): this.type = { + ValueSerializer.addArgInfo(info) + w.putInt(x); this + } + + @inline override def putUInt(x: Long, info: DataInfo[Vlq[U[Int]]]): this.type = { + ValueSerializer.addArgInfo(info) + w.putUInt(x); this + } + + @inline override def putLong(x: Long, info: DataInfo[Vlq[ZigZag[Long]]]): this.type = { + ValueSerializer.addArgInfo(info) + w.putLong(x); this + } + + @inline override def putULong(x: Long, info: DataInfo[Vlq[U[Long]]]): this.type = { + ValueSerializer.addArgInfo(info) + w.putULong(x); this + } + + @inline override def putBytes(xs: Array[Byte], info: DataInfo[Array[Byte]]): this.type = { + ValueSerializer.addArgInfo(info) + w.putBytes(xs); this + } + + @inline override def putBits(xs: Array[Boolean], info: DataInfo[Bits]): this.type = { + ValueSerializer.addArgInfo(info) + w.putBits(xs); + this + } + + @inline override def putType[T <: SType](x: T, info: DataInfo[SType]): this.type = { + ValueSerializer.addArgInfo(info) + TypeSerializer.serialize(x, this); this + } + + @inline def putValue[T <: SType](x: Value[T]): this.type = { ValueSerializer.serialize(x, this); this } + @inline def putValue[T <: SType](x: Value[T], info: DataInfo[SValue]): this.type = { + ValueSerializer.addArgInfo(info) + ValueSerializer.serialize(x, this); this + } + @inline def putValues[T <: SType](xs: Seq[Value[T]]): this.type = { + putUInt(xs.length) + xs.foreach(putValue(_)) + this + } + @inline def putValues[T <: SType](xs: Seq[Value[T]], info: DataInfo[Seq[SValue]], itemInfo: DataInfo[SValue]): this.type = { + putUInt(xs.length, valuesLengthInfo) + foreach("\\#items", xs) { x => + putValue(x, itemInfo) + } + this + } +} + +object SigmaByteWriter { + implicit case object ValueFmt extends FormatDescriptor[SValue] { + override def size: String = "[1, *]" + override def toString: String = "Expr" + } + + def valuesItemInfo(info: DataInfo[Seq[SValue]]): DataInfo[SValue] = { + val itemFmt = info.format.asInstanceOf[SeqFmt[SValue]].fmt + DataInfo(ArgInfo(info.info.name + "_i", s"i-th item in the ${info.info.description}"), itemFmt) + } +} \ No newline at end of file diff --git a/data/shared/src/main/scala/sigma/serialization/SigmaPropBytesSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SigmaPropBytesSerializer.scala new file mode 100644 index 0000000000..fc55e7b5ca --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/SigmaPropBytesSerializer.scala @@ -0,0 +1,21 @@ +package sigma.serialization + +import sigma.ast.{SType, SigmaPropBytes, Value} +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.syntax._ +import SigmaByteWriter._ + +object SigmaPropBytesSerializer extends ValueSerializer[SigmaPropBytes] { + import sigma.ast.Operations.SigmaPropBytesInfo._ + override def opDesc = SigmaPropBytes + val thisInfo: DataInfo[SValue] = thisArg + + def serialize(obj: SigmaPropBytes, w: SigmaByteWriter): Unit = { + w.putValue(obj.input, thisInfo) + } + + def parse(r: SigmaByteReader): Value[SType] = { + val p = r.getValue().asSigmaProp + SigmaPropBytes(p) + } +} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/SigmaPropIsProvenSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SigmaPropIsProvenSerializer.scala similarity index 53% rename from interpreter/shared/src/main/scala/sigmastate/serialization/SigmaPropIsProvenSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/SigmaPropIsProvenSerializer.scala index 0350f582bb..66e7491dfd 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/SigmaPropIsProvenSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaPropIsProvenSerializer.scala @@ -1,9 +1,7 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.{Values, SType} -import sigmastate.lang.Terms._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.SigmaPropIsProven +import sigma.ast.{SType, SigmaPropIsProven, Value} +import sigma.ast.syntax._ object SigmaPropIsProvenSerializer extends ValueSerializer[SigmaPropIsProven] { override def opDesc = SigmaPropIsProven @@ -12,7 +10,7 @@ object SigmaPropIsProvenSerializer extends ValueSerializer[SigmaPropIsProven] { w.putValue(obj.input) } - def parse(r: SigmaByteReader): Values.Value[SType] = { + def parse(r: SigmaByteReader): Value[SType] = { val p = r.getValue().asSigmaProp SigmaPropIsProven(p) } diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/SigmaSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala similarity index 91% rename from interpreter/shared/src/main/scala/sigmastate/serialization/SigmaSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala index eea880521a..cdb28d724b 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/SigmaSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SigmaSerializer.scala @@ -1,14 +1,11 @@ -package sigmastate.serialization +package sigma.serialization import java.nio.ByteBuffer - -import org.ergoplatform.SigmaConstants -import org.ergoplatform.validation.SigmaValidationSettings import scorex.util.ByteArrayBuilder -import sigmastate.utils._ import scorex.util.serialization._ -import sigmastate.exceptions.SerializerException -import sigmastate.serialization.OpCodes.OpCode +import sigma.data.SigmaConstants +import sigma.validation.SigmaValidationSettings +import sigma.serialization.ValueCodes.OpCode object SigmaSerializer { type Position = Int diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/SubstConstantsSerializer.scala b/data/shared/src/main/scala/sigma/serialization/SubstConstantsSerializer.scala similarity index 67% rename from interpreter/shared/src/main/scala/sigmastate/serialization/SubstConstantsSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/SubstConstantsSerializer.scala index 2c55458280..f019d35b59 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/SubstConstantsSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/SubstConstantsSerializer.scala @@ -1,14 +1,14 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.SCollection.{SIntArray, SByteArray} -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.{SCollection, SubstConstants, SType} +import sigma.ast.SCollection.{SByteArray, SIntArray} +import sigma.ast.{SubstConstants, Value} +import sigma.ast.syntax._ +import SigmaByteWriter._ +import sigma.ast.{SCollection, SType} +import sigma.serialization.CoreByteWriter.DataInfo object SubstConstantsSerializer extends ValueSerializer[SubstConstants[SType]] { - import sigmastate.Operations.SubstConstantsInfo._ + import sigma.ast.Operations.SubstConstantsInfo._ override def opDesc = SubstConstants val scriptBytesInfo: DataInfo[SValue] = scriptBytesArg val positionsInfo: DataInfo[SValue] = positionsArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/TaggedVariableSerializer.scala b/data/shared/src/main/scala/sigma/serialization/TaggedVariableSerializer.scala similarity index 71% rename from interpreter/shared/src/main/scala/sigmastate/serialization/TaggedVariableSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/TaggedVariableSerializer.scala index bc99becd19..cd6f33eacb 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/TaggedVariableSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/TaggedVariableSerializer.scala @@ -1,9 +1,9 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast.SType +import sigma.ast._ +// TODO v6.0: remove this class (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/584) case class TaggedVariableSerializer(cons: (Byte, SType) => Value[SType]) extends ValueSerializer[TaggedVariable[_ <: SType]] { override def opDesc = TaggedVariable diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/TupleSerializer.scala b/data/shared/src/main/scala/sigma/serialization/TupleSerializer.scala similarity index 79% rename from interpreter/shared/src/main/scala/sigmastate/serialization/TupleSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/TupleSerializer.scala index a88a17fa5f..89a7ea457c 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/TupleSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/TupleSerializer.scala @@ -1,12 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.{SType, ArgInfo} -import sigmastate.Values._ -import sigmastate.utils.{SigmaByteWriter, SigmaByteReader} -import sigmastate.serialization.ValueSerializer._ +import sigma.ast._ +import sigma.serialization.ValueSerializer._ import sigma.util.safeNewArray -import sigmastate.utils.SigmaByteWriter.{DataInfo, U} +import SigmaByteWriter._ import debox.cfor +import sigma.ast.SType +import sigma.ast.syntax.{CollectionOps, SValue} +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo, U} case class TupleSerializer(cons: Seq[Value[SType]] => Value[SType]) extends ValueSerializer[Tuple] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/TwoArgumentsSerializer.scala b/data/shared/src/main/scala/sigma/serialization/TwoArgumentsSerializer.scala similarity index 71% rename from interpreter/shared/src/main/scala/sigmastate/serialization/TwoArgumentsSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/TwoArgumentsSerializer.scala index a4bb89fa29..b6f4561ba0 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/TwoArgumentsSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/TwoArgumentsSerializer.scala @@ -1,10 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.{TwoArgumentsOperation, SType, TwoArgumentOperationCompanion} +import sigma.ast.{SType, TwoArgumentOperationCompanion, TwoArgumentsOperation} +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.Value +import sigma.ast.syntax._ +import SigmaByteWriter._ case class TwoArgumentsSerializer[LIV <: SType, RIV <: SType, OV <: Value[SType]] (override val opDesc: TwoArgumentOperationCompanion, constructor: (Value[LIV], Value[RIV]) => Value[SType]) diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ValDefSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ValDefSerializer.scala similarity index 88% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ValDefSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ValDefSerializer.scala index cdac6309f8..b775c14327 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ValDefSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ValDefSerializer.scala @@ -1,13 +1,11 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.serialization.OpCodes._ +import debox.cfor import scorex.util.Extensions._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import ValueSerializer._ +import sigma.ast._ import sigma.util.safeNewArray -import debox.cfor +import sigma.serialization.OpCodes._ +import sigma.serialization.ValueSerializer._ case class ValDefSerializer(override val opDesc: ValueCompanion) extends ValueSerializer[ValDef] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ValDefTypeStore.scala b/data/shared/src/main/scala/sigma/serialization/ValDefTypeStore.scala similarity index 78% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ValDefTypeStore.scala rename to data/shared/src/main/scala/sigma/serialization/ValDefTypeStore.scala index 62c72317d5..8ae41ebb99 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ValDefTypeStore.scala +++ b/data/shared/src/main/scala/sigma/serialization/ValDefTypeStore.scala @@ -1,6 +1,6 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.SType +import sigma.ast.SType import scala.collection.mutable diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ValUseSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ValUseSerializer.scala similarity index 84% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ValUseSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ValUseSerializer.scala index 523139c3ab..49aae39843 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ValUseSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ValUseSerializer.scala @@ -1,8 +1,6 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast._ case class ValUseSerializer(cons: (Int, SType) => Value[SType]) extends ValueSerializer[ValUse[SType]] { override def opDesc = ValUse diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/data/shared/src/main/scala/sigma/serialization/ValueSerializer.scala similarity index 94% rename from interpreter/shared/src/main/scala/sigmastate/serialization/ValueSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/ValueSerializer.scala index 056da7587b..3eb6c75713 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/ValueSerializer.scala @@ -1,30 +1,19 @@ -package sigmastate.serialization +package sigma.serialization import org.ergoplatform.validation.ValidationRules.CheckValidOpCode -import org.ergoplatform._ +import sigma.ast.SCollection.SByteArray +import sigma.ast.TypeCodes.LastConstantCode +import sigma.ast._ +import sigma.serialization.CoreByteWriter.DataInfo import sigma.util.Extensions.toUByte -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ -import sigmastate._ -import sigmastate.lang.DeserializationSigmaBuilder -import sigmastate.serialization.OpCodes._ -import sigmastate.serialization.transformers._ -import sigmastate.serialization.trees.{QuadrupleSerializer, Relation2Serializer} -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils._ -import sigmastate.utxo.ComplexityTable._ -import sigmastate.utxo._ - -import scala.collection.compat.immutable.ArraySeq +import sigma.serialization.ValueCodes.OpCode +import sigma.serialization.transformers._ +import sigma.serialization.trees.{QuadrupleSerializer, Relation2Serializer} +import sigma.utils.SparseArrayContainer + import scala.collection.mutable -import scala.collection.mutable.{HashMap, Map} abstract class ValueSerializer[V <: Value[SType]] extends SigmaSerializer[Value[SType], V] { - import scala.language.implicitConversions - private val companion = ValueSerializer - - def getComplexity: Int = OpCodeComplexity.getOrElse(opCode, MinimalComplexity) - lazy val complexity: Int = getComplexity def opDesc: ValueCompanion /** Code of the corresponding tree node (Value.opCode) which is used to lookup this serizalizer @@ -101,7 +90,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { LogicalTransformerSerializer(AND, mkAND), LogicalTransformerSerializer(OR, mkOR), LogicalTransformerSerializer(XorOf, mkXorOf), - TaggedVariableSerializer(mkTaggedVariable), + TaggedVariableSerializer(mkTaggedVariable), // TODO v6.0: remove this serializer https://github.com/ScorexFoundation/sigmastate-interpreter/issues/584 GetVarSerializer(mkGetVar), MapCollectionSerializer(mkMapCollection), BooleanTransformerSerializer[SType](Exists, mkExists), @@ -256,7 +245,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { def printSerInfo(): String = { serializerInfo.map { case (_, s) => - val ser = getSerializer(s.opCode) + getSerializer(s.opCode) // sanity check to make sure only resolvable serializers got printed s.toString }.mkString("\n") } @@ -398,13 +387,11 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { val firstByte = toUByte(r.peekByte()) val v = if (firstByte <= LastConstantCode) { // look ahead byte tell us this is going to be a Constant - r.addComplexity(constantSerializer.complexity) constantSerializer.deserialize(r) } else { val opCode = r.getByte().asInstanceOf[OpCode] val ser = getSerializer(opCode) - r.addComplexity(ser.complexity) ser.parse(r) } r.level = r.level - 1 diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/AppendSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/AppendSerializer.scala similarity index 63% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/AppendSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/AppendSerializer.scala index 970c1b66d0..c5995cfe7d 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/AppendSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/AppendSerializer.scala @@ -1,12 +1,11 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Operations.AppendInfo -import sigmastate.Values.Value -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.Append -import sigmastate.{SCollection, SType} +import sigma.ast.Operations.AppendInfo +import sigma.ast.{Append, Value} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ +import sigma.ast.{SCollection, SType} case class AppendSerializer(cons: (Value[SCollection[SType]], Value[SCollection[SType]]) => Value[SCollection[SType]]) extends ValueSerializer[Append[SType]] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/AtLeastSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/AtLeastSerializer.scala similarity index 61% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/AtLeastSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/AtLeastSerializer.scala index a401921fed..876f83f2d1 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/AtLeastSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/AtLeastSerializer.scala @@ -1,11 +1,11 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Operations.AtLeastInfo -import sigmastate.Values.{Value, SigmaPropValue} -import sigmastate.lang.Terms._ -import sigmastate._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.ast.syntax.SigmaPropValue +import sigma.ast.{AtLeast, SCollection, SInt, SSigmaProp, Value} +import sigma.ast.Operations.AtLeastInfo +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ case class AtLeastSerializer(cons: (Value[SInt.type], Value[SCollection[SSigmaProp.type]]) => SigmaPropValue) extends ValueSerializer[AtLeast] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/BooleanTransformerSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/BooleanTransformerSerializer.scala similarity index 62% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/BooleanTransformerSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/BooleanTransformerSerializer.scala index 8c5975cf8a..be3b91dcca 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/BooleanTransformerSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/BooleanTransformerSerializer.scala @@ -1,12 +1,12 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.{BooleanTransformer, BooleanTransformerCompanion} -import sigmastate.{SCollection, SBoolean, SType, SFunc} +import sigma.ast.syntax.SValue +import sigma.ast.{BooleanTransformer, BooleanTransformerCompanion, Value} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ +import sigma.ast.{SBoolean, SCollection, SFunc, SType} +import sigma.serialization.CoreByteWriter.DataInfo case class BooleanTransformerSerializer[T <: SType] (opDesc: BooleanTransformerCompanion, diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ByIndexSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/ByIndexSerializer.scala similarity index 67% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ByIndexSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/ByIndexSerializer.scala index 15ea23db30..e0c43d1cc5 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ByIndexSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/ByIndexSerializer.scala @@ -1,14 +1,14 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer +import sigma.ast.{ByIndex, Value} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} import ValueSerializer._ -import sigmastate.Operations.ByIndexInfo._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.ByIndex -import sigmastate.{SInt, SCollection, SType} +import sigma.ast.syntax.SValue +import sigma.ast.Operations.ByIndexInfo._ +import sigma.serialization.SigmaByteWriter._ +import sigma.ast.{SCollection, SInt, SType} +import sigma.serialization.CoreByteWriter.DataInfo case class ByIndexSerializer(cons: (Value[SCollection[SType]], Value[SInt.type], Option[Value[SType]]) => Value[SType]) extends ValueSerializer[ByIndex[SType]] { diff --git a/data/shared/src/main/scala/sigma/serialization/transformers/CreateProveDHTupleSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/CreateProveDHTupleSerializer.scala new file mode 100644 index 0000000000..5a16d35365 --- /dev/null +++ b/data/shared/src/main/scala/sigma/serialization/transformers/CreateProveDHTupleSerializer.scala @@ -0,0 +1,32 @@ +package sigma.serialization.transformers + +import sigma.ast.{CreateProveDHTuple, SGroupElement} +import sigma.ast.syntax.SigmaPropValue +import sigma.ast.Value +import sigma.ast.syntax._ +import sigma.serialization._ +import SigmaByteWriter._ + +case class CreateProveDHTupleSerializer(cons: (Value[SGroupElement.type], + Value[SGroupElement.type], + Value[SGroupElement.type], + Value[SGroupElement.type]) => SigmaPropValue) + extends ValueSerializer[CreateProveDHTuple] { + import sigma.ast.Operations.CreateProveDHTupleInfo._ + override def opDesc = CreateProveDHTuple + + override def serialize(obj: CreateProveDHTuple, w: SigmaByteWriter): Unit = { + w.putValue(obj.gv, gArg) + w.putValue(obj.hv, hArg) + w.putValue(obj.uv, uArg) + w.putValue(obj.vv, vArg) + } + + override def parse(r: SigmaByteReader) = { + val gv = r.getValue().asValue[SGroupElement.type] + val hv = r.getValue().asValue[SGroupElement.type] + val uv = r.getValue().asValue[SGroupElement.type] + val vv = r.getValue().asValue[SGroupElement.type] + cons(gv, hv, uv, vv) + } +} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/DeserializeContextSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/DeserializeContextSerializer.scala similarity index 66% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/DeserializeContextSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/DeserializeContextSerializer.scala index f2fe92434d..9058617601 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/DeserializeContextSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/DeserializeContextSerializer.scala @@ -1,12 +1,10 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.{SType, ArgInfo} -import sigmastate.Values._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.DeserializeContext -import SigmaByteWriter._ -import sigmastate.Operations.DeserializeContextInfo +import sigma.ast._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.ast.SType +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} +import Operations.DeserializeContextInfo case class DeserializeContextSerializer(cons: (Byte, SType) => Value[SType]) extends ValueSerializer[DeserializeContext[SType]] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/DeserializeRegisterSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/DeserializeRegisterSerializer.scala similarity index 72% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/DeserializeRegisterSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/DeserializeRegisterSerializer.scala index 47c1ead8e4..05d5f7b4db 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/DeserializeRegisterSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/DeserializeRegisterSerializer.scala @@ -1,15 +1,15 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId -import sigmastate.{ArgInfo, SType} -import sigmastate.Values.{Value, SValue} -import sigmastate.serialization.ValueSerializer +import sigma.ast.{DeserializeRegister, Value} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} import ValueSerializer._ -import sigmastate.Operations.DeserializeRegisterInfo._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.DeserializeRegister +import sigma.ast.SType +import sigma.ast.syntax.SValue +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} +import sigma.ast.Operations.DeserializeRegisterInfo._ +import sigma.serialization.SigmaByteWriter._ case class DeserializeRegisterSerializer(cons: (RegisterId, SType, Option[Value[SType]]) => Value[SType]) extends ValueSerializer[DeserializeRegister[SType]] { diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ExtractRegisterAsSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/ExtractRegisterAsSerializer.scala similarity index 70% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ExtractRegisterAsSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/ExtractRegisterAsSerializer.scala index f84933b332..e2b09e2a84 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ExtractRegisterAsSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/ExtractRegisterAsSerializer.scala @@ -1,17 +1,17 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId -import sigmastate.Values.{Value, SValue} -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.ExtractRegisterAs -import sigmastate.{SBox, SOption, ArgInfo, SType} +import sigma.ast.syntax.SValue +import sigma.ast.{ExtractRegisterAs, Value} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ +import sigma.ast.{SBox, SOption, SType} +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} case class ExtractRegisterAsSerializer(cons: (Value[SBox.type], RegisterId, SOption[SType]) => Value[SType]) extends ValueSerializer[ExtractRegisterAs[SType]] { - import sigmastate.Operations.ExtractRegisterAsInfo._ + import sigma.ast.Operations.ExtractRegisterAsInfo._ override def opDesc = ExtractRegisterAs val thisInfo: DataInfo[SValue] = thisArg val regIdInfo: DataInfo[Byte] = regIdArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/FilterSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/FilterSerializer.scala similarity index 63% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/FilterSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/FilterSerializer.scala index 1c16a9d932..ba8c82a56e 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/FilterSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/FilterSerializer.scala @@ -1,11 +1,9 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.Value -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.Filter -import sigmastate.{SCollection, SType, SFunc} +import sigma.ast.{Filter, Value} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.ast.{SCollection, SType, SFunc} case class FilterSerializer(cons: (Value[SCollection[SType]], Value[SFunc]) => Value[SCollection[SType]]) extends ValueSerializer[Filter[SType]] { override def opDesc = Filter diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/FoldSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/FoldSerializer.scala similarity index 64% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/FoldSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/FoldSerializer.scala index 0159e10a0a..a1f59f04d3 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/FoldSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/FoldSerializer.scala @@ -1,17 +1,17 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.Fold -import sigmastate.{SCollection, SType, SFunc} +import sigma.ast.syntax.SValue +import sigma.ast.{Fold, Value} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ +import sigma.ast.{SCollection, SFunc, SType} +import sigma.serialization.CoreByteWriter.DataInfo case class FoldSerializer(cons: (Value[SCollection[SType]], Value[SType], Value[SFunc]) => Value[SType]) extends ValueSerializer[Fold[SType, SType]] { override def opDesc = Fold - import sigmastate.Operations.FoldInfo._ + import sigma.ast.Operations.FoldInfo._ val thisInfo: DataInfo[SValue] = thisArg val zeroInfo: DataInfo[SValue] = zeroArg val opInfo: DataInfo[SValue] = opArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/LogicalTransformerSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/LogicalTransformerSerializer.scala similarity index 58% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/LogicalTransformerSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/LogicalTransformerSerializer.scala index 18b251f483..29c323341d 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/LogicalTransformerSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/LogicalTransformerSerializer.scala @@ -1,12 +1,12 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.Transformer -import sigmastate.{SCollection, SBoolean, LogicalTransformerCompanion} +import sigma.ast.syntax.SValue +import sigma.ast.{LogicalTransformerCompanion, SBoolean, SCollection, Transformer} +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.Value +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ case class LogicalTransformerSerializer[I <: SCollection[SBoolean.type], O <: SBoolean.type] (opDesc: LogicalTransformerCompanion, diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/MapCollectionSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/MapCollectionSerializer.scala similarity index 60% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/MapCollectionSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/MapCollectionSerializer.scala index 3e6fab5949..de289b6f64 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/MapCollectionSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/MapCollectionSerializer.scala @@ -1,16 +1,16 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.MapCollection -import sigmastate.{SCollection, SType, SFunc} +import sigma.ast.syntax.SValue +import sigma.ast.{MapCollection, Value} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ +import sigma.ast.{SCollection, SFunc, SType} +import sigma.serialization.CoreByteWriter.DataInfo case class MapCollectionSerializer(cons: (Value[SCollection[SType]], Value[SFunc]) => Value[SType]) extends ValueSerializer[MapCollection[SType, SType]] { - import sigmastate.Operations.MapCollectionInfo._ + import sigma.ast.Operations.MapCollectionInfo._ override def opDesc = MapCollection val thisInfo: DataInfo[SValue] = thisArg val fInfo: DataInfo[SValue] = fArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/NumericCastSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/NumericCastSerializer.scala similarity index 66% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/NumericCastSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/NumericCastSerializer.scala index 88d521ae09..34730c1a39 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/NumericCastSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/NumericCastSerializer.scala @@ -1,12 +1,11 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.{Value, SValue} -import sigmastate._ -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.Transformer +import sigma.ast.syntax.SValue +import sigma.ast.{NumericCastCompanion, SNumericType, SType, Transformer, Value} +import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ case class NumericCastSerializer(opDesc: NumericCastCompanion, cons: (Value[SNumericType], SNumericType) => Value[SNumericType]) diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SigmaTransformerSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/SigmaTransformerSerializer.scala similarity index 75% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SigmaTransformerSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/SigmaTransformerSerializer.scala index e7981bb725..afcbec4a4a 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SigmaTransformerSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/SigmaTransformerSerializer.scala @@ -1,12 +1,12 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.{SigmaTransformerCompanion, SigmaTransformer} -import sigmastate.Values.{SigmaPropValue, SValue} -import sigmastate.serialization.ValueSerializer +import sigma.ast.syntax.{SValue, SigmaPropValue} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} import sigma.util.safeNewArray -import sigmastate.utils.SigmaByteWriter.{DataInfo, valuesItemInfo} -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.serialization.SigmaByteWriter._ import debox.cfor +import sigma.ast.{SigmaTransformer, SigmaTransformerCompanion} +import sigma.serialization.CoreByteWriter.DataInfo case class SigmaTransformerSerializer[I <: SigmaPropValue, O <: SigmaPropValue] (opDesc: SigmaTransformerCompanion, cons: Seq[SigmaPropValue] => SigmaPropValue) diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SimpleTransformerSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/SimpleTransformerSerializer.scala similarity index 54% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SimpleTransformerSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/SimpleTransformerSerializer.scala index f01ae8fd20..38fb571a41 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SimpleTransformerSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/SimpleTransformerSerializer.scala @@ -1,12 +1,11 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.SType -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.{Transformer, SimpleTransformerCompanion} +import sigma.ast.{SType, SimpleTransformerCompanion, Transformer} +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast.Value +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ case class SimpleTransformerSerializer[I <: SType, O <: SType] (opDesc: SimpleTransformerCompanion, diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SliceSerializer.scala b/data/shared/src/main/scala/sigma/serialization/transformers/SliceSerializer.scala similarity index 66% rename from interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SliceSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/transformers/SliceSerializer.scala index ed4f2a15b7..3b159788ba 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/SliceSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/transformers/SliceSerializer.scala @@ -1,16 +1,15 @@ -package sigmastate.serialization.transformers +package sigma.serialization.transformers -import sigmastate.Values.{Value, SValue} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.Slice -import sigmastate.{SInt, SCollection, SType} +import sigma.ast.{Slice, Value} +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ +import sigma.ast.{SCollection, SInt, SType} +import sigma.serialization.CoreByteWriter.DataInfo case class SliceSerializer(cons: (Value[SCollection[SType]], Value[SInt.type], Value[SInt.type]) => Value[SCollection[SType]]) extends ValueSerializer[Slice[SType]] { - import sigmastate.Operations.SliceInfo._ + import sigma.ast.Operations.SliceInfo._ override def opDesc = Slice val thisInfo: DataInfo[SValue] = thisArg val fromInfo: DataInfo[SValue] = fromArg diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/trees/QuadrupleSerializer.scala b/data/shared/src/main/scala/sigma/serialization/trees/QuadrupleSerializer.scala similarity index 74% rename from interpreter/shared/src/main/scala/sigmastate/serialization/trees/QuadrupleSerializer.scala rename to data/shared/src/main/scala/sigma/serialization/trees/QuadrupleSerializer.scala index f6d3f91ff1..d3bdf580a0 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/trees/QuadrupleSerializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/trees/QuadrupleSerializer.scala @@ -1,11 +1,10 @@ -package sigmastate.serialization.trees +package sigma.serialization.trees -import sigmastate.Values._ -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.{Quadruple, _} +import sigma.serialization.CoreByteWriter.DataInfo +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.SigmaByteWriter._ case class QuadrupleSerializer[S1 <: SType, S2 <: SType, S3 <: SType, S4 <: SType] (override val opDesc: QuadrupleCompanion, diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/trees/Relation2Serializer.scala b/data/shared/src/main/scala/sigma/serialization/trees/Relation2Serializer.scala similarity index 84% rename from interpreter/shared/src/main/scala/sigmastate/serialization/trees/Relation2Serializer.scala rename to data/shared/src/main/scala/sigma/serialization/trees/Relation2Serializer.scala index 655fa8f2d2..6b7bca6426 100644 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/trees/Relation2Serializer.scala +++ b/data/shared/src/main/scala/sigma/serialization/trees/Relation2Serializer.scala @@ -1,11 +1,12 @@ -package sigmastate.serialization.trees +package sigma.serialization.trees -import sigmastate.Values._ -import sigmastate._ -import sigmastate.serialization.OpCodes._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.serialization.ValueSerializer._ +import sigma.ast.{SBoolean, SType} +import sigma.serialization.CoreByteWriter.{ArgInfo, Bits, DataInfo, maxBitsInfo} +import sigma.ast._ +import sigma.ast.syntax.SValue +import sigma.serialization.OpCodes._ +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer} +import sigma.serialization.ValueSerializer._ import sigma.util.Extensions._ case class Relation2Serializer[S1 <: SType, S2 <: SType, R <: Value[SBoolean.type]] diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/SparseArrayContainer.scala b/data/shared/src/main/scala/sigma/utils/SparseArrayContainer.scala similarity index 79% rename from interpreter/shared/src/main/scala/sigmastate/utils/SparseArrayContainer.scala rename to data/shared/src/main/scala/sigma/utils/SparseArrayContainer.scala index dad6437f5d..dd65676687 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utils/SparseArrayContainer.scala +++ b/data/shared/src/main/scala/sigma/utils/SparseArrayContainer.scala @@ -1,8 +1,7 @@ -package sigmastate.utils +package sigma.utils -import sigmastate.SType -import sigmastate.Values.Value -import sigmastate.serialization.ValueSerializer +import sigma.ast.{SType, Value} +import sigma.serialization.ValueSerializer import scala.reflect.ClassTag @@ -20,15 +19,20 @@ class SparseArrayContainer[T: ClassTag](values: Seq[(Byte, T)]) { val dupGroups = sers.groupBy { case (b, _) => b }.filter { case (_, g) => g.size > 1 }.toList s"expected distinct codes, got duplicated: $dupGroups" }) - val array = Array.fill[T](256)(null.asInstanceOf[T]) // one item for each OpCode + val array = Array.fill[T](256)(null.asInstanceOf[T]) // one item for each code sers.foreach { case (code, value) => - array(codeToIndex(code)) = value + array(codeToIndex(code)) = value } array } @inline - private def codeToIndex(code: Byte): Int = code + 128 + private def codeToIndex(code: Byte): Int = code + 128 // -128..127 -> 0..255 + + /** @return true if value for the given code is defined + * @param code of a value + */ + @inline def contains(code: Byte): Boolean = sparseArray(codeToIndex(code)) != null /** * Returns value for the given code @@ -62,7 +66,7 @@ class SparseArrayContainer[T: ClassTag](values: Seq[(Byte, T)]) { } object SparseArrayContainer { - + /** Build a container for the given serializers. */ def buildForSerializers(sers: Seq[ValueSerializer[_ <: Value[SType]]]): SparseArrayContainer[ValueSerializer[_ <: Value[SType]]] = { new SparseArrayContainer(sers.map(s => (s.opCode, s))) } diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala new file mode 100644 index 0000000000..45047d4ec8 --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala @@ -0,0 +1,23 @@ +package sigma.interpreter.js + +import sigma.js.JsWrapper +import sigmastate.interpreter.HintsBag + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel + +/** Represents hints used by [[SigmaPropProver]] to perform operations as part of + * multi-signature scheme. See [EIP-11](https://github.com/ergoplatform/eips/pull/8). + */ +@JSExportTopLevel("ProverHints") +class ProverHints(override val wrappedValue: HintsBag) + extends JsWrapper[HintsBag] { +} + +@JSExportTopLevel("ProverHints$") +object ProverHints extends js.Object { + private lazy val _empty = new ProverHints(HintsBag.empty) + + /** Empty bag of hints. Immutable value can be reused where necessary. */ + def empty(): ProverHints = _empty +} diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala new file mode 100644 index 0000000000..176341587c --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala @@ -0,0 +1,50 @@ +package sigma.interpreter.js + +import sigma.data.{CBigInt, ProveDHTuple} +import sigma.js.{Isos, JsWrapper, SigmaProp} +import sigma.util.Extensions.BigIntOps +import sigmastate.crypto.DLogProtocol.DLogProverInput +import sigmastate.crypto.{DiffieHellmanTupleProverInput, SigmaProtocolPrivateInput} + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel + +/** Represents one secret (aka SigmaProtocolPrivateInput) used by [[SigmaPropProver]]. */ +@JSExportTopLevel("ProverSecret") +class ProverSecret( + override val wrappedValue: SigmaProtocolPrivateInput[sigma.data.SigmaLeaf] +) extends JsWrapper[SigmaProtocolPrivateInput[sigma.data.SigmaLeaf]] { + /** Public key generated from the secret. + * Represents proof of knowledge sigma proposition. + */ + def publicKey(): SigmaProp = new SigmaProp(wrappedValue.publicImage) + + /** Secret random number stored in this instance. */ + def secret(): js.BigInt = Isos.isoBigInt.from(CBigInt(wrappedValue.w)) +} + +@JSExportTopLevel("ProverSecret$") +object ProverSecret extends js.Object { + /** Creates a new [[ProverSecret]] instance for the given secret of descrete logarithm + * sigma protocol. + * @param w secret exponent value + */ + def dlog(w: js.BigInt): ProverSecret = { + val input = DLogProverInput(Isos.isoBigInt.to(w).toBigInteger) + new ProverSecret(input) + } + + /** Creates a new [[ProverSecret]] instance for the given secret of Diffie Hellman tuple + * sigma protocol. + * @param w secret exponent value used to compute `u = g^w` and `v = h^w`, where `g` and `h` are generators + * @param dhtProp a [[SigmaProp]] representing public key of Diffie Hellman tuple sigma protocol, should be created using `w` + */ + def dht(w: js.BigInt, dhtProp: SigmaProp): ProverSecret = { + dhtProp.sigmaBoolean match { + case dht: ProveDHTuple => + val input = DiffieHellmanTupleProverInput(Isos.isoBigInt.to(w).toBigInteger, dht) + new ProverSecret(input) + case _ => throw new Exception("Expected ProveDHTuple sigma proposition") + } + } +} \ No newline at end of file diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala new file mode 100644 index 0000000000..a04d32fa54 --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala @@ -0,0 +1,108 @@ +package sigma.interpreter.js + +import sigma.data.{Iso, SigmaBoolean} +import sigma.exceptions.InterpreterException +import sigma.js.{Isos, JsWrapper, SigmaProp} + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array +import scala.util.{Failure, Success} + +/** Prover which can sign messages (generate proofs) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. + * + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. + * + * @see SigmaPropVerifier + */ +@JSExportTopLevel("SigmaPropProver") +class SigmaPropProver(override val wrappedValue: org.ergoplatform.SigmaPropProver) + extends JsWrapper[org.ergoplatform.SigmaPropProver] { + private val isoSP = Isos.isoArrayToIndexed(Iso.identityIso[SigmaProp]) + + private def toSigmaBooleanSeq(sps: js.Array[SigmaProp]): Seq[SigmaBoolean] = { + isoSP.to(sps).map(_.sigmaBoolean) + } + + /** + * A method which is generating commitments for all the public keys provided. + * This is used as part of multi-signature scheme. + * + * Currently only keys in form of ProveDlog and ProveDiffieHellman are supported, not more complex subtrees. + * + * @param sigmaTree - crypto-tree which is being signed + * @param generateFor - public keys for which commitments should be generated + * @return generated commitments in a form of prover hints + * - private, containing secret randomness + * - public, containing only commitments + */ + def generateCommitmentsFor( + sigmaTree: SigmaProp, + generateFor: js.Array[SigmaProp]): ProverHints = { + val pks = toSigmaBooleanSeq(generateFor) + val reduced = wrappedValue.generateCommitmentsFor(sigmaTree.sigmaBoolean, pks) + new ProverHints(reduced) + } + + /** + * A method which is extracting partial proofs of secret knowledge for particular secrets with their + * respective public images given. Useful for distributed signature applications. + * + * See DistributedSigSpecification for examples of usage. + * + * @param sigmaTree - public key (in form of a sigma-tree) + * @param proof - signature for the key + * @param realSecretsToExtract - public keys of secrets with real proofs + * @param simulatedSecretsToExtract - public keys of secrets with simulated proofs + * @return - bag of OtherSecretProven and OtherCommitment hints + */ + def hintsForMultisig( + sigmaTree: SigmaProp, + proof: Int8Array, + realSecretsToExtract: js.Array[SigmaProp], + simulatedSecretsToExtract: js.Array[SigmaProp]): ProverHints = { + val realsToExtract = toSigmaBooleanSeq(realSecretsToExtract) + val simsToExtract = toSigmaBooleanSeq(simulatedSecretsToExtract) + val hints = wrappedValue.bagForMultisig( + context = null, + sigmaTree = sigmaTree.sigmaBoolean, proof.toArray, + realSecretsToExtract = realsToExtract, + simulatedSecretsToExtract = simsToExtract) + new ProverHints(hints) + } + + /** + * Generate commitments for given crypto-tree (sigma-tree) for prover's secrets. + */ + def generateCommitments(sigmaTree: SigmaProp): ProverHints = { + val hints = wrappedValue.generateCommitments(sigmaTree.sigmaBoolean) + new ProverHints(hints) + } + + /** Sign arbitrary message under a key representing a statement provable via a sigma-protocol. + * + * @param sigmaProp - public key + * @param message - message to sign + * @param hintsBag - additional hints for a signer (useful for distributed signing) + * @return - signature or error + */ + def signMessage( + sigmaProp: SigmaProp, + message: Int8Array, + hintsBag: ProverHints): Int8Array = { + wrappedValue.signMessage(sigmaProp.sigmaBoolean, message.toArray, hintsBag.wrappedValue) match { + case Success(signature) => Int8Array.of(signature:_*) + case Failure(t) => throw new InterpreterException("Failed to sign message", Some(t)) + } + } +} + +@JSExportTopLevel("SigmaPropProver$") +object SigmaPropProver extends js.Object { + /** Creates a new [[SigmaPropProver]] with the given secrets. */ + def withSecrets(secrets: js.Array[ProverSecret]): SigmaPropProver = { + val privateInputs = Isos.isoArrayToIndexed(Iso.identityIso[ProverSecret]).to(secrets).map(_.wrappedValue) + new SigmaPropProver(new org.ergoplatform.SigmaPropProver(privateInputs)) + } +} \ No newline at end of file diff --git a/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala new file mode 100644 index 0000000000..caddc37e1a --- /dev/null +++ b/interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala @@ -0,0 +1,43 @@ +package sigma.interpreter.js + +import sigma.js.{JsWrapper, SigmaProp} + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExportTopLevel +import scala.scalajs.js.typedarray.Int8Array + +/** Verifier which can verify signature (proof) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. + * + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. + * + * @see SigmaPropProver + */ +@JSExportTopLevel("SigmaPropVerifier") +class SigmaPropVerifier(override val wrappedValue: org.ergoplatform.SigmaPropVerifier) + extends JsWrapper[org.ergoplatform.SigmaPropVerifier] { + /** + * Verify a signature on given (arbitrary) message for a given sigma proposition (public key). + * + * @param sigmaProp public key (represented as a sigma proposition) + * @param message message + * @param signature signature for the message + * @return whether signature is valid or not (valid signature contains proofs for the sigma proposition) + */ + def verifySignature( + sigmaProp: SigmaProp, + message: Int8Array, + signature: Int8Array): Boolean = { + val ok = wrappedValue.verifySignature(sigmaProp.sigmaBoolean, message.toArray, signature.toArray)(null) + ok + } +} + +@JSExportTopLevel("SigmaPropVerifier$") +object SigmaPropVerifier extends js.Object { + /** Create a new instance of SigmaPropVerifier. */ + def create(): SigmaPropVerifier = { + new SigmaPropVerifier(new org.ergoplatform.SigmaPropVerifier()) + } +} + diff --git a/interpreter/js/src/test/scala/sigmastate/crypto/CryptoContextJsSpec.scala b/interpreter/js/src/test/scala/sigmastate/crypto/CryptoContextJsSpec.scala index e6d8c93009..4fe1e23726 100644 --- a/interpreter/js/src/test/scala/sigmastate/crypto/CryptoContextJsSpec.scala +++ b/interpreter/js/src/test/scala/sigmastate/crypto/CryptoContextJsSpec.scala @@ -2,6 +2,7 @@ package sigmastate.crypto import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec +import sigma.crypto.CryptoContextJs import scala.scalajs.js diff --git a/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala b/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala index 63839e67ef..d80e4fec51 100644 --- a/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala +++ b/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala @@ -2,6 +2,9 @@ package sigmastate.crypto import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec +import sigma.crypto.Platform.Ecp +import sigma.crypto.utils.bytesToHex +import sigma.crypto.{CryptoFacade, CryptoFacadeJs, Point} import scala.scalajs.js import scala.scalajs.js.typedarray.Uint8Array @@ -68,9 +71,9 @@ class CryptoFacadeJsSpec extends AnyPropSpec with Matchers with CryptoTesting { } property("CryptoFacade.getEncodedOfFieldElem") { - utils.bytesToHex(CryptoFacadeJs.getEncodedOfFieldElem(p1.x)) shouldEqual + bytesToHex(CryptoFacadeJs.getEncodedOfFieldElem(p1.x)) shouldEqual "81c5275b1d50c39a0c36c4561c3a37bff1d87e37a9ad69eab029e426c0b1a8ac" - utils.bytesToHex(CryptoFacadeJs.getEncodedOfFieldElem(p1.y)) shouldBe + bytesToHex(CryptoFacadeJs.getEncodedOfFieldElem(p1.y)) shouldBe "db5d999704ec84b62962f3e35889901d04a619cd6d81d251c69d0f625c2dc4f3" } @@ -87,14 +90,14 @@ class CryptoFacadeJsSpec extends AnyPropSpec with Matchers with CryptoTesting { property("CryptoFacade.hashHmacSHA512") { val key = Uint8Array.from(bytesToJsShorts(CryptoFacade.BitcoinSeed)) val data = Uint8Array.from(bytesToJsShorts("abc".getBytes(CryptoFacade.Encoding))) - val res = utils.bytesToHex(CryptoFacadeJs.hashHmacSHA512(key, data)) + val res = bytesToHex(CryptoFacadeJs.hashHmacSHA512(key, data)) res shouldBe "2c15e87cde0f876fd8f060993748330cbe5f37c8bb3355e8ef44cea57890ec1d9b3274ef2b67bbe046cf8a012fba69796ec7803b1cc227521b9f5191e80a7da2" } property("CryptoFacade.generatePbkdf2Key") { val mnemonic = "slow silly start wash bundle suffer bulb ancient height spin express remind today effort helmet" val password = "pwd" - val res = utils.bytesToHex(CryptoFacadeJs.generatePbkdf2Key(mnemonic, password)) + val res = bytesToHex(CryptoFacadeJs.generatePbkdf2Key(mnemonic, password)) res shouldBe "0a8ea2ea0c4c12a9df88b005bda00c4de51ff36834b5fcd6a83667c371ad1da94bca1798690d87f2603b8f51d5ae025209e31f6cf81e12b84e4c543d236e58d0" } } diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index 05b5bde4e2..8468175631 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -1,19 +1,18 @@ package org.ergoplatform -import org.ergoplatform.validation.SigmaValidationSettings -import sigmastate.SType._ -import sigmastate.Values._ -import sigmastate._ +import debox.cfor +import sigma.Extensions.ArrayOps +import sigma.ast.SType.{AnyOps, TypeCode} +import sigma.ast._ +import sigma.data.{AvlTreeData, CAvlTree, CSigmaDslBuilder, SigmaConstants} +import sigma.eval.Extensions.toAnyValue +import sigma.exceptions.InterpreterException +import sigma.interpreter.ContextExtension +import sigma.validation.SigmaValidationSettings +import sigma.{AnyValue, Coll, Header, PreHeader} import sigmastate.eval.Extensions._ import sigmastate.eval._ -import sigmastate.interpreter.ErgoTreeEvaluator.DataEnv -import sigmastate.interpreter.{ContextExtension, ErgoTreeEvaluator, Interpreter, InterpreterContext} -import sigmastate.exceptions.InterpreterException -import sigmastate.serialization.OpCodes -import sigmastate.serialization.OpCodes.OpCode -import sigma.Coll -import sigma.{AnyValue, Header, PreHeader} -import debox.cfor +import sigmastate.interpreter.InterpreterContext /** Represents a script evaluation context to be passed to a prover and a verifier to execute and * validate guarding proposition of input boxes of a transaction. @@ -136,8 +135,8 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData, def withTransaction(newSpendingTransaction: ErgoLikeTransactionTemplate[_ <: UnsignedInput]): ErgoLikeContext = ErgoLikeContext.copy(this)(spendingTransaction = newSpendingTransaction) - override def toSigmaContext(extensions: Map[Byte, AnyValue] = Map()): sigma.Context = { - import Evaluation._ + override def toSigmaContext(): sigma.Context = { + import sigma.Evaluation._ def contextVars(m: Map[Byte, AnyValue]): Coll[AnyValue] = { val maxKey = if (m.keys.isEmpty) 0 else m.keys.max // TODO optimize: max takes 90% of this method @@ -145,7 +144,7 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData, for ((id, v) <- m) { res(id) = v } - CostingSigmaDslBuilder.Colls.fromArray(res) + CSigmaDslBuilder.Colls.fromArray(res) } val dataInputs = this.dataBoxes.toArray.map(_.toTestBox).toColl @@ -161,14 +160,14 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData, val tVal = stypeToRType[SType](v.tpe) k -> toAnyValue(v.value.asWrappedType)(tVal) }.toMap - val vars = contextVars(varMap ++ extensions) + val vars = contextVars(varMap) val avlTree = CAvlTree(lastBlockUtxoRoot) // so selfBox is never one of the `inputs` instances // as result selfBoxIndex is always (erroneously) returns -1 in ErgoTree v0, v1 val selfBox = boxesToSpend(selfIndex).toTestBox val ergoTreeVersion = currentErgoTreeVersion.getOrElse( - Interpreter.error(s"Undefined context property: currentErgoTreeVersion")) - CostingDataContext( + syntax.error(s"Undefined context property: currentErgoTreeVersion")) + CContext( dataInputs, headers, preHeader, inputs, outputs, preHeader.height, selfBox, selfIndex, avlTree, preHeader.minerPk.getEncoded, vars, activatedScriptVersion, ergoTreeVersion) } @@ -243,119 +242,4 @@ 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 - /** Cost of calling Context.minerPubkey Scala method. */ - override val costKind = FixedCost(JitCost(20)) - override val opType = SFunc(SContext, SCollection.SByteArray) - override def companion = this - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - E.context.minerPubKey - } -} - -/** When interpreted evaluates to a IntConstant built from Context.currentHeight */ -case object Height extends NotReadyValueInt with FixedCostValueCompanion { - override def companion = this - override def opCode: OpCode = OpCodes.HeightCode - /** Cost of: 1) Calling Context.HEIGHT Scala method. */ - override val costKind = FixedCost(JitCost(26)) - override val opType = SFunc(SContext, SInt) - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - E.context.HEIGHT - } -} - -/** When interpreted evaluates to a collection of BoxConstant built from Context.boxesToSpend */ -case object Inputs extends LazyCollection[SBox.type] with FixedCostValueCompanion { - override def companion = this - override def opCode: OpCode = OpCodes.InputsCode - /** Cost of: 1) Calling Context.INPUTS Scala method. */ - override val costKind = FixedCost(JitCost(10)) - override def tpe = SCollection.SBoxArray - override val opType = SFunc(SContext, tpe) - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - E.context.INPUTS - } -} - -/** When interpreted evaluates to a collection of BoxConstant built from Context.spendingTransaction.outputs */ -case object Outputs extends LazyCollection[SBox.type] with FixedCostValueCompanion { - override def companion = this - override def opCode: OpCode = OpCodes.OutputsCode - /** Cost of: 1) Calling Context.OUTPUTS Scala method. */ - override val costKind = FixedCost(JitCost(10)) - override def tpe = SCollection.SBoxArray - override val opType = SFunc(SContext, tpe) - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - E.context.OUTPUTS - } -} - -/** When interpreted evaluates to a AvlTreeConstant built from Context.lastBlockUtxoRoot */ -case object LastBlockUtxoRootHash extends NotReadyValueAvlTree with ValueCompanion { - override def companion = this - override def opCode: OpCode = OpCodes.LastBlockUtxoRootHashCode - /** Cost of: 1) Calling Context.LastBlockUtxoRootHash Scala method. */ - override val costKind = FixedCost(JitCost(15)) - - override val opType = SFunc(SContext, tpe) - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - E.context.LastBlockUtxoRootHash - } -} - - -/** When interpreted evaluates to a BoxConstant built from context.boxesToSpend(context.selfIndex) */ -case object Self extends NotReadyValueBox with FixedCostValueCompanion { - override def companion = this - override def opCode: OpCode = OpCodes.SelfCode - /** Cost of: 1) Calling Context.SELF Scala method. */ - override val costKind = FixedCost(JitCost(10)) - override val opType = SFunc(SContext, SBox) - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - E.context.SELF - } -} - -/** When interpreted evaluates to the singleton instance of [[sigma.Context]]. - * Corresponds to `CONTEXT` variable in ErgoScript which can be used like `CONTEXT.headers`. - */ -case object Context extends NotReadyValue[SContext.type] with ValueCompanion { - override def companion = this - override def opCode: OpCode = OpCodes.ContextCode - - /** Cost of: 1) accessing global Context instance. */ - override val costKind = FixedCost(JitCost(1)) - - override def tpe: SContext.type = SContext - override val opType: SFunc = SFunc(SUnit, SContext) - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - E.context - } -} - -/** When interpreted evaluates to the singleton instance of [[sigma.SigmaDslBuilder]]. - * Corresponds to `Global` variable in ErgoScript which can be used like `Global.groupGenerator`. - */ -case object Global extends NotReadyValue[SGlobal.type] with FixedCostValueCompanion { - override def companion = this - override def opCode: OpCode = OpCodes.GlobalCode - /** Cost of: 1) accessing Global instance. */ - override val costKind = FixedCost(JitCost(5)) - override def tpe: SGlobal.type = SGlobal - override val opType: SFunc = SFunc(SUnit, SGlobal) - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(this.costKind) - CostingSigmaDslBuilder - } -} diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala index 1730bed277..e986905fb0 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala @@ -1,9 +1,9 @@ package org.ergoplatform -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ +import sigma.ast.SCollection.SByteArray +import sigma.ast._ +import sigma.ast.syntax._ import sigmastate.interpreter.Interpreter -import sigmastate.utxo._ /** Base class of verifying interpreter which expects ErgoLikeContext as input of diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoTreePredef.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoTreePredef.scala index 8f01a46864..8bba479745 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoTreePredef.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoTreePredef.scala @@ -1,29 +1,29 @@ package org.ergoplatform import org.ergoplatform.settings.MonetarySettings -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.CryptoConstants -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.SCollection.SByteArray -import sigmastate.Values.{LongConstant, SigmaPropConstant, IntArrayConstant, TrueSigmaProp, Value, FalseSigmaProp, SigmaPropValue, IntConstant, ErgoTree} -import sigmastate.utxo._ +import sigma.ast.SCollection.SByteArray +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.data.ProveDlog +import ErgoTree.{HeaderType, ZeroHeader} +import sigma.crypto.CryptoConstants +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate._ -import sigmastate.lang.Terms.ValueOps object ErgoTreePredef { /** Create ErgoTree with `false` proposition, which is never true. * - * @param headerFlags ErgoTree header flags to be combined with default header + * @param header ErgoTree header to be used in the tree * @see ErgoTree.headerWithVersion() */ - def FalseProp(headerFlags: Byte): ErgoTree = ErgoTree.withoutSegregation(headerFlags, FalseSigmaProp) + def FalseProp(header: HeaderType): ErgoTree = ErgoTree.withoutSegregation(header, FalseSigmaProp) /** Create ErgoTree with `true` proposition, which is always true. * - * @param headerFlags ErgoTree header flags to be combined with default header + * @param header ErgoTree header to be used in the tree * @see ErgoTree.headerWithVersion() */ - def TrueProp(headerFlags: Byte): ErgoTree = ErgoTree.withoutSegregation(headerFlags, TrueSigmaProp) + def TrueProp(header: HeaderType): ErgoTree = ErgoTree.withoutSegregation(header, TrueSigmaProp) /** * Byte array value of the serialized reward output script proposition with pk being substituted @@ -41,7 +41,7 @@ object ErgoTreePredef { // first segregated constant is delta, so key is second constant val positions = IntArrayConstant(Array[Int](1)) val minerPubkeySigmaProp = CreateProveDlog(DecodePoint(minerPkBytesVal)) - val newVals = Values.ConcreteCollection(Array[SigmaPropValue](minerPubkeySigmaProp), SSigmaProp) + val newVals = ConcreteCollection(Array[SigmaPropValue](minerPubkeySigmaProp), SSigmaProp) SubstConstants(genericMinerPropBytes, positions, newVals) } @@ -49,10 +49,10 @@ object ErgoTreePredef { * Required script of the box, that collects mining rewards */ def rewardOutputScript(delta: Int, minerPk: ProveDlog): ErgoTree = { - SigmaAnd( + ErgoTree.withSegregation(ZeroHeader, SigmaAnd( GE(Height, Plus(boxCreationHeight(Self), IntConstant(delta))).toSigmaProp, SigmaPropConstant(minerPk) - ).treeWithSegregation + )) } /** @@ -61,11 +61,11 @@ object ErgoTreePredef { */ def feeProposition(delta: Int = 720): ErgoTree = { val out = ByIndex(Outputs, IntConstant(0)) - AND( + ErgoTree.withSegregation(ZeroHeader, AND( EQ(Height, boxCreationHeight(out)), EQ(ExtractScriptBytes(out), expectedMinerOutScriptBytesVal(delta, MinerPubkey)), EQ(SizeOf(Outputs), 1) - ).toSigmaProp.treeWithSegregation + ).toSigmaProp) } /** @@ -91,11 +91,13 @@ object ErgoTreePredef { EQ(ExtractScriptBytes(minerOut), expectedMinerOutScriptBytesVal(s.minerRewardDelay, MinerPubkey)), EQ(Height, boxCreationHeight(minerOut)) ) - AND( - heightIncreased, - correctMinerOutput, - OR(AND(outputsNum, sameScriptRule, correctCoinsConsumed, heightCorrect), lastCoins) - ).toSigmaProp.treeWithSegregation + ErgoTree.withSegregation(ZeroHeader, + AND( + heightIncreased, + correctMinerOutput, + OR(AND(outputsNum, sameScriptRule, correctCoinsConsumed, heightCorrect), lastCoins) + ).toSigmaProp + ) } /** @@ -152,7 +154,7 @@ object ErgoTreePredef { // check, that additional rules defined by foundation members are satisfied val customProposition = DeserializeRegister(ErgoBox.R4, SSigmaProp) // combine 3 conditions above with AND conjunction - SigmaAnd(amountCorrect.toSigmaProp, sameScriptRule.toSigmaProp, customProposition).treeWithSegregation + ErgoTree.withSegregation(ZeroHeader, SigmaAnd(amountCorrect.toSigmaProp, sameScriptRule.toSigmaProp, customProposition)) } /** diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropProver.scala b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropProver.scala new file mode 100644 index 0000000000..09843554fb --- /dev/null +++ b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropProver.scala @@ -0,0 +1,14 @@ +package org.ergoplatform + +import sigmastate.crypto.SigmaProtocolPrivateInput +import sigmastate.interpreter.ProverInterpreter + +/** Prover which can reduce ErgoTrees and prove sigma propositions using provided secrets. + * + * @param secrets All secrets available for this prover. + */ +class SigmaPropProver(override val secrets: Seq[SigmaProtocolPrivateInput[_]]) + extends ErgoLikeInterpreter + with ProverInterpreter { + override type CTX = ErgoLikeContext +} diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropVerifier.scala b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropVerifier.scala new file mode 100644 index 0000000000..382dd9a470 --- /dev/null +++ b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaPropVerifier.scala @@ -0,0 +1,6 @@ +package org.ergoplatform + +/** Verifier which can verify proofs generated by [[SigmaPropProver]]. */ +class SigmaPropVerifier + extends ErgoLikeInterpreter { +} diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala b/interpreter/shared/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala index 627b0a3f54..8acb51403e 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/dsl/AvlTreeHelpers.scala @@ -1,13 +1,12 @@ package org.ergoplatform.dsl import sigma.Coll -import sigmastate.eval.{CAvlTree, CostingSigmaDslBuilder} import scorex.crypto.authds.{ADKey, ADValue} -import scorex.crypto.hash.{Digest32, Blake2b256} -import sigmastate.{AvlTreeData, AvlTreeFlags} +import scorex.crypto.hash.{Blake2b256, Digest32} import sigma.AvlTree import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert} -import CostingSigmaDslBuilder.Colls +import sigma.data.CSigmaDslBuilder.Colls +import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree} object AvlTreeHelpers { diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/settings/MonetarySettings.scala b/interpreter/shared/src/main/scala/org/ergoplatform/settings/MonetarySettings.scala index 15f7b9edbb..60ced82c88 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/settings/MonetarySettings.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/settings/MonetarySettings.scala @@ -1,8 +1,8 @@ package org.ergoplatform.settings -import sigmastate.Values.ErgoTree import org.ergoplatform.ErgoTreePredef import org.ergoplatform.mining.emission.EmissionRules +import sigma.ast.ErgoTree /** * Configuration file for monetary settings of Ergo chain diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/RuleStatusSerializer.scala b/interpreter/shared/src/main/scala/org/ergoplatform/validation/RuleStatusSerializer.scala index e032f0f647..081d573cf1 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/RuleStatusSerializer.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/validation/RuleStatusSerializer.scala @@ -1,7 +1,7 @@ package org.ergoplatform.validation -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.validation.{ChangedRule, DisabledRule, EnabledRule, ReplacedRule, RuleStatus} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} object RuleStatusSerializer extends SigmaSerializer[RuleStatus, RuleStatus] { import RuleStatus._ diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala b/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala index 2c5156ccae..23c18a7b05 100644 --- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala +++ b/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala @@ -1,9 +1,9 @@ package org.ergoplatform.validation -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.serialization.{SerializerException, SigmaByteReader, SigmaByteWriter} +import sigma.serialization.SigmaSerializer import sigma.util.Extensions.IntOps -import sigmastate.exceptions.SerializerException +import sigma.validation.{MapSigmaValidationSettings, SigmaValidationSettings} /** The rules are serialized ordered by ruleId. * This serializer preserves roundtrip identity `deserialize(serialize(_)) = identity` diff --git a/interpreter/shared/src/main/scala/sigmastate/InterpreterReflection.scala b/interpreter/shared/src/main/scala/sigmastate/InterpreterReflection.scala index c6cec6c3da..9a06322166 100644 --- a/interpreter/shared/src/main/scala/sigmastate/InterpreterReflection.scala +++ b/interpreter/shared/src/main/scala/sigmastate/InterpreterReflection.scala @@ -1,91 +1,23 @@ package sigmastate -import org.ergoplatform.ErgoBox.RegisterId +import sigma.SigmaDataReflection +import sigma.data.{CAND, COR, CTHRESHOLD} +import sigma.reflection.mkConstructor import sigma.reflection.ReflectionData.registerClassEntry -import sigma.reflection.{ReflectionData, mkConstructor, mkMethod} -import sigma.Coll -import sigma.{AvlTree, SigmaDslBuilder} -import sigmastate.SAvlTree.KeyValueColl -import sigmastate.SCollection.{SBooleanArray, SByteArray, SIntArray} -import sigmastate.Values._ -import sigmastate.crypto.VerifierMessage.Challenge import sigmastate.crypto.GF2_192_Poly -import sigmastate.interpreter.ErgoTreeEvaluator -import sigmastate.lang.Terms._ -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.utxo._ +import sigmastate.crypto.VerifierMessage.Challenge /** Reflection metadata for `interpreter` module. + * Such metadata is only used on JS platform to support reflection-like interfaces of + * RClass, RMethod, RConstructor. These interfaces implemented on JVM using Java + * reflection. + * * For each class of this module that needs reflection metadata, * we register a class entry with the necessary information. * Only information that is needed at runtime is registered. */ object InterpreterReflection { - val reflection = ReflectionData - - registerClassEntry(classOf[AND], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new AND(args(0).asInstanceOf[Value[SBooleanArray]]) - } - ) - ) - - registerClassEntry(classOf[ArithOp[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Byte])) { args => - new ArithOp(args(0).asInstanceOf[SValue], args(1).asInstanceOf[SValue], args(2).asInstanceOf[OpCode]) - } - ) - ) - - registerClassEntry(classOf[AtLeast], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new AtLeast(args(0).asInstanceOf[IntValue], args(1).asInstanceOf[CollectionValue[SSigmaProp.type]]) - } - ) - ) - - registerClassEntry(classOf[BinAnd], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new BinAnd(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[BoolValue]) - } - ) - ) - - registerClassEntry(classOf[BinOr], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new BinOr(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[BoolValue]) - } - ) - ) - - registerClassEntry(classOf[BinXor], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new BinXor(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[BoolValue]) - } - ) - ) - - registerClassEntry(classOf[BoolToSigmaProp], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new BoolToSigmaProp(args(0).asInstanceOf[BoolValue]) - } - ) - ) - - registerClassEntry(classOf[ByteArrayToBigInt], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new ByteArrayToBigInt(args(0).asInstanceOf[Value[SByteArray]]) - } - ) - ) + val reflection = SigmaDataReflection registerClassEntry(classOf[CAndUncheckedNode], constructors = Array( @@ -151,579 +83,4 @@ object InterpreterReflection { } ) ) - - registerClassEntry(classOf[CalcBlake2b256], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new CalcBlake2b256(args(0).asInstanceOf[Value[SByteArray]]) - } - ) - ) - - registerClassEntry(classOf[CalcSha256], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new CalcSha256(args(0).asInstanceOf[Value[SByteArray]]) - } - ) - ) - - registerClassEntry(classOf[CreateProveDHTuple], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => - new CreateProveDHTuple(args(0).asInstanceOf[GroupElementValue], - args(1).asInstanceOf[GroupElementValue], - args(2).asInstanceOf[GroupElementValue], - args(3).asInstanceOf[GroupElementValue]) - } - ) - ) - - registerClassEntry(classOf[Downcast[_,_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[SNumericType])) { args => - new Downcast(args(0).asInstanceOf[Value[SNumericType]], args(1).asInstanceOf[SNumericType]) - } - ) - ) - - registerClassEntry(classOf[EQ[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new EQ(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[Exponentiate], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new Exponentiate(args(0).asInstanceOf[GroupElementValue], args(1).asInstanceOf[BigIntValue]) - } - ) - ) - - registerClassEntry(classOf[GE[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new GE(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[GT[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new GT(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[If[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => - new If(args(0).asInstanceOf[BoolValue], args(1).asInstanceOf[SAnyValue], args(2).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[LE[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new LE(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[LT[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new LT(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[LogicalNot], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new LogicalNot(args(0).asInstanceOf[BoolValue]) - } - ) - ) - - registerClassEntry(classOf[MultiplyGroup], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new MultiplyGroup(args(0).asInstanceOf[GroupElementValue], args(1).asInstanceOf[GroupElementValue]) - } - ) - ) - - registerClassEntry(classOf[NEQ[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new NEQ(args(0).asInstanceOf[SAnyValue], args(1).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[Negation[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new Negation(args(0).asInstanceOf[SAnyValue]) - } - ) - ) - - registerClassEntry(classOf[OR], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new OR(args(0).asInstanceOf[Value[SBooleanArray]]) - } - ) - ) - - { val clazz = SAvlTree.getClass - registerClassEntry(clazz, - methods = Map( - mkMethod(clazz, "update_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SAvlTree.type].update_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[AvlTree], - args(2).asInstanceOf[KeyValueColl], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "contains_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SAvlTree.type].contains_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[AvlTree], - args(2).asInstanceOf[Coll[Byte]], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "get_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SAvlTree.type].get_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[AvlTree], - args(2).asInstanceOf[Coll[Byte]], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "getMany_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SAvlTree.type].getMany_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[AvlTree], - args(2).asInstanceOf[Coll[Coll[Byte]]], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "remove_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SAvlTree.type].remove_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[AvlTree], - args(2).asInstanceOf[Coll[Coll[Byte]]], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "insert_eval", Array[Class[_]](classOf[MethodCall], classOf[AvlTree], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SAvlTree.type].insert_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[AvlTree], - args(2).asInstanceOf[KeyValueColl], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - } - ) - ) - } - - { val clazz = SCollection.getClass - registerClassEntry(clazz, - methods = Map( - mkMethod(clazz, "zip_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].zip_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], - args(2).asInstanceOf[Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "getOrElse_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Int], classOf[java.lang.Object], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].getOrElse_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], - args(2).asInstanceOf[Int], - args(3).asInstanceOf[Any])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "patch_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Int], classOf[Coll[_]], classOf[Int], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].patch_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], - args(2).asInstanceOf[Int], - args(3).asInstanceOf[Coll[Any]], - args(4).asInstanceOf[Int])(args(5).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "map_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].map_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], - args(2).asInstanceOf[Any => Any])(args(3).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "updated_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Int], classOf[java.lang.Object], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].updated_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], - args(2).asInstanceOf[Int], - args(3))(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "indexOf_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[java.lang.Object], classOf[Int], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].indexOf_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], args(2), args(3).asInstanceOf[Int])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "updateMany_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].updateMany_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], - args(2).asInstanceOf[Coll[Int]], - args(3).asInstanceOf[Coll[Any]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "indices_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].indices_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]])(args(2).asInstanceOf[ErgoTreeEvaluator]) - }, - mkMethod(clazz, "flatMap_eval", Array[Class[_]](classOf[MethodCall], classOf[Coll[_]], classOf[Function1[_,_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SCollection.type].flatMap_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[Coll[Any]], args(2).asInstanceOf[Any => Coll[Any]])(args(3).asInstanceOf[ErgoTreeEvaluator]) - } - ) - ) - } - - registerClassEntry(classOf[SCollectionType[_]], - constructors = Array( - mkConstructor(Array(classOf[SType])) { args => - new SCollectionType(args(0).asInstanceOf[SType]) - } - ) - ) - - { val clazz = SGlobal.getClass - registerClassEntry(clazz, - methods = Map( - mkMethod(clazz, "xor_eval", Array[Class[_]](classOf[MethodCall], classOf[SigmaDslBuilder], classOf[Coll[_]], classOf[Coll[_]], classOf[ErgoTreeEvaluator])) { (obj, args) => - obj.asInstanceOf[SGlobal.type].xor_eval(args(0).asInstanceOf[MethodCall], - args(1).asInstanceOf[SigmaDslBuilder], - args(2).asInstanceOf[Coll[Byte]], - args(3).asInstanceOf[Coll[Byte]])(args(4).asInstanceOf[ErgoTreeEvaluator]) - } - ) - ) - } - - registerClassEntry(classOf[SOption[_]], - constructors = Array( - mkConstructor(Array(classOf[SType])) { args => - new SOption(args(0).asInstanceOf[SType]) - } - ) - ) - - registerClassEntry(classOf[STuple], - constructors = Array( - mkConstructor(Array(classOf[IndexedSeq[_]])) { args => - new STuple(args(0).asInstanceOf[IndexedSeq[SType]]) - } - ) - ) - - registerClassEntry(classOf[SigmaAnd], - constructors = Array( - mkConstructor(Array(classOf[Seq[_]])) { args => - new SigmaAnd(args(0).asInstanceOf[Seq[SigmaPropValue]]) - } - ) - ) - - registerClassEntry(classOf[SigmaOr], - constructors = Array( - mkConstructor(Array(classOf[Seq[_]])) { args => - new SigmaOr(args(0).asInstanceOf[Seq[SigmaPropValue]]) - } - ) - ) - - registerClassEntry(classOf[SubstConstants[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => - new SubstConstants(args(0).asInstanceOf[Value[SByteArray]], - args(1).asInstanceOf[Value[SIntArray]], - args(2).asInstanceOf[CollectionValue[SType]]) - } - ) - ) - - registerClassEntry(classOf[Upcast[_,_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[SNumericType])) { args => - new Upcast(args(0).asInstanceOf[Value[SNumericType]], args(1).asInstanceOf[SNumericType]) - } - ) - ) - - registerClassEntry(classOf[BlockValue], - constructors = Array( - mkConstructor(Array(classOf[IndexedSeq[_]], classOf[Value[_]])) { args => - new BlockValue(args(0).asInstanceOf[IndexedSeq[BlockItem]], args(1).asInstanceOf[SValue]) - } - ) - ) - - registerClassEntry(classOf[ConcreteCollection[_]], - constructors = Array( - mkConstructor(Array(classOf[Seq[_]], classOf[SType])) { args => - new ConcreteCollection(args(0).asInstanceOf[Seq[SValue]], args(1).asInstanceOf[SType]) - } - ) - ) - - registerClassEntry(classOf[FuncValue], - constructors = Array( - mkConstructor(Array(classOf[IndexedSeq[_]], classOf[Value[_]])) { args => - new FuncValue(args(0).asInstanceOf[IndexedSeq[(Int, SType)]], args(1).asInstanceOf[SValue]) - } - ) - ) - - registerClassEntry(classOf[Tuple], - constructors = Array( - mkConstructor(Array(classOf[IndexedSeq[_]])) { args => - new Tuple(args(0).asInstanceOf[IndexedSeq[SValue]]) - } - ) - ) - - registerClassEntry(classOf[ValDef], - constructors = Array( - mkConstructor(Array(classOf[Int], classOf[Seq[_]], classOf[Value[_]])) { args => - new ValDef(args(0).asInstanceOf[Int], args(1).asInstanceOf[Seq[STypeVar]], args(2).asInstanceOf[SValue]) - } - ) - ) - - registerClassEntry(classOf[Apply], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[IndexedSeq[_]])) { args => - new Apply(args(0).asInstanceOf[SValue], args(1).asInstanceOf[IndexedSeq[SValue]]) - } - ) - ) - - registerClassEntry(classOf[ApplyTypes], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Seq[_]])) { args => - new ApplyTypes(args(0).asInstanceOf[SValue], args(1).asInstanceOf[Seq[SType]]) - } - ) - ) - - registerClassEntry(classOf[Block], - constructors = Array( - mkConstructor(Array(classOf[Seq[_]], classOf[Value[_]])) { args => - new Block(args(0).asInstanceOf[Seq[Val]], args(1).asInstanceOf[SValue]) - } - ) - ) - - registerClassEntry(classOf[Lambda], - constructors = Array( - mkConstructor(Array(classOf[Seq[_]], classOf[IndexedSeq[_]], classOf[SType], classOf[Option[_]])) { args => - new Lambda(args(0).asInstanceOf[Seq[STypeParam]], - args(1).asInstanceOf[IndexedSeq[(String, SType)]], - args(2).asInstanceOf[SType], - args(3).asInstanceOf[Option[SValue]]) - } - ) - ) - - registerClassEntry(classOf[MethodCall], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[SMethod], classOf[IndexedSeq[_]], classOf[scala.collection.immutable.Map[_,_]])) { args => - new MethodCall(args(0).asInstanceOf[SValue], - args(1).asInstanceOf[SMethod], - args(2).asInstanceOf[IndexedSeq[SValue]], - args(3).asInstanceOf[Map[STypeVar,SType]]) - } - ) - ) - - registerClassEntry(classOf[MethodCallLike], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[java.lang.String], classOf[IndexedSeq[_]], classOf[SType])) { args => - new MethodCallLike(args(0).asInstanceOf[SValue], - args(1).asInstanceOf[String], - args(2).asInstanceOf[IndexedSeq[SValue]], - args(3).asInstanceOf[SType]) - } - ) - ) - - registerClassEntry(classOf[Select], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[java.lang.String], classOf[Option[_]])) { args => - new Select(args(0).asInstanceOf[SValue], args(1).asInstanceOf[String], args(2).asInstanceOf[Option[SType]]) - } - ) - ) - - registerClassEntry(classOf[ValNode], - constructors = Array( - mkConstructor(Array(classOf[java.lang.String], classOf[SType], classOf[Value[_]])) { args => - new ValNode(args(0).asInstanceOf[String], args(1).asInstanceOf[SType], args(2).asInstanceOf[SValue]) - } - ) - ) - - registerClassEntry(classOf[Append[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new Append(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[CollectionValue[SType]]) - } - ) - ) - - registerClassEntry(classOf[ByIndex[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Option[_]])) { args => - new ByIndex(args(0).asInstanceOf[CollectionValue[SType]], - args(1).asInstanceOf[IntValue], args(2).asInstanceOf[Option[SValue]]) - } - ) - ) - - registerClassEntry(classOf[Exists[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new Exists(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) - } - ) - ) - - registerClassEntry(classOf[ExtractAmount], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new ExtractAmount(args(0).asInstanceOf[BoxValue]) - } - ) - ) - - registerClassEntry(classOf[ExtractBytesWithNoRef], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new ExtractBytesWithNoRef(args(0).asInstanceOf[BoxValue]) - } - ) - ) - - registerClassEntry(classOf[ExtractCreationInfo], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new ExtractCreationInfo(args(0).asInstanceOf[BoxValue]) - } - ) - ) - - registerClassEntry(classOf[ExtractId], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new ExtractId(args(0).asInstanceOf[BoxValue]) - } - ) - ) - - registerClassEntry(classOf[ExtractRegisterAs[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[org.ergoplatform.ErgoBox.RegisterId], classOf[SOption[_]])) { args => - new ExtractRegisterAs(args(0).asInstanceOf[BoxValue], args(1).asInstanceOf[RegisterId], args(2).asInstanceOf[SOption[SAny.type]]) - } - ) - ) - - registerClassEntry(classOf[ExtractScriptBytes], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new ExtractScriptBytes(args(0).asInstanceOf[BoxValue]) - } - ) - ) - - registerClassEntry(classOf[Filter[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new Filter(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) - } - ) - ) - - registerClassEntry(classOf[Fold[_,_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => - new Fold(args(0).asInstanceOf[CollectionValue[SType]], - args(1).asInstanceOf[SValue], args(2).asInstanceOf[Value[SFunc]]) - } - ) - ) - - registerClassEntry(classOf[ForAll[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new ForAll(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) - } - ) - ) - - registerClassEntry(classOf[MapCollection[_,_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new MapCollection(args(0).asInstanceOf[CollectionValue[SType]], args(1).asInstanceOf[Value[SFunc]]) - } - ) - ) - - registerClassEntry(classOf[OptionGet[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new OptionGet(args(0).asInstanceOf[Value[SOption[SType]]]) - } - ) - ) - - registerClassEntry(classOf[OptionGetOrElse[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]])) { args => - new OptionGetOrElse(args(0).asInstanceOf[Value[SOption[SType]]], args(1).asInstanceOf[SValue]) - } - ) - ) - - registerClassEntry(classOf[OptionIsDefined[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new OptionIsDefined(args(0).asInstanceOf[Value[SOption[SType]]]) - } - ) - ) - - registerClassEntry(classOf[SelectField], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Byte])) { args => - new SelectField(args(0).asInstanceOf[Value[STuple]], args(1).asInstanceOf[Byte]) - } - ) - ) - - registerClassEntry(classOf[SigmaPropBytes], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new SigmaPropBytes(args(0).asInstanceOf[SigmaPropValue]) - } - ) - ) - - registerClassEntry(classOf[SizeOf[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]])) { args => - new SizeOf(args(0).asInstanceOf[CollectionValue[SType]]) - } - ) - ) - - registerClassEntry(classOf[Slice[_]], - constructors = Array( - mkConstructor(Array(classOf[Value[_]], classOf[Value[_]], classOf[Value[_]])) { args => - new Slice(args(0).asInstanceOf[CollectionValue[SType]], - args(1).asInstanceOf[IntValue], args(2).asInstanceOf[IntValue]) - } - ) - ) } diff --git a/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala index 20341cbcc6..fabd120d1c 100644 --- a/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala +++ b/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala @@ -1,19 +1,19 @@ -package sigmastate +package sigma.serialization -import sigmastate.crypto.{BigIntegers, GF2_192_Poly} +import debox.cfor import scorex.util.encode.Base16 -import sigmastate.Values.SigmaBoolean -import sigmastate.crypto.DLogProtocol.{ProveDlog, SecondDLogProverMessage} -import sigmastate.crypto.VerifierMessage.Challenge -import sigmastate.crypto.{CryptoConstants, ProveDHTuple, SecondDHTupleProverMessage} -import sigmastate.interpreter.ErgoTreeEvaluator.{fixedCostOp, perItemCostOp} -import sigmastate.interpreter.{ErgoTreeEvaluator, NamedDesc, OperationCostInfo} -import sigmastate.serialization.SigmaSerializer +import sigma.Extensions.ArrayOps +import sigma.ast.{FixedCost, JitCost, NamedDesc, OperationCostInfo, PerItemCost} +import sigma.crypto.{BigIntegers, CryptoConstants} +import sigma.data.{CAND, COR, CTHRESHOLD, ProveDHTuple, ProveDlog, SigmaBoolean} import sigma.util.safeNewArray -import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter} -import debox.cfor -import sigmastate.eval.Extensions.ArrayOps -import sigmastate.exceptions.SerializerException +import sigmastate.{CAndUncheckedNode, COrUncheckedNode, CThresholdUncheckedNode, NoProof, UncheckedDiffieHellmanTuple, UncheckedSchnorr, UncheckedSigmaTree, UncheckedTree} +import sigmastate.crypto.DLogProtocol.SecondDLogProverMessage +import sigmastate.crypto.VerifierMessage.Challenge +import sigmastate.crypto.{GF2_192_Poly, SecondDHTupleProverMessage} +import sigmastate.interpreter.CErgoTreeEvaluator.{fixedCostOp, perItemCostOp} +import sigmastate.interpreter.CErgoTreeEvaluator +import sigmastate.utils.Helpers /** Contains implementation of signature (aka proof) serialization. * @@ -27,7 +27,7 @@ class SigSerializer { val hashSize = CryptoConstants.soundnessBits / 8 /** Number of bytes to represent any group element as byte array */ - val order = CryptoConstants.groupSize + val order = sigma.crypto.groupSize /** Recursively traverses the given node and serializes challenges and prover messages * to the given writer. @@ -119,7 +119,7 @@ class SigSerializer { * the execution. * @return An instance of [[UncheckedTree]] i.e. either [[NoProof]] or [[UncheckedSigmaTree]] */ - def parseAndComputeChallenges(exp: SigmaBoolean, proof: Array[Byte])(implicit E: ErgoTreeEvaluator): UncheckedTree = { + def parseAndComputeChallenges(exp: SigmaBoolean, proof: Array[Byte])(implicit E: CErgoTreeEvaluator): UncheckedTree = { if (proof.isEmpty) NoProof else { @@ -181,7 +181,7 @@ class SigSerializer { def parseAndComputeChallenges( exp: SigmaBoolean, r: SigmaByteReader, - challengeOpt: Challenge = null)(implicit E: ErgoTreeEvaluator): UncheckedSigmaTree = { + challengeOpt: Challenge = null)(implicit E: CErgoTreeEvaluator): UncheckedSigmaTree = { // Verifier Step 2: Let e_0 be the challenge in the node here (e_0 is called "challenge" in the code) val challenge = if (challengeOpt == null) { Challenge @@ readBytesChecked(r, hashSize, diff --git a/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala b/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala index a8b9bf4faf..2072b2e647 100644 --- a/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala +++ b/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala @@ -1,9 +1,9 @@ package sigmastate -import sigmastate.crypto.DLogProtocol.{FirstDLogProverMessage, ProveDlog, SecondDLogProverMessage} +import sigma.data.{ProveDHTuple, ProveDlog} +import sigmastate.crypto.DLogProtocol.{FirstDLogProverMessage, SecondDLogProverMessage} import sigmastate.crypto.VerifierMessage.Challenge -import sigmastate.crypto.{FirstDHTupleProverMessage, ProveDHTuple, SecondDHTupleProverMessage} -import sigmastate.crypto.GF2_192_Poly +import sigmastate.crypto.{FirstDHTupleProverMessage, GF2_192_Poly, SecondDHTupleProverMessage} sealed trait UncheckedTree extends ProofTree diff --git a/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala b/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala index 4e45c230f2..4324dfdd09 100644 --- a/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala +++ b/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala @@ -1,18 +1,18 @@ package sigmastate -import java.math.BigInteger -import sigmastate.Values.{ErgoTree, SigmaBoolean, SigmaPropConstant} -import sigmastate.crypto.DLogProtocol.{FirstDLogProverMessage, ProveDlog} -import sigmastate.crypto.VerifierMessage.Challenge -import sigmastate.crypto.{FirstDHTupleProverMessage, FirstProverMessage, ProveDHTuple} -import sigmastate.interpreter.{ErgoTreeEvaluator, NamedDesc, OperationCostInfo} -import sigmastate.interpreter.ErgoTreeEvaluator.fixedCostOp -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.SigmaByteWriter import debox.cfor -import sigmastate.crypto.GF2_192_Poly -import scala.language.existentials +import sigma.data.{CAND, COR, CTHRESHOLD, ProveDHTuple, ProveDlog, SigmaBoolean, SigmaLeaf} +import sigma.ast.ErgoTree.ZeroHeader +import sigma.ast.{ErgoTree, FixedCost, JitCost, NamedDesc, OperationCostInfo, SigmaPropConstant} +import sigma.eval.ErgoTreeEvaluator +import sigmastate.crypto.DLogProtocol.FirstDLogProverMessage +import sigmastate.crypto.VerifierMessage.Challenge +import sigmastate.crypto.{FirstDHTupleProverMessage, FirstProverMessage, GF2_192_Poly} +import sigmastate.interpreter.CErgoTreeEvaluator.fixedCostOp +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer +import sigma.serialization.{SigmaByteWriter, SigmaSerializer} + +import java.math.BigInteger object ConjectureType extends Enumeration { val AndConjecture = Value(0) @@ -255,7 +255,7 @@ object FiatShamirTree { case _: UncheckedDiffieHellmanTuple | _: UnprovenDiffieHellmanTuple => ToBytes_DHT } fixedCostOp(costInfo) { - val propTree = ErgoTree.withSegregation(SigmaPropConstant(l.proposition)) + val propTree = ErgoTree.withSegregation(ZeroHeader, SigmaPropConstant(l.proposition)) val propBytes = DefaultSerializer.serializeErgoTree(propTree) val commitmentBytes = l.commitmentOpt.get.bytes w.put(leafPrefix) diff --git a/interpreter/shared/src/main/scala/sigmastate/Values.scala b/interpreter/shared/src/main/scala/sigmastate/Values.scala deleted file mode 100644 index df79471ee8..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/Values.scala +++ /dev/null @@ -1,1497 +0,0 @@ -package sigmastate - -import java.math.BigInteger -import java.util.{Arrays, Objects} -import sigma.kiama.rewriting.Rewriter.{count, everywherebu, strategy} -import org.ergoplatform.settings.ErgoAlgos -import org.ergoplatform.validation.ValidationException -import sigma.data.{Nullable, RType} -import sigma.util.CollectionUtil._ -import sigmastate.SCollection.{SByteArray, SIntArray} -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.interpreter.{CompanionDesc, ErgoTreeEvaluator, Interpreter, NamedDesc} -import sigmastate.serialization._ -import sigmastate.serialization.OpCodes._ -import sigmastate.TrivialProp.{FalseProp, TrueProp} -import sigmastate.Values.ErgoTree.substConstants -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.{CryptoConstants, ProveDHTuple} -import sigmastate.lang.Terms._ -import sigmastate.utxo._ -import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigma.util.Extensions.ByteOps -import sigmastate.interpreter.ErgoTreeEvaluator._ -import debox.cfor -import scorex.util.encode.Base16 -import sigmastate.exceptions.InterpreterException - -import scala.language.implicitConversions -import scala.reflect.ClassTag -import sigmastate.lang.CheckingSigmaBuilder._ -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.serialization.transformers.ProveDHTupleSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigma.{AvlTree, Coll, Colls, Header, PreHeader, _} -import sigmastate.lang.SourceContext -import sigma.util.safeNewArray - -import scala.collection.compat.immutable.ArraySeq -import scala.collection.mutable - -object Values { - /** Force initialization of reflection. */ - val reflection = InterpreterReflection - - type SValue = Value[SType] - - /** Base class for all ErgoTree expression nodes. - * @see [[sigmastate.Values.ErgoTree]] - */ - abstract class Value[+S <: SType] extends SigmaNode { - /** The companion node descriptor with opCode, cost and other metadata. */ - def companion: ValueCompanion - - /** Unique id of the node class used in serialization of ErgoTree. */ - def opCode: OpCode = companion.opCode - - /** The type of the value represented by this node. If the value is an operation it is - * the type of operation result. */ - def tpe: S - - /** Every value represents an operation and that operation can be associated with a function type, - * describing functional meaning of the operation, kind of operation signature. - * Thus, we can obtain global operation identifiers by combining Value.opName with Value.opType, - * so that if (v1.opName == v2.opName) && (v1.opType == v2.opType) then v1 and v2 are functionally - * point-wise equivalent. - * This in particular means that if two _different_ ops have the same opType they _should_ have - * different opNames. - * Thus defined op ids are used in a v4.x Cost Model - a table of all existing primitives coupled with - * performance parameters. - * */ - def opType: SFunc - - /** Name of the operation. */ - def opName: String = this.getClass.getSimpleName - - /** Transforms this expression to SigmaProp expression or throws an exception. */ - def toSigmaProp: SigmaPropValue = this match { - case b if b.tpe == SBoolean => BoolToSigmaProp(this.asBoolValue) - case p if p.tpe == SSigmaProp => p.asSigmaProp - case _ => sys.error(s"Expected SBoolean or SSigmaProp typed value, but was: $this") - } - - /** Parser has some source information like line,column in the text. We need to keep it up until RuntimeCosting. - * The way to do this is to add Nullable property to every Value. Since Parser is always using SigmaBuilder - * to create nodes, - * Adding additional (implicit source: SourceContext) parameter to every builder method would pollute its API - * and also doesn't make sence during deserialization, where Builder is also used. - * We can assume some indirect mechanism to pass current source context into every mkXXX method of Builder. - * We can pass it using `scala.util.DynamicVariable` by wrapping each mkXXX call into `withValue { }` calls. - * The same will happen in Typer. - * We can take sourceContext from untyped nodes and use it while creating typed nodes. - * And we can make sourceContext of every Value writeOnce value, i.e. it will be Nullable.Null by default, - * but can be set afterwards, but only once. - * This property will not participate in equality and other operations, so will be invisible for existing code. - * But Builder can use it to set sourceContext if it is present. - */ - private[sigmastate] var _sourceContext: Nullable[SourceContext] = Nullable.None - def sourceContext: Nullable[SourceContext] = _sourceContext - def sourceContext_=(srcCtx: Nullable[SourceContext]): Unit = - if (_sourceContext.isEmpty) { - _sourceContext = srcCtx - } else { - sys.error("_sourceContext can be set only once") - } - - /** Defines an evaluation semantics of this tree node (aka Value or expression) in the given data environment. - * Should be implemented by all the ErgoTree nodes (aka operations). - * Thus, the ErgoTree interpreter implementation consists of combined implementations of this method. - * NOTE, this method shouldn't be called directly, instead use `evalTo` method. - * - * @param E Evaluator which defines evaluation context, cost accumulator, settings etc. - * @param env immutable map, which binds variables (given by ids) to the values - * @return the data value which is the result of evaluation - */ - protected def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = - sys.error(s"Should be overriden in ${this.getClass}: $this") - - /** Evaluates this node to the value of the given expected type. - * This method should called from all `eval` implementations. - * - * @tparam T expected type of the resulting value - * @param E Evaluator which defines evaluation context, cost accumulator, settings etc. - * @param env immutable map, which binds variables (given by ids) to the values - * @return the data value which is the result of evaluation - */ - @inline - final def evalTo[T](env: DataEnv)(implicit E: ErgoTreeEvaluator): T = { - if (E.settings.isMeasureOperationTime) E.profiler.onBeforeNode(this) - val v = eval(env) - if (E.settings.isMeasureOperationTime) E.profiler.onAfterNode(this) - v.asInstanceOf[T] - } - - /** Add the cost given by the kind to the accumulator and associate it with this operation - * node. - */ - @inline - final def addCost(costKind: FixedCost)(implicit E: ErgoTreeEvaluator): Unit = { - E.addCost(costKind, this.companion.opDesc) - } - - /** Add the cost given by the descriptor to the accumulator and associate it with this operation - * node. - */ - @inline - final def addCost[R](costKind: TypeBasedCost, tpe: SType)(block: () => R)(implicit E: ErgoTreeEvaluator): R = { - E.addTypeBasedCost(costKind, tpe, this.companion.opDesc)(block) - } - - /** Add the cost of a repeated operation to the accumulator and associate it with this - * operation. The number of items (loop iterations) is known in advance (like in - * Coll.map operation) - * - * @param costKind cost descriptor of the operation - * @param nItems number of operations known in advance (before loop execution) - */ - @inline - final def addSeqCostNoOp(costKind: PerItemCost, nItems: Int) - (implicit E: ErgoTreeEvaluator): Unit = { - E.addSeqCostNoOp(costKind, nItems, this.companion.opDesc) - } - - /** Add the cost of a repeated operation to the accumulator and associate it with this - * operation. The number of items (loop iterations) is known in advance (like in - * Coll.map operation) - * - * @param costKind cost descriptor of the operation - * @param nItems number of operations known in advance (before loop execution) - * @param block operation executed under the given cost - * @tparam R result type of the operation - */ - @inline - final def addSeqCost[R](costKind: PerItemCost, nItems: Int) - (block: () => R)(implicit E: ErgoTreeEvaluator): R = { - E.addSeqCost(costKind, nItems, this.companion.opDesc)(block) - } - } - - object Value { - type PropositionCode = Byte - - implicit def liftByte (n: Byte) : Value[SByte.type] = ByteConstant(n) - implicit def liftShort(n: Short): Value[SShort.type] = ShortConstant(n) - implicit def liftInt (n: Int) : Value[SInt.type] = IntConstant(n) - implicit def liftLong (n: Long) : Value[SLong.type] = LongConstant(n) - - implicit def liftByteArray(arr: Array[Byte]): Value[SByteArray] = ByteArrayConstant(arr) - - implicit def liftBigInt(arr: BigInt): Value[SBigInt.type] = BigIntConstant(arr) - - implicit def liftGroupElement(g: GroupElement): Value[SGroupElement.type] = GroupElementConstant(g) - implicit def liftECPoint(g: EcPointType): Value[SGroupElement.type] = GroupElementConstant(g) - - implicit def liftSigmaProp(g: SigmaProp): Value[SSigmaProp.type] = SigmaPropConstant(g) - implicit def liftSigmaBoolean(sb: SigmaBoolean): Value[SSigmaProp.type] = SigmaPropConstant(SigmaDsl.SigmaProp(sb)) - - object Typed { - def unapply(v: SValue): Option[(SValue, SType)] = Some((v, v.tpe)) - } - def notSupportedError(v: Any, opName: String) = - throw new IllegalArgumentException(s"Method $opName is not supported for node $v") - - /** Immutable empty array of values. Can be used to avoid allocation. */ - val EmptyArray = Array.empty[SValue] - - /** Immutable empty Seq of values. Can be used to avoid allocation. */ - val EmptySeq: IndexedSeq[SValue] = EmptyArray - - /** Traverses the given expression tree and counts the number of deserialization - * operations. If it is non-zero, returns true. */ - def hasDeserialize(exp: SValue): Boolean = { - val deserializeNode: PartialFunction[Any, Int] = { - case _: DeserializeContext[_] => 1 - case _: DeserializeRegister[_] => 1 - } - val c = count(deserializeNode)(exp) - c > 0 - } - - def typeError(node: SValue, evalResult: Any) = { - val tpe = node.tpe - throw new InterpreterException( - s"""Invalid type returned by evaluator: - | expression: $node - | expected type: $tpe - | resulting value: $evalResult - """.stripMargin) - } - - def typeError(tpe: SType, evalResult: Any) = { - throw new InterpreterException( - s"""Invalid type returned by evaluator: - | expected type: $tpe - | resulting value: $evalResult - """.stripMargin) - } - - def checkType(node: SValue, evalResult: Any) = { - val tpe = node.tpe - if (!SType.isValueOfType(evalResult, tpe)) - typeError(node, evalResult) - } - - def checkType(tpe: SType, evalResult: Any) = { - if (!SType.isValueOfType(evalResult, tpe)) - typeError(tpe, evalResult) - } - } - - /** Base class for all companion objects which are used as operation descriptors. */ - trait ValueCompanion extends SigmaNodeCompanion { - import ValueCompanion._ - /** Unique id of the node class used in serialization of ErgoTree. */ - def opCode: OpCode - - /** Returns cost descriptor of this operation. */ - def costKind: CostKind - - override def toString: String = s"${this.getClass.getSimpleName}(${opCode.toUByte})" - - def typeName: String = this.getClass.getSimpleName.replace("$", "") - - def init() { - if (this.opCode != 0 && _allOperations.contains(this.opCode)) - throw sys.error(s"Operation $this already defined") - _allOperations += (this.opCode -> this) - } - - init() - - val opDesc = CompanionDesc(this) - } - object ValueCompanion { - private val _allOperations: mutable.HashMap[Byte, ValueCompanion] = mutable.HashMap.empty - lazy val allOperations = _allOperations.toMap - } - - /** Should be inherited by companion objects of operations with fixed cost kind. */ - trait FixedCostValueCompanion extends ValueCompanion { - /** Returns cost descriptor of this operation. */ - override def costKind: FixedCost - } - - /** Should be inherited by companion objects of operations with per-item cost kind. */ - trait PerItemCostValueCompanion extends ValueCompanion { - /** Returns cost descriptor of this operation. */ - override def costKind: PerItemCost - } - - /** Base class for ErgoTree nodes which represents a data value which has already been - * evaluated and no further evaluation (aka reduction) is necessary by the interpreter. - * - * @see Constant, ConcreteCollection, Tuple - */ - abstract class EvaluatedValue[+S <: SType] extends Value[S] { - /** The evaluated data value of the corresponding underlying data type. */ - val value: S#WrappedType - - override def opType: SFunc = { - val resType = tpe match { - case ct : SCollection[_] => - SCollection(ct.typeParams.head.ident) - case ft @ SFunc(_, _, _) => - ft.getGenericType - case _ => tpe - } - SFunc(ArraySeq.empty, resType) - } - } - - /** Base class for all constant literals whose data value is already known and never - * changes. - * @see ConstantNode - */ - abstract class Constant[+S <: SType] extends EvaluatedValue[S] - - /** ErgoTree node which represents data literals, i.e. data values embedded in an - * expression. - * - * @param value data value of the underlying Scala type - * @param tpe type descriptor of the data value and also the type of the value - * represented by this node. - * @see Constant - */ - case class ConstantNode[S <: SType](value: S#WrappedType, tpe: S) extends Constant[S] { - require(sigmastate.crypto.Platform.isCorrectType(value, tpe), - s"Invalid type of constant value $value, expected type $tpe") - override def companion: ValueCompanion = Constant - override def opCode: OpCode = companion.opCode - override def opName: String = s"Const" - - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(Constant.costKind) - value - } - - override def equals(obj: scala.Any): Boolean = (obj != null) && (this.eq(obj.asInstanceOf[AnyRef]) || (obj match { - case c: Constant[_] => tpe == c.tpe && Objects.deepEquals(value, c.value) - case _ => false - })) - - override def hashCode(): Int = Arrays.deepHashCode(Array(value.asInstanceOf[AnyRef], tpe)) - - override def toString: String = tpe.asInstanceOf[SType] match { - case SGroupElement if value.isInstanceOf[GroupElement] => - s"ConstantNode(${showECPoint(value.asInstanceOf[GroupElement])},$tpe)" - case SGroupElement => - sys.error(s"Invalid value in Constant($value, $tpe)") - case SInt => s"IntConstant($value)" - case SLong => s"LongConstant($value)" - case SBoolean if value == true => "TrueLeaf" - case SBoolean if value == false => "FalseLeaf" - case _ => s"ConstantNode($value,$tpe)" - } - } - - object Constant extends FixedCostValueCompanion { - override def opCode: OpCode = ConstantCode - /** Cost of: returning value from Constant node. */ - override val costKind = FixedCost(JitCost(5)) - - /** Immutable empty array, can be used to save allocations in many places. */ - val EmptyArray = Array.empty[Constant[SType]] - - /** Immutable empty IndexedSeq, can be used to save allocations in many places. */ - val EmptySeq: IndexedSeq[Constant[SType]] = Array.empty[Constant[SType]] - - /** Helper factory method. */ - def apply[S <: SType](value: S#WrappedType, tpe: S): Constant[S] = ConstantNode(value, tpe) - - /** Recognizer of Constant tree nodes used in patterns. */ - def unapply[S <: SType](v: EvaluatedValue[S]): Option[(S#WrappedType, S)] = v match { - case ConstantNode(value, tpe) => Some((value, tpe)) - case _ => None - } - - } - - /** Placeholder for a constant in ErgoTree. Zero based index in ErgoTree.constants array. */ - case class ConstantPlaceholder[S <: SType](id: Int, override val tpe: S) extends Value[S] { - def opType = SFunc(SInt, tpe) - override def companion: ValueCompanion = ConstantPlaceholder - override protected def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - val c = E.constants(id) - addCost(ConstantPlaceholder.costKind) - val res = c.value - Value.checkType(c, res) - res - } - } - object ConstantPlaceholder extends ValueCompanion { - override def opCode: OpCode = ConstantPlaceholderCode - /** Cost of: accessing Constant in array by index. */ - override val costKind = FixedCost(JitCost(1)) - } - - trait NotReadyValue[S <: SType] extends Value[S] { - } - - /** Base class for references to context variables. */ - trait ContextVariable[S <: SType] extends NotReadyValue[S] { - } - - /** Reference a context variable by id. */ - trait TaggedVariable[T <: SType] extends ContextVariable[T] { - val varId: Byte - } - - case class TaggedVariableNode[T <: SType](varId: Byte, override val tpe: T) - extends TaggedVariable[T] { - override def companion = TaggedVariable - def opType: SFunc = Value.notSupportedError(this, "opType") - } - - object TaggedVariable extends ValueCompanion { - override def opCode: OpCode = TaggedVariableCode - override def costKind: CostKind = FixedCost(JitCost(1)) - def apply[T <: SType](varId: Byte, tpe: T): TaggedVariable[T] = - TaggedVariableNode(varId, tpe) - } - - /** High-level interface to internal representation of Unit constants in ErgoTree. */ - object UnitConstant { - /** ErgoTree node that represent a literal of Unit type. It is global immutable value - * which should be reused wherever necessary to avoid allocations. - */ - val instance = apply() - - /** Constucts a fresh new instance of Unit literal node. */ - def apply() = Constant[SUnit.type]((), SUnit) - - /** Recognizer to pattern match on Unit constant literal nodes (aka Unit constants). */ - def unapply(node: SValue): Boolean = node match { - case ConstantNode(_, SUnit) => true - case _ => false - } - } - - type BoolValue = Value[SBoolean.type] - type ByteValue = Value[SByte.type] - type ShortValue = Value[SShort.type] - type IntValue = Value[SInt.type] - type LongValue = Value[SLong.type] - type StringValue = Value[SString.type] - type BigIntValue = Value[SBigInt.type] - type BoxValue = Value[SBox.type] - type GroupElementValue = Value[SGroupElement.type] - type SigmaPropValue = Value[SSigmaProp.type] - type AvlTreeValue = Value[SAvlTree.type] - type SAnyValue = Value[SAny.type] - - type ByteConstant = Constant[SByte.type] - type ShortConstant = Constant[SShort.type] - type IntConstant = Constant[SInt.type] - type LongConstant = Constant[SLong.type] - type StringConstant = Constant[SString.type] - type BigIntConstant = Constant[SBigInt.type] - type BoxConstant = Constant[SBox.type] - type GroupElementConstant = Constant[SGroupElement.type] - type SigmaPropConstant = Constant[SSigmaProp.type] - type AvlTreeConstant = Constant[SAvlTree.type] - - object ByteConstant { - def apply(value: Byte): Constant[SByte.type] = Constant[SByte.type](value, SByte) - } - object ShortConstant { - def apply(value: Short): Constant[SShort.type] = Constant[SShort.type](value, SShort) - } - object IntConstant { - def apply(value: Int): Constant[SInt.type] = Constant[SInt.type](value, SInt) - def unapply(v: SValue): Option[Int] = v match { - case Constant(value: Int, SInt) => Some(value) - case _ => None - } - - def Zero = IntConstant(0) - } - object LongConstant { - def apply(value: Long): Constant[SLong.type] = Constant[SLong.type](value, SLong) - def unapply(v: SValue): Option[Long] = v match { - case Constant(value: Long, SLong) => Some(value) - case _ => None - } - } - object BigIntConstant { - def apply(value: BigInt): Constant[SBigInt.type] = Constant[SBigInt.type](value, SBigInt) - def apply(value: BigInteger): Constant[SBigInt.type] = Constant[SBigInt.type](SigmaDsl.BigInt(value), SBigInt) - def apply(value: Long): Constant[SBigInt.type] = Constant[SBigInt.type](SigmaDsl.BigInt(BigInteger.valueOf(value)), SBigInt) - } - - object StringConstant { - def apply(value: String): Constant[SString.type] = Constant[SString.type](value, SString) - def unapply(v: SValue): Option[String] = v match { - case Constant(value: String, SString) => Some(value) - case _ => None - } - } - - object BoxConstant { - def apply(value: Box): Constant[SBox.type] = Constant[SBox.type](value, SBox) - } - - object GroupElementConstant { - def apply(value: EcPointType): Constant[SGroupElement.type] = apply(SigmaDsl.GroupElement(value)) - def apply(value: GroupElement): Constant[SGroupElement.type] = Constant[SGroupElement.type](value, SGroupElement) - def unapply(v: SValue): Option[GroupElement] = v match { - case Constant(value: GroupElement, SGroupElement) => Some(value) - case _ => None - } - } - - val FalseSigmaProp = SigmaPropConstant(SigmaDsl.SigmaProp(TrivialProp.FalseProp)) - val TrueSigmaProp = SigmaPropConstant(SigmaDsl.SigmaProp(TrivialProp.TrueProp)) - - implicit def boolToSigmaProp(b: BoolValue): SigmaPropValue = BoolToSigmaProp(b) - - object SigmaPropConstant { - def apply(value: SigmaProp): Constant[SSigmaProp.type] = Constant[SSigmaProp.type](value, SSigmaProp) - def apply(value: SigmaBoolean): Constant[SSigmaProp.type] = Constant[SSigmaProp.type](SigmaDsl.SigmaProp(value), SSigmaProp) - def unapply(v: SValue): Option[SigmaProp] = v match { - case Constant(value: SigmaProp, SSigmaProp) => Some(value) - case _ => None - } - } - - object AvlTreeConstant { - def apply(value: AvlTree): Constant[SAvlTree.type] = Constant[SAvlTree.type](value, SAvlTree) - } - - object PreHeaderConstant { - def apply(value: PreHeader): Constant[SPreHeader.type] = Constant[SPreHeader.type](value, SPreHeader) - def unapply(v: SValue): Option[PreHeader] = v match { - case Constant(value: PreHeader, SPreHeader) => Some(value) - case _ => None - } - } - - object HeaderConstant { - def apply(value: Header): Constant[SHeader.type] = Constant[SHeader.type](value, SHeader) - def unapply(v: SValue): Option[Header] = v match { - case Constant(value: Header, SHeader) => Some(value) - case _ => None - } - } - - trait NotReadyValueInt extends NotReadyValue[SInt.type] { - override def tpe = SInt - } - - trait NotReadyValueLong extends NotReadyValue[SLong.type] { - override def tpe = SLong - } - - trait NotReadyValueBigInt extends NotReadyValue[SBigInt.type] { - override def tpe = SBigInt - } - - type TaggedBoolean = TaggedVariable[SBoolean.type] - type TaggedByte = TaggedVariable[SByte.type] - type TaggedShort = TaggedVariable[SShort.type] - type TaggedInt = TaggedVariable[SInt.type] - type TaggedLong = TaggedVariable[SLong.type] - type TaggedBigInt = TaggedVariable[SBigInt.type] - type TaggedBox = TaggedVariable[SBox.type] - type TaggedGroupElement = TaggedVariable[SGroupElement.type] - type TaggedSigmaProp = TaggedVariable[SSigmaProp.type] - type TaggedAvlTree = TaggedVariable[SAvlTree.type] - type TaggedByteArray = TaggedVariable[SCollection[SByte.type]] - - def TaggedBox(id: Byte): Value[SBox.type] = mkTaggedVariable(id, SBox) - def TaggedAvlTree(id: Byte): Value[SAvlTree.type] = mkTaggedVariable(id, SAvlTree) - - /** Base type for evaluated tree nodes of Coll type. */ - trait EvaluatedCollection[T <: SType, C <: SCollection[T]] extends EvaluatedValue[C] { - /** Type descriptor of the collection elements. */ - def elementType: T - } - - type CollectionConstant[T <: SType] = Constant[SCollection[T]] - type CollectionValue[T <: SType] = Value[SCollection[T]] - - object CollectionConstant { - def apply[T <: SType](value: Coll[T#WrappedType], elementType: T): Constant[SCollection[T]] = - Constant[SCollection[T]](value, SCollection(elementType)) - def unapply[T <: SType](node: Value[SCollection[T]]): Option[(Coll[T#WrappedType], T)] = node match { - case c: Constant[SCollection[a]] @unchecked if c.tpe.isCollection => - val v = c.value.asInstanceOf[Coll[T#WrappedType]] - val t = c.tpe.elemType - Some((v, t)) - case _ => None - } - } - - val ByteArrayTypeCode = (SCollectionType.CollectionTypeCode + SByte.typeCode).toByte - - object ByteArrayConstant { - def apply(value: Coll[Byte]): CollectionConstant[SByte.type] = CollectionConstant[SByte.type](value, SByte) - def apply(value: Array[Byte]): CollectionConstant[SByte.type] = CollectionConstant[SByte.type](value.toColl, SByte) - def unapply(node: SValue): Option[Coll[Byte]] = node match { - case coll: CollectionConstant[SByte.type] @unchecked => coll match { - case CollectionConstant(arr, SByte) => Some(arr) - case _ => None - } - case _ => None - } - } - - object ShortArrayConstant { - def apply(value: Coll[Short]): CollectionConstant[SShort.type] = CollectionConstant[SShort.type](value, SShort) - def apply(value: Array[Short]): CollectionConstant[SShort.type] = CollectionConstant[SShort.type](value.toColl, SShort) - def unapply(node: SValue): Option[Coll[Short]] = node match { - case coll: CollectionConstant[SShort.type] @unchecked => coll match { - case CollectionConstant(arr, SShort) => Some(arr) - case _ => None - } - case _ => None - } - } - - object IntArrayConstant { - def apply(value: Coll[Int]): CollectionConstant[SInt.type] = CollectionConstant[SInt.type](value, SInt) - def apply(value: Array[Int]): CollectionConstant[SInt.type] = CollectionConstant[SInt.type](value.toColl, SInt) - def unapply(node: SValue): Option[Coll[Int]] = node match { - case coll: CollectionConstant[SInt.type] @unchecked => coll match { - case CollectionConstant(arr, SInt) => Some(arr) - case _ => None - } - case _ => None - } - } - - object LongArrayConstant { - def apply(value: Coll[Long]): CollectionConstant[SLong.type] = CollectionConstant[SLong.type](value, SLong) - def apply(value: Array[Long]): CollectionConstant[SLong.type] = CollectionConstant[SLong.type](value.toColl, SLong) - def unapply(node: SValue): Option[Coll[Long]] = node match { - case coll: CollectionConstant[SLong.type] @unchecked => coll match { - case CollectionConstant(arr, SLong) => Some(arr) - case _ => None - } - case _ => None - } - } - - object BigIntArrayConstant { - def apply(value: Coll[BigInt]): CollectionConstant[SBigInt.type] = CollectionConstant[SBigInt.type](value, SBigInt) - def apply(value: Array[BigInt]): CollectionConstant[SBigInt.type] = CollectionConstant[SBigInt.type](value.toColl, SBigInt) - def unapply(node: SValue): Option[Coll[BigInt]] = node match { - case coll: CollectionConstant[SBigInt.type] @unchecked => coll match { - case CollectionConstant(arr, SBigInt) => Some(arr) - case _ => None - } - case _ => None - } - } - - val BoolArrayTypeCode = (SCollectionType.CollectionTypeCode + SBoolean.typeCode).toByte - - object BoolArrayConstant { - def apply(value: Coll[Boolean]): CollectionConstant[SBoolean.type] = CollectionConstant[SBoolean.type](value, SBoolean) - def apply(value: Array[Boolean]): CollectionConstant[SBoolean.type] = apply(value.toColl) - def unapply(node: SValue): Option[Coll[Boolean]] = node match { - case coll: CollectionConstant[SBoolean.type] @unchecked => coll match { - case CollectionConstant(arr, SBoolean) => Some(arr) - case _ => None - } - case _ => None - } - } - - trait NotReadyValueByteArray extends NotReadyValue[SByteArray] { - override def tpe = SByteArray - } - - trait NotReadyValueAvlTree extends NotReadyValue[SAvlTree.type] { - override def tpe = SAvlTree - } - - /** ErgoTree node that represents the operation of obtaining the generator of elliptic curve group. - * The generator g of the group is an element of the group such that, when written - * multiplicative form, every element of the group is a power of g. - */ - case object GroupGenerator extends EvaluatedValue[SGroupElement.type] with ValueCompanion { - override def opCode: OpCode = OpCodes.GroupGeneratorCode - override val costKind = FixedCost(JitCost(10)) - override def tpe = SGroupElement - override val value = SigmaDsl.GroupElement(CryptoConstants.dlogGroup.generator) - override def companion = this - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(costKind) - SigmaDsl.groupGenerator - } - } - - - trait NotReadyValueGroupElement extends NotReadyValue[SGroupElement.type] { - override def tpe = SGroupElement - } - - type BooleanConstant = Constant[SBoolean.type] - - object BooleanConstant { - def fromBoolean(v: Boolean): BooleanConstant = if (v) TrueLeaf else FalseLeaf - def apply(value: Boolean): BooleanConstant = Constant[SBoolean.type](value, SBoolean) - def unapply(v: SValue): Option[Boolean] = v match { - case Constant(value: Boolean, SBoolean) => Some(value) - case _ => None - } - } - - /** ErgoTree node which represents `true` literal. */ - object TrueLeaf extends ConstantNode[SBoolean.type](true, SBoolean) with ValueCompanion { - override def companion = this - override def opCode: OpCode = TrueCode - override def costKind: FixedCost = Constant.costKind - override def toString: String = "TrueLeaf" - } - - /** ErgoTree node which represents `false` literal. */ - object FalseLeaf extends ConstantNode[SBoolean.type](false, SBoolean) with ValueCompanion { - override def companion = this - override def opCode: OpCode = FalseCode - override def costKind: FixedCost = Constant.costKind - override def toString: String = "FalseLeaf" - } - - trait NotReadyValueBoolean extends NotReadyValue[SBoolean.type] { - override def tpe = SBoolean - } - - /** Algebraic data type of sigma proposition expressions. - * Values of this type are used as values of SigmaProp type of SigmaScript and SigmaDsl - */ - trait SigmaBoolean { - /** Unique id of the node class used in serialization of SigmaBoolean. */ - val opCode: OpCode - /** Size of the proposition tree (number of nodes). */ - def size: Int - } - - object SigmaBoolean { - /** Compute total size of the trees in the collection of children. */ - def totalSize(children: Seq[SigmaBoolean]): Int = { - var res = 0 - val len = children.length - cfor(0)(_ < len, _ + 1) { i => - res += children(i).size - } - res - } - - /** HOTSPOT: don't beautify this code */ - object serializer extends SigmaSerializer[SigmaBoolean, SigmaBoolean] { - val dhtSerializer = ProveDHTupleSerializer(ProveDHTuple.apply) - val dlogSerializer = ProveDlogSerializer(ProveDlog.apply) - - override def serialize(data: SigmaBoolean, w: SigmaByteWriter): Unit = { - w.put(data.opCode) - data match { - case dlog: ProveDlog => dlogSerializer.serialize(dlog, w) - case dht: ProveDHTuple => dhtSerializer.serialize(dht, w) - case _: TrivialProp => // besides opCode no additional bytes - case and: CAND => - val nChildren = and.children.length - w.putUShort(nChildren) - cfor(0)(_ < nChildren, _ + 1) { i => - val c = and.children(i) - serializer.serialize(c, w) - } - - case or: COR => - val nChildren = or.children.length - w.putUShort(nChildren) - cfor(0)(_ < nChildren, _ + 1) { i => - val c = or.children(i) - serializer.serialize(c, w) - } - - case th: CTHRESHOLD => - w.putUShort(th.k) - val nChildren = th.children.length - w.putUShort(nChildren) - cfor(0)(_ < nChildren, _ + 1) { i => - val c = th.children(i) - serializer.serialize(c, w) - } - } - } - - override def parse(r: SigmaByteReader): SigmaBoolean = { - val depth = r.level - r.level = depth + 1 - val opCode = r.getByte() - val res = opCode match { - case FalseProp.opCode => FalseProp - case TrueProp.opCode => TrueProp - case ProveDlogCode => dlogSerializer.parse(r) - case ProveDiffieHellmanTupleCode => dhtSerializer.parse(r) - case AndCode => - val n = r.getUShort() - val children = safeNewArray[SigmaBoolean](n) - cfor(0)(_ < n, _ + 1) { i => - children(i) = serializer.parse(r) - } - CAND(children) - case OrCode => - val n = r.getUShort() - val children = safeNewArray[SigmaBoolean](n) - cfor(0)(_ < n, _ + 1) { i => - children(i) = serializer.parse(r) - } - COR(children) - case AtLeastCode => - val k = r.getUShort() - val n = r.getUShort() - val children = safeNewArray[SigmaBoolean](n) - cfor(0)(_ < n, _ + 1) { i => - children(i) = serializer.parse(r) - } - CTHRESHOLD(k, children) - } - r.level = r.level - 1 - res - } - } - } - - trait NotReadyValueBox extends NotReadyValue[SBox.type] { - def tpe = SBox - } - - /** ErgoTree node which converts a collection of expressions into a tuple of data values - * of different types. Each data value of the resulting collection is obtained by - * evaluating the corresponding expression in `items`. All items may have different - * types. - * - * @param items source collection of expressions - */ - case class Tuple(items: IndexedSeq[Value[SType]]) - extends EvaluatedValue[STuple] // note, this superclass is required as Tuple can be in a register - with EvaluatedCollection[SAny.type, STuple] { - override def companion = Tuple - override lazy val tpe = STuple(items.map(_.tpe)) - override def opType: SFunc = ??? - override def elementType: SAny.type = SAny - - override lazy val value = { - val xs = items.cast[EvaluatedValue[SAny.type]].map(_.value) - Colls.fromArray(xs.toArray(SAny.classTag.asInstanceOf[ClassTag[SAny.WrappedType]]))(sigma.AnyType) - } - - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - // in v5.0 version we support only tuples of 2 elements to be equivalent with v4.x - if (items.length != 2) - Interpreter.error(s"Invalid tuple $this") - - val item0 = items(0) - val x = item0.evalTo[Any](env) - Value.checkType(item0, x) - - val item1 = items(1) - val y = item1.evalTo[Any](env) - Value.checkType(item1, y) - - val res = (x, y) // special representation for pairs (to pass directly to Coll primitives) - - addCost(Tuple.costKind) - res - } - } - - object Tuple extends FixedCostValueCompanion { - override def opCode: OpCode = TupleCode - /** Cost of: 1) allocating a new tuple (of limited max size)*/ - override val costKind = FixedCost(JitCost(15)) - def apply(items: Value[SType]*): Tuple = Tuple(items.toIndexedSeq) - } - - /** ErgoTree node which converts a collection of expressions into a collection of data - * values. Each data value of the resulting collection is obtained by evaluating the - * corresponding expression in `items`. All items must have the same type. - * - * @param items source collection of expressions - * @param elementType type descriptor of elements in the resulting collection - */ - case class ConcreteCollection[V <: SType](items: Seq[Value[V]], elementType: V) - extends EvaluatedCollection[V, SCollection[V]] { -// TODO uncomment and make sure Ergo works with it, i.e. complex data types are never used for `items`. -// There is nothing wrong in using List, Vector and other fancy types as a concrete representation -// of `items`, but these types have sub-optimal performance (2-3x overhead comparing to WrappedArray) -// which is immediately visible in profile. -// NOTE, the assert below should be commented before production release. -// Is it there for debuging only, basically to catch call stacks where the fancy types may -// occasionally be used. -// assert( -// items.isInstanceOf[mutable.WrappedArray[_]] || -// items.isInstanceOf[ArrayBuffer[_]] || -// items.isInstanceOf[mutable.ArraySeq[_]], -// s"Invalid types of items ${items.getClass}") - - private val isBooleanConstants = elementType == SBoolean && items.forall(_.isInstanceOf[Constant[_]]) - override def companion = - if (isBooleanConstants) ConcreteCollectionBooleanConstant - else ConcreteCollection - - val tpe = SCollection[V](elementType) - implicit lazy val tElement: RType[V#WrappedType] = Evaluation.stypeToRType(elementType) - - // TODO refactor: this method is not used and can be removed - lazy val value = { - val xs = items.cast[EvaluatedValue[V]].map(_.value) - Colls.fromArray(xs.toArray(elementType.classTag.asInstanceOf[ClassTag[V#WrappedType]])) - } - - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - val len = items.length - addCost(ConcreteCollection.costKind) - val is = Array.ofDim[V#WrappedType](len)(tElement.classTag) - cfor(0)(_ < len, _ + 1) { i => - val item = items(i) - val itemV = item.evalTo[V#WrappedType](env) - Value.checkType(item, itemV) // necessary because cast to V#WrappedType is erased - is(i) = itemV - } - Colls.fromArray(is) - } - } - object ConcreteCollection extends FixedCostValueCompanion { - override def opCode: OpCode = ConcreteCollectionCode - /** Cost of: allocating new collection - * @see ConcreteCollection_PerItem */ - override val costKind = FixedCost(JitCost(20)) - - def fromSeq[V <: SType](items: Seq[Value[V]])(implicit tV: V): ConcreteCollection[V] = - ConcreteCollection(items, tV) - - def fromItems[V <: SType](items: Value[V]*)(implicit tV: V): ConcreteCollection[V] = - ConcreteCollection(items, tV) - } - object ConcreteCollectionBooleanConstant extends ValueCompanion { - override def opCode: OpCode = ConcreteCollectionBooleanConstantCode - override def costKind = ConcreteCollection.costKind - } - - trait LazyCollection[V <: SType] extends NotReadyValue[SCollection[V]] - - implicit class CollectionOps[T <: SType](val coll: Value[SCollection[T]]) extends AnyVal { - def length: Int = matchCase(_.items.length, _.value.length, _.items.length) - def items = matchCase(_.items, _ => sys.error(s"Cannot get 'items' property of node $coll"), _.items) -// def isEvaluatedCollection = -// coll.evaluated && matchCase(_.items.forall(_.evaluated), _ => true, _.items.forall(_.evaluated)) - def matchCase[R]( - whenConcrete: ConcreteCollection[T] => R, - whenConstant: CollectionConstant[T] => R, - whenTuple: Tuple => R - ): R = coll match { - case cc: ConcreteCollection[T]@unchecked => whenConcrete(cc) - case const: CollectionConstant[T]@unchecked => whenConstant(const) - case tuple: Tuple => whenTuple(tuple) - case _ => sys.error(s"Unexpected node $coll") - } - } - - implicit class SigmaPropValueOps(val p: Value[SSigmaProp.type]) extends AnyVal { - def isProven: Value[SBoolean.type] = SigmaPropIsProven(p) - def propBytes: Value[SByteArray] = SigmaPropBytes(p) - def treeWithSegregation: ErgoTree = ErgoTree.withSegregation(p) - def treeWithSegregation(headerFlags: Byte): ErgoTree = - ErgoTree.withSegregation(headerFlags, p) - } - - implicit class SigmaBooleanOps(val sb: SigmaBoolean) extends AnyVal { - def toSigmaProp: SigmaPropValue = SigmaPropConstant(sb) - def isProven: Value[SBoolean.type] = SigmaPropIsProven(SigmaPropConstant(sb)) - def showToString: String = sb match { - case ProveDlog(v) => - s"ProveDlog(${showECPoint(v)})" - case ProveDHTuple(gv, hv, uv, vv) => - s"ProveDHTuple(${showECPoint(gv)}, ${showECPoint(hv)}, ${showECPoint(uv)}, ${showECPoint(vv)})" - case _ => sb.toString - } - } - - sealed trait BlockItem extends NotReadyValue[SType] { - def id: Int - def rhs: SValue - def isValDef: Boolean - } - object BlockItem { - /** Immutable empty array, can be used to save allocations in many places. */ - val EmptyArray = Array.empty[BlockItem] - - /** Immutable empty IndexedSeq to save allocations in many places. */ - val EmptySeq: IndexedSeq[BlockItem] = EmptyArray - } - - /** IR node for let-bound expressions `let x = rhs` which is ValDef, or `let f[T] = rhs` which is FunDef. - * These nodes are used to represent ErgoTrees after common sub-expression elimination. - * This representation is more compact in serialized form. - * @param id unique identifier of the variable in the current scope. */ - case class ValDef(override val id: Int, - tpeArgs: Seq[STypeVar], - override val rhs: SValue) extends BlockItem { - require(id >= 0, "id must be >= 0") - override def companion = if (tpeArgs.isEmpty) ValDef else FunDef - override def tpe: SType = rhs.tpe - override def isValDef: Boolean = tpeArgs.isEmpty - /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = Value.notSupportedError(this, "opType") - } - object ValDef extends ValueCompanion { - override def opCode: OpCode = ValDefCode - override def costKind = Value.notSupportedError(this, "costKind") - def apply(id: Int, rhs: SValue): ValDef = ValDef(id, Nil, rhs) - } - object FunDef extends ValueCompanion { - override def opCode: OpCode = FunDefCode - override def costKind = Value.notSupportedError(this, "costKind") - def unapply(d: BlockItem): Option[(Int, Seq[STypeVar], SValue)] = d match { - case ValDef(id, targs, rhs) if !d.isValDef => Some((id, targs, rhs)) - case _ => None - } - } - - /** Special node which represents a reference to ValDef it was introduced as result of - * CSE. */ - case class ValUse[T <: SType](valId: Int, tpe: T) extends NotReadyValue[T] { - override def companion = ValUse - /** This is not used as operation, but rather to form a program structure */ - def opType: SFunc = Value.notSupportedError(this, "opType") - - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(ValUse.costKind) - val res = env.getOrElse(valId, error(s"cannot resolve $this")) - Value.checkType(this, res) - res - } - } - object ValUse extends FixedCostValueCompanion { - override def opCode: OpCode = ValUseCode - /** Cost of: 1) Lookup in immutable HashMap by valId: Int 2) alloc of Some(v) */ - override val costKind = FixedCost(JitCost(5)) - } - - /** The order of ValDefs in the block is used to assign ids to ValUse(id) nodes - * For all i: items(i).id == {number of ValDefs preceded in a graph} with respect to topological order. - * Specific topological order doesn't really matter, what is important is to preserve semantic linkage - * between ValUse(id) and ValDef with the corresponding id. - * This convention allow to valid serializing ids because we always serializing and deserializing - * in a fixed well defined order. - */ - case class BlockValue(items: IndexedSeq[BlockItem], result: SValue) extends NotReadyValue[SType] { - override def companion = BlockValue - def tpe: SType = result.tpe - - /** This is not used as operation, but rather to form a program structure */ - def opType: SFunc = Value.notSupportedError(this, "opType") - - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - var curEnv = env - val len = items.length - addSeqCostNoOp(BlockValue.costKind, len) - cfor(0)(_ < len, _ + 1) { i => - val vd = items(i).asInstanceOf[ValDef] - val v = vd.rhs.evalTo[Any](curEnv) - Value.checkType(vd, v) - E.addFixedCost(FuncValue.AddToEnvironmentDesc_CostKind, - FuncValue.AddToEnvironmentDesc) { - curEnv = curEnv + (vd.id -> v) - } - } - val res = result.evalTo[Any](curEnv) - Value.checkType(result, res) - res - } - } - object BlockValue extends ValueCompanion { - override def opCode: OpCode = BlockValueCode - override val costKind = PerItemCost( - baseCost = JitCost(1), perChunkCost = JitCost(1), chunkSize = 10) - } - /** - * @param args parameters list, where each parameter has an id and a type. - * @param body expression, which refers function parameters with ValUse. - */ - case class FuncValue(args: IndexedSeq[(Int,SType)], body: Value[SType]) extends NotReadyValue[SFunc] { - import FuncValue._ - override def companion = FuncValue - lazy val tpe: SFunc = { - val nArgs = args.length - val argTypes = new Array[SType](nArgs) - cfor(0)(_ < nArgs, _ + 1) { i => - argTypes(i) = args(i)._2 - } - SFunc(argTypes, body.tpe) - } - /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = SFunc(ArraySeq.empty, tpe) - - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(FuncValue.costKind) - if (args.length == 1) { - val arg0 = args(0) - (vArg: Any) => { - Value.checkType(arg0._2, vArg) - var env1: DataEnv = null - E.addFixedCost(AddToEnvironmentDesc_CostKind, AddToEnvironmentDesc) { - env1 = env + (arg0._1 -> vArg) - } - val res = body.evalTo[Any](env1) - Value.checkType(body, res) - res - } - } else { - Interpreter.error(s"Function must have 1 argument, but was: $this") - } - } - } - object FuncValue extends FixedCostValueCompanion { - val AddToEnvironmentDesc = NamedDesc("AddToEnvironment") - /** Cost of: adding value to evaluator environment */ - val AddToEnvironmentDesc_CostKind = FixedCost(JitCost(5)) - override def opCode: OpCode = FuncValueCode - /** Cost of: 1) switch on the number of args 2) allocating a new Scala closure - * Old cost: ("Lambda", "() => (D1) => R", lambdaCost),*/ - override val costKind = FixedCost(JitCost(5)) - def apply(argId: Int, tArg: SType, body: SValue): FuncValue = - FuncValue(IndexedSeq((argId,tArg)), body) - } - - implicit class OptionValueOps[T <: SType](val p: Value[SOption[T]]) extends AnyVal { - def get: Value[T] = OptionGet(p) - def getOrElse(default: Value[T]): Value[T] = OptionGetOrElse(p, default) - def isDefined: Value[SBoolean.type] = OptionIsDefined(p) - } - - def GetVarBoolean(varId: Byte): GetVar[SBoolean.type] = GetVar(varId, SBoolean) - def GetVarByte(varId: Byte): GetVar[SByte.type] = GetVar(varId, SByte) - def GetVarShort(varId: Byte): GetVar[SShort.type] = GetVar(varId, SShort) - def GetVarInt(varId: Byte): GetVar[SInt.type] = GetVar(varId, SInt) - def GetVarLong(varId: Byte): GetVar[SLong.type] = GetVar(varId, SLong) - def GetVarBigInt(varId: Byte): GetVar[SBigInt.type] = GetVar(varId, SBigInt) - def GetVarBox(varId: Byte): GetVar[SBox.type] = GetVar(varId, SBox) - def GetVarSigmaProp(varId: Byte): GetVar[SSigmaProp.type] = GetVar(varId, SSigmaProp) - def GetVarByteArray(varId: Byte): GetVar[SCollection[SByte.type]] = GetVar(varId, SByteArray) - def GetVarIntArray(varId: Byte): GetVar[SCollection[SInt.type]] = GetVar(varId, SIntArray) - - /** This is alternative representation of ErgoTree expression when it cannot be parsed - * due to `error`. This is used by the nodes running old versions of code to recognize - * soft-fork conditions and skip validation of box propositions which are unparsable. */ - case class UnparsedErgoTree(bytes: mutable.WrappedArray[Byte], error: ValidationException) - - /** The root of ErgoScript IR. Serialized instances of this class are self sufficient and can be passed around. - * ErgoTreeSerializer defines top-level serialization format of the scripts. - * The interpretation of the byte array depend on the first `header` byte, which uses VLQ encoding up to 30 bits. - * Currently we define meaning for only first byte, which may be extended in future versions. - * 7 6 5 4 3 2 1 0 - * ------------------------- - * | | | | | | | | | - * ------------------------- - * Bit 7 == 1 if the header contains more than 1 byte (default == 0) - * Bit 6 - reserved for GZIP compression (should be 0) - * Bit 5 == 1 - reserved for context dependent costing (should be = 0) - * Bit 4 == 1 if constant segregation is used for this ErgoTree (default = 0) - * (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/264) - * Bit 3 == 1 if size of the whole tree is serialized after the header byte (default = 0) - * Bits 2-0 - language version (current version == 0) - * - * Currently we don't specify interpretation for the second and other bytes of the header. - * We reserve the possibility to extend header by using Bit 7 == 1 and chain additional bytes as in VLQ. - * Once the new bytes are required, a new version of the language should be created and implemented. - * That new language will give an interpretation for the new bytes. - * - * Consistency between fields is ensured by private constructor and factory methods in `ErgoTree` object. - * For performance reasons, ErgoTreeSerializer can be configured to perform additional constant segregation. - * In such a case after deserialization there may be more constants segregated. This is done for example to - * support caching optimization described in #264 mentioned above. - * - * The default behavior of ErgoTreeSerializer is to preserve original structure of ErgoTree and check - * consistency. In case of any inconsistency the serializer throws exception. - * - * @param header the first byte of serialized byte array which determines interpretation of the rest of the array - * - * @param constants If isConstantSegregation == true contains the constants for which there may be - * ConstantPlaceholders in the tree. - * If isConstantSegregation == false this array should be empty and any placeholder in - * the tree will lead to exception. - * - * @param root On the right side it has valid expression of `SigmaProp` type. Or alternatively, - * on the left side, it has unparsed bytes along with the ValidationException, - * which caused the deserializer to fail. - * `Right(tree)` if isConstantSegregation == true contains ConstantPlaceholder - * instead of some Constant nodes. Otherwise, it may not contain placeholders. - * It is possible to have both constants and placeholders in the tree, but for every placeholder - * there should be a constant in `constants` array. - * @param givenComplexity structural complexity of the tree or 0 if is not specified at construction time. - * Access to this private value is provided via `complexity` property. - * In case of 0, the complexity is computed using ErgoTree deserializer, which can do this. - * When specified it should be computed as the sum of complexity values taken - * from ComplexityTable for all tree nodes. It approximates the time needed to process - * the tree by sigma compiler to obtain cost formula. Overly complex trees can thus - * be rejected even before compiler starts working. - * @param propositionBytes original bytes of this tree from which it has been deserialized. - * If null then the bytes are not provided, and will be lazily generated when `bytes` - * method is called. - * These bytes are obtained in two ways: - * 1) in the ErgoTreeSerializer from Reader - * 2) in the alternative constructor using ErgoTreeSerializer.serializeErgoTree - * @param givenDeserialize optional flag, which contains information about presence of - * deserialization operations in the tree. If it is None, the information is not - * available. If Some(true) then there are deserialization operations, otherwise - * the tree doesn't contain deserialization and is eligible - * for optimized execution. - * ErgoTreeSerializer parsing method computes the value of - * this flag and provides it to the constructor. - */ - case class ErgoTree private[sigmastate]( - header: Byte, - constants: IndexedSeq[Constant[SType]], - root: Either[UnparsedErgoTree, SigmaPropValue], - private val givenComplexity: Int, - private val propositionBytes: Array[Byte], - private val givenDeserialize: Option[Boolean] - ) { - - def this(header: Byte, - constants: IndexedSeq[Constant[SType]], - root: Either[UnparsedErgoTree, SigmaPropValue]) = - this( - header, constants, root, 0, - propositionBytes = DefaultSerializer.serializeErgoTree( - ErgoTree(header, constants, root, 0, null, None) - ), - givenDeserialize = None - ) - - require(isConstantSegregation || constants.isEmpty) - require(version == 0 || hasSize, s"For newer version the size bit is required: $this") - - /** Then it throws the error from UnparsedErgoTree. - * It does so on every usage of `proposition` because the lazy value remains uninitialized. - */ - @deprecated("Use toProposition instead", "v2.1") - lazy val proposition: SigmaPropValue = toProposition(isConstantSegregation) - - @inline final def version: Byte = ErgoTree.getVersion(header) - @inline final def isRightParsed: Boolean = root.isRight - @inline final def isConstantSegregation: Boolean = ErgoTree.isConstantSegregation(header) - @inline final def hasSize: Boolean = ErgoTree.hasSize(header) - - private[sigmastate] var _bytes: Array[Byte] = propositionBytes - - /** Serialized bytes of this tree. */ - final def bytes: Array[Byte] = { - if (_bytes == null) { - _bytes = DefaultSerializer.serializeErgoTree(this) - } - _bytes - } - - /** Hexadecimal encoded string of ErgoTree.bytes. */ - final def bytesHex: String = ErgoAlgos.encode(bytes) - - private var _complexity: Int = givenComplexity - - /** Structural complexity estimation of this tree. - * @see ComplexityTable - */ - lazy val complexity: Int = { - if (_complexity == 0) { - _complexity = DefaultSerializer.deserializeErgoTree(bytes).complexity - } - _complexity - } - - private[sigmastate] var _hasDeserialize: Option[Boolean] = givenDeserialize - - /** Returns true if the tree contains at least one deserialization operation, - * false otherwise. - */ - lazy val hasDeserialize: Boolean = { - if (_hasDeserialize.isEmpty) { - _hasDeserialize = Some(root match { - case Right(p) => Value.hasDeserialize(p) - case _ => false - }) - } - _hasDeserialize.get - } - - /** Serialized proposition expression of SigmaProp type with - * ConstantPlaceholder nodes not replaced by Constant nodes. - */ - lazy val template: Array[Byte] = { - val r = SigmaSerializer.startReader(bytes) - DefaultSerializer.deserializeHeaderWithTreeBytes(r)._4 - } - - /** Base16 encoding of `template` bytes. */ - def templateHex: String = Base16.encode(template) - - /** Get proposition expression from this contract. - * When root.isRight then - * if replaceConstants == false this is the same as `root.right.get`. - * Otherwise, it is equivalent to `root.right.get` where all placeholders are replaced by Constants. - * When root.isLeft then - * throws the error from UnparsedErgoTree. - * It does so on every usage of `proposition` because the lazy value remains uninitialized. - */ - def toProposition(replaceConstants: Boolean): SigmaPropValue = root match { - case Right(tree) => - val prop = if (replaceConstants) - substConstants(tree, constants).asSigmaProp - else - tree - prop - case Left(UnparsedErgoTree(_, error)) => - throw error - } - - /** The default equality of case class is overridden to exclude `complexity`. */ - override def canEqual(that: Any): Boolean = that.isInstanceOf[ErgoTree] - - override def hashCode(): Int = header * 31 + Objects.hash(constants, root) - - override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || - ((obj.asInstanceOf[AnyRef] != null) && (obj match { - case other: ErgoTree => - other.header == header && other.constants == constants && other.root == root - case _ => false - })) - } - - object ErgoTree { - /** Current version of ErgoTree serialization format (aka bite-code language version)*/ - val VersionFlag: Byte = 0 - - /** Default value of ErgoTree.header byte */ - val DefaultHeader: Byte = (0 | VersionFlag).toByte - - /** Header flag to indicate that constant segregation should be applied. */ - val ConstantSegregationFlag: Byte = 0x10 - - /** Header flag to indicate that whole size of ErgoTree should be saved before tree content. */ - val SizeFlag: Byte = 0x08 - - /** Header mask to extract version bits. */ - val VersionMask: Byte = 0x07 - - /** Default header with constant segregation enabled. */ - val ConstantSegregationHeader: Byte = (DefaultHeader | ConstantSegregationFlag).toByte - - /** @return true if the constant segregation flag is set to 1 in the given header byte. */ - @inline final def isConstantSegregation(header: Byte): Boolean = (header & ConstantSegregationFlag) != 0 - - /** @return true if the size flag is set to 1 in the given header byte. */ - @inline final def hasSize(header: Byte): Boolean = (header & SizeFlag) != 0 - - /** @return a value of the version bits from the given header byte. */ - @inline final def getVersion(header: Byte): Byte = (header & VersionMask).toByte - - /** Update the version bits of the given header byte with the given version value. */ - @inline final def updateVersionBits(header: Byte, version: Byte): Byte = { - require(version < 8, s"ErgoTree.version should be < 8: $version") - (header | version).toByte - } - - /** Creates valid header byte with the given version. - * The SizeFlag is set if version > 0 */ - @inline def headerWithVersion(version: Byte): Byte = { - // take default header and embedd the given version in it - var h = updateVersionBits(DefaultHeader, version) - if (version > 0) { - // set SizeFlag if version is greater then 0 (see require() in ErgoTree constructor) - h = (h | ErgoTree.SizeFlag).toByte - } - h - } - - /** Substitute [[ConstantPlaceholder]] nodes in the given expression with the constants - * taken from the given collection. - * @param root expression to transform - * @param constants collection of constants to replace placeholders - * @return new expression without placeholders - */ - def substConstants(root: SValue, constants: IndexedSeq[Constant[SType]]): SValue = { - val store = new ConstantStore(constants) - val substRule = strategy[Any] { - case ph: ConstantPlaceholder[_] => - Some(store.get(ph.id)) - case _ => None - } - everywherebu(substRule)(root).fold(root)(_.asInstanceOf[SValue]) - } - - /** Create an ErgoTree with the given parameters. */ - def apply(header: Byte, constants: IndexedSeq[Constant[SType]], root: SigmaPropValue): ErgoTree = { - new ErgoTree(header, constants, Right(root)) - } - - val EmptyConstants: IndexedSeq[Constant[SType]] = Array[Constant[SType]]() - - /** Create new ErgoTree for the given proposition using the given header flags and - * without performing constant segregation. - */ - def withoutSegregation(root: SigmaPropValue): ErgoTree = - ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, root) - - /** Create new ErgoTree for the given proposition using the given header flags and - * without performing constant segregation. - */ - def withoutSegregation(headerFlags: Byte, root: SigmaPropValue): ErgoTree = - ErgoTree((ErgoTree.DefaultHeader | headerFlags).toByte, EmptyConstants, root) - - /** Create new ErgoTree for the given proposition using default header. - * If the property is not a simple constant, then constant segregation is performed. - */ - implicit def fromProposition(prop: SigmaPropValue): ErgoTree = { - fromProposition(ErgoTree.DefaultHeader, prop) - } - - /** Create new ErgoTree for the given proposition using the given header flags. - * If the property is not a simple constant, then constant segregation is performed. - */ - def fromProposition(headerFlags: Byte, prop: SigmaPropValue): ErgoTree = { - prop match { - case SigmaPropConstant(_) => withoutSegregation(headerFlags, prop) - case _ => withSegregation(headerFlags, prop) - } - } - - /** Create new ErgoTree for the given sigma proposition using default header and - * without performing constant segregation. - */ - implicit def fromSigmaBoolean(pk: SigmaBoolean): ErgoTree = { - withoutSegregation(pk.toSigmaProp) - } - - /** Create new ErgoTree for the given sigma proposition using the given header flags - * and without performing constant segregation. - */ - def fromSigmaBoolean(headerFlags: Byte, pk: SigmaBoolean): ErgoTree = { - withoutSegregation(headerFlags, pk.toSigmaProp) - } - - /** Build ErgoTree via serialization of the value with ConstantSegregationHeader, constants segregated - * from the tree and ConstantPlaceholders referring to the segregated constants. - * - * This method uses single traverse of the tree to: - * 1) find and segregate all constants; - * 2) replace constants with ConstantPlaceholders in the `tree`; - * 3) write the `tree` to the Writer's buffer obtaining `treeBytes`; - * 4) deserialize `tree` with ConstantPlaceholders. - * @param headerFlags additional header flags to combine with - * ConstantSegregationHeader flag. - * @param prop expression to be transformed into ErgoTree - **/ - def withSegregation(headerFlags: Byte, prop: SigmaPropValue): ErgoTree = { - val constantStore = new ConstantStore() - val byteWriter = SigmaSerializer.startWriter(constantStore) - // serialize value and segregate constants into constantStore - ValueSerializer.serialize(prop, byteWriter) - val extractedConstants = constantStore.getAll - val r = SigmaSerializer.startReader(byteWriter.toBytes) - r.constantStore = new ConstantStore(extractedConstants) - // deserialize value with placeholders - val valueWithPlaceholders = ValueSerializer.deserialize(r).asSigmaProp - val header = (ErgoTree.ConstantSegregationHeader | headerFlags).toByte - new ErgoTree(header, extractedConstants, Right(valueWithPlaceholders)) - } - - /** Create new ErgoTree for the given sigma proposition using default header and - * also performing constant segregation. - */ - def withSegregation(prop: SigmaPropValue): ErgoTree = - withSegregation(DefaultHeader, prop) - - /** Deserializes an ErgoTree instance from a hexadecimal string. - * - * @param hex a hexadecimal string representing the serialized ErgoTree - */ - def fromHex(hex: String): ErgoTree = { - val bytes = Base16.decode(hex).get - fromBytes(bytes) - } - - /** Deserializes an ErgoTree instance from an array of bytes. - * - * @param bytes an array of bytes representing the serialized ErgoTree - */ - def fromBytes(bytes: Array[Byte]): ErgoTree = { - ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes) - } - - } - -} diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoFunctions.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoFunctions.scala index ff8d395d44..5a579ffb2c 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoFunctions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/crypto/CryptoFunctions.scala @@ -1,6 +1,7 @@ package sigmastate.crypto import scorex.crypto.hash.Blake2b256 +import sigma.crypto.CryptoConstants object CryptoFunctions { lazy val soundnessBytes: Int = CryptoConstants.soundnessBits / 8 diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/DLogProtocol.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/DLogProtocol.scala index a078da2ceb..7fa2dae3a1 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/DLogProtocol.scala +++ b/interpreter/shared/src/main/scala/sigmastate/crypto/DLogProtocol.scala @@ -1,49 +1,25 @@ package sigmastate.crypto -import java.math.BigInteger - -import sigmastate.Values._ -import Value.PropositionCode import scorex.util.encode.Base16 -import sigmastate._ -import sigmastate.eval._ +import sigma.crypto.{BigIntegers, EcPointType} +import sigma.data.ProveDlog +import sigma.serialization.GroupElementSerializer +import sigma.crypto.CryptoConstants.dlogGroup import sigmastate.crypto.VerifierMessage.Challenge -import CryptoConstants.{EcPointType, dlogGroup} -import sigmastate.serialization.{OpCodes, GroupElementSerializer} -import sigmastate.serialization.OpCodes.OpCode -import sigma.SigmaProp -object DLogProtocol { - - trait DLogSigmaProtocol extends SigmaProtocol[DLogSigmaProtocol] { - override type A = FirstDLogProverMessage - override type Z = SecondDLogProverMessage - } - - /** Construct a new SigmaBoolean value representing public key of discrete logarithm signature protocol. */ - case class ProveDlog(value: EcPointType) extends SigmaLeaf { - override def size: Int = 1 - override val opCode: OpCode = OpCodes.ProveDlogCode - /** Serialized bytes of the elliptic curve point (using GroupElementSerializer). */ - lazy val pkBytes: Array[Byte] = GroupElementSerializer.toBytes(value) - } - - object ProveDlog { - val Code: PropositionCode = 102: Byte - } +import java.math.BigInteger - /** Helper extractor to match SigmaProp values and extract ProveDlog out of it. */ - object ProveDlogProp { - def unapply(p: SigmaProp): Option[ProveDlog] = SigmaDsl.toSigmaBoolean(p) match { - case d: ProveDlog => Some(d) - case _ => None - } - } +/** Implementation of sigma protocol steps using discrete logarithm problem in EC group. */ +object DLogProtocol { + /** Secret key of discrete logarithm signature protocol. + * @param w secret number in [0, q-1] + * where q - an order of DLog group. + */ case class DLogProverInput(w: BigInteger) extends SigmaProtocolPrivateInput[ProveDlog] { - import CryptoConstants.dlogGroup + import sigma.crypto.CryptoConstants.dlogGroup override lazy val publicImage: ProveDlog = { val g = dlogGroup.generator @@ -53,7 +29,7 @@ object DLogProtocol { object DLogProverInput { - import CryptoConstants.dlogGroup + import sigma.crypto.CryptoConstants.dlogGroup /** Create random secret in a range 0..q-1, where q - an order of DLog group. */ def random(): DLogProverInput = { @@ -63,8 +39,13 @@ object DLogProtocol { } } + /** First message of dlog-based sigma protocol. + * @param ecData commitment to randomness ecData = G^r, where + * G - generator of EC group, + * r - random number known only to prover from [0, q-1] range, + * where q - order of EC group. + */ case class FirstDLogProverMessage(ecData: EcPointType) extends FirstProverMessage { - override type SP = DLogSigmaProtocol override def bytes: Array[Byte] = { GroupElementSerializer.toBytes(ecData) } @@ -72,23 +53,35 @@ object DLogProtocol { override def toString = s"FirstDLogProverMessage(${Base16.encode(bytes)})" } - case class SecondDLogProverMessage(z: BigInt) extends SecondProverMessage { - override type SP = DLogSigmaProtocol - } + /** Second message of dlog-based sigma protocol. + * @param z response to the challenge from the verifier + * @see responseToChallenge() + */ + case class SecondDLogProverMessage(z: BigInt) extends SecondProverMessage - object DLogInteractiveProver extends SigmaProtocolProver { - import CryptoConstants.secureRandom + /** Prover operations of dlog-based sigma protocol. */ + object DLogProver extends SigmaProtocolProver { + import sigma.crypto.CryptoConstants.secureRandom + /** Generate a first prover message with a commitment to a secret random number. */ def firstMessage(): (BigInteger, FirstDLogProverMessage) = { - import CryptoConstants.dlogGroup + import sigma.crypto.CryptoConstants.dlogGroup - val qMinusOne = dlogGroup.order.subtract(BigInteger.ONE) - val r = BigIntegers.createRandomInRange(BigInteger.ZERO, qMinusOne, secureRandom) - val a = dlogGroup.exponentiate(dlogGroup.generator, r) + val qMinusOne = dlogGroup.order.subtract(BigInteger.ONE) // q - 1 + val r = BigIntegers.createRandomInRange(BigInteger.ZERO, qMinusOne, secureRandom) // r <- [0, q-1] + val a = dlogGroup.exponentiate(dlogGroup.generator, r) // g^r r -> FirstDLogProverMessage(a) } + /** Creates a second message of sigma protocol by computing + * `z = rnd + challenge * privateInput.w mod q`, where q - order of EC group. + * + * @param privateInput secret known only to prover from [0, q-1] range, + * @param rnd random number generated by the prover (secret random number used to + * compute commitment) + * @param challenge from the verifier (also computed by the prover in non-interactive case) + */ def secondMessage(privateInput: DLogProverInput, rnd: BigInteger, challenge: Challenge): SecondDLogProverMessage = { val z = responseToChallenge(privateInput, rnd, challenge) SecondDLogProverMessage(z) @@ -117,9 +110,9 @@ object DLogProtocol { * * g^z = a*h^e => a = g^z/h^e * - * @param proposition - * @param challenge - * @param secondMessage + * @param proposition proposition being proved + * @param challenge challenge from verifier + * @param secondMessage prover's response to the challenge * @return */ def computeCommitment(proposition: ProveDlog, @@ -128,6 +121,7 @@ object DLogProtocol { val g = dlogGroup.generator val h = proposition.value + // COMPUTE a = g^z / h^e dlogGroup.multiplyGroupElements( dlogGroup.exponentiate(g, secondMessage.z.underlying()), dlogGroup.inverseOf(dlogGroup.exponentiate(h, new BigInteger(1, challenge.toArray)))) diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/DiffieHellmanTupleProtocol.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/DiffieHellmanTupleProtocol.scala index a30e187105..ba1a6c2e0a 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/DiffieHellmanTupleProtocol.scala +++ b/interpreter/shared/src/main/scala/sigmastate/crypto/DiffieHellmanTupleProtocol.scala @@ -1,21 +1,12 @@ package sigmastate.crypto -import java.math.BigInteger - -import sigmastate.Values.Value.PropositionCode -import sigmastate._ +import sigma.crypto.{BigIntegers, CryptoConstants, EcPointType} +import sigma.data.ProveDHTuple +import sigma.serialization.GroupElementSerializer import sigmastate.crypto.VerifierMessage.Challenge -import sigmastate.eval.SigmaDsl -import CryptoConstants.EcPointType -import sigmastate.serialization.{OpCodes, GroupElementSerializer} -import sigmastate.serialization.OpCodes.OpCode -import sigma.SigmaProp +import java.math.BigInteger -trait DiffieHellmanTupleProtocol extends SigmaProtocol[DiffieHellmanTupleProtocol] { - override type A = FirstDHTupleProverMessage - override type Z = SecondDHTupleProverMessage -} case class DiffieHellmanTupleProverInput(w: BigInteger, commonInput: ProveDHTuple) extends SigmaProtocolPrivateInput[ProveDHTuple] { @@ -43,13 +34,10 @@ object DiffieHellmanTupleProverInput { /** First message of Diffie Hellman tuple sigma protocol. * @param a commitment to secret randomness `a = g^r`, where `g` is default generator of the group * @param b commitment to secret randomness `b = h^r`, where `h` is another random generator of the group - * @see createRandomGenerator in [[sigmastate.crypto.CryptoConstants.dlogGroup]] + * @see createRandomGenerator in [[CryptoConstants.dlogGroup]] */ case class FirstDHTupleProverMessage(a: EcPointType, b: EcPointType) extends FirstProverMessage { - - override type SP = DiffieHellmanTupleProtocol - override def bytes: Array[Byte] = { GroupElementSerializer.toBytes(a) ++ GroupElementSerializer.toBytes(b) } @@ -62,35 +50,9 @@ case class FirstDHTupleProverMessage(a: EcPointType, b: EcPointType) * `w` is the prover's secret. * `q` is the group order */ -case class SecondDHTupleProverMessage(z: BigInteger) extends SecondProverMessage { - override type SP = DiffieHellmanTupleProtocol -} - -/** Construct a new SigmaProp value representing public key of Diffie Hellman signature protocol. - * Common input: (g,h,u,v) */ -case class ProveDHTuple(gv: EcPointType, hv: EcPointType, uv: EcPointType, vv: EcPointType) - extends SigmaLeaf { - override val opCode: OpCode = OpCodes.ProveDiffieHellmanTupleCode - override def size: Int = 4 // one node for each EcPoint - lazy val g = gv - lazy val h = hv - lazy val u = uv - lazy val v = vv -} - -object ProveDHTuple { - val Code: PropositionCode = 103: Byte -} - -/** Helper extractor to match SigmaProp values and extract ProveDHTuple out of it. */ -object ProveDHTupleProp { - def unapply(p: SigmaProp): Option[ProveDHTuple] = SigmaDsl.toSigmaBoolean(p) match { - case d: ProveDHTuple => Some(d) - case _ => None - } -} +case class SecondDHTupleProverMessage(z: BigInteger) extends SecondProverMessage -object DiffieHellmanTupleInteractiveProver extends SigmaProtocolProver { +object DiffieHellmanTupleProver extends SigmaProtocolProver { import CryptoConstants.dlogGroup diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala index 176edb201c..78f45afaa9 100644 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/crypto/SigmaProtocolFunctions.scala @@ -1,9 +1,9 @@ package sigmastate.crypto -import sigmastate.SigmaLeaf -import sigmastate.crypto.CryptoConstants.dlogGroup +import sigma.crypto.CryptoConstants.dlogGroup import sigmastate.crypto.VerifierMessage.Challenge import sigma.Coll +import sigma.data.SigmaLeaf import supertagged.TaggedType import java.math.BigInteger @@ -11,7 +11,6 @@ import java.math.BigInteger /* Abstracting Sigma protocols Functionality to get: - - Interactive Sigma protocols(via actors) - Zero-knowledge proof from a Sigma protocol - Non-interactive Sigma protocols - Commitment from any Sigma protocol @@ -37,26 +36,13 @@ object VerifierMessage { /** First message from the prover (message `a` of `SigmaProtocol`)*/ trait FirstProverMessage extends ProverMessage { - type SP <: SigmaProtocol[SP] - def bytes: Array[Byte] } /** Second message from the prover (message `z` of `SigmaProtocol`)*/ -trait SecondProverMessage extends ProverMessage { - type SP <: SigmaProtocol[SP] -} - -/** Abstract template for sigma protocols. - * For details see the following book - * [1] Efficient Secure Two-Party Protocols - Techniques and Constructions, p.150)*/ -trait SigmaProtocol[SP <: SigmaProtocol[SP]] { - type A <: FirstProverMessage - type Z <: SecondProverMessage -} - +trait SecondProverMessage extends ProverMessage -trait SigmaProtocolPrivateInput[CI <: SigmaLeaf] { +trait SigmaProtocolPrivateInput[+CI <: SigmaLeaf] { /** Public image generated from the secret. * Represents proof of knowledge proposition. */ @@ -66,6 +52,9 @@ trait SigmaProtocolPrivateInput[CI <: SigmaLeaf] { def w: BigInteger } +/** Represents any prover of a sigma protocol. + * @see DLogInteractiveProver, DiffieHellmanTupleProver + */ trait SigmaProtocolProver { /** Computes response for the challenge in non-interactive sigma protocol. * @@ -79,10 +68,10 @@ trait SigmaProtocolProver { privateInput: SigmaProtocolPrivateInput[_ <: SigmaLeaf], rnd: BigInteger, challenge: Challenge): BigInteger = { - val q: BigInteger = dlogGroup.order + val q: BigInteger = dlogGroup.order // order of the group val e: BigInteger = new BigInteger(1, challenge.toArray) - val ew: BigInteger = e.multiply(privateInput.w).mod(q) - val z: BigInteger = rnd.add(ew).mod(q) + val ew: BigInteger = e.multiply(privateInput.w).mod(q) // e * w mod q + val z: BigInteger = rnd.add(ew).mod(q) // r + ew mod q z } } diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/package.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/package.scala deleted file mode 100644 index c7ef439d87..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/crypto/package.scala +++ /dev/null @@ -1,15 +0,0 @@ -package sigmastate - -package object crypto { - /** Instance of Elliptic Curve descriptor. */ - type Curve = Platform.Curve - - /** Instance of Elliptic Curve point. */ - type Ecp = Platform.Ecp - - /** Instance of Elliptic Curve field element. */ - type ECFieldElem = Platform.ECFieldElem - - /** A cryptographically strong random number generator. */ - type SecureRandom = Platform.SecureRandom -} diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CAvlTreeVerifier.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CAvlTreeVerifier.scala new file mode 100644 index 0000000000..5739e65ade --- /dev/null +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CAvlTreeVerifier.scala @@ -0,0 +1,59 @@ +package sigmastate.eval + +import scorex.crypto.authds.avltree.batch.{BatchAVLVerifier, Insert, Lookup, Remove, Update} +import scorex.crypto.authds.{ADDigest, ADKey, ADValue, SerializedAdProof} +import scorex.crypto.hash.{Blake2b256, Digest32} +import sigma.data.CAvlTree +import sigma.eval.AvlTreeVerifier +import sigma.{AvlTree, Coll} + +import scala.util.Try + +/** Implements operations of AVL tree verifier based on + * [[scorex.crypto.authds.avltree.batch.BatchAVLVerifier]]. + * + * @see BatchAVLVerifier, CAvlTreeVerifier + */ +class CAvlTreeVerifier private( + startingDigest: ADDigest, + proof: SerializedAdProof, + override val keyLength: Int, + override val valueLengthOpt: Option[Int]) + extends BatchAVLVerifier[Digest32, Blake2b256.type]( + startingDigest, proof, keyLength, valueLengthOpt) with AvlTreeVerifier { + def treeHeight: Int = rootNodeHeight + + override def performLookup(key: Array[Byte]): Try[Option[Array[Byte]]] = + performOneOperation(Lookup(ADKey @@ key)) + + override def performInsert(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] = + performOneOperation(Insert(ADKey @@ key, ADValue @@ value)) + + override def performUpdate(key: Array[Byte], value: Array[Byte]): Try[Option[Array[Byte]]] = + performOneOperation(Update(ADKey @@ key, ADValue @@ value)) + + override def performRemove(key: Array[Byte]): Try[Option[Array[Byte]]] = + performOneOperation(Remove(ADKey @@ key)) + + override def digest: Option[ADDigest] = super.digest + + /** Override default logging which outputs stack trace to the console. */ + override protected def logError(t: Throwable): Unit = {} +} + +object CAvlTreeVerifier { + /** Create an instance of [[CAvlTreeVerifier]] for the given tree and proof. + * Both tree and proof are immutable. + * + * @param tree represents a tree state to verify + * @param proof proof of tree operations leading to the state digest in the tree + * @return a new verifier instance + */ + def apply(tree: AvlTree, proof: Coll[Byte]): CAvlTreeVerifier = { + val treeData = tree.asInstanceOf[CAvlTree].treeData + val adProof = SerializedAdProof @@ proof.toArray + val bv = new CAvlTreeVerifier( + ADDigest @@ treeData.digest.toArray, adProof, treeData.keyLength, treeData.valueLengthOpt) + bv + } +} \ No newline at end of file diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala new file mode 100644 index 0000000000..2b076403ad --- /dev/null +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CContext.scala @@ -0,0 +1,101 @@ +package sigmastate.eval + +import debox.cfor +import sigma.Extensions.ArrayOps +import sigma._ +import sigma.data._ +import sigma.exceptions.InvalidType + +import scala.annotation.unused +import scala.reflect.ClassTag + +/** A default implementation of [[Context]] interface. + * @see [[Context]] for detailed descriptions + */ +case class CContext( + _dataInputs: Coll[Box], + override val headers: Coll[Header], + override val preHeader: PreHeader, + inputs: Coll[Box], + outputs: Coll[Box], + height: Int, + selfBox: Box, + private val selfIndex: Int, + lastBlockUtxoRootHash: AvlTree, + _minerPubKey: Coll[Byte], + vars: Coll[AnyValue], + override val activatedScriptVersion: Byte, + override val currentErgoTreeVersion: Byte +) extends Context { + @inline override def builder: SigmaDslBuilder = CSigmaDslBuilder + + @inline override def HEIGHT: Int = height + + @inline override def SELF: Box = selfBox + + @inline override def dataInputs: Coll[Box] = _dataInputs + + @inline override def INPUTS = inputs + + @inline override def OUTPUTS = outputs + + @inline override def LastBlockUtxoRootHash = lastBlockUtxoRootHash + + @inline override def minerPubKey = _minerPubKey + + override def selfBoxIndex: Int = { + if (VersionContext.current.isJitActivated) { + // starting from v5.0 this is fixed + selfIndex + } else { + // this used to be a bug in v4.x (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603) + -1 + } + } + + override def getVar[T](id: Byte)(implicit tT: RType[T]): Option[T] = { + @unused // avoid warning about unused ctA + implicit val tag: ClassTag[T] = tT.classTag + if (id < 0 || id >= vars.length) return None + val value = vars(id) + if (value != null) { + // once the value is not null it should be of the right type + value match { + case value: CAnyValue[_] if value.value != null && value.tA == tT => + Some(value.value.asInstanceOf[T]) + case _ => + throw new InvalidType(s"Cannot getVar[${tT.name}]($id): invalid type of value $value at id=$id") + } + } else None + } + + /** Return a new context instance with variables collection updated. + * @param bindings a new binding of the context variables with new values. + * @return a new instance (if `bindings` non-empty) with the specified bindings. + * other existing bindings are copied to the new instance + */ + def withUpdatedVars(bindings: (Int, AnyValue)*): CContext = { + if (bindings.isEmpty) return this + + val ids = bindings.map(_._1).toArray + val values = bindings.map(_._2).toArray + val maxVarId = ids.max // INV: ids is not empty + val requiredNewLength = maxVarId + 1 + + val newVars = if (vars.length < requiredNewLength) { + // grow vars collection + val currVars = vars.toArray + val buf = new Array[AnyValue](requiredNewLength) + Array.copy(currVars, 0, buf, 0, currVars.length) + cfor(0)(_ < ids.length, _ + 1) { i => + buf(ids(i)) = values(i) + } + buf.toColl + } else { + vars.updateMany(ids.toColl, values.toColl) + } + + this.copy(vars = newVars) + } +} + diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CHeader.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CHeader.scala new file mode 100644 index 0000000000..7062fa0f0e --- /dev/null +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CHeader.scala @@ -0,0 +1,34 @@ +package sigmastate.eval + +import sigma.data.SigmaConstants +import sigma.{AvlTree, BigInt, Coll, GroupElement, Header} + +/** A default implementation of [[Header]] interface. + * + * @see [[Header]] for detailed descriptions + */ +case class CHeader( + id: Coll[Byte], + version: Byte, + parentId: Coll[Byte], + ADProofsRoot: Coll[Byte], + stateRoot: AvlTree, + transactionsRoot: Coll[Byte], + timestamp: Long, + nBits: Long, + height: Int, + extensionRoot: Coll[Byte], + minerPk: GroupElement, + powOnetimePk: GroupElement, + powNonce: Coll[Byte], + powDistance: BigInt, + votes: Coll[Byte] +) extends Header + +object CHeader { + /** Size of of Header.votes array. */ + val VotesSize: Int = SigmaConstants.VotesArraySize.value + + /** Size of nonce array from Autolykos POW solution in Header.powNonce array. */ + val NonceSize: Int = SigmaConstants.AutolykosPowSolutionNonceArraySize.value +} \ No newline at end of file diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CPreHeader.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CPreHeader.scala new file mode 100644 index 0000000000..8076ab5a9c --- /dev/null +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CPreHeader.scala @@ -0,0 +1,17 @@ +package sigmastate.eval + +import sigma.{Coll, GroupElement, PreHeader} + +/** A default implementation of [[PreHeader]] interface. + * + * @see [[PreHeader]] for detailed descriptions + */ +case class CPreHeader( + version: Byte, + parentId: Coll[Byte], + timestamp: Long, + nBits: Long, + height: Int, + minerPk: GroupElement, + votes: Coll[Byte] +) extends PreHeader diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/Profiler.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CProfiler.scala similarity index 94% rename from interpreter/shared/src/main/scala/sigmastate/eval/Profiler.scala rename to interpreter/shared/src/main/scala/sigmastate/eval/CProfiler.scala index 80266c9063..ced8a357b8 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/Profiler.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/CProfiler.scala @@ -1,16 +1,15 @@ package sigmastate.eval -import sigmastate.{FixedCost, JitCost, SMethod} -import sigmastate.Values.SValue -import sigmastate.serialization.{OpCodes, ValueSerializer} -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.serialization.ValueSerializer.getSerializer -import sigma.util.Extensions.ByteOps -import debox.{Buffer => DBuffer, Map => DMap} -import debox.sp -import sigmastate.eval.Extensions.DBufferOps -import sigmastate.interpreter.{CostItem, FixedCostItem, SeqCostItem, TypeBasedCostItem} -import sigmastate.lang.Terms.{MethodCall, PropertyCall} +import debox.{sp, Buffer => DBuffer, Map => DMap} +import sigma.ast.{CostItem, FixedCost, FixedCostItem, JitCost, SMethod, SeqCostItem, TypeBasedCostItem} +import sigma.ast.TypeCodes.LastConstantCode +import sigma.ast.syntax._ +import sigma.util.Extensions.{ByteOps, DBufferOps} +import sigma.ast.{MethodCall, PropertyCall} +import sigma.eval.Profiler +import sigma.serialization.ValueCodes.OpCode +import sigma.serialization.ValueSerializer +import sigma.serialization.ValueSerializer.getSerializer import scala.reflect.ClassTag @@ -105,7 +104,7 @@ class StatCollection[@sp(Int) K, @sp(Long, Double) V] } /** A simple profiler to measure average execution times of ErgoTree operations. */ -class Profiler { +class CProfiler extends Profiler { // NOTE: this class is mutable so better to keep it private private class OpStat( @@ -199,7 +198,7 @@ class Profiler { /** Timings of cost items */ private val costItemsStat = new StatCollection[CostItemKey, Long]() - def addCostItem(costItem: CostItem, time: Long) = { + override def addCostItem(costItem: CostItem, time: Long) = { costItemsStat.addPoint(new CostItemKey(costItem), time) } @@ -256,7 +255,7 @@ class Profiler { val suggestedCost = suggestCost(time) val warn = if (suggestedCost > cost) "!!!" else "" val comment = s"count: $count, suggestedCost: $suggestedCost, actualCost: $cost$warn" - (opName, (opCode.toUByte - OpCodes.LastConstantCode).toString, time, comment) + (opName, (opCode.toUByte - LastConstantCode).toString, time, comment) }.filter(line => line != null && line._1.nonEmpty) .sortBy(_._3)(Ordering[Long].reverse) diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CostingDataContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CostingDataContext.scala deleted file mode 100644 index 870cbbb5bf..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/eval/CostingDataContext.scala +++ /dev/null @@ -1,760 +0,0 @@ -package sigmastate.eval - -import debox.cfor -import org.ergoplatform.validation.{ValidationRules, SigmaValidationSettings} -import org.ergoplatform.{SigmaConstants, ErgoBox} -import sigma.data.OverloadHack.Overloaded1 -import sigma.data.RType -import sigma.util.Extensions.BigIntegerOps -import scorex.crypto.authds.avltree.batch._ -import scorex.crypto.authds.{SerializedAdProof, ADDigest, ADValue, ADKey} -import scorex.crypto.hash.{Blake2b256, Digest32, Sha256} -import scorex.utils.{Longs, Ints} -import sigmastate.SCollection.SByteArray -import sigmastate.Values.ErgoTree.EmptyConstants -import sigmastate.Values.{EvaluatedValue, SValue, ConstantNode, ErgoTree, SigmaBoolean} -import sigmastate._ -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.{ProveDHTuple, CryptoConstants, Ecp, CryptoFacade} -import sigmastate.eval.Extensions._ -import sigmastate.interpreter.Interpreter -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.serialization.{GroupElementSerializer, SigmaSerializer} -import sigma.{VersionContext, _} - -import java.math.BigInteger -import java.util.Arrays -import scala.annotation.unused -import scala.reflect.ClassTag -import scala.util.{Success, Failure} - -/** Interface implmented by wrappers to provide access to the underlying wrapped value. */ -trait WrapperOf[T] { - /** The data value wrapped by this wrapper. */ - def wrappedValue: T -} - -/** A default implementation of [[BigInt]] interface. - * @see [[BigInt]] for detailed descriptions - */ -case class CBigInt(override val wrappedValue: BigInteger) extends BigInt with WrapperOf[BigInteger] { - val dsl = CostingSigmaDslBuilder - override def toByte : Byte = wrappedValue.toByteExact - override def toShort: Short = wrappedValue.toShortExact - override def toInt : Int = wrappedValue.toIntExact - override def toLong : Long = wrappedValue.toLongExact - - override def toBytes: Coll[Byte] = dsl.Colls.fromArray(wrappedValue.toByteArray) - - override def toAbs: BigInt = dsl.BigInt(wrappedValue.abs()) - - override def compareTo(that: BigInt): Int = - wrappedValue.compareTo(that.asInstanceOf[CBigInt].wrappedValue) - - override def toBits: Coll[Boolean] = ??? - - override def modQ: BigInt = ??? - - override def plusModQ(other: BigInt): BigInt = ??? - - override def minusModQ(other: BigInt): BigInt = ??? - - override def multModQ(other: BigInt): BigInt = ??? - - override def inverseModQ: BigInt = ??? - - override def signum: Int = wrappedValue.signum() - - override def add(that: BigInt): BigInt = dsl.BigInt(wrappedValue.add(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) - - override def subtract(that: BigInt): BigInt = dsl.BigInt(wrappedValue.subtract(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) - - override def multiply(that: BigInt): BigInt = dsl.BigInt(wrappedValue.multiply(that.asInstanceOf[CBigInt].wrappedValue).to256BitValueExact) - - override def divide(that: BigInt): BigInt = dsl.BigInt(wrappedValue.divide(that.asInstanceOf[CBigInt].wrappedValue)) - - override def mod(m: BigInt): BigInt = dsl.BigInt(wrappedValue.mod(m.asInstanceOf[CBigInt].wrappedValue)) - - override def remainder(that: BigInt): BigInt = dsl.BigInt(wrappedValue.remainder(that.asInstanceOf[CBigInt].wrappedValue)) - - override def min(that: BigInt): BigInt = dsl.BigInt(wrappedValue.min(that.asInstanceOf[CBigInt].wrappedValue)) - - override def max(that: BigInt): BigInt = dsl.BigInt(wrappedValue.max(that.asInstanceOf[CBigInt].wrappedValue)) - - override def negate(): BigInt = dsl.BigInt(wrappedValue.negate().to256BitValueExact) - - override def and(that: BigInt): BigInt = dsl.BigInt(wrappedValue.and(that.asInstanceOf[CBigInt].wrappedValue)) - - override def or(that: BigInt): BigInt = dsl.BigInt(wrappedValue.or(that.asInstanceOf[CBigInt].wrappedValue)) -} - -/** A default implementation of [[GroupElement]] interface. - * @see [[GroupElement]] for detailed descriptions - */ -case class CGroupElement(override val wrappedValue: Ecp) extends GroupElement with WrapperOf[Ecp] { - val dsl = CostingSigmaDslBuilder - - override def toString: String = s"GroupElement(${sigmastate.eval.Extensions.showECPoint(wrappedValue)})" - - override def getEncoded: Coll[Byte] = - dsl.Colls.fromArray(GroupElementSerializer.toBytes(wrappedValue)) - - override def isIdentity: Boolean = CryptoFacade.isInfinityPoint(wrappedValue) - - override def exp(k: BigInt): GroupElement = - dsl.GroupElement(CryptoFacade.exponentiatePoint(wrappedValue, k.asInstanceOf[CBigInt].wrappedValue)) - - override def multiply(that: GroupElement): GroupElement = - dsl.GroupElement(CryptoFacade.multiplyPoints(wrappedValue, that.asInstanceOf[CGroupElement].wrappedValue)) - - override def negate: GroupElement = - dsl.GroupElement(CryptoFacade.negatePoint(wrappedValue)) -} - -/** A default implementation of [[SigmaProp]] interface. - * @see [[SigmaProp]] for detailed descriptions - */ -case class CSigmaProp(sigmaTree: SigmaBoolean) extends SigmaProp with WrapperOf[SigmaBoolean] { - override def wrappedValue: SigmaBoolean = sigmaTree - - // TODO refactor: remove this (it shouldn't be used in interpreter) - override def isValid: Boolean = sigmaTree match { - case p: TrivialProp => p.condition - case _ => sys.error(s"Method CostingSigmaProp.isValid is not defined for $sigmaTree") - } - - override def propBytes: Coll[Byte] = { - // in order to have comparisons like `box.propositionBytes == pk.propBytes` we need to make sure - // the same serialization method is used in both cases - // TODO v6.0: add `pk.propBytes(version)` (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/903) - val root = sigmaTree.toSigmaProp - val ergoTree = new ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, Right(root), 0, null, None) - val bytes = DefaultSerializer.serializeErgoTree(ergoTree) - Colls.fromArray(bytes) - } - - override def &&(other: SigmaProp): SigmaProp = other match { - case other: CSigmaProp => - CSigmaProp(CAND.normalized(Array(sigmaTree, other.sigmaTree))) - } - - override def ||(other: SigmaProp): SigmaProp = other match { - case other: CSigmaProp => - CSigmaProp(COR.normalized(Array(sigmaTree, other.sigmaTree))) - } - - override def toString: String = s"SigmaProp(${wrappedValue.showToString})" -} - -/** Implements operations of AVL tree verifier based on - * [[scorex.crypto.authds.avltree.batch.BatchAVLVerifier]]. - * - * @see BatchAVLVerifier - */ -class AvlTreeVerifier private (startingDigest: ADDigest, - proof: SerializedAdProof, - override val keyLength: Int, - override val valueLengthOpt: Option[Int]) - extends BatchAVLVerifier[Digest32, Blake2b256.type]( - startingDigest, proof, keyLength, valueLengthOpt) { - def treeHeight: Int = rootNodeHeight - - /** Override default logging which outputs stack trace to the console. */ - override protected def logError(t: Throwable): Unit = {} -} -object AvlTreeVerifier { - /** Create an instance of [[AvlTreeVerifier]] for the given tree and proof. - * Both tree and proof are immutable. - * @param tree represents a tree state to verify - * @param proof proof of tree operations leading to the state digest in the tree - * @return a new verifier instance - */ - def apply(tree: AvlTree, proof: Coll[Byte]): AvlTreeVerifier = { - val treeData = tree.asInstanceOf[CAvlTree].treeData - val adProof = SerializedAdProof @@ proof.toArray - val bv = new AvlTreeVerifier( - ADDigest @@ treeData.digest.toArray, adProof, treeData.keyLength, treeData.valueLengthOpt) - bv - } -} - -/** A default implementation of [[AvlTree]] interface. - * @see [[AvlTree]] for detailed descriptions - */ -case class CAvlTree(treeData: AvlTreeData) extends AvlTree with WrapperOf[AvlTreeData] { - override def wrappedValue: AvlTreeData = treeData - - override def keyLength: Int = treeData.keyLength - - override def enabledOperations = treeData.treeFlags.serializeToByte - - override def isInsertAllowed: Boolean = treeData.treeFlags.insertAllowed - - override def isUpdateAllowed: Boolean = treeData.treeFlags.updateAllowed - - override def isRemoveAllowed: Boolean = treeData.treeFlags.removeAllowed - - override def updateOperations(newOperations: Byte): AvlTree = { - val td = treeData.copy(treeFlags = AvlTreeFlags(newOperations)) - this.copy(treeData = td) - } - - override def valueLengthOpt: Option[Int] = treeData.valueLengthOpt - - override def digest: Coll[Byte] = treeData.digest - - override def updateDigest(newDigest: Coll[Byte]): AvlTree = { - val td = treeData.copy(digest = newDigest) - this.copy(treeData = td) - } - - override def contains(key: Coll[Byte], proof: Coll[Byte]): Boolean = { - val keyBytes = key.toArray - val bv = AvlTreeVerifier(this, proof) - bv.performOneOperation(Lookup(ADKey @@ keyBytes)) match { - case Success(r) => r match { - case Some(_) => true - case _ => false - } - case Failure(_) => false - } - } - - override def get(key: Coll[Byte], proof: Coll[Byte]): Option[Coll[Byte]] = { - val keyBytes = key.toArray - val bv = AvlTreeVerifier(this, proof) - bv.performOneOperation(Lookup(ADKey @@ keyBytes)) match { - case Success(r) => r match { - case Some(v) => Some(Colls.fromArray(v)) - case _ => None - } - case Failure(_) => Interpreter.error(s"Tree proof is incorrect $treeData") - } - } - - override def getMany(keys: Coll[Coll[Byte]], proof: Coll[Byte]): Coll[Option[Coll[Byte]]] = { - val bv = AvlTreeVerifier(this, proof) - keys.map { key => - bv.performOneOperation(Lookup(ADKey @@ key.toArray)) match { - case Success(r) => r match { - case Some(v) => Some(Colls.fromArray(v)) - case _ => None - } - case Failure(_) => Interpreter.error(s"Tree proof is incorrect $treeData") - } - } - } - - override def insert(entries: Coll[(Coll[Byte], Coll[Byte])], proof: Coll[Byte]): Option[AvlTree] = { - if (!isInsertAllowed) { - None - } else { - val bv = AvlTreeVerifier(this, proof) - entries.forall { case (key, value) => - val insertRes = bv.performOneOperation(Insert(ADKey @@ key.toArray, ADValue @@ value.toArray)) - if (insertRes.isFailure) { - Interpreter.error(s"Incorrect insert for $treeData (key: $key, value: $value, digest: $digest): ${insertRes.failed.get}}") - } - insertRes.isSuccess - } - bv.digest match { - case Some(d) => Some(updateDigest(Colls.fromArray(d))) - case _ => None - } - } - } - - override def update(operations: Coll[(Coll[Byte], Coll[Byte])], proof: Coll[Byte]): Option[AvlTree] = { - if (!isUpdateAllowed) { - None - } else { - val bv = AvlTreeVerifier(this, proof) - operations.forall { case (key, value) => - bv.performOneOperation(Update(ADKey @@ key.toArray, ADValue @@ value.toArray)).isSuccess - } - bv.digest match { - case Some(d) => Some(updateDigest(Colls.fromArray(d))) - case _ => None - } - } - } - - override def remove(operations: Coll[Coll[Byte]], proof: Coll[Byte]): Option[AvlTree] = { - if (!isRemoveAllowed) { - None - } else { - val bv = AvlTreeVerifier(this, proof) - cfor(0)(_ < operations.length, _ + 1) { i => - val key = operations(i).toArray - bv.performOneOperation(Remove(ADKey @@ key)) - } - bv.digest match { - case Some(v) => Some(updateDigest(Colls.fromArray(v))) - case _ => None - } - } - } -} - -/** Default implementation of AnyValue interface. */ -case class CAnyValue[A](value: A, tVal: RType[Any]) extends AnyValue { - def tA: RType[A] = tVal.asInstanceOf[RType[A]] - override def toString = s"TestValue($value)" -} - -object CAnyValue { - def apply[A](value: A)(implicit t: RType[A], @unused o: Overloaded1): CAnyValue[A] = - new CAnyValue(value, t.asInstanceOf[RType[Any]]) -} - -import sigmastate.eval.CostingBox._ - -/** A default implementation of [[Box]] interface. - * @see [[Box]] for detailed descriptions - */ -case class CostingBox(ebox: ErgoBox) extends Box with WrapperOf[ErgoBox] { - val builder = CostingSigmaDslBuilder - - val value = ebox.value - lazy val id: Coll[Byte] = Colls.fromArray(ebox.id) - lazy val bytes: Coll[Byte] = Colls.fromArray(ebox.bytes) - lazy val bytesWithoutRef: Coll[Byte] = Colls.fromArray(ebox.bytesWithNoRef) - lazy val propositionBytes: Coll[Byte] = Colls.fromArray(ebox.propositionBytes) - lazy val registers: Coll[AnyValue] = regs(ebox) - - override def wrappedValue: ErgoBox = ebox - - override def getReg[T](i: Int)(implicit tT: RType[T]): Option[T] = { - if (i < 0 || i >= registers.length) return None - val value = registers(i) - if (value != null ) { - // once the value is not null it should be of the right type - value match { - case value: CAnyValue[_] if value.value != null && value.tA == tT => - Some(value.value.asInstanceOf[T]) - case _ => - throw new InvalidType(s"Cannot getReg[${tT.name}]($i): invalid type of value $value at id=$i") - } - } else None - } - - override def creationInfo: (Int, Coll[Byte]) = { - this.getReg[(Int, Coll[Byte])](3).get.asInstanceOf[Any] match { - case info: Tuple2[Int, Coll[Byte]]@unchecked => info - case ConstantNode(arr: Array[Any], STuple(IndexedSeq(SInt, SByteArray))) if arr.length == 2 => - (arr(0).asInstanceOf[Int], builder.Colls.fromArray(arr(1).asInstanceOf[Array[Byte]])) - case v => - sys.error(s"Invalid value $v of creationInfo register R3") - } - } - - override def tokens: Coll[(Coll[Byte], Long)] = { - this.getReg[Coll[(Coll[Byte], Long)]](ErgoBox.R2.asIndex).get - } - - override def executeFromRegister[T](regId: Byte)(implicit cT: RType[T]): T = ??? // TODO implement - - override def hashCode(): Int = Ints.fromByteArray(id.toArray) - - override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj != null && { - obj match { - case obj: Box => Arrays.equals(id.toArray, obj.id.toArray) - case _ => - // this case was missing in v4.x, however has never been a problem - // Thus, v5.0 interpreter will not fail (while v4.x would fail here) - false - } - }) -} - -object CostingBox { - - import Evaluation._ - - def regs(ebox: ErgoBox): Coll[AnyValue] = { - val res = new Array[AnyValue](ErgoBox.maxRegisters) - - def checkNotYetDefined(id: Int, newValue: SValue) = - require(res(id) == null, s"register $id is defined more then once: previous value ${res(id)}, new value $newValue") - - for ((k, v: EvaluatedValue[t]) <- ebox.additionalRegisters) { - checkNotYetDefined(k.number, v) - res(k.number) = toAnyValue(v.value)(stypeToRType(v.tpe)) - } - - for (r <- ErgoBox.mandatoryRegisters) { - val regId = r.number - val v = ebox.get(r).get.asInstanceOf[EvaluatedValue[SType]] - checkNotYetDefined(regId, v) - res(regId) = toAnyValue(v.value)(stypeToRType(v.tpe)) - } - Colls.fromArray(res) - } - -} - -/** This class represents context variable and register value of a functional type A => B. - * When variable or register is accessed using `getVar[A => B](id).get` or - * `box.getReg[A => B].get an instance of this class is returned. - * - * It internally transforms a given `tree` into executable function. - * This it similar to what happens during validation of propositions in the input boxes: - * - size check of underlying ErgoTree against limits - * - construction of `calcF` and `costF` graphs, both are stored together with resulting function. - * - check the types of `calcF` graph to be compatible with expected types A and B - * If anything goes wrong, this operation fails and if it is used in the script, the script also fails. - * - * When f is obtained as `val f = getVar[Int => Int](id).get` then any application `f(x)` involves size estimation - * using underlying `costF(x)`. - * */ -//case class CFunc[A,B](context: sigmastate.interpreter.Context, tree: SValue) -// (implicit tDom: RType[A], tRange: RType[B], IR: IRContext) extends (A => B) { -// import CFunc._ -// -// private val compiled = { -// import IR._ -// val IR.Pair(calcF, costF) = IR.doCosting(emptyEnv, tree) -// -// val eDom = asElem[Any](IR.rtypeToElem(tDom)) -// val eRange = asElem[Any](IR.rtypeToElem(tRange)) -// -// IR.verifyCalcFunc[Any => Any](asRep[Context => (Any => Any)](calcF), IR.funcElement(eDom, eRange)) -//// IR.verifyCostFunc(costF).getOrThrow -//// IR.verifyIsProven(calcF).getOrThrow -// -// // check cost -//// val costingCtx = context.toSigmaContext(IR, isCost = true) -//// val costFun = IR.compile[SInt.type](IR.getDataEnv, costF) -//// val IntConstant(estimatedCost) = costFun(costingCtx) -//// if (estimatedCost > maxCost) { -//// throw new Error(s"Estimated execution cost $estimatedCost exceeds the limit $maxCost in $tree") -//// } -// // check calc -// val calcCtx = context.toSigmaContext(IR, isCost = false) -// val valueFun = IR.compile[SFunc](IR.getDataEnv, asRep[Context => SFunc#WrappedType](calcF)) -// val res = valueFun(calcCtx) match { -// case Constant(f, fTpe: SFunc) => f -// case v => v -// } -// res.asInstanceOf[A => B] -// } -// -// override def apply(x: A): B = compiled(x) -//} -object CFunc { - /** The cost of creating resulting function but not its execution. - * Thus it is expected to be small. It can be increased if useful cases are found - * such that `tree` should contains heavy operations. */ - val maxCost = 1000 -} - -/** A default implementation of [[PreHeader]] interface. - * @see [[PreHeader]] for detailed descriptions - */ -case class CPreHeader( - version: Byte, - parentId: Coll[Byte], - timestamp: Long, - nBits: Long, - height: Int, - minerPk: GroupElement, - votes: Coll[Byte] - ) extends PreHeader {} - -/** A default implementation of [[Header]] interface. - * @see [[Header]] for detailed descriptions - */ -case class CHeader( - id: Coll[Byte], - version: Byte, - parentId: Coll[Byte], - ADProofsRoot: Coll[Byte], - stateRoot: AvlTree, - transactionsRoot: Coll[Byte], - timestamp: Long, - nBits: Long, - height: Int, - extensionRoot: Coll[Byte], - minerPk: GroupElement, - powOnetimePk: GroupElement, - powNonce: Coll[Byte], - powDistance: BigInt, - votes: Coll[Byte] - ) extends Header { -} - -object CHeader { - val VotesSize: Int = SigmaConstants.VotesArraySize.value - val NonceSize: Int = SigmaConstants.AutolykosPowSolutionNonceArraySize.value -} - -/** A default implementation of [[SigmaDslBuilder]] interface. - * @see [[SigmaDslBuilder]] for detailed descriptions - */ -class CostingSigmaDslBuilder extends SigmaDslBuilder { dsl => - implicit val validationSettings: SigmaValidationSettings = ValidationRules.currentSettings - - override val Colls: CollBuilder = sigma.Colls - - override def BigInt(n: BigInteger): BigInt = CBigInt(n) - - override def toBigInteger(n: BigInt): BigInteger = n.asInstanceOf[CBigInt].wrappedValue - - /** Wraps the given elliptic curve point into GroupElement type. */ - def GroupElement(p: Ecp): GroupElement = p match { - case ept: EcPointType => CGroupElement(ept) - case m => sys.error(s"Point of type ${m.getClass} is not supported") - } - - /** Wraps the given sigma proposition into SigmaDsl value of type SigmaProp. */ - def SigmaProp(sigmaTree: SigmaBoolean): SigmaProp = new CSigmaProp(sigmaTree) - - /** Extract `sigmastate.Values.SigmaBoolean` from DSL's `SigmaProp` type. */ - @inline def toSigmaBoolean(p: SigmaProp): SigmaBoolean = p.asInstanceOf[CSigmaProp].sigmaTree - - /** Extract `sigmastate.AvlTreeData` from DSL's `AvlTree` type. */ - def toAvlTreeData(p: AvlTree): AvlTreeData = p.asInstanceOf[CAvlTree].treeData - - /** Extract `sigmastate.crypto.Ecp` from DSL's `GroupElement` type. */ - def toECPoint(ge: GroupElement): Ecp = ge.asInstanceOf[CGroupElement].wrappedValue - - /** Creates a new AvlTree instance with the given parameters. - * @see AvlTreeData for details - */ - override def avlTree(operationFlags: Byte, digest: Coll[Byte], keyLength: Int, valueLengthOpt: Option[Int]): CAvlTree = { - val treeData = AvlTreeData(digest, AvlTreeFlags(operationFlags), keyLength, valueLengthOpt) - CAvlTree(treeData) - } - - /** Wraps the given tree data into SigmaDsl value of type [[AvlTree]]. */ - def avlTree(treeData: AvlTreeData): AvlTree = { - CAvlTree(treeData) - } - - /** Wraps the given [[ErgoBox]] into SigmaDsl value of type [[Box]]. - * @param ebox the value to be wrapped - * @see [[sigmastate.SBox]], [[sigma.Box]] - */ - def Box(ebox: ErgoBox): Box = CostingBox(ebox) - - /** Extracts [[ErgoBox]] from the given [[Box]] instance. This is inverse to the Box method. */ - def toErgoBox(b: Box): ErgoBox = b.asInstanceOf[CostingBox].ebox - - /** HOTSPOT: don't beautify this code */ - private def toSigmaTrees(props: Array[SigmaProp]): Array[SigmaBoolean] = { - val len = props.length - val res = new Array[SigmaBoolean](len) - cfor(0)(_ < len, _ + 1) { i => - res(i) = toSigmaBoolean(props(i)) - } - res - } - - @inline private def toEcPointType(ge: GroupElement): EcPointType = - toECPoint(ge).asInstanceOf[EcPointType] - - override def atLeast(bound: Int, props: Coll[SigmaProp]): SigmaProp = { - if (props.length > AtLeast.MaxChildrenCount) - throw new IllegalArgumentException(s"Expected input elements count should not exceed ${AtLeast.MaxChildrenCount}, actual: ${props.length}") - val sigmaTrees = toSigmaTrees(props.toArray) - val tree = AtLeast.reduce(bound, sigmaTrees) - CSigmaProp(tree) - } - - override def allOf(conditions: Coll[Boolean]): Boolean = - conditions.forall(c => c) - - override def anyOf(conditions: Coll[Boolean]): Boolean = - conditions.exists(c => c) - - override def xorOf(conditions: Coll[Boolean]): Boolean = { - if (VersionContext.current.isJitActivated) { - val len = conditions.length - if (len == 0) false - else if (len == 1) conditions(0) - else { - var res = conditions(0) - cfor(1)(_ < len, _ + 1) { i => - res ^= conditions(i) - } - res - } - } else { - // This is buggy version used in v4.x interpreter (for ErgoTrees v0, v1) - conditions.toArray.distinct.length == 2 - } - } - - override def allZK(props: Coll[SigmaProp]): SigmaProp = { - val sigmaTrees = toSigmaTrees(props.toArray) - val tree = CAND.normalized(sigmaTrees) - CSigmaProp(tree) - } - - override def anyZK(props: Coll[SigmaProp]): SigmaProp = { - val sigmaTrees = toSigmaTrees(props.toArray) - val tree = COR.normalized(sigmaTrees) - CSigmaProp(tree) - } - - override def xor(l: Coll[Byte], r: Coll[Byte]): Coll[Byte] = - Colls.xor(l, r) - - override def sigmaProp(b: Boolean): SigmaProp = { - CSigmaProp(TrivialProp(b)) - } - - override def blake2b256(bytes: Coll[Byte]): Coll[Byte] = { - val h = Blake2b256.hash(bytes.toArray) - Colls.fromArray(h) - } - - override def sha256(bytes: Coll[Byte]): Coll[Byte] = { - val h = Sha256.hash(bytes.toArray) - Colls.fromArray(h) - } - - override def byteArrayToBigInt(bytes: Coll[Byte]): BigInt = { - val bi = new BigInteger(bytes.toArray).to256BitValueExact - this.BigInt(bi) - } - - override def longToByteArray(l: Long): Coll[Byte] = - Colls.fromArray(Longs.toByteArray(l)) - - override def byteArrayToLong(bytes: Coll[Byte]): Long = - Longs.fromByteArray(bytes.toArray) - - override def proveDlog(ge: GroupElement): SigmaProp = - CSigmaProp(ProveDlog(toECPoint(ge).asInstanceOf[EcPointType])) - - override def proveDHTuple(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement): SigmaProp = { - val dht = ProveDHTuple(toEcPointType(g), toEcPointType(h), toEcPointType(u), toEcPointType(v)) - CSigmaProp(dht) - } - - private lazy val _generatorElement = this.GroupElement(CryptoConstants.dlogGroup.generator) - override def groupGenerator: GroupElement = _generatorElement - - /** - * @return the identity of the Dlog group used in ErgoTree - */ - def groupIdentity: GroupElement = { - this.GroupElement(CryptoConstants.dlogGroup.identity) - } - - override def substConstants[T](scriptBytes: Coll[Byte], - positions: Coll[Int], - newValues: Coll[T]): Coll[Byte] = { - val constants = try newValues.toArrayOfConstants - catch { - case e: Throwable => - throw new RuntimeException(s"Cannot evaluate substConstants($scriptBytes, $positions, $newValues)", e) - } - val (res, _) = SubstConstants.eval(scriptBytes.toArray, positions.toArray, constants)(validationSettings) - Colls.fromArray(res) - } - - override def decodePoint(encoded: Coll[Byte]): GroupElement = { - val r = SigmaSerializer.startReader(encoded.toArray) - val p = GroupElementSerializer.parse(r) - this.GroupElement(p) - } - -} - -/** Default singleton instance of Global object, which implements global ErgoTree functions. */ -object CostingSigmaDslBuilder extends CostingSigmaDslBuilder - -/** A default implementation of [[Context]] interface. - * @see [[Context]] for detailed descriptions - */ -case class CostingDataContext( - _dataInputs: Coll[Box], - override val headers: Coll[Header], - override val preHeader: PreHeader, - inputs: Coll[Box], - outputs: Coll[Box], - height: Int, - selfBox: Box, - private val selfIndex: Int, - lastBlockUtxoRootHash: AvlTree, - _minerPubKey: Coll[Byte], - vars: Coll[AnyValue], - override val activatedScriptVersion: Byte, - override val currentErgoTreeVersion: Byte - ) - extends Context { - @inline override def builder: SigmaDslBuilder = CostingSigmaDslBuilder - - @inline override def HEIGHT: Int = height - - @inline override def SELF: Box = selfBox - - @inline override def dataInputs: Coll[Box] = _dataInputs - - @inline override def INPUTS = inputs - - @inline override def OUTPUTS = outputs - - @inline override def LastBlockUtxoRootHash = lastBlockUtxoRootHash - - @inline override def minerPubKey = _minerPubKey - - override def selfBoxIndex: Int = { - if (VersionContext.current.isJitActivated) { - // starting from v5.0 this is fixed - selfIndex - } else { - // this used to be a bug in v4.x (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603) - -1 - } - } - - override def getVar[T](id: Byte)(implicit tT: RType[T]): Option[T] = { - @unused // avoid warning about unused ctA - implicit val tag: ClassTag[T] = tT.classTag - if (id < 0 || id >= vars.length) return None - val value = vars(id) - if (value != null) { - // once the value is not null it should be of the right type - value match { - case value: CAnyValue[_] if value.value != null && value.tA == tT => - Some(value.value.asInstanceOf[T]) - case _ => - throw new InvalidType(s"Cannot getVar[${tT.name}]($id): invalid type of value $value at id=$id") - } - } else None - } - - /** Return a new context instance with variables collection updated. - * @param bindings a new binding of the context variables with new values. - * @return a new instance (if `bindings` non-empty) with the specified bindings. - * other existing bindings are copied to the new instance - */ - def withUpdatedVars(bindings: (Int, AnyValue)*): CostingDataContext = { - if (bindings.isEmpty) return this - - val ids = bindings.map(_._1).toArray - val values = bindings.map(_._2).toArray - val maxVarId = ids.max // INV: ids is not empty - val requiredNewLength = maxVarId + 1 - - val newVars = if (vars.length < requiredNewLength) { - // grow vars collection - val currVars = vars.toArray - val buf = new Array[AnyValue](requiredNewLength) - Array.copy(currVars, 0, buf, 0, currVars.length) - cfor(0)(_ < ids.length, _ + 1) { i => - buf(ids(i)) = values(i) - } - buf.toColl - } else { - vars.updateMany(ids.toColl, values.toColl) - } - - this.copy(vars = newVars) - } -} - diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala b/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala index c7cac60d81..f9cb0e9f75 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala @@ -1,43 +1,21 @@ package sigmastate.eval -import debox.{cfor, Buffer => DBuffer} +import debox.cfor import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.TokenId -import sigma.data.{Nullable, RType} +import scorex.crypto.authds.avltree.batch.{Insert, Lookup, Remove, Update} +import scorex.crypto.authds.{ADKey, ADValue} import scorex.util.encode.Base16 -import sigmastate.SType.AnyOps -import sigmastate.Values.{Constant, ConstantNode} -import sigmastate.crypto.{CryptoFacade, Ecp} -import sigmastate.lang.{CheckingSigmaBuilder, TransformingSigmaBuilder} -import sigmastate.utils.Helpers -import sigmastate.{Platform, SCollection, SCollectionType, SType} -import sigma.Coll +import sigma.ast.SType.AnyOps +import sigma.ast._ +import sigma.data.{CBox, Digest32Coll, RType} import sigma._ -import java.math.BigInteger +import scala.util.{Failure, Success} object Extensions { - implicit class ByteExt(val b: Byte) extends AnyVal { - @inline def toBigInt: BigInt = CostingSigmaDslBuilder.BigInt(BigInteger.valueOf(b.toLong)) - } - - implicit class IntExt(val x: Int) extends AnyVal { - /** Convert this value to BigInt. */ - @inline def toBigInt: BigInt = CostingSigmaDslBuilder.BigInt(BigInteger.valueOf(x.toLong)) - } - - implicit class LongExt(val x: Long) extends AnyVal { - /** Convert this value to BigInt. */ - @inline def toBigInt: BigInt = CostingSigmaDslBuilder.BigInt(BigInteger.valueOf(x)) - } - - implicit class ArrayOps[T: RType](arr: Array[T]) { - /** Wraps array into Coll instance. The source array in not cloned. */ - @inline def toColl: Coll[T] = Colls.fromArray(arr) - } - - /** Extension methods for `Coll[Byte]` not available for generic `Array[T]`. */ + /** Extension methods for `Array[Byte]` not available for generic `Array[T]`. */ implicit class ArrayByteOps(val arr: Array[Byte]) extends AnyVal { /** Wraps array into TokenId instance. The source array in not cloned. */ @inline def toTokenId: TokenId = Digest32Coll @@ Colls.fromArray(arr) @@ -45,53 +23,12 @@ object Extensions { @inline def toHex: String = Base16.encode(arr) } - implicit class EvalIterableOps[T: RType](seq: Iterable[T]) { - @inline def toColl: Coll[T] = Colls.fromArray[T](seq.toArray(RType[T].classTag)) - } - - implicit class EvalCollOps[T](val coll: Coll[T]) extends AnyVal { - /** Helper type synonym. */ - type ElemTpe = SType { type WrappedType = T} - - /** Wraps the collection into ConstantNode using collection's element type. */ - def toConstant: Constant[SCollection[ElemTpe]] = { - val elemTpe = Evaluation.rtypeToSType(coll.tItem).asInstanceOf[ElemTpe] - ConstantNode[SCollection[ElemTpe]](coll, SCollectionType(elemTpe)) - } - - /** Transforms this collection into array of constants. - * - * This method have exactly the same semantics on JS and JVM IF `coll.tItem` - * precisely describes the type of elements in `call`. (Which is the case for all - * collections created by ErgoTree interpreter). - * - * However, if it is not the case, then JVM and JS will have different semantics for Byte and Short. - * - * The JVM version preserves v5.0 consensus protocol semantics. - * The JS version is a reasonable approximation of the JVM version. - */ - def toArrayOfConstants: Array[Constant[SType]] = { - val constants = coll.toArray.map { v => - // see ScalaDoc for ensureTypeCarringValue - val valToLift = Helpers.ensureTypeCarringValue(v, coll.tItem.asInstanceOf[RType[Any]]) - // call platform-specific method to transform the value to a Constant - Platform.liftToConstant(valToLift, TransformingSigmaBuilder) match { - case Nullable(c) => c - case _ => sys.error(s"Cannot liftToConstant($valToLift)") - } - } - constants - } - } - implicit class DslDataOps[A](data: A)(implicit tA: RType[A]) { def toTreeData: Constant[SType] = { CheckingSigmaBuilder.mkConstant(data.asWrappedType, Evaluation.rtypeToSType(tA)) } } - def toAnyValue[A:RType](x: A) = new CAnyValue(x, RType[A].asInstanceOf[RType[Any]]) - implicit class ErgoBoxOps(val ebox: ErgoBox) extends AnyVal { def toTestBox: Box = { /* NOHF PROOF: @@ -100,39 +37,103 @@ object Extensions { Safety: used in ErgoLikeContext where boxes cannot be null Examined ergo code: all that leads to ErgoLikeContext creation. */ - CostingBox(ebox) + CBox(ebox) } } - /** Shortened String representation of `source` GroupElement. */ - def showECPoint(p: Ecp): String = { - if (p.isIdentity) { - "IDENTITY" + implicit class AvlTreeOps(val tree: AvlTree) extends AnyVal { + + def contains(key: Coll[Byte], proof: Coll[Byte]): Boolean = { + val keyBytes = key.toArray + val bv = CAvlTreeVerifier(tree, proof) + bv.performOneOperation(Lookup(ADKey @@ keyBytes)) match { + case Success(r) => r match { + case Some(_) => true + case _ => false + } + case Failure(_) => false + } } - else { - CryptoFacade.showPoint(p) + + def get(key: Coll[Byte], proof: Coll[Byte]): Option[Coll[Byte]] = { + val keyBytes = key.toArray + val bv = CAvlTreeVerifier(tree, proof) + bv.performOneOperation(Lookup(ADKey @@ keyBytes)) match { + case Success(r) => r match { + case Some(v) => Some(Colls.fromArray(v)) + case _ => None + } + case Failure(_) => syntax.error(s"Tree proof is incorrect $tree") + } } - } - implicit class EcpOps(val source: Ecp) extends AnyVal { - /** Extracts [[GroupElement]] from the Ecp instance. */ - def toGroupElement: GroupElement = SigmaDsl.GroupElement(source) - } + def getMany( + keys: Coll[Coll[Byte]], + proof: Coll[Byte]): Coll[Option[Coll[Byte]]] = { + val bv = CAvlTreeVerifier(tree, proof) + keys.map { key => + bv.performOneOperation(Lookup(ADKey @@ key.toArray)) match { + case Success(r) => r match { + case Some(v) => Some(Colls.fromArray(v)) + case _ => None + } + case Failure(_) => syntax.error(s"Tree proof is incorrect $tree") + } + } + } - implicit class GroupElementOps(val source: GroupElement) extends AnyVal { - /** Shortened String representation of `source` GroupElement. */ - def showToString: String = showECPoint(source.asInstanceOf[CGroupElement].wrappedValue) - } + def insert( + entries: Coll[(Coll[Byte], Coll[Byte])], + proof: Coll[Byte]): Option[AvlTree] = { + if (!tree.isInsertAllowed) { + None + } else { + val bv = CAvlTreeVerifier(tree, proof) + entries.forall { case (key, value) => + val insertRes = bv.performOneOperation(Insert(ADKey @@ key.toArray, ADValue @@ value.toArray)) + if (insertRes.isFailure) { + syntax.error(s"Incorrect insert for $tree (key: $key, value: $value, digest: ${tree.digest}): ${insertRes.failed.get}}") + } + insertRes.isSuccess + } + bv.digest match { + case Some(d) => Some(tree.updateDigest(Colls.fromArray(d))) + case _ => None + } + } + } + + def update( + operations: Coll[(Coll[Byte], Coll[Byte])], + proof: Coll[Byte]): Option[AvlTree] = { + if (!tree.isUpdateAllowed) { + None + } else { + val bv = CAvlTreeVerifier(tree, proof) + operations.forall { case (key, value) => + bv.performOneOperation(Update(ADKey @@ key.toArray, ADValue @@ value.toArray)).isSuccess + } + bv.digest match { + case Some(d) => Some(tree.updateDigest(Colls.fromArray(d))) + case _ => None + } + } + } - implicit class DBufferOps[A](val buf: DBuffer[A]) extends AnyVal { - /** Sum all values in `buf` using the given Numeric. */ - def sumAll(implicit n: Numeric[A]): A = { - val limit = buf.length - var result: A = n.zero - cfor(0)(_ < limit, _ + 1) { i => - result = n.plus(result, buf.elems(i)) + def remove(operations: Coll[Coll[Byte]], proof: Coll[Byte]): Option[AvlTree] = { + if (!tree.isRemoveAllowed) { + None + } else { + val bv = CAvlTreeVerifier(tree, proof) + cfor(0)(_ < operations.length, _ + 1) { i => + val key = operations(i).toArray + bv.performOneOperation(Remove(ADKey @@ key)) + } + bv.digest match { + case Some(v) => Some(tree.updateDigest(Colls.fromArray(v))) + case _ => None + } } - result } } } diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/package.scala b/interpreter/shared/src/main/scala/sigmastate/eval/package.scala index 12b73de5f6..089e11c0b8 100644 --- a/interpreter/shared/src/main/scala/sigmastate/eval/package.scala +++ b/interpreter/shared/src/main/scala/sigmastate/eval/package.scala @@ -1,50 +1,18 @@ package sigmastate -import java.math.BigInteger - import org.ergoplatform.ErgoBox -import sigma.data.RType -import scorex.crypto.hash.Digest32 -import sigmastate.Values.SigmaBoolean -import sigmastate.crypto.CryptoConstants.EcPointType -import sigma.{Coll, CollBuilder} import sigma._ -import supertagged.TaggedType +import sigma.data.{AvlTreeData, SigmaBoolean} +import sigma.eval.SigmaDsl +import sigma.exceptions.CostLimitException import scala.language.implicitConversions package object eval { - /** The primary reference to Global instance of SigmaDsl. - * Besides operations of SigmaDslBuilder class, this instance also contains methods, - * which are not available in Dsl code, and which are not in SigmaDslBuilder interface. - * For example methods like `Box`, `toErgoBox` are available here, but not available in Dsl. - * @see SigmaDslBuilder - */ - val SigmaDsl = CostingSigmaDslBuilder - - /** Constructor of tuple value with more than 2 items. - * Such long tuples are represented as Coll[Any]. - * This representaion of tuples is different from representation of pairs (x, y), - * where Tuple2 type is used instead of Coll. */ - def TupleColl(items: Any*): Coll[Any] = Colls.fromItems(items:_*)(sigma.AnyType) - - trait BaseDigestColl extends TaggedType[Coll[Byte]] - - object Digest32Coll extends BaseDigestColl - - type Digest32Coll = Digest32Coll.Type - implicit val Digest32CollRType: RType[Digest32Coll] = RType[Coll[Byte]].asInstanceOf[RType[Digest32Coll] ] - /** Implicit conversions between Dsl type and the type wrapped by the corresponding type Dsl type. * Here BigInt is Dsl type and BigInteger is wrapped type. * @see `sigma.CBigInt` */ - implicit def bigIntegerToBigInt(bi: BigInteger): BigInt = SigmaDsl.BigInt(bi) - implicit def bigIntToBigInteger(bi: BigInt): BigInteger = SigmaDsl.toBigInteger(bi) - - implicit def ecPointToGroupElement(p: EcPointType): GroupElement = SigmaDsl.GroupElement(p) - implicit def groupElementToECPoint(p: GroupElement): EcPointType = SigmaDsl.toECPoint(p).asInstanceOf[EcPointType] - implicit def sigmaBooleanToSigmaProp(p: SigmaBoolean): SigmaProp = SigmaDsl.SigmaProp(p) implicit def sigmaPropToSigmaBoolean(p: SigmaProp): SigmaBoolean = SigmaDsl.toSigmaBoolean(p) @@ -53,4 +21,35 @@ package object eval { implicit def ergoBoxToBox(p: ErgoBox): Box = SigmaDsl.Box(p) implicit def boxToErgoBox(p: Box): ErgoBox = SigmaDsl.toErgoBox(p) + + def msgCostLimitError( + cost: Long, + limit: Long) = s"Estimated execution cost $cost exceeds the limit $limit" + + /** Helper method to accumulate cost while checking limit. + * + * @param current current cost value + * @param delta additional cost to add to the current value + * @param limit total cost limit + * @param msgSuffix use case-specific error message suffix + * @return new increased cost when it doesn't exceed the limit + * @throws CostLimitException + */ + def addCostChecked( + current: Long, + delta: Long, + limit: Long, + msgSuffix: => String = ""): Long = { + val newCost = java7.compat.Math.addExact(current, delta) + if (newCost > limit) { + throw new CostLimitException( + estimatedCost = newCost, + message = { + val suffix = if (msgSuffix.isEmpty) "" else s": $msgSuffix" + msgCostLimitError(newCost, limit) + suffix + }, + cause = None) + } + newCost + } } diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaSerializerExceptions.scala b/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaSerializerExceptions.scala deleted file mode 100644 index ee49cb433a..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaSerializerExceptions.scala +++ /dev/null @@ -1,23 +0,0 @@ -package sigmastate.exceptions - -/** Thrown by TypeSerializer when type prefix <= 0. */ -final class InvalidTypePrefix(message: String, cause: Option[Throwable] = None) - extends SerializerException(message, cause) - -/** Thrown when the current reader position > positionLimit which is set in the Reader. - * @see [[org.ergoplatform.validation.ValidationRules.CheckPositionLimit]] - */ -final class ReaderPositionLimitExceeded( - message: String, - val position: Int, - val positionLimit: Int, - cause: Option[Throwable] = None) - extends SerializerException(message, cause) - -/** Thrown when the current depth level > maxDepthLevel which is set in the Reader. */ -final class DeserializeCallDepthExceeded(message: String, cause: Option[Throwable] = None) - extends SerializerException(message, cause) - -/** Thrown by [[org.ergoplatform.validation.ValidationRules.CheckValidOpCode]] validation rule. */ -final class InvalidOpCode(message: String, cause: Option[Throwable] = None) - extends SerializerException(message, cause) diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala b/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala index b5802e39ca..8ff9cb370f 100644 --- a/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/exceptions/SigmaTyperExceptions.scala @@ -1,6 +1,7 @@ package sigmastate.exceptions -import sigmastate.lang.SourceContext +import sigma.ast.SourceContext +import sigma.exceptions.TyperException final class InvalidBinaryOperationParameters(message: String, source: Option[SourceContext] = None) extends TyperException(message, source) diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala similarity index 64% rename from interpreter/shared/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala rename to interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala index e1bcd3119c..de8aa6b620 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/CErgoTreeEvaluator.scala @@ -1,86 +1,22 @@ package sigmastate.interpreter import org.ergoplatform.ErgoLikeContext -import sigmastate.{FixedCost, JitCost, PerItemCost, SType, TypeBasedCost} -import sigmastate.Values._ -import sigmastate.eval.Profiler -import sigmastate.interpreter.ErgoTreeEvaluator.DataEnv +import sigma.ast._ +import sigma.ast.syntax._ +import sigmastate.eval.{CAvlTreeVerifier, CProfiler} import sigmastate.interpreter.Interpreter.ReductionResult -import sigma.{Context, SigmaProp} +import sigma.{AvlTree, Coll, Colls, Context, VersionContext} import sigma.util.Extensions._ -import sigmastate.interpreter.EvalSettings._ -import supertagged.TaggedType -import debox.{Buffer => DBuffer} -import sigma.VersionContext -import scala.collection.compat.immutable.ArraySeq -import scala.util.DynamicVariable - -/** Configuration parameters of the evaluation run. */ -case class EvalSettings( - /** Used together with [[ErgoTreeEvaluator.profiler]] to measure individual operations timings. */ - isMeasureOperationTime: Boolean, - /** Used together with [[ErgoTreeEvaluator.profiler]] to measure script timings. */ - isMeasureScriptTime: Boolean, - /** Used by [[ErgoTreeEvaluator]] to conditionally perform debug mode operations. */ - isDebug: Boolean = false, - /** Used by [[ErgoTreeEvaluator]] to conditionally emit log messages. */ - isLogEnabled: Boolean = false, - /** Used by [[ErgoTreeEvaluator]] to conditionally build a trace of added costs. - * @see Value.addCost - */ - costTracingEnabled: Boolean = false, - /** Profiler which, when defined, should be used in [[ErgoTreeEvaluator]] constructor. */ - profilerOpt: Option[Profiler] = None, - /** Should be set to true, if evaluation is performed as part of test suite. - * In such a case, additional operations may be performed (such as sanity checks). */ - isTestRun: Boolean = false, - /** If true, then expected test vectors are pretty-printed. */ - printTestVectors: Boolean = false, - /** When Some(mode) is specified then it defines which version of the Interpreter.verify - * and Interpreter.prove methods should use. - * The default value is None, which means the version is defined by ErgoTree.version - * and Context.activatedScriptVersion. - */ - evaluationMode: Option[EvaluationMode] = None, - /** Maximum execution cost of a script used by profiler. - * @see ErgoTreeEvaluator - */ - scriptCostLimitInEvaluator: Int = 1000000 -) - -object EvalSettings { - /** Enumeration type of evaluation modes of [[Interpreter]]. - * This type can be removed in v5.x releases together with AOT implementation once v5.0 - * protocol is activated. - */ - object EvaluationMode extends TaggedType[Int] { - implicit class EvaluationModeOps(val x: EvaluationMode) extends AnyVal { - def name: String = x match { - case AotEvaluationMode => "AotEvaluationMode" - case JitEvaluationMode => "JitEvaluationMode" - } - - /** Returns true if AOT interpreter should be evaluated. */ - def okEvaluateAot: Boolean = { - x == AotEvaluationMode - } - - /** Returns true if JIT interpreter should be evaluated. */ - def okEvaluateJit: Boolean = { - x == JitEvaluationMode - } - } - } - type EvaluationMode = EvaluationMode.Type - - /** Evaluation mode when the interpreter is executing using AOT costing implementation - * of v4.x protocol. */ - val AotEvaluationMode: EvaluationMode = EvaluationMode @@ 1 // first bit +import debox.{cfor, Buffer => DBuffer} +import scorex.crypto.authds.ADKey +import sigma.ast.SAvlTreeMethods._ +import sigma.ast.SType +import sigma.data.{CSigmaProp, KeyValueColl, SigmaBoolean} +import sigma.eval.{AvlTreeVerifier, ErgoTreeEvaluator, EvalSettings, Profiler} +import sigma.eval.ErgoTreeEvaluator.DataEnv - /** Evaluation mode when the interpreter is executing using JIT costing implementation - * of v5.x protocol. */ - val JitEvaluationMode: EvaluationMode = EvaluationMode @@ 2 // second bit -} +import scala.collection.compat.immutable.ArraySeq +import scala.util.{DynamicVariable, Failure, Success} /** Result of JITC evaluation with costing. */ case class JitEvalResult[A](value: A, cost: JitCost) @@ -121,17 +57,169 @@ case class JitEvalResult[A](value: A, cost: JitCost) * @param profiler Performs operations profiling and time measurements (if enabled in settings). * @param settings Settings to be used during evaluation. */ -class ErgoTreeEvaluator( +class CErgoTreeEvaluator( val context: Context, val constants: Seq[Constant[SType]], protected val coster: CostAccumulator, val profiler: Profiler, - val settings: EvalSettings) { + val settings: EvalSettings) extends ErgoTreeEvaluator { + + override def createTreeVerifier(tree: AvlTree, proof: Coll[Byte]): AvlTreeVerifier = + CAvlTreeVerifier(tree, proof) + + /** Creates [[sigma.eval.AvlTreeVerifier]] for the given tree and proof. */ + def createVerifier(tree: AvlTree, proof: Coll[Byte]) = { + // the cost of tree reconstruction from proof is O(proof.length) + addSeqCost(CreateAvlVerifier_Info, proof.length) { () => + CAvlTreeVerifier(tree, proof) + } + } + + override def contains_eval(mc: MethodCall, tree: AvlTree, key: Coll[Byte], proof: Coll[Byte]): Boolean = { + val bv = createVerifier(tree, proof) + val nItems = bv.treeHeight + var res = false + // the cost of tree lookup is O(bv.treeHeight) + addSeqCost(LookupAvlTree_Info, nItems) { () => + res = bv.performLookup(ADKey @@ key.toArray) match { + case Success(r) => r match { + case Some(_) => true + case _ => false + } + case Failure(_) => false + } + } + res + } + + override def get_eval(mc: MethodCall, tree: AvlTree, key: Coll[Byte], proof: Coll[Byte]): Option[Coll[Byte]] = { + val bv = createVerifier(tree, proof) + val nItems = bv.treeHeight + + // the cost of tree lookup is O(bv.treeHeight) + addSeqCost(LookupAvlTree_Info, nItems) { () => + bv.performLookup(ADKey @@ key.toArray) match { + case Success(r) => r match { + case Some(v) => Some(Colls.fromArray(v)) + case _ => None + } + case Failure(_) => syntax.error(s"Tree proof is incorrect $tree") + } + } + } + + override def getMany_eval( + mc: MethodCall, + tree: AvlTree, + keys: Coll[Coll[Byte]], + proof: Coll[Byte]): Coll[Option[Coll[Byte]]] = { + val bv = createVerifier(tree, proof) + val nItems = bv.treeHeight + keys.map { key => + // the cost of tree lookup is O(bv.treeHeight) + addSeqCost(LookupAvlTree_Info, nItems) { () => + bv.performLookup(ADKey @@ key.toArray) match { + case Success(r) => r match { + case Some(v) => Some(Colls.fromArray(v)) + case _ => None + } + case Failure(_) => syntax.error(s"Tree proof is incorrect $tree") + } + } + } + } + + override def insert_eval(mc: MethodCall, tree: AvlTree, entries: KeyValueColl, proof: Coll[Byte]): Option[AvlTree] = { + addCost(isInsertAllowed_Info) + if (!tree.isInsertAllowed) { + None + } else { + val bv = createVerifier(tree, proof) + // when the tree is empty we still need to add the insert cost + val nItems = Math.max(bv.treeHeight, 1) + entries.forall { case (key, value) => + var res = true + // the cost of tree lookup is O(bv.treeHeight) + addSeqCost(InsertIntoAvlTree_Info, nItems) { () => + val insertRes = bv.performInsert(key.toArray, value.toArray) + // TODO v6.0: throwing exception is not consistent with update semantics + // however it preserves v4.0 semantics (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) + if (insertRes.isFailure) { + syntax.error(s"Incorrect insert for $tree (key: $key, value: $value, digest: ${tree.digest}): ${insertRes.failed.get}}") + } + res = insertRes.isSuccess + } + res + } + bv.digest match { + case Some(d) => + addCost(updateDigest_Info) + Some(tree.updateDigest(Colls.fromArray(d))) + case _ => None + } + } + } + + override def update_eval( + mc: MethodCall, tree: AvlTree, + operations: KeyValueColl, proof: Coll[Byte]): Option[AvlTree] = { + addCost(isUpdateAllowed_Info) + if (!tree.isUpdateAllowed) { + None + } else { + val bv = createVerifier(tree, proof) + // when the tree is empty we still need to add the insert cost + val nItems = Math.max(bv.treeHeight, 1) + + // here we use forall as looping with fast break on first failed tree oparation + operations.forall { case (key, value) => + var res = true + // the cost of tree update is O(bv.treeHeight) + addSeqCost(UpdateAvlTree_Info, nItems) { () => + val updateRes = bv.performUpdate(key.toArray, value.toArray) + res = updateRes.isSuccess + } + res + } + bv.digest match { + case Some(d) => + addCost(updateDigest_Info) + Some(tree.updateDigest(Colls.fromArray(d))) + case _ => None + } + } + } + + override def remove_eval( + mc: MethodCall, tree: AvlTree, + operations: Coll[Coll[Byte]], proof: Coll[Byte]): Option[AvlTree] = { + addCost(isRemoveAllowed_Info) + if (!tree.isRemoveAllowed) { + None + } else { + val bv = createVerifier(tree, proof) + // when the tree is empty we still need to add the insert cost + val nItems = Math.max(bv.treeHeight, 1) + cfor(0)(_ < operations.length, _ + 1) { i => + addSeqCost(RemoveAvlTree_Info, nItems) { () => + val key = operations(i).toArray + bv.performRemove(key) + } + } + addCost(digest_Info) + bv.digest match { + case Some(d) => + addCost(updateDigest_Info) + Some(tree.updateDigest(Colls.fromArray(d))) + case _ => None + } + } + } /** Evaluates the given expression in the given data environment. */ def eval(env: DataEnv, exp: SValue): Any = { VersionContext.checkVersions(context.activatedScriptVersion, context.currentErgoTreeVersion) - ErgoTreeEvaluator.currentEvaluator.withValue(this) { + CErgoTreeEvaluator.currentEvaluator.withValue(this) { exp.evalTo[Any](env)(this) } } @@ -168,33 +256,18 @@ class ErgoTreeEvaluator( costTrace.clear() } - /** Adds the given cost to the `coster`. If tracing is enabled, associates the cost with - * the given operation. - * - * @param costKind kind of the cost to be added to `coster` - * @param opDesc operation descriptor to associate the cost with (when costTracingEnabled) - */ - final def addCost(costKind: FixedCost, opDesc: OperationDesc): Unit = { + override def addCost(costKind: FixedCost, opDesc: OperationDesc): Unit = { coster.add(costKind.cost) if (settings.costTracingEnabled) { costTrace += FixedCostItem(opDesc, costKind) } } - @inline final def addCost(costInfo: OperationCostInfo[FixedCost]): Unit = { + override def addCost(costInfo: OperationCostInfo[FixedCost]): Unit = { addCost(costInfo.costKind, costInfo.opDesc) } - /** Add the cost given by the cost descriptor and the type to the accumulator and - * associate it with this operation descriptor. - * - * @param costKind descriptor of the cost - * @param tpe specific type for which the cost should be computed by this descriptor - * (see costFunc method) - * @param opDesc operation which is associated with this cost - */ - @inline - final def addTypeBasedCost[R](costKind: TypeBasedCost, + override def addTypeBasedCost[R](costKind: TypeBasedCost, tpe: SType, opDesc: OperationDesc)(block: () => R): R = { var costItem: TypeBasedCostItem = null if (settings.costTracingEnabled) { @@ -219,14 +292,8 @@ class ErgoTreeEvaluator( } } - /** Adds the given cost to the `coster`. If tracing is enabled, associates the cost with - * the given operation. - * @param costKind kind of the cost to be added to `coster` - * @param opDesc the operation descriptor to associate the cost with (when costTracingEnabled) - * @param block operation executed under the given cost - * @hotspot don't beautify the code - */ - final def addFixedCost(costKind: FixedCost, opDesc: OperationDesc)(block: => Unit): Unit = { + /** @hotspot don't beautify the code */ + override def addFixedCost(costKind: FixedCost, opDesc: OperationDesc)(block: => Unit): Unit = { var costItem: FixedCostItem = null if (settings.costTracingEnabled) { costItem = FixedCostItem(opDesc, costKind) @@ -247,20 +314,12 @@ class ErgoTreeEvaluator( } } - @inline - final def addFixedCost(costInfo: OperationCostInfo[FixedCost])(block: => Unit): Unit = { + override def addFixedCost(costInfo: OperationCostInfo[FixedCost])(block: => Unit): Unit = { addFixedCost(costInfo.costKind, costInfo.opDesc)(block) } - /** Adds the given cost to the `coster`. If tracing is enabled, creates a new cost item - * with the given operation. - * - * @param costKind the cost to be added to `coster` for each item - * @param nItems the number of items - * @param opDesc the operation to associate the cost with (when costTracingEnabled) - * @hotspot don't beautify the code - */ - final def addSeqCostNoOp(costKind: PerItemCost, nItems: Int, opDesc: OperationDesc): Unit = { + /** @hotspot don't beautify the code */ + override def addSeqCostNoOp(costKind: PerItemCost, nItems: Int, opDesc: OperationDesc): Unit = { var costItem: SeqCostItem = null if (settings.costTracingEnabled) { costItem = SeqCostItem(opDesc, costKind, nItems) @@ -270,17 +329,7 @@ class ErgoTreeEvaluator( coster.add(cost) } - /** Adds the given cost to the `coster`. If tracing is enabled, creates a new cost item - * with the given operation. - * - * @param costKind the cost to be added to `coster` for each item - * @param nItems the number of items - * @param opDesc the operation to associate the cost with (when costTracingEnabled) - * @param block operation executed under the given cost - * @tparam R result type of the operation - * @hotspot don't beautify the code - */ - final def addSeqCost[R](costKind: PerItemCost, nItems: Int, opDesc: OperationDesc)(block: () => R): R = { + override def addSeqCost[R](costKind: PerItemCost, nItems: Int, opDesc: OperationDesc)(block: () => R): R = { var costItem: SeqCostItem = null if (settings.costTracingEnabled) { costItem = SeqCostItem(opDesc, costKind, nItems) @@ -304,29 +353,12 @@ class ErgoTreeEvaluator( } } - /** Adds the cost to the `coster`. See the other overload for details. */ - @inline - final def addSeqCost[R](costInfo: OperationCostInfo[PerItemCost], nItems: Int) + override def addSeqCost[R](costInfo: OperationCostInfo[PerItemCost], nItems: Int) (block: () => R): R = { addSeqCost(costInfo.costKind, nItems, costInfo.opDesc)(block) } - /** Adds the cost to the `coster`. If tracing is enabled, creates a new cost item with - * the given operation descriptor and cost kind. If time measuring is enabled also - * performs profiling. - * - * WARNING: The cost is accumulated AFTER the block is executed. - * Each usage of this method should be accompanied with a proof of why this cannot lead - * to unbounded execution (see all usages). - * - * @param costKind the cost descriptor to be used to compute the cost based on the - * actual number of items returned by the `block` - * @param opDesc the operation to associate the cost with (when costTracingEnabled) - * @param block operation executed under the given cost descriptors, returns the - * actual number of items processed - * @hotspot don't beautify the code - */ - final def addSeqCost(costKind: PerItemCost, opDesc: OperationDesc)(block: () => Int): Unit = { + override def addSeqCost(costKind: PerItemCost, opDesc: OperationDesc)(block: () => Int): Unit = { var costItem: SeqCostItem = null var nItems = 0 if (settings.isMeasureOperationTime) { @@ -350,18 +382,15 @@ class ErgoTreeEvaluator( } } - /** Adds the cost to the `coster`. See the other overload for details. */ - final def addSeqCost(costInfo: OperationCostInfo[PerItemCost])(block: () => Int): Unit = { + override def addSeqCost(costInfo: OperationCostInfo[PerItemCost])(block: () => Int): Unit = { addSeqCost(costInfo.costKind, costInfo.opDesc)(block) } } -object ErgoTreeEvaluator { - /** Immutable data environment used to assign data values to graph nodes. */ - type DataEnv = Map[Int, Any] +object CErgoTreeEvaluator { /** Size of data block in bytes. Used in JIT cost calculations. - * @see [[sigmastate.NEQ]], + * @see [[NEQ]], */ val DataBlockSize: Int = 512 @@ -369,7 +398,7 @@ object ErgoTreeEvaluator { val EmptyDataEnv: DataEnv = Map.empty /** A profiler which is used by default if [[EvalSettings.isMeasureOperationTime]] is enabled. */ - val DefaultProfiler = new Profiler + val DefaultProfiler = new CProfiler /** Default global [[EvalSettings]] instance. */ val DefaultEvalSettings = EvalSettings( @@ -378,26 +407,27 @@ object ErgoTreeEvaluator { /** Evaluator currently is being executed on the current thread. * This variable is set in a single place, specifically in the `eval` method of - * [[ErgoTreeEvaluator]]. + * [[CErgoTreeEvaluator]]. + * * @see getCurrentEvaluator */ - private[sigmastate] val currentEvaluator = new DynamicVariable[ErgoTreeEvaluator](null) + private[sigmastate] val currentEvaluator = new DynamicVariable[CErgoTreeEvaluator](null) /** Returns a current evaluator for the current thread. */ - def getCurrentEvaluator: ErgoTreeEvaluator = currentEvaluator.value + def getCurrentEvaluator: CErgoTreeEvaluator = currentEvaluator.value - /** Creates a new [[ErgoTreeEvaluator]] instance with the given profiler and settings. + /** Creates a new [[CErgoTreeEvaluator]] instance with the given profiler and settings. * The returned evaluator can be used to initialize the `currentEvaluator` variable. * As a result, cost-aware operations (code blocks) can be implemented, even when those * operations don't involve ErgoTree evaluation. * As an example, see methods in [[sigmastate.SigSerializer]] and * [[sigmastate.FiatShamirTree]] where cost-aware code blocks are used. */ - def forProfiling(profiler: Profiler, evalSettings: EvalSettings): ErgoTreeEvaluator = { + def forProfiling(profiler: CProfiler, evalSettings: EvalSettings): CErgoTreeEvaluator = { val acc = new CostAccumulator( initialCost = JitCost(0), costLimit = Some(JitCost.fromBlockCost(evalSettings.scriptCostLimitInEvaluator))) - new ErgoTreeEvaluator( + new CErgoTreeEvaluator( context = null, constants = ArraySeq.empty, acc, profiler, evalSettings.copy(profilerOpt = Some(profiler))) @@ -406,7 +436,7 @@ object ErgoTreeEvaluator { /** Executes [[FixedCost]] code `block` and use the given evaluator `E` to perform * profiling and cost tracing. * This helper method allows implementation of cost-aware code blocks by using - * thread-local instance of [[ErgoTreeEvaluator]]. + * thread-local instance of [[CErgoTreeEvaluator]]. * If the `currentEvaluator` [[DynamicVariable]] is not initialized (equals to null), * then the block is executed with minimal overhead. * @@ -434,7 +464,7 @@ object ErgoTreeEvaluator { /** Executes [[PerItemCost]] code `block` and use the given evaluator `E` to perform * profiling and cost tracing. * This helper method allows implementation of cost-aware code blocks by using - * thread-local instance of [[ErgoTreeEvaluator]]. + * thread-local instance of [[CErgoTreeEvaluator]]. * If the `currentEvaluator` [[DynamicVariable]] is not initialized (equals to null), * then the block is executed with minimal overhead. * @@ -467,8 +497,7 @@ object ErgoTreeEvaluator { def evalToCrypto(context: ErgoLikeContext, ergoTree: ErgoTree, evalSettings: EvalSettings): ReductionResult = { val (res, cost) = eval(context, ergoTree.constants, ergoTree.toProposition(replaceConstants = false), evalSettings) val sb = res match { - case sp: SigmaProp => - sigmastate.eval.SigmaDsl.toSigmaBoolean(sp) + case sp: CSigmaProp => sp.wrappedValue case sb: SigmaBoolean => sb case _ => error(s"Expected SigmaBoolean but was: $res") } @@ -504,7 +533,7 @@ object ErgoTreeEvaluator { * @param costAccumulator [[CostAccumulator]] instance used for accumulating costs * @param constants collection of segregated constants which can be refered by * [[ConstantPlaceholder]]s in `exp` - * @param exp ErgoTree expression represented as [[sigmastate.Values.Value]] + * @param exp ErgoTree expression represented as [[sigma.ast.Value]] * @param evalSettings evaluation settings * @return 1) the result of evaluating `exp` in a given context and * 2) an accumulated JIT cost estimation. @@ -514,9 +543,9 @@ object ErgoTreeEvaluator { constants: Seq[Constant[SType]], exp: SValue, evalSettings: EvalSettings): (Any, Int) = { - val evaluator = new ErgoTreeEvaluator( + val evaluator = new CErgoTreeEvaluator( sigmaContext, constants, costAccumulator, DefaultProfiler, evalSettings) - val res = evaluator.eval(Map(), exp) + val res = evaluator.eval(Map(), exp) val cost = costAccumulator.totalCost.toBlockCost // scale to block cost (res, cost) } diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/CostAccumulator.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/CostAccumulator.scala index fe86acf9a4..c79a9b77df 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/CostAccumulator.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/CostAccumulator.scala @@ -1,7 +1,7 @@ package sigmastate.interpreter -import sigmastate.JitCost -import sigmastate.exceptions.CostLimitException +import sigma.ast.JitCost +import sigma.exceptions.CostLimitException /** Encapsulate simple monotonic (add only) counter with reset. */ class CostCounter(val initialCost: JitCost) { diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala index 2e894502f4..d4ce5bf155 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala @@ -1,8 +1,8 @@ package sigmastate.interpreter +import sigma.data.{SigmaBoolean, SigmaLeaf} import java.math.BigInteger -import sigmastate.{NodePosition, SigmaLeaf, UncheckedTree} -import sigmastate.Values.SigmaBoolean +import sigmastate.{NodePosition, UncheckedTree} import sigmastate.crypto.FirstProverMessage import sigmastate.crypto.VerifierMessage.Challenge diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala index cb519e12ae..6b673038c7 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -1,30 +1,32 @@ package sigmastate.interpreter -import java.util -import sigma.kiama.rewriting.Rewriter.{everywherebu, rule, strategy} +import debox.cfor import org.ergoplatform.ErgoLikeContext -import org.ergoplatform.validation.SigmaValidationSettings import org.ergoplatform.validation.ValidationRules._ -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ -import sigmastate.crypto.DLogProtocol.{DLogInteractiveProver, FirstDLogProverMessage, ProveDlog} +import sigma.VersionContext +import sigma.ast.SCollection.SByteArray +import sigma.ast.syntax._ +import sigma.ast._ +import sigma.data.{CAND, COR, CTHRESHOLD, ProveDHTuple, ProveDlog, SigmaBoolean, TrivialProp} +import sigma.kiama.rewriting.Rewriter.{everywherebu, rule, strategy} +import sigma.kiama.rewriting.Strategy +import sigma.serialization.SigSerializer._ +import sigma.serialization.{SigSerializer, SigmaSerializer, ValueSerializer} +import sigma.validation.SigmaValidationSettings +import sigma.validation.ValidationRules.trySoftForkable +import sigmastate.FiatShamirTree._ +import sigmastate._ +import sigmastate.crypto.DLogProtocol.{DLogProver, FirstDLogProverMessage} import sigmastate.crypto._ +import sigmastate.eval.{CProfiler, addCostChecked} +import sigmastate.interpreter.CErgoTreeEvaluator.fixedCostOp import sigmastate.interpreter.Interpreter._ -import sigmastate.serialization.{SigmaSerializer, ValueSerializer} -import sigmastate.utxo.DeserializeContext -import sigmastate.{SType, _} -import sigmastate.eval.{Evaluation, Profiler, SigmaDsl} -import sigmastate.FiatShamirTree._ -import sigmastate.SigSerializer._ -import sigmastate.eval.Evaluation.addCostChecked -import sigmastate.interpreter.ErgoTreeEvaluator.fixedCostOp +import sigma.ast.syntax.ValueOps +import sigma.eval.{EvalSettings, SigmaDsl} +import sigma.exceptions.{CostLimitException, InterpreterException} +import sigma.interpreter.ProverResult +import sigma.util.CollectionUtil import sigmastate.utils.Helpers._ -import sigmastate.lang.Terms.ValueOps -import debox.cfor -import sigma.VersionContext -import sigma.kiama.rewriting.Strategy -import sigmastate.exceptions.{CostLimitException, InterpreterException} import scala.util.{Success, Try} @@ -52,10 +54,13 @@ trait Interpreter { type ProofT = UncheckedTree - /** Evaluation settings used by [[ErgoTreeEvaluator]] which is used by this + /** Force initialization of reflection. */ + private val _ = InterpreterReflection + + /** Evaluation settings used by [[CErgoTreeEvaluator]] which is used by this * interpreter to perform fullReduction. */ - protected def evalSettings: EvalSettings = ErgoTreeEvaluator.DefaultEvalSettings + protected def evalSettings: EvalSettings = CErgoTreeEvaluator.DefaultEvalSettings /** Logs the given message string. Can be overridden in the derived interpreter classes * to redefine the default behavior. */ @@ -70,13 +75,13 @@ trait Interpreter { /** The cost of Value[T] deserialization is O(n), where n is the length of its bytes * array. To evaluate [[DeserializeContext]] and - * [[sigmastate.utxo.DeserializeRegister]] we add the following cost of deserialization + * [[DeserializeRegister]] we add the following cost of deserialization * for each byte. */ val CostPerByteDeserialized = 2 /** The cost of substituting [[DeserializeContext]] and - * [[sigmastate.utxo.DeserializeRegister]] nodes with the deserialized expression is + * [[DeserializeRegister]] nodes with the deserialized expression is * O(n), where n is the number of bytes in ErgoTree. * The following is the cost added for each ErgoTree.bytes. */ @@ -96,7 +101,7 @@ trait Interpreter { val script = ValueSerializer.deserialize(r) // Why ValueSerializer? read NOTE above val scriptComplexity = java7.compat.Math.multiplyExact(scriptBytes.length, CostPerByteDeserialized) - val currCost = Evaluation.addCostChecked(context.initCost, scriptComplexity, context.costLimit) + val currCost = addCostChecked(context.initCost, scriptComplexity, context.costLimit) val ctx1 = context.withInitCost(currCost).asInstanceOf[CTX] (ctx1, script) } @@ -170,7 +175,7 @@ trait Interpreter { val (resProp, cost) = { val ctx = context.asInstanceOf[ErgoLikeContext] - ErgoTreeEvaluator.eval(ctx, ErgoTree.EmptyConstants, exp, evalSettings) match { + CErgoTreeEvaluator.eval(ctx, ErgoTree.EmptyConstants, exp, evalSettings) match { case (p: sigma.SigmaProp, c) => (p, c) case (res, _) => sys.error(s"Invalid result type of $res: expected SigmaProp when evaluating $exp") @@ -199,7 +204,6 @@ trait Interpreter { def fullReduction(ergoTree: ErgoTree, ctx: CTX, env: ScriptEnv): ReductionResult = { - implicit val vs: SigmaValidationSettings = ctx.validationSettings val context = ctx.withErgoTreeVersion(ergoTree.version).asInstanceOf[CTX] VersionContext.withVersions(context.activatedScriptVersion, ergoTree.version) { val prop = propositionFromErgoTree(ergoTree, context) @@ -210,12 +214,12 @@ trait Interpreter { // NOTE, evaluator cost unit needs to be scaled to the cost unit of context val evalCost = Eval_SigmaPropConstant.costKind.cost.toBlockCost - val resCost = Evaluation.addCostChecked(context.initCost, evalCost, context.costLimit) + val resCost = addCostChecked(context.initCost, evalCost, context.costLimit) ReductionResult(sb, resCost) case _ if !ergoTree.hasDeserialize => val ctx = context.asInstanceOf[ErgoLikeContext] val res = VersionContext.withVersions(ctx.activatedScriptVersion, ergoTree.version) { - ErgoTreeEvaluator.evalToCrypto(ctx, ergoTree, evalSettings) + CErgoTreeEvaluator.evalToCrypto(ctx, ergoTree, evalSettings) } res case _ => @@ -241,7 +245,7 @@ trait Interpreter { implicit val vs: SigmaValidationSettings = context.validationSettings val res = VersionContext.withVersions(context.activatedScriptVersion, ergoTree.version) { val deserializeSubstitutionCost = java7.compat.Math.multiplyExact(ergoTree.bytes.length, CostPerTreeByte) - val currCost = Evaluation.addCostChecked(context.initCost, deserializeSubstitutionCost, context.costLimit) + val currCost = addCostChecked(context.initCost, deserializeSubstitutionCost, context.costLimit) val context1 = context.withInitCost(currCost).asInstanceOf[CTX] val (propTree, context2) = trySoftForkable[(SigmaPropValue, CTX)](whenSoftFork = (TrueSigmaProp, context1)) { applyDeserializeContextJITC(context, prop) @@ -361,7 +365,7 @@ trait Interpreter { val fullCost = addCryptoCost(reduced.value, reduced.cost, context.costLimit) val ok = if (evalSettings.isMeasureOperationTime) { - val E = ErgoTreeEvaluator.forProfiling(verifySignatureProfiler, evalSettings) + val E = CErgoTreeEvaluator.forProfiling(verifySignatureProfiler, evalSettings) verifySignature(reduced.value, message, proof)(E) } else { verifySignature(reduced.value, message, proof)(null) @@ -374,10 +378,10 @@ trait Interpreter { } // Perform Verifier Steps 4-6 - private def checkCommitments(sp: UncheckedSigmaTree, message: Array[Byte])(implicit E: ErgoTreeEvaluator): Boolean = { + private def checkCommitments(sp: UncheckedSigmaTree, message: Array[Byte])(implicit E: CErgoTreeEvaluator): Boolean = { // Perform Verifier Step 4 val newRoot = computeCommitments(sp).get.asInstanceOf[UncheckedSigmaTree] - val bytes = concatArrays(FiatShamirTree.toBytes(newRoot), message) + val bytes = CollectionUtil.concatArrays(FiatShamirTree.toBytes(newRoot), message) /** * Verifier Steps 5-6: Convert the tree to a string `s` for input to the Fiat-Shamir hash function, * using the same conversion as the prover in 7 @@ -393,20 +397,20 @@ trait Interpreter { * 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. */ - private[sigmastate] val computeCommitments: Strategy = everywherebu(rule[Any] { + val computeCommitments: Strategy = everywherebu(rule[Any] { case c: UncheckedConjecture => c // Do nothing for internal nodes case sn: UncheckedSchnorr => - implicit val E = ErgoTreeEvaluator.getCurrentEvaluator + implicit val E = CErgoTreeEvaluator.getCurrentEvaluator fixedCostOp(ComputeCommitments_Schnorr) { - val a = DLogInteractiveProver.computeCommitment(sn.proposition, sn.challenge, sn.secondMessage) + val a = DLogProver.computeCommitment(sn.proposition, sn.challenge, sn.secondMessage) sn.copy(commitmentOpt = Some(FirstDLogProverMessage(a))) } case dh: UncheckedDiffieHellmanTuple => - implicit val E = ErgoTreeEvaluator.getCurrentEvaluator + implicit val E = CErgoTreeEvaluator.getCurrentEvaluator fixedCostOp(ComputeCommitments_DHT) { - val (a, b) = DiffieHellmanTupleInteractiveProver.computeCommitment(dh.proposition, dh.challenge, dh.secondMessage) + val (a, b) = DiffieHellmanTupleProver.computeCommitment(dh.proposition, dh.challenge, dh.secondMessage) dh.copy(commitmentOpt = Some(FirstDHTupleProverMessage(a, b))) } @@ -450,7 +454,7 @@ trait Interpreter { */ def verifySignature(sigmaTree: SigmaBoolean, message: Array[Byte], - signature: Array[Byte])(implicit E: ErgoTreeEvaluator): Boolean = { + signature: Array[Byte])(implicit E: CErgoTreeEvaluator): Boolean = { // Perform Verifier Steps 1-3 try { SigSerializer.parseAndComputeChallenges(sigmaTree, signature) match { @@ -582,7 +586,7 @@ object Interpreter { /** An instance of profiler used to measure cost parameters of verifySignature * operations. */ - val verifySignatureProfiler = new Profiler + val verifySignatureProfiler = new CProfiler private def toValidScriptTypeJITC(exp: SValue): SigmaPropValue = exp match { case v: Value[SBoolean.type]@unchecked if v.tpe == SBoolean => v.toSigmaProp @@ -590,7 +594,4 @@ object Interpreter { case x => throw new Error(s"Context-dependent pre-processing should produce tree of type Boolean or SigmaProp but was $x") } - /** Helper method to throw errors from Interpreter. */ - def error(msg: String) = throw new InterpreterException(msg) - } \ No newline at end of file diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala index 864146ca07..38fc78fdb6 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala @@ -1,59 +1,8 @@ package sigmastate.interpreter -import org.ergoplatform.validation.SigmaValidationSettings -import sigmastate.SType -import sigmastate.Values.EvaluatedValue -import sigmastate.interpreter.ContextExtension.VarBinding -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteWriter, SigmaByteReader} -import sigma.AnyValue - -/** - * User-defined variables to be put into context. - * Each variable is identified by `id: Byte` and can be accessed from a script - * using `getVar[T](id)` operation. - * The value of the variable is represented by [[sigmastate.Values.Constant]] instance, - * which contains both data value and [[SType]] descriptor. The descriptor is checked - * against the type `T` expected in the script operation. If the types don't match, - * exception is thrown and the box spending (protected by the script) fails. - * - * @param values internal container of the key-value pairs - */ -case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) { - def add(bindings: VarBinding*): ContextExtension = - ContextExtension(values ++ bindings) -} - -object ContextExtension { - /** Immutable instance of empty ContextExtension, which can be shared to avoid - * allocations. */ - val empty = ContextExtension(Map()) - - /** Type of context variable binding. */ - type VarBinding = (Byte, EvaluatedValue[_ <: SType]) - - object serializer extends SigmaSerializer[ContextExtension, ContextExtension] { - override def serialize(obj: ContextExtension, w: SigmaByteWriter): Unit = { - val size = obj.values.size - if (size > Byte.MaxValue) - error(s"Number of ContextExtension values $size exceeds ${Byte.MaxValue}.") - w.putUByte(size) - obj.values.foreach { case (id, v) => w.put(id).putValue(v) } - } - - override def parse(r: SigmaByteReader): ContextExtension = { - val extSize = r.getByte() - if (extSize < 0) - error(s"Negative amount of context extension values: $extSize") - - val values = (0 until extSize) - .map(_ => (r.getByte(), r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]])) - - ContextExtension(values.toMap) - } - } -} - +import sigma.interpreter.ContextExtension +import sigma.interpreter.ContextExtension.VarBinding +import sigma.validation.SigmaValidationSettings /** Base class of the context passed to verifier and prover. * @see [[sigmastate.interpreter.Interpreter]] @@ -111,12 +60,8 @@ trait InterpreterContext { * Thus, this method performs transformation from Ergo to internal Sigma representation * of all context data. * - * @param extensions additional context variables which will be merged with those in the - * `extension` of this instance, overriding existing bindings in case - * variable ids overlap. - * * @see sigmastate.eval.Evaluation */ - def toSigmaContext(extensions: Map[Byte, AnyValue] = Map()): sigma.Context + def toSigmaContext(): sigma.Context } diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala index 2953e8ed0c..ed2349eb0f 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala @@ -4,18 +4,23 @@ import sigma.kiama.rewriting.Rewriter.{everywherebu, everywheretd, rule} import sigma.util.CollectionUtil._ import sigma.VersionContext import sigma.kiama.rewriting.Strategy -import sigmastate.TrivialProp.{FalseProp, TrueProp} -import sigmastate.Values._ +import sigma.data.TrivialProp.{FalseProp, TrueProp} +import sigma.ast._ import sigma.VersionContext.MaxSupportedScriptVersion import sigmastate._ import sigmastate.crypto.DLogProtocol._ import sigmastate.crypto.VerifierMessage.Challenge import sigmastate.crypto._ import sigmastate.crypto.{GF2_192, GF2_192_Poly} -import sigmastate.eval.Extensions.ArrayOps -import sigmastate.exceptions.InterpreterException import sigmastate.utils.Helpers import sigma.Coll +import sigma.Extensions.ArrayOps +import sigma.crypto.CryptoConstants +import sigma.data.{CAND, COR, CTHRESHOLD, ProveDHTuple, ProveDlog, SigmaBoolean} +import sigma.exceptions.InterpreterException +import sigma.interpreter.CostedProverResult +import sigma.serialization.SigSerializer +import sigma.util.CollectionUtil import java.math.BigInteger import scala.util.Try @@ -26,7 +31,7 @@ import scala.util.Try */ trait ProverInterpreter extends Interpreter with ProverUtils { - import CryptoConstants.secureRandomBytes + import sigma.crypto.CryptoConstants.secureRandomBytes import Interpreter._ override type ProofT = UncheckedTree @@ -94,7 +99,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils { // Prover Step 8: compute the challenge for the root of the tree as the Fiat-Shamir hash of propBytes // and the message being signed. - val rootChallenge = Challenge @@ CryptoFunctions.hashFn(Helpers.concatArrays(propBytes, message)).toColl + val rootChallenge = Challenge @@ CryptoFunctions.hashFn(CollectionUtil.concatArrays(propBytes, message)).toColl val step8 = step6.withChallenge(rootChallenge) // Prover Step 9: complete the proof by computing challenges at real nodes and additionally responses at real leaves @@ -146,7 +151,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils { hintsBag: HintsBag): Array[Byte] = { val proofTree = sb match { case TrueProp => NoProof - case FalseProp => error("Script reduced to false") + case FalseProp => syntax.error("Script reduced to false") case sigmaTree => val unprovenTree = convertToUnproven(sigmaTree) prove(unprovenTree, message, hintsBag) @@ -188,7 +193,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils { } ul.withSimulated(!isReal) case t: UnprovenTree => - error(s"Don't know how to markReal($t)") + syntax.error(s"Don't know how to markReal($t)") }) /** @@ -385,11 +390,11 @@ trait ProverInterpreter extends Interpreter with ProverUtils { if (su.simulated) { // Step 5 (simulated leaf -- complete the simulation) assert(su.challengeOpt.isDefined) - val (fm, sm) = DLogInteractiveProver.simulate(su.proposition, su.challengeOpt.get) + val (fm, sm) = DLogProver.simulate(su.proposition, su.challengeOpt.get) UncheckedSchnorr(su.proposition, Some(fm), su.challengeOpt.get, sm) } else { // Step 6 -- compute the commitment - val (r, commitment) = DLogInteractiveProver.firstMessage() + val (r, commitment) = DLogProver.firstMessage() su.copy(commitmentOpt = Some(commitment), randomnessOpt = Some(r)) } } @@ -405,23 +410,23 @@ trait ProverInterpreter extends Interpreter with ProverUtils { if (dhu.simulated) { // Step 5 (simulated leaf -- complete the simulation) assert(dhu.challengeOpt.isDefined) - val (fm, sm) = DiffieHellmanTupleInteractiveProver.simulate(dhu.proposition, dhu.challengeOpt.get) + val (fm, sm) = DiffieHellmanTupleProver.simulate(dhu.proposition, dhu.challengeOpt.get) UncheckedDiffieHellmanTuple(dhu.proposition, Some(fm), dhu.challengeOpt.get, sm) } else { // Step 6 -- compute the commitment - val (r, fm) = DiffieHellmanTupleInteractiveProver.firstMessage(dhu.proposition) + val (r, fm) = DiffieHellmanTupleProver.firstMessage(dhu.proposition) dhu.copy(commitmentOpt = Some(fm), randomnessOpt = Some(r)) } } - case t: ProofTree => error(s"Don't know how to challengeSimulated($t)") + case t: ProofTree => syntax.error(s"Don't know how to challengeSimulated($t)") }) private def extractChallenge(pt: ProofTree): Option[Challenge] = pt match { case upt: UnprovenTree => upt.challengeOpt case sn: UncheckedSchnorr => Some(sn.challenge) case dh: UncheckedDiffieHellmanTuple => Some(dh.challenge) - case _ => error(s"Cannot extractChallenge($pt)") + case _ => syntax.error(s"Cannot extractChallenge($pt)") } /** @@ -493,12 +498,12 @@ trait ProverInterpreter extends Interpreter with ProverUtils { val z = privKeyOpt match { case Some(privKey: DLogProverInput) => hintsBag.ownCommitments.find(_.position == su.position).map { oc => - DLogInteractiveProver.secondMessage( + DLogProver.secondMessage( privKey, oc.secretRandomness, su.challengeOpt.get) }.getOrElse { - DLogInteractiveProver.secondMessage( + DLogProver.secondMessage( privKey, su.randomnessOpt.get, su.challengeOpt.get) @@ -526,12 +531,12 @@ trait ProverInterpreter extends Interpreter with ProverUtils { val z = privKeyOpt match { case Some(privKey) => hintsBag.ownCommitments.find(_.position == dhu.position).map { oc => - DiffieHellmanTupleInteractiveProver.secondMessage( + DiffieHellmanTupleProver.secondMessage( privKey.asInstanceOf[DiffieHellmanTupleProverInput], oc.secretRandomness, dhu.challengeOpt.get) }.getOrElse { - DiffieHellmanTupleInteractiveProver.secondMessage( + DiffieHellmanTupleProver.secondMessage( privKey.asInstanceOf[DiffieHellmanTupleProverInput], dhu.randomnessOpt.get, dhu.challengeOpt.get) @@ -579,7 +584,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils { case dh: ProveDHTuple => UnprovenDiffieHellmanTuple(dh, None, None, None, simulated = false) case _ => - error(s"Cannot convertToUnproven($sigmaTree)") + syntax.error(s"Cannot convertToUnproven($sigmaTree)") } //converts ProofTree => UncheckedSigmaTree @@ -593,7 +598,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils { case s: UncheckedSchnorr => s case d: UncheckedDiffieHellmanTuple => d case a: Any => - error(s"Cannot convertToUnproven($a)") + syntax.error(s"Cannot convertToUnproven($a)") } /** diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala index 0e66cb55c5..15daaec382 100644 --- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala +++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala @@ -1,9 +1,11 @@ package sigmastate.interpreter +import sigma.ast.ErgoTree +import sigma.data.{ProveDHTuple, ProveDlog, SigmaBoolean, SigmaConjecture, SigmaLeaf} +import sigma.serialization.SigSerializer import sigmastate._ -import sigmastate.Values.{ErgoTree, SigmaBoolean} -import sigmastate.crypto.DLogProtocol.{DLogInteractiveProver, ProveDlog} -import sigmastate.crypto.{DiffieHellmanTupleInteractiveProver, ProveDHTuple} +import sigmastate.crypto.DLogProtocol.DLogProver +import sigmastate.crypto.DiffieHellmanTupleProver trait ProverUtils extends Interpreter { @@ -44,9 +46,9 @@ trait ProverUtils extends Interpreter { if (generateFor.contains(leaf)) { val (r, a) = leaf match { case _: ProveDlog => - DLogInteractiveProver.firstMessage() + DLogProver.firstMessage() case pdh: ProveDHTuple => - DiffieHellmanTupleInteractiveProver.firstMessage(pdh) + DiffieHellmanTupleProver.firstMessage(pdh) case _ => ??? } val hints = Seq(OwnCommitment(leaf, r, a, position), RealCommitment(leaf, a, position)) diff --git a/interpreter/shared/src/main/scala/sigmastate/lang/Terms.scala b/interpreter/shared/src/main/scala/sigmastate/lang/Terms.scala deleted file mode 100644 index bbb19434bf..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/lang/Terms.scala +++ /dev/null @@ -1,466 +0,0 @@ -package sigmastate.lang - -import sigma.kiama.rewriting.Rewriter._ -import sigma.data.Nullable -import sigmastate.SCollection.{SIntArray, SByteArray} -import sigmastate.Values._ -import sigmastate.utils.Overloading.Overload1 -import sigmastate._ -import sigmastate.interpreter.{Interpreter, ErgoTreeEvaluator} -import sigmastate.interpreter.ErgoTreeEvaluator.DataEnv -import sigmastate.serialization.OpCodes -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.lang.TransformingSigmaBuilder._ - -import scala.language.implicitConversions -import scala.collection.compat.immutable.ArraySeq -import debox.cfor - -object Terms { - - /** Frontend representation of a block of Val definitions. - * { val x = ...; val y = ... } - * This node is not part of ErgoTree and hence have Undefined opCode. */ - case class Block(bindings: Seq[Val], result: SValue) extends Value[SType] { - override def companion = Block - override def tpe: SType = result.tpe - - /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = Value.notSupportedError(this, "opType") - } - object Block extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - def apply(let: Val, result: SValue)(implicit o1: Overload1): Block = - Block(Seq(let), result) - } - - /** IR node to represent explicit Zero Knowledge scope in ErgoTree. - * Compiler checks Zero Knowledge properties and issue error message is case of violations. - * ZK-scoping is optional, it can be used when the user want to ensure Zero Knowledge of - * specific set of operations. - * Usually it will require simple restructuring of the code to make the scope body explicit. - * Invariants checked by the compiler: - * - single ZKProof in ErgoTree in a root position - * - no boolean operations in the body, because otherwise the result may be disclosed - * - all the operations are over SigmaProp values - * - * For motivation and details see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/236 - * */ - case class ZKProofBlock(body: SigmaPropValue) extends BoolValue { - override def companion = ZKProofBlock - override def tpe = SBoolean - override def opType: SFunc = ZKProofBlock.OpType - } - object ZKProofBlock extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - val OpType = SFunc(SSigmaProp, SBoolean) - } - - trait Val extends Value[SType] { - val name: String - val givenType: SType - val body: SValue - } - object Val { - def apply(name: String, body: SValue): Val = ValNode(name, NoType, body) - def apply(name: String, givenType: SType, body: SValue): Val = ValNode(name, givenType, body) - def unapply(v: SValue): Option[(String, SType, SValue)] = v match { - case ValNode(name, givenType, body) => Some((name, givenType, body)) - case _ => None - } - } - - case class ValNode(name: String, - givenType: SType, - body: SValue) extends Val { - override def companion = ValNode - override def tpe: SType = givenType ?: body.tpe - /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = Value.notSupportedError(this, "opType") - } - object ValNode extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - } - - /** Frontend node to select a field from an object. Should be transformed to SelectField*/ - case class Select(obj: Value[SType], field: String, resType: Option[SType] = None) extends Value[SType] { - override def companion = Select - override val tpe: SType = resType.getOrElse(obj.tpe match { - case p: SProduct => - val i = p.methodIndex(field) - if (i == -1) NoType - else p.methods(i).stype - case _ => NoType - }) - override def opType: SFunc = SFunc(obj.tpe, tpe) - } - object Select extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - } - - /** Frontend node to represent variable names parsed in a source code. - * Should be resolved during compilation to lambda argument, Val definition or - * compilation environment value. */ - case class Ident(name: String, tpe: SType = NoType) extends Value[SType] { - override def companion = Ident - override def opType: SFunc = SFunc(ArraySeq.empty, tpe) - } - object Ident extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - def apply(name: String): Ident = Ident(name, NoType) - } - - // TODO refactor: move to sigmastate.Values - /** ErgoTree node which represents application of function `func` to the given arguments. - * @param func expression which evaluates to a function - * @param args arguments of the function application - */ - case class Apply(func: Value[SType], args: IndexedSeq[Value[SType]]) extends Value[SType] { - override def companion = Apply - override lazy val tpe: SType = func.tpe match { - case SFunc(_, r, _) => r - case tColl: SCollectionType[_] => tColl.elemType - case _ => NoType - } - override lazy val opType: SFunc = { - val nArgs = args.length - val argTypes = new Array[SType](nArgs + 1) - argTypes(0) = func.tpe - cfor(0)(_ < nArgs, _ + 1) { i => - argTypes(i + 1) = args(i).tpe - } - SFunc(argTypes, tpe) - } - - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - addCost(Apply.costKind) - if (args.length == 1) { - val fV = func.evalTo[Any => Any](env) - val argV = args(0).evalTo[Any](env) - fV(argV) - } else { - // zero or more than 1 argument functions are not supported in v4.x, v5.0 - // see `case Terms.Apply(f, Seq(x))` in RuntimeCosting which means other cases are not supported. - Interpreter.error(s"Function application must have 1 argument, but was: $this") - } - } - } - object Apply extends FixedCostValueCompanion { - override def opCode: OpCode = OpCodes.FuncApplyCode - /** Cost of: 1) switch on the number of args 2) Scala method call 3) add args to env - * Old cost: lambdaInvoke == 30 */ - override val costKind = FixedCost(JitCost(30)) - } - - /** Apply types for type parameters of input value. */ - case class ApplyTypes(input: Value[SType], tpeArgs: Seq[SType]) extends Value[SType] { node => - override def companion = ApplyTypes - override lazy val tpe: SType = input.tpe match { - case funcType: SFunc => - val subst = funcType.tpeParams.map(_.ident).zip(tpeArgs).toMap - Terms.applySubst(input.tpe, subst) - case _ => input.tpe - } - /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = Value.notSupportedError(this, "opType") - } - object ApplyTypes extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - } - - /** Frontend node to represent potential method call in a source code. - * Should be resolved during compilation to MethodCall. - * Cannot be serialized to ErgoTree. */ - case class MethodCallLike(obj: Value[SType], name: String, args: IndexedSeq[Value[SType]], tpe: SType = NoType) extends Value[SType] { - override def companion = MethodCallLike - override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) - } - object MethodCallLike extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - } - - /** Represents in ErgoTree an invocation of method of the object `obj` with arguments `args`. - * The SMethod instances in STypeCompanions may have type STypeIdent in methods types, - * but valid ErgoTree should have SMethod instances specialized for specific types of - * obj and args using `specializeFor`. - * This means, if we save typeId, methodId, and we save all the arguments, - * we can restore the specialized SMethod instance. - * This work by induction, if we assume all arguments are monomorphic, - * then we can make MethodCall monomorphic. - * Thus, all ErgoTree instances are monomorphic by construction. - * - * @param obj object on which method will be invoked - * @param method method to be invoked - * @param args arguments passed to the method on invocation - * @param typeSubst a map of concrete type for each generic type parameter - */ - case class MethodCall(obj: Value[SType], - method: SMethod, - args: IndexedSeq[Value[SType]], - typeSubst: Map[STypeVar, SType]) extends Value[SType] { - override def companion = if (args.isEmpty) PropertyCall else MethodCall - override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) - override val tpe: SType = method.stype match { - case f: SFunc => f.tRange.withSubstTypes(typeSubst) - case t => t.withSubstTypes(typeSubst) - } - - /** @hotspot don't beautify this code */ - protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { - val objV = obj.evalTo[Any](env) - addCost(MethodCall.costKind) // MethodCall overhead - method.costKind match { - case fixed: FixedCost => - val extra = method.extraDescriptors - val extraLen = extra.length - val len = args.length - val argsBuf = new Array[Any](len + extraLen) - cfor(0)(_ < len, _ + 1) { i => - argsBuf(i) = args(i).evalTo[Any](env) - } - cfor(0)(_ < extraLen, _ + 1) { i => - argsBuf(len + i) = extra(i) - } - var res: Any = null - E.addFixedCost(fixed, method.opDesc) { - res = method.invokeFixed(objV, argsBuf) - } - res - case _ => - val len = args.length - val argsBuf = new Array[Any](len + 3) - argsBuf(0) = this - argsBuf(1) = objV - cfor(0)(_ < len, _ + 1) { i => - argsBuf(i + 2) = args(i).evalTo[Any](env) - } - argsBuf(argsBuf.length - 1) = E - - val evalMethod = method.genericMethod.evalMethod - evalMethod.invoke(method.objType, argsBuf.asInstanceOf[Array[AnyRef]]:_*) - } - } - } - - object MethodCall extends FixedCostValueCompanion { - override def opCode: OpCode = OpCodes.MethodCallCode - /** Cost of: 1) packing args into Array 2) RMethod.invoke */ - override val costKind = FixedCost(JitCost(4)) - - /** Helper constructor which allows to cast the resulting node to the specified - * [[sigmastate.Values.Value]] type `T`. - * @see [[sigmastate.lang.Terms.MethodCall]] - */ - def typed[T <: SValue](obj: Value[SType], - method: SMethod, - args: IndexedSeq[Value[SType]], - typeSubst: Map[STypeVar, SType]): T = { - MethodCall(obj, method, args, typeSubst).asInstanceOf[T] - } - } - object PropertyCall extends FixedCostValueCompanion { - override def opCode: OpCode = OpCodes.PropertyCallCode - /** Cost of: 1) packing args into Array 2) RMethod.invoke */ - override val costKind = FixedCost(JitCost(4)) - } - - case class STypeParam(ident: STypeVar, upperBound: Option[SType] = None, lowerBound: Option[SType] = None) { - assert(upperBound.isEmpty && lowerBound.isEmpty, s"Type parameters with bounds are not supported, but found $this") - override def toString = ident.toString + upperBound.fold("")(u => s" <: $u") + lowerBound.fold("")(l => s" >: $l") - } - object STypeParam { - implicit def typeIdentToTypeParam(id: STypeVar): STypeParam = STypeParam(id) - } - - /** Frontend implementation of lambdas. Should be transformed to FuncValue. */ - case class Lambda( - tpeParams: Seq[STypeParam], - args: IndexedSeq[(String,SType)], - givenResType: SType, - body: Option[Value[SType]]) extends Value[SFunc] - { - require(!(tpeParams.nonEmpty && body.nonEmpty), s"Generic function definitions are not supported, but found $this") - override def companion = Lambda - override lazy val tpe: SFunc = { - val sRange = givenResType ?: body.fold(NoType: SType)(_.tpe) - SFunc(args.map(_._2), sRange, tpeParams) - } - /** This is not used as operation, but rather to form a program structure */ - override def opType: SFunc = SFunc(Vector(), tpe) - } - object Lambda extends ValueCompanion { - override def opCode: OpCode = OpCodes.Undefined - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - def apply(args: IndexedSeq[(String,SType)], resTpe: SType, body: Value[SType]): Lambda = - Lambda(Nil, args, resTpe, Some(body)) - def apply(args: IndexedSeq[(String,SType)], resTpe: SType, body: Option[Value[SType]]): Lambda = - Lambda(Nil, args, resTpe, body) - def apply(args: IndexedSeq[(String,SType)], body: Value[SType]): Lambda = Lambda(Nil, args, NoType, Some(body)) - } - - /** Operation identity descriptor used in AOT costing (see usages in RuntimeCosting and - * CostTable) */ - case class OperationId(name: String, opType: SFunc) - - implicit class ValueOps(val v: Value[SType]) extends AnyVal { - def asValue[T <: SType]: Value[T] = v.asInstanceOf[Value[T]] - def asNumValue: Value[SNumericType] = v.asInstanceOf[Value[SNumericType]] - def asStringValue: Value[SString.type] = v.asInstanceOf[Value[SString.type]] - def asBoolValue: Value[SBoolean.type] = v.asInstanceOf[Value[SBoolean.type]] - def asByteValue: Value[SByte.type] = v.asInstanceOf[Value[SByte.type]] - def asShortValue: Value[SShort.type] = v.asInstanceOf[Value[SShort.type]] - def asIntValue: Value[SInt.type] = v.asInstanceOf[Value[SInt.type]] - def asLongValue: Value[SLong.type] = v.asInstanceOf[Value[SLong.type]] - def asBigInt: Value[SBigInt.type] = v.asInstanceOf[Value[SBigInt.type]] - def asBox: Value[SBox.type] = v.asInstanceOf[Value[SBox.type]] - def asGroupElement: Value[SGroupElement.type] = v.asInstanceOf[Value[SGroupElement.type]] - def asSigmaProp: Value[SSigmaProp.type] = v.asInstanceOf[Value[SSigmaProp.type]] - def asByteArray: Value[SByteArray] = v.asInstanceOf[Value[SByteArray]] - def asIntArray: Value[SIntArray] = v.asInstanceOf[Value[SIntArray]] - def asCollection[T <: SType]: Value[SCollection[T]] = v.asInstanceOf[Value[SCollection[T]]] - def asOption[T <: SType]: Value[SOption[T]] = v.asInstanceOf[Value[SOption[T]]] - def asTuple: Value[STuple] = v.asInstanceOf[Value[STuple]] - def asFunc: Value[SFunc] = v.asInstanceOf[Value[SFunc]] - def asConcreteCollection[T <: SType]: ConcreteCollection[T] = v.asInstanceOf[ConcreteCollection[T]] - def upcastTo[T <: SNumericType](targetType: T): Value[T] = { - assert(v.tpe.isInstanceOf[SNumericType], - s"Cannot upcast value of type ${v.tpe} to $targetType: only numeric types can be upcasted.") - val tV = v.asValue[SNumericType] - assert(targetType.max(tV.tpe) == targetType, - s"Invalid upcast from $tV to $targetType: target type should be larger than source type.") - if (targetType == tV.tpe) v.asValue[T] - else - mkUpcast(tV, targetType).withSrcCtx(v.sourceContext) - } - def withSrcCtx[T <: SType](sourceContext: Nullable[SourceContext]): Value[T] = { - v.sourceContext = sourceContext - v.asValue[T] - } - /** - * Set source context only if it's empty - */ - def withEnsuredSrcCtx[T <: SType](sourceContext: Nullable[SourceContext]): Value[T] = { - if (v.sourceContext.isEmpty) v.sourceContext = sourceContext - v.asValue[T] - } - /** - * Set source context to all nodes missing source context in the given tree. - * @param srcCtx source context to set - * @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[Any] { - case node: SValue if node != null && node.sourceContext.isEmpty => - node.withSrcCtx(srcCtx) - }))(v).asValue[T] - } - - } - - /** Type alias for a substitution of type variables with their corresponding types. */ - type STypeSubst = Map[STypeVar, SType] - - /** Immutable and sharable empty substitution. */ - val EmptySubst = Map.empty[STypeVar, SType] - - /** Performs pairwise type unification making sure each type variable is equally - * substituted in all items. */ - def unifyTypeLists(items1: Seq[SType], items2: Seq[SType]): Option[Terms.STypeSubst] = { - // unify items pairwise independently - val itemsUni = (items1, items2).zipped.map((t1, t2) => unifyTypes(t1, t2)) - if (itemsUni.forall(_.isDefined)) { - // merge substitutions making sure the same id is equally substituted in all items - val merged = itemsUni.foldLeft(EmptySubst)((acc, subst) => { - var res = acc - for ( (id, t) <- subst.get ) { - if (res.contains(id) && res(id) != t) return None - res = res + (id -> t) - } - res - }) - Some(merged) - } else - None - } - - private val unifiedWithoutSubst = Some(EmptySubst) - - /** Finds a substitution `subst` of type variables such that unifyTypes(applySubst(t1, subst), t2) shouldBe Some(emptySubst) */ - def unifyTypes(t1: SType, t2: SType): Option[Terms.STypeSubst] = (t1, t2) match { - case (_ @ STypeVar(n1), _ @ STypeVar(n2)) => - if (n1 == n2) unifiedWithoutSubst else None - case (id1 @ STypeVar(_), _) => - Some(Map(id1 -> t2)) - case (e1: SCollectionType[_], e2: SCollectionType[_]) => - unifyTypes(e1.elemType, e2.elemType) - case (e1: SCollectionType[_], _: STuple) => - unifyTypes(e1.elemType, SAny) - case (e1: SOption[_], e2: SOption[_]) => - unifyTypes(e1.elemType, e2.elemType) - case (e1: STuple, e2: STuple) if e1.items.length == e2.items.length => - unifyTypeLists(e1.items, e2.items) - case (e1: SFunc, e2: SFunc) if e1.tDom.length == e2.tDom.length => - unifyTypeLists(e1.tDom :+ e1.tRange, e2.tDom :+ e2.tRange) - case (STypeApply(name1, args1), STypeApply(name2, args2)) - if name1 == name2 && args1.length == args2.length => - unifyTypeLists(args1, args2) - case (SBoolean, SSigmaProp) => // it is necessary for implicit conversion in Coll(bool, prop, bool) - unifiedWithoutSubst - case (SPrimType(e1), SPrimType(e2)) if e1 == e2 => - unifiedWithoutSubst - case (SAny, _) => - unifiedWithoutSubst - case _ => None - } - - /** Applies a type substitution to a given type. - * - * @param tpe the type to apply the substitution to - * @param subst the type substitution to apply - * @return the type after applying the substitution - */ - def applySubst(tpe: SType, subst: Terms.STypeSubst): SType = tpe match { - case SFunc(args, res, tparams) => - val remainingVars = tparams.filterNot { p => subst.contains(p.ident) } - SFunc(args.map(applySubst(_, subst)), applySubst(res, subst), remainingVars) - case _ => - val substRule = rule[Any] { - case id: STypeVar if subst.contains(id) => subst(id) - } - rewrite(everywherebu(substRule))(tpe) - } - - /** Computes the most general type given two types. - * - * @param t1 the first type - * @param t2 the second type - * @return the most general type if it exists, otherwise None - */ - def msgType(t1: SType, t2: SType): Option[SType] = unifyTypes(t1, t2) match { - case Some(_) => Some(t1) - case None => unifyTypes(t2, t1).map(_ => t2) - } - - /** Most Specific Generalized (MSG) type of ts. - * Currently just the type of the first element as long as all the elements have the same type. */ - def msgTypeOf(ts: Seq[SType]): Option[SType] = { - if (ts.isEmpty) None - else { - var res: SType = ts.head - for ( t <- ts.iterator.drop(1) ) { - msgType(t, res) match { - case Some(msg) => res = msg //assign new - case None => return None - } - } - Some(res) - } - } -} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/BoolToSigmaPropSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/BoolToSigmaPropSerializer.scala deleted file mode 100644 index bb3a282935..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/BoolToSigmaPropSerializer.scala +++ /dev/null @@ -1,22 +0,0 @@ -package sigmastate.serialization - -import sigmastate.Values.{BoolValue, SigmaPropValue, SValue} -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.{BoolToSigmaProp, SType, Values} - -case class BoolToSigmaPropSerializer(cons: BoolValue => SigmaPropValue) extends ValueSerializer[BoolToSigmaProp] { - import sigmastate.Operations.BoolToSigmaPropInfo._ - override def opDesc = BoolToSigmaProp - val conditionInfo: DataInfo[SValue] = conditionArg - - def serialize(obj: BoolToSigmaProp, w: SigmaByteWriter): Unit = { - w.putValue(obj.value, conditionInfo) - } - - def parse(r: SigmaByteReader): Values.Value[SType] = { - val p = r.getValue().asBoolValue - cons(p) - } -} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/CaseObjectSerialization.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/CaseObjectSerialization.scala deleted file mode 100644 index a1ece38023..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/CaseObjectSerialization.scala +++ /dev/null @@ -1,13 +0,0 @@ -package sigmastate.serialization - -import sigmastate.SType -import sigmastate.Values.{Value, ValueCompanion} -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} - -case class CaseObjectSerialization[V <: Value[SType]](override val opDesc: ValueCompanion, obj: V) - extends ValueSerializer[V] { - - override def serialize(obj: V, w: SigmaByteWriter): Unit = () - - override def parse(r: SigmaByteReader): V = obj -} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ProveDlogSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/ProveDlogSerializer.scala deleted file mode 100644 index 89400938fe..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/ProveDlogSerializer.scala +++ /dev/null @@ -1,39 +0,0 @@ -package sigmastate.serialization - -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.{SGroupElement, CreateProveDlog} -import sigmastate.Values.{Value, SValue, SigmaPropValue} -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} - -case class ProveDlogSerializer(cons: EcPointType => ProveDlog) - extends SigmaSerializer[ProveDlog, ProveDlog] { - - override def serialize(obj: ProveDlog, w: SigmaByteWriter): Unit = - GroupElementSerializer.serialize(obj.value, w) - - override def parse(r: SigmaByteReader) = { - val res = GroupElementSerializer.parse(r) - cons(res) - } -} - -case class CreateProveDlogSerializer(cons: Value[SGroupElement.type] => SigmaPropValue) - extends ValueSerializer[CreateProveDlog] { - import sigmastate.Operations.CreateProveDlogInfo._ - override def opDesc = CreateProveDlog - val valueInfo: DataInfo[SValue] = valueArg - - override def serialize(obj: CreateProveDlog, w: SigmaByteWriter): Unit = { - w.putValue(obj.value, valueInfo) - } - - override def parse(r: SigmaByteReader) = { - val v = r.getValue().asValue[SGroupElement.type] - cons(v) - } -} - - diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/SigmaPropBytesSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/SigmaPropBytesSerializer.scala deleted file mode 100644 index 754880cd16..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/SigmaPropBytesSerializer.scala +++ /dev/null @@ -1,23 +0,0 @@ -package sigmastate.serialization - -import sigmastate.Values.SValue -import sigmastate.{Values, SType} -import sigmastate.lang.Terms._ -import sigmastate.utils.SigmaByteWriter.DataInfo -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.utxo.SigmaPropBytes - -object SigmaPropBytesSerializer extends ValueSerializer[SigmaPropBytes] { - import sigmastate.Operations.SigmaPropBytesInfo._ - override def opDesc = SigmaPropBytes - val thisInfo: DataInfo[SValue] = thisArg - - def serialize(obj: SigmaPropBytes, w: SigmaByteWriter): Unit = { - w.putValue(obj.input, thisInfo) - } - - def parse(r: SigmaByteReader): Values.Value[SType] = { - val p = r.getValue().asSigmaProp - SigmaPropBytes(p) - } -} diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ProveDHTupleSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ProveDHTupleSerializer.scala deleted file mode 100644 index 39406a35c0..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/serialization/transformers/ProveDHTupleSerializer.scala +++ /dev/null @@ -1,53 +0,0 @@ -package sigmastate.serialization.transformers - -import sigmastate.{SGroupElement, CreateProveDHTuple} -import sigmastate.Values.{Value, SigmaPropValue} -import sigmastate.crypto.ProveDHTuple -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.lang.Terms._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.serialization._ - -case class ProveDHTupleSerializer( - cons: (EcPointType, EcPointType, EcPointType, EcPointType) => ProveDHTuple - ) extends SigmaSerializer[ProveDHTuple, ProveDHTuple] { - - override def serialize(obj: ProveDHTuple, w: SigmaByteWriter): Unit = { - GroupElementSerializer.serialize(obj.gv, w) - GroupElementSerializer.serialize(obj.hv, w) - GroupElementSerializer.serialize(obj.uv, w) - GroupElementSerializer.serialize(obj.vv, w) - } - - override def parse(r: SigmaByteReader) = { - val gv = GroupElementSerializer.parse(r) - val hv = GroupElementSerializer.parse(r) - val uv = GroupElementSerializer.parse(r) - val vv = GroupElementSerializer.parse(r) - cons(gv, hv, uv, vv) - } -} - -case class CreateProveDHTupleSerializer(cons: (Value[SGroupElement.type], - Value[SGroupElement.type], - Value[SGroupElement.type], - Value[SGroupElement.type]) => SigmaPropValue) - extends ValueSerializer[CreateProveDHTuple] { - import sigmastate.Operations.CreateProveDHTupleInfo._ - override def opDesc = CreateProveDHTuple - - override def serialize(obj: CreateProveDHTuple, w: SigmaByteWriter): Unit = { - w.putValue(obj.gv, gArg) - w.putValue(obj.hv, hArg) - w.putValue(obj.uv, uArg) - w.putValue(obj.vv, vArg) - } - - override def parse(r: SigmaByteReader) = { - val gv = r.getValue().asValue[SGroupElement.type] - val hv = r.getValue().asValue[SGroupElement.type] - val uv = r.getValue().asValue[SGroupElement.type] - val vv = r.getValue().asValue[SGroupElement.type] - cons(gv, hv, uv, vv) - } -} diff --git a/interpreter/shared/src/main/scala/sigmastate/sigmastate.scala b/interpreter/shared/src/main/scala/sigmastate/sigmastate.scala index 185fbebb9d..1c7b8d5ab6 100644 --- a/interpreter/shared/src/main/scala/sigmastate/sigmastate.scala +++ b/interpreter/shared/src/main/scala/sigmastate/sigmastate.scala @@ -1,30 +1,13 @@ -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoLikeContext} -import sigma.data.{RType, GeneralType} -import sigmastate.Values._ -import sigmastate.lang.CheckingSigmaBuilder +import org.ergoplatform.ErgoLikeContext +import sigma.ast._ +import sigma.data.RType -import scala.annotation.nowarn import scala.reflect.classTag package object sigmastate { import CheckingSigmaBuilder._ - /** Shadow the implicit from sigma package so it doesn't interfere with the resolution - * of ClassTags below. - */ - @nowarn private def rtypeToClassTag = ??? - - /** RType descriptors for predefined types used in AOTC-based interpreter. */ - - implicit val SigmaBooleanRType : RType[SigmaBoolean] = RType.fromClassTag(classTag[SigmaBoolean]) - - implicit val ErgoBoxRType : RType[ErgoBox] = RType.fromClassTag(classTag[ErgoBox]) - - implicit val ErgoBoxCandidateRType: RType[ErgoBoxCandidate] = RType.fromClassTag(classTag[ErgoBoxCandidate]) - - implicit val AvlTreeDataRType : RType[AvlTreeData] = GeneralType(classTag[AvlTreeData]) - - implicit val ErgoLikeContextRType : RType[ErgoLikeContext] = RType.fromClassTag(classTag[ErgoLikeContext]) + implicit val ErgoLikeContextRType: RType[ErgoLikeContext] = RType.fromClassTag(classTag[ErgoLikeContext]) /** Helper method to create "+" operation node. */ def Plus[T <: SNumericType](left: Value[T], right: Value[T]): Value[T] = @@ -53,5 +36,4 @@ package object sigmastate { /** Helper method to create "max" operation node. */ def Max[T <: SNumericType](left: Value[T], right: Value[T]): Value[T] = mkMax(left, right) - } diff --git a/interpreter/shared/src/main/scala/sigmastate/types.scala b/interpreter/shared/src/main/scala/sigmastate/types.scala deleted file mode 100644 index 586a06432e..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/types.scala +++ /dev/null @@ -1,2601 +0,0 @@ -package sigmastate - -import java.math.BigInteger -import org.ergoplatform._ -import org.ergoplatform.validation._ -import sigma.data.{Nullable, RType} -import sigma.data.GeneralType -import sigmastate.SType.TypeCode -import sigmastate.interpreter._ -import sigmastate.utils.Overloading.Overload1 -import scorex.crypto.authds.{ADKey, ADValue} -import scorex.crypto.authds.avltree.batch.{Insert, Lookup, Remove, Update} -import sigmastate.Values._ -import sigmastate.lang.Terms._ -import sigmastate.lang.{SigmaBuilder, Terms} -import sigmastate.SCollection._ -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.serialization.OpCodes -import sigma.Coll -import sigma._ - -import scala.language.implicitConversions -import scala.reflect.{ClassTag, classTag} -import scala.collection.compat.immutable.ArraySeq -import sigmastate.SMethod.{InvokeDescBuilder, MethodCallIrBuilder, MethodCostFunc, javaMethodOf} -import sigmastate.utxo._ -import sigmastate.lang.Terms.STypeSubst -import sigmastate.eval.Evaluation.stypeToRType -import sigmastate.eval._ -import debox.cfor -import sigma.reflection.{RClass, RMethod} -import sigma.util.Extensions.{IntOps, LongOps, ShortOps} - -import scala.util.{Failure, Success} - -/** Base type for all AST nodes of sigma lang. */ -trait SigmaNode extends Product - -/** Base type for all companions of AST nodes of sigma lang. */ -trait SigmaNodeCompanion - -/** Every type descriptor is a tree represented by nodes in SType hierarchy. - * In order to extend type family: - * - Implement concrete class derived from SType - * - Implement serializer (see SCollectionSerializer) and register it in STypeSerializer.table - * Each SType is serialized to array of bytes by: - * - emitting typeCode of each node (see special case for collections below) - * - then recursively serializing subtrees from left to right on each level - * - for each collection of primitive type there is special type code to emit single byte instead of two bytes - * Types code intervals - * - (1 .. MaxPrimTypeCode) // primitive types - * - (CollectionTypeCode .. CollectionTypeCode + MaxPrimTypeCode) // collections of primitive types - * - (MaxCollectionTypeCode ..) // Other types - * Collection of non-primitive type is serialized as (CollectionTypeCode, serialize(elementType)) - * */ -sealed trait SType extends SigmaNode { - /** The underlying Scala type of data values described by this type descriptor. - * E.g. scala.Int for SInt descriptor. - */ - type WrappedType - - /** Type code used in serialization of SType values. - * @see TypeSerializer - */ - val typeCode: SType.TypeCode - - /** 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 - - /** Elvis operator for types. See https://en.wikipedia.org/wiki/Elvis_operator*/ - def ?:(whenNoType: => SType): SType = if (this == NoType) whenNoType else this - - /** Applies a type substitution to this type. - * - * @param subst the type substitution to apply - * @return the type after applying the substitution - */ - def withSubstTypes(subst: Map[STypeVar, SType]): SType = - if (subst.isEmpty) this - else - Terms.applySubst(this, subst) - - /** Returns parsable type term string of the type described by this type descriptor. - * For every type it should be inverse to SigmaTyper.parseType. - * This is default fallback implementation, should be overriden if it - * is not correct for a particular type. */ - def toTermString: String = { - val t = Evaluation.stypeToRType(this) - t.name - } -} - -object SType { - /** Representation of type codes used in serialization. */ - type TypeCode = Byte - - val DummyValue = 0.asWrappedType - - implicit val typeByte : SByte.type = SByte - implicit val typeShort : SShort.type = SShort - implicit val typeInt : SInt.type = SInt - implicit val typeLong : SLong.type = SLong - implicit val typeBigInt : SBigInt.type = SBigInt - implicit val typeBoolean : SBoolean.type = SBoolean - implicit val typeAvlTree : SAvlTree.type = SAvlTree - implicit val typeGroupElement: SGroupElement.type = SGroupElement - implicit val typeSigmaProp : SSigmaProp.type = SSigmaProp - implicit val typeBox : SBox.type = SBox - - /** Costructs a collection type with the given type of elements. */ - implicit def typeCollection[V <: SType](implicit tV: V): SCollection[V] = SCollection[V](tV) - - /** 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 paramIVSeq: Seq[STypeParam] = Array(paramIV) - - val IndexedSeqOfT1: IndexedSeq[SType] = Array(SType.tT) - val IndexedSeqOfT2: IndexedSeq[SType] = Array(SType.tT, SType.tT) - - /** Immutable empty array, can be used to avoid repeated allocations. */ - val EmptyArray = Array.empty[SType] - - /** Immutable empty IndexedSeq, can be used to avoid repeated allocations. */ - val EmptySeq: IndexedSeq[SType] = EmptyArray - - /** 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[SType] = Array(SBoolean, SByte, SShort, SInt, SLong, SBigInt, SContext, - SGlobal, SHeader, SPreHeader, SAvlTree, SGroupElement, SSigmaProp, SString, SBox, - SUnit, SAny) - - /** A mapping of object types supporting MethodCall operations. For each serialized - * typeId this map contains a companion object which can be used to access the list of - * corresponding methods. - * - * NOTE: in the current implementation only monomorphic methods are supported (without - * type parameters) - * - * NOTE2: in v3.x SNumericType.typeId is silently shadowed by SGlobal.typeId as part of - * `toMap` operation. As a result, the methods collected into SByte.methods cannot be - * resolved (using SMethod.fromIds()) for all numeric types (SByte, SShort, SInt, - * SLong, SBigInt). See the corresponding regression `property("MethodCall on numerics")`. - * However, this "shadowing" is not a problem since all casting methods are implemented - * via Downcast, Upcast opcodes and the remaining `toBytes`, `toBits` methods are not - * implemented at all. - * In order to allow MethodCalls on numeric types in future versions the SNumericType.typeId - * should be changed and SGlobal.typeId should be preserved. The regression tests in - * `property("MethodCall Codes")` should pass. - */ - // TODO v6.0: should contain all numeric types (including also SNumericType) - // to support method calls like 10.toByte which encoded as MethodCall with typeId = 4, methodId = 1 - // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 - lazy val types: Map[Byte, STypeCompanion] = Seq( - SBoolean, SNumericType, SString, STuple, SGroupElement, SSigmaProp, SContext, SGlobal, SHeader, SPreHeader, - SAvlTree, SBox, SOption, SCollection, SBigInt - ).map { t => (t.typeId, t) }.toMap - - /** Checks that the type of the value corresponds to the descriptor `tpe`. - * If the value has complex structure only root type constructor is checked. - * NOTE, this method is used in ErgoTree evaluation to systematically check that each - * tree node evaluates to a value of the expected type. - * Shallow runtime checks are enough if: - * 1) ErgoTree is well-typed, i.e. each sub-expression has correct types (agree with - * the argument type). - * 2) `isValueOfType == true` for each tree leaf - * 3) `isValueOfType == true` for each sub-expression - * - * @param value value to check type - * @param tpe type descriptor to check value against - * @return true if the given `value` is of type tpe` - */ - def isValueOfType[T <: SType](x: Any, tpe: T): Boolean = tpe match { - case SBoolean => x.isInstanceOf[Boolean] - case SByte => x.isInstanceOf[Byte] - case SShort => x.isInstanceOf[Short] - case SInt => x.isInstanceOf[Int] - case SLong => x.isInstanceOf[Long] - case SBigInt => x.isInstanceOf[BigInt] - case SGroupElement => x.isInstanceOf[GroupElement] - case SSigmaProp => x.isInstanceOf[SigmaProp] - case SBox => x.isInstanceOf[Box] - case _: SCollectionType[_] => x.isInstanceOf[Coll[_]] - case _: SOption[_] => x.isInstanceOf[Option[_]] - case t: STuple => - if (t.items.length == 2) x.isInstanceOf[Tuple2[_,_]] - else sys.error(s"Unsupported tuple type $t") - case tF: SFunc => - if (tF.tDom.length == 1) x.isInstanceOf[Function1[_,_]] - else sys.error(s"Unsupported function type $tF") - case SContext => x.isInstanceOf[Context] - case SAvlTree => x.isInstanceOf[AvlTree] - case SGlobal => x.isInstanceOf[SigmaDslBuilder] - case SHeader => x.isInstanceOf[Header] - case SPreHeader => x.isInstanceOf[PreHeader] - case SUnit => x.isInstanceOf[Unit] - case _ => sys.error(s"Unknown type $tpe") - } - - implicit class STypeOps(val tpe: SType) extends AnyVal { - def isCollectionLike: Boolean = tpe.isInstanceOf[SCollection[_]] - def isCollection: Boolean = tpe.isInstanceOf[SCollectionType[_]] - def isOption: Boolean = tpe.isInstanceOf[SOption[_]] - def isBox: Boolean = tpe.isInstanceOf[SBox.type] - def isGroupElement: Boolean = tpe.isInstanceOf[SGroupElement.type] - def isSigmaProp: Boolean = tpe.isInstanceOf[SSigmaProp.type] - def isAvlTree: Boolean = tpe.isInstanceOf[SAvlTree.type] - def isFunc : Boolean = tpe.isInstanceOf[SFunc] - def isTuple: Boolean = tpe.isInstanceOf[STuple] - - /** 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] - def asTuple: STuple = tpe.asInstanceOf[STuple] - def asOption[T <: SType]: SOption[T] = tpe.asInstanceOf[SOption[T]] - def whenFunc[T](action: SFunc => Unit) = if(tpe.isInstanceOf[SFunc]) action(tpe.asFunc) - def asCollection[T <: SType] = tpe.asInstanceOf[SCollection[T]] - - /** Returns the [[ClassTag]] for the given [[SType]]. */ - def classTag[T <: SType#WrappedType]: ClassTag[T] = (tpe match { - case SBoolean => reflect.classTag[Boolean] - case SByte => reflect.classTag[Byte] - case SShort => reflect.classTag[Short] - case SInt => reflect.classTag[Int] - case SLong => reflect.classTag[Long] - case SBigInt => reflect.classTag[BigInt] - case SAvlTree => reflect.classTag[AvlTree] - case SGroupElement => reflect.classTag[EcPointType] - case SSigmaProp => reflect.classTag[SigmaBoolean] - case SUnit => reflect.classTag[Unit] - case SBox => reflect.classTag[ErgoBox] - case SAny => reflect.classTag[Any] - case opt: SOption[a] => reflect.classTag[Option[a]] - case _: STuple => reflect.classTag[Array[Any]] - case tColl: SCollection[a] => - val elemType = tColl.elemType - implicit val ca = elemType.classTag[elemType.WrappedType] - reflect.classTag[Array[elemType.WrappedType]] - case _ => sys.error(s"Cannot get ClassTag for type $tpe") - }).asInstanceOf[ClassTag[T]] - } - - implicit class AnyOps(val x: Any) extends AnyVal { - /** Helper method to simplify type casts. */ - def asWrappedType: SType#WrappedType = x.asInstanceOf[SType#WrappedType] - } -} - -/** Basic interface for all type companions. - * This is necessary to make distinction between concrete type descriptor of a type like Coll[Int] - * and generic descriptor of Coll[T] type constructor. - * Some simple types like Int, GroupElement inherit from both SType and STypeCompanion. - * @see SInt, SGroupElement, SType - */ -trait STypeCompanion { - /** Force initialization of reflection. */ - val reflection = InterpreterReflection - - /** Type identifier to use in method serialization */ - def typeId: Byte - - /** If this is SType instance then returns the name of the corresponding RType. - * Otherwise returns the name of type companion object (e.g. SCollection). - */ - def typeName: String = { - this match { - case t: SType => - val rtype = stypeToRType(t) - rtype.name - case _ => this.getClass.getSimpleName.replace("$", "") - } - } - - /** List of methods defined for instances of this type. */ - def methods: Seq[SMethod] - - private lazy val _methodsMap: Map[Byte, Map[Byte, SMethod]] = methods - .groupBy(_.objType.typeId) - .map { case (typeId, ms) => (typeId -> ms.map(m => m.methodId -> m).toMap) } - - /** Lookup method by its id in this type. */ - @inline def getMethodById(methodId: Byte): Option[SMethod] = - _methodsMap.get(typeId) match { - case Some(ms) => ms.get(methodId) - case None => None - } - - /** Lookup method in this type by method's id or throw ValidationException. - * This method can be used in trySoftForkable section to either obtain valid method - * or catch ValidatioinException which can be checked for soft-fork condition. - * It delegate to getMethodById to lookup method. - * @see getMethodById - */ - def methodById(methodId: Byte): SMethod = { - ValidationRules.CheckAndGetMethod(this, methodId) - } - - /** Looks up the method descriptor by the method name. */ - def getMethodByName(name: String): SMethod = methods.find(_.name == name).get - - /** Class which represents values of this type. When method call is executed, the corresponding method - * of this class is invoked via [[RMethod]].invoke(). */ - def reprClass: RClass[_] - - /** Represents class of `this`. */ - lazy val thisRClass: RClass[_] = RClass(this.getClass) -} - -/** Defines recognizer method which allows the derived object to be used in patterns - * to recognize method descriptors by method name. - * @see SCollecton - */ -trait MethodByNameUnapply extends STypeCompanion { - def unapply(methodName: String): Option[SMethod] = methods.find(_.name == methodName) -} - -/** Base trait for all types which have methods (and properties) */ -trait SProduct extends SType { - /** Returns -1 if `method` is not found. */ - def methodIndex(name: String): Int = methods.indexWhere(_.name == name) - - /** Returns true if this type has a method with the given name. */ - def hasMethod(name: String): Boolean = methodIndex(name) != -1 - - /** This method should be overriden in derived classes to add new methods in addition to inherited. - * Typical override: `super.getMethods() ++ Seq(m1, m2, m3)` */ - protected def getMethods(): Seq[SMethod] = Nil - - /** Returns all the methods of this type. */ - lazy val methods: Seq[SMethod] = { - val ms = getMethods() - assert(ms.map(_.name).distinct.length == ms.length, s"Duplicate method names in $this") - ms.groupBy(_.objType).foreach { case (comp, ms) => - assert(ms.map(_.methodId).distinct.length == ms.length, s"Duplicate method ids in $comp: $ms") - } - ms - } - - /** Finds a method descriptor [[SMethod]] for the given name. */ - def method(methodName: String): Option[SMethod] = methods.find(_.name == methodName) -} - -/** Base trait implemented by all generic types (those which has type parameters, - * e.g. Coll[T], Option[T], etc.)*/ -trait SGenericType { - /** Type parameters of this generic type. */ - def typeParams: Seq[STypeParam] -} - -/** Meta information which can be attached to each argument of SMethod. - * @param name name of the argument - * @param description argument description. */ -case class ArgInfo(name: String, description: String) - -/** Meta information which can be attached to SMethod. - * @param opDesc optional operation descriptor - * @param description human readable description of the method - * @param args one item for each argument */ -case class OperationInfo(opDesc: Option[ValueCompanion], description: String, args: Seq[ArgInfo]) { - def isFrontendOnly: Boolean = opDesc.isEmpty - def opTypeName: String = opDesc.map(_.typeName).getOrElse("(FRONTEND ONLY)") -} - -object OperationInfo { - /** Convenience factory method. */ - def apply(opDesc: ValueCompanion, description: String, args: Seq[ArgInfo]): OperationInfo = - OperationInfo(Some(opDesc), description, args) -} - -/** Meta information connecting SMethod with ErgoTree. - * The optional builder is used by front-end ErgoScript compiler to replace method calls - * with ErgoTree nodes. In many cases [[SMethod.MethodCallIrBuilder]] builder is used. - * However there are specific cases where more complex builders are used, see for example - * usage of `withIRInfo` in the declaration of [[SCollection.GetOrElseMethod]]. - * @param irBuilder optional method call recognizer and ErgoTree node builder. - * When the partial function is defined on a tuple - * (builder, obj, m, args, subst) it transforms it to a new ErgoTree - * node, which is then used in the resuting ErgoTree coming out of - * the ErgoScript compiler. - * @param javaMethod Java [[Method]] which should be used to evaluate - * [[sigmastate.lang.Terms.MethodCall]] node of ErgoTree. - * @param invokeDescsBuilder optional builder of additional type descriptors (see extraDescriptors) - */ -case class MethodIRInfo( - irBuilder: Option[PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue]], - javaMethod: Option[RMethod], - invokeDescsBuilder: Option[InvokeDescBuilder] -) - -/** Represents method descriptor. - * - * @param objType type or type constructor descriptor - * @param name method name - * @param stype method signature type, - * where `stype.tDom`` - argument type and - * `stype.tRange` - method result type. - * @param methodId method code, it should be unique among methods of the same objType. - * @param costKind cost descriptor for this method - * @param irInfo meta information connecting SMethod with ErgoTree (see [[MethodIRInfo]]) - * @param docInfo optional human readable method description data - * @param costFunc optional specification of how the cost should be computed for the - * given method call (See ErgoTreeEvaluator.calcCost method). - */ -case class SMethod( - objType: STypeCompanion, - name: String, - stype: SFunc, - methodId: Byte, - costKind: CostKind, - irInfo: MethodIRInfo, - docInfo: Option[OperationInfo], - costFunc: Option[MethodCostFunc]) { - - /** Operation descriptor of this method. */ - lazy val opDesc = MethodDesc(this) - - /** Finds and keeps the [[RMethod]] instance which corresponds to this method descriptor. - * The lazy value is forced only if irInfo.javaMethod == None - */ - lazy val javaMethod: RMethod = { - irInfo.javaMethod.getOrElse { - val paramTypes = stype.tDom.drop(1).map(t => t match { - case _: STypeVar => classOf[AnyRef] - case _: SFunc => classOf[_ => _] - case _ => Evaluation.stypeToRType(t).classTag.runtimeClass - }).toArray - val m = objType.reprClass.getMethod(name, paramTypes:_*) - m - } - } - - /** Additional type descriptors, which are necessary to perform invocation of Method - * associated with this instance. - * @see MethodCall.eval - */ - lazy val extraDescriptors: Seq[RType[_]] = { - irInfo.invokeDescsBuilder match { - case Some(builder) => - builder(stype).map(Evaluation.stypeToRType) - case None => - ArraySeq.empty[RType[_]] - } - } - - /** Invoke this method on the given object with the arguments. - * This is used for methods with FixedCost costKind. */ - def invokeFixed(obj: Any, args: Array[Any])(implicit E: ErgoTreeEvaluator): Any = { - javaMethod.invoke(obj, args.asInstanceOf[Array[AnyRef]]:_*) - } - - // TODO optimize: avoid lookup when this SMethod is created via `specializeFor` - /** Return generic template of this method. */ - @inline final def genericMethod: SMethod = { - objType.getMethodById(methodId).get - } - - /** Returns refection [[RMethod]] which must be invoked to evaluate this method. - * The method is resolved by its name using `name + "_eval"` naming convention. - * @see `map_eval`, `flatMap_eval` and other `*_eval` methods. - * @hotspot don't beautify the code */ - lazy val evalMethod: RMethod = { - val argTypes = stype.tDom - val nArgs = argTypes.length - val paramTypes = new Array[Class[_]](nArgs + 2) - paramTypes(0) = classOf[MethodCall] - cfor(0)(_ < nArgs, _ + 1) { i => - paramTypes(i + 1) = argTypes(i) match { - case _: STypeVar => classOf[AnyRef] - case _: SFunc => classOf[_ => _] - case _: SCollectionType[_] => classOf[Coll[_]] - case _: SOption[_] => classOf[Option[_]] - case t => - Evaluation.stypeToRType(t).classTag.runtimeClass - } - } - paramTypes(paramTypes.length - 1) = classOf[ErgoTreeEvaluator] - - val methodName = name + "_eval" - val m = try { - objType.thisRClass.getMethod(methodName, paramTypes:_*) - } - catch { case e: NoSuchMethodException => - throw new RuntimeException(s"Cannot find eval method def $methodName(${Seq(paramTypes:_*)})", e) - } - m - } - - /** Create a new instance with the given stype. */ - def withSType(newSType: SFunc): SMethod = copy(stype = newSType) - - /** Create a new instance with the given cost function. */ - def withCost(costFunc: MethodCostFunc): SMethod = copy(costFunc = Some(costFunc)) - - /** Create a new instance in which the `stype` field transformed using - * the given substitution. */ - def withConcreteTypes(subst: Map[STypeVar, SType]): SMethod = - withSType(stype.withSubstTypes(subst).asFunc) - - /** Name of a language operation represented by this method. */ - def opName = objType.getClass.getSimpleName + "." + name - - /** Returns [[OperationId]] for AOT costing. */ - def opId: OperationId = { - OperationId(opName, stype) - } - - /** Specializes this instance by creating a new [[SMethod]] instance where signature - * is specialized with respect to the given object and args types. It is used in - * [[sigmastate.serialization.MethodCallSerializer]] `parse` method, so it is part of - * consensus protocol. - * - * @param objTpe specific type of method receiver (aka object) - * @param args specific types of method arguments - * @return new instance of method descriptor with specialized signature - * @consensus - */ - def specializeFor(objTpe: SType, args: Seq[SType]): SMethod = { - Terms.unifyTypeLists(stype.tDom, objTpe +: args) match { - case Some(subst) if subst.nonEmpty => - withConcreteTypes(subst) - case _ => this - } - } - - /** Create a new instance with the given [[OperationInfo]] parameters. */ - def withInfo(opDesc: ValueCompanion, desc: String, args: ArgInfo*): SMethod = { - this.copy(docInfo = Some(OperationInfo(opDesc, desc, ArgInfo("this", "this instance") +: args.toSeq))) - } - - /** Create a new instance with the given [[OperationInfo]] parameters. - * NOTE: opDesc parameter is not defined and falls back to None. - */ - def withInfo(desc: String, args: ArgInfo*): SMethod = { - this.copy(docInfo = Some(OperationInfo(None, desc, ArgInfo("this", "this instance") +: args.toSeq))) - } - - /** Create a new instance with the given IR builder (aka MethodCall rewriter) parameter. */ - def withIRInfo( - irBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue], - javaMethod: RMethod = null, - invokeHandler: InvokeDescBuilder = null): SMethod = { - this.copy(irInfo = MethodIRInfo(Some(irBuilder), Option(javaMethod), Option(invokeHandler))) - } - - /** Lookup [[ArgInfo]] for the given argName or throw an exception. */ - def argInfo(argName: String): ArgInfo = - docInfo.get.args.find(_.name == argName).get -} - - -object SMethod { - /** Type of functions used to assign cost to method call nodes. - * For a function `f: (mc, obj, args) => cost` it is called before the evaluation of - * the `mc` node with the given `obj` as method receiver and `args` as method - * arguments. - */ - abstract class MethodCostFunc extends Function4[ErgoTreeEvaluator, MethodCall, Any, Array[Any], CostDetails] { - /** - * The function returns an estimated cost of evaluation BEFORE actual evaluation of - * the method. For this reason [[MethodCostFunc]] is not used for higher-order - * operations like `Coll.map`, `Coll.filter` etc. - */ - def apply(E: ErgoTreeEvaluator, mc: MethodCall, obj: Any, args: Array[Any]): CostDetails - } - - /** Returns a cost function which always returs the given cost. */ - def givenCost(costKind: FixedCost): MethodCostFunc = new MethodCostFunc { - override def apply(E: ErgoTreeEvaluator, - mc: MethodCall, - obj: Any, args: Array[Any]): CostDetails = { - if (E.settings.costTracingEnabled) - TracedCost(Array(FixedCostItem(MethodDesc(mc.method), costKind))) - else - GivenCost(costKind.cost) - } - } - - /** Returns a cost function which expects `obj` to be of `Coll[T]` type and - * uses its length to compute SeqCostItem */ - def perItemCost(costKind: PerItemCost): MethodCostFunc = new MethodCostFunc { - override def apply(E: ErgoTreeEvaluator, - mc: MethodCall, - obj: Any, args: Array[Any]): CostDetails = obj match { - case coll: Coll[a] => - if (E.settings.costTracingEnabled) { - val desc = MethodDesc(mc.method) - TracedCost(Array(SeqCostItem(desc, costKind, coll.length))) - } - else - GivenCost(costKind.cost(coll.length)) - case _ => - ErgoTreeEvaluator.error( - s"Invalid object $obj of method call $mc: Coll type is expected") - } - } - - /** Some runtime methods (like Coll.map, Coll.flatMap) require additional RType descriptors. - * The builder can extract those descriptors from the given type of the method signature. - */ - type InvokeDescBuilder = SFunc => Seq[SType] - - /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. - * @param methodName the name of the method to lookup - * @param cT the class where to search the methodName - * @param cA1 the class of the method argument - */ - def javaMethodOf[T, A1](methodName: String) - (implicit cT: ClassTag[T], cA1: ClassTag[A1]): RMethod = - RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass) - - /** Return [[Method]] descriptor for the given `methodName` on the given `cT` type. - * @param methodName the name of the method to lookup - * @param cT the class where to search the methodName - * @param cA1 the class of the method's first argument - * @param cA2 the class of the method's second argument - */ - def javaMethodOf[T, A1, A2] - (methodName: String) - (implicit cT: ClassTag[T], cA1: ClassTag[A1], cA2: ClassTag[A2]): RMethod = - RClass(cT.runtimeClass).getMethod(methodName, cA1.runtimeClass, cA2.runtimeClass) - - /** Default fallback method call recognizer which builds MethodCall ErgoTree nodes. */ - val MethodCallIrBuilder: PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue] = { - case (builder, obj, method, args, tparamSubst) => - builder.mkMethodCall(obj, method, args.toIndexedSeq, tparamSubst) - } - - /** Convenience factory method. */ - def apply(objType: STypeCompanion, name: String, stype: SFunc, - methodId: Byte, - costKind: CostKind): SMethod = { - SMethod( - objType, name, stype, methodId, costKind, - MethodIRInfo(None, None, None), None, None) - } - - /** Looks up [[SMethod]] instance for the given type and method ids. - * - * @param typeId id of a type which can contain methods - * @param methodId id of a method of the type given by `typeId` - * @return an instance of [[SMethod]] which may contain generic type variables in the - * signature (see SMethod.stype). As a result `specializeFor` is called by - * deserializer to obtain monomorphic method descriptor. - * @consensus this is method is used in [[sigmastate.serialization.MethodCallSerializer]] - * `parse` method and hence it is part of consensus protocol - */ - def fromIds(typeId: Byte, methodId: Byte): SMethod = { - ValidationRules.CheckTypeWithMethods(typeId, SType.types.contains(typeId)) - val typeCompanion = SType.types(typeId) - val method = typeCompanion.methodById(methodId) - method - } -} - -/** Special type to represent untyped values. - * Interpreter raises an error when encounter a Value with this type. - * All Value nodes with this type should be elimitanted during typing. - * If no specific type can be assigned statically during typing, - * then either error should be raised or type SAny should be assigned - * which is interpreted as dynamic typing. */ -case object NoType extends SType { - type WrappedType = Nothing - override val typeCode = 0: Byte -} - -/** Base trait for all pre-defined types which are not necessary primitive (e.g. Box, AvlTree). - */ -trait SPredefType extends SType { -} - -/** Base trait for all embeddable types. - */ -trait SEmbeddable extends SType { - override def isEmbeddable: Boolean = true - /** Type code of embeddable type can be combined with code of type constructor. - * Resulting code can be serialized. This simple convention allows to save space for most frequently used types. - * See TypeSerializer */ - @inline final def embedIn(typeConstrId: Byte): Byte = (typeConstrId + this.typeCode).toByte -} - -/** Base trait for all primitive types (aka atoms) which don't have internal type items. - * All primitive types can occupy a reserved interval of codes from 1 to MaxPrimTypeCode. */ -trait SPrimType extends SType with SPredefType { -} - -/** Primitive type recognizer to pattern match on TypeCode */ -object SPrimType { - def unapply(t: SType): Option[SType] = SType.allPredefTypes.find(_ == t) - - /** Type code of the last valid prim type so that (1 to LastPrimTypeCode) is a range of valid codes. */ - final val LastPrimTypeCode: Byte = 8: Byte - - /** Upper limit of the interval of valid type codes for primitive types */ - final val MaxPrimTypeCode: Byte = 11: Byte - - /** Max possible number of primitive types. */ - final val PrimRange: Byte = (MaxPrimTypeCode + 1).toByte -} - -/** Marker trait for all numeric types. */ -trait SNumericType extends SProduct { - import SNumericType._ - protected override def getMethods(): Seq[SMethod] = { - super.getMethods() ++ SNumericType.methods.map { - m => m.copy(stype = Terms.applySubst(m.stype, Map(tNum -> this)).asFunc) - } - } - - /** Checks if the given name is a cast method name. - * @return true if it is. */ - def isCastMethod (name: String): Boolean = castMethods.contains(name) - - /** Upcasts the given value of a smaller type to this larger type. - * Corresponds to section 5.1.2 Widening Primitive Conversion of Java Language Spec. - * @param n numeric value to be converted - * @return a value of WrappedType of this type descriptor's instance. - * @throw exception if `n` has actual type which is larger than this type. - */ - def upcast(n: AnyVal): WrappedType - - /** Downcasts the given value of a larger type to this smaller type. - * Corresponds to section 5.1.3 Narrowing Primitive Conversion of Java Language Spec. - * @param n numeric value to be converted - * @return a value of WrappedType of this type descriptor's instance. - * @throw exception if the actual value of `i` cannot fit into this type. - */ - def downcast(n: AnyVal): WrappedType - - /** Returns a type which is larger. */ - @inline def max(that: SNumericType): SNumericType = - if (this.numericTypeIndex > that.numericTypeIndex) this else that - - /** Returns true if this numeric type is larger than that. */ - @inline final def >(that: SNumericType): Boolean = this.numericTypeIndex > that.numericTypeIndex - - /** Numeric types are ordered by the number of bytes to store the numeric values. - * @return index in the array of all numeric types. */ - def numericTypeIndex: Int - - override def toString: String = this.getClass.getSimpleName -} -object SNumericType extends STypeCompanion { - /** Array of all numeric types ordered by number of bytes in the representation. */ - final val allNumericTypes = Array(SByte, SShort, SInt, SLong, SBigInt) - - // TODO v6.0: this typeId is now shadowed by SGlobal.typeId - // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 - override def typeId: TypeCode = 106: Byte - - /** Since this object is not used in SMethod instances. */ - override def reprClass: RClass[_] = sys.error(s"Shouldn't be called.") - - /** Type variable used in generic signatures of method descriptors. */ - val tNum = STypeVar("TNum") - - /** Cost function which is assigned for numeric cast MethodCall nodes in ErgoTree. - * It is called as part of MethodCall.eval method. */ - val costOfNumericCast: MethodCostFunc = new MethodCostFunc { - override def apply(E: ErgoTreeEvaluator, - mc: MethodCall, - obj: Any, - args: Array[Any]): CostDetails = { - val targetTpe = mc.method.stype.tRange - val cast = getNumericCast(mc.obj.tpe, mc.method.name, targetTpe).get - val costKind = if (cast == Downcast) Downcast.costKind else Upcast.costKind - TracedCost(Array(TypeBasedCostItem(MethodDesc(mc.method), costKind, targetTpe))) - } - } - - /** The following SMethod instances are descriptors of methods available on all numeric - * types. - * @see `val methods` below - * */ - val ToByteMethod: SMethod = SMethod(this, "toByte", SFunc(tNum, SByte), 1, null) - .withCost(costOfNumericCast) - .withInfo(PropertyCall, "Converts this numeric value to \\lst{Byte}, throwing exception if overflow.") - val ToShortMethod: SMethod = SMethod(this, "toShort", SFunc(tNum, SShort), 2, null) - .withCost(costOfNumericCast) - .withInfo(PropertyCall, "Converts this numeric value to \\lst{Short}, throwing exception if overflow.") - val ToIntMethod: SMethod = SMethod(this, "toInt", SFunc(tNum, SInt), 3, null) - .withCost(costOfNumericCast) - .withInfo(PropertyCall, "Converts this numeric value to \\lst{Int}, throwing exception if overflow.") - val ToLongMethod: SMethod = SMethod(this, "toLong", SFunc(tNum, SLong), 4, null) - .withCost(costOfNumericCast) - .withInfo(PropertyCall, "Converts this numeric value to \\lst{Long}, throwing exception if overflow.") - val ToBigIntMethod: SMethod = SMethod(this, "toBigInt", SFunc(tNum, SBigInt), 5, null) - .withCost(costOfNumericCast) - .withInfo(PropertyCall, "Converts this numeric value to \\lst{BigInt}") - - /** Cost of: 1) creating Byte collection from a numeric value */ - val ToBytes_CostKind = FixedCost(JitCost(5)) - - val ToBytesMethod: SMethod = SMethod( - this, "toBytes", SFunc(tNum, SByteArray), 6, ToBytes_CostKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ Returns a big-endian representation of this numeric value in a collection of bytes. - | For example, the \lst{Int} value \lst{0x12131415} would yield the - | collection of bytes \lst{[0x12, 0x13, 0x14, 0x15]}. - """.stripMargin) - - /** Cost of: 1) creating Boolean collection (one bool for each bit) from a numeric - * value. */ - val ToBits_CostKind = FixedCost(JitCost(5)) - - val ToBitsMethod: SMethod = SMethod( - this, "toBits", SFunc(tNum, SBooleanArray), 7, ToBits_CostKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ Returns a big-endian representation of this numeric in a collection of Booleans. - | Each boolean corresponds to one bit. - """.stripMargin) - - override val methods: Seq[SMethod] = Array( - ToByteMethod, // see Downcast - ToShortMethod, // see Downcast - ToIntMethod, // see Downcast - ToLongMethod, // see Downcast - ToBigIntMethod, // see Downcast - ToBytesMethod, - ToBitsMethod - ) - - /** Collection of names of numeric casting methods (like `toByte`, `toInt`, etc). */ - val castMethods: Array[String] = - Array(ToByteMethod, ToShortMethod, ToIntMethod, ToLongMethod, ToBigIntMethod) - .map(_.name) - - /** Checks the given name is numeric type cast method (like toByte, toInt, etc.).*/ - def isCastMethod(name: String): Boolean = castMethods.contains(name) - - /** Convert the given method to a cast operation from fromTpe to resTpe. */ - def getNumericCast(fromTpe: SType, methodName: String, resTpe: SType): Option[NumericCastCompanion] = (fromTpe, resTpe) match { - case (from: SNumericType, to: SNumericType) if isCastMethod(methodName) => - val op = if (to > from) Upcast else Downcast - Some(op) - case _ => None // the method in not numeric type cast - } - -} - -/** Base type for SBoolean and SSigmaProp. */ -trait SLogical extends SType { -} - -/** Monomorphic type descriptor i.e. a type without generic parameters. - * @see `SGenericType` - */ -trait SMonoType extends SType with STypeCompanion { - /** Helper method to create method descriptors for properties (i.e. methods without args). */ - protected def propertyCall(name: String, tpeRes: SType, id: Byte, costKind: CostKind): SMethod = - SMethod(this, name, SFunc(this, tpeRes), id, costKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, "") - - /** Helper method to create method descriptors for properties (i.e. methods without args). */ - protected def property(name: String, tpeRes: SType, id: Byte, valueCompanion: ValueCompanion): SMethod = - SMethod(this, name, SFunc(this, tpeRes), id, valueCompanion.costKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(valueCompanion, "") -} - -/** Descriptor of ErgoTree type `Boolean` holding `true` or `false` values. */ -case object SBoolean extends SPrimType with SEmbeddable with SLogical with SProduct with SMonoType { - override type WrappedType = Boolean - override val typeCode: TypeCode = 1: Byte - override def typeId = typeCode - override val reprClass: RClass[_] = RClass(classOf[Boolean]) - - val ToByte = "toByte" - protected override def getMethods() = super.getMethods() - /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - ++ Seq( - SMethod(this, ToByte, SFunc(this, SByte), 1) - .withInfo(PropertyCall, "Convert true to 1 and false to 0"), - ) - */ -} - -/** Descriptor of ErgoTree type `Byte` - 8-bit signed integer. */ -case object SByte extends SPrimType with SEmbeddable with SNumericType with SMonoType { - override type WrappedType = Byte - override val typeCode: TypeCode = 2: Byte - override val reprClass: RClass[_] = RClass(classOf[Byte]) - override def typeId = typeCode - override def numericTypeIndex: Int = 0 - override def upcast(v: AnyVal): Byte = v match { - case b: Byte => b - case _ => sys.error(s"Cannot upcast value $v to the type $this") - } - override def downcast(v: AnyVal): Byte = v match { - case b: Byte => b - case s: Short => s.toByteExact - case i: Int => i.toByteExact - case l: Long => l.toByteExact - case _ => sys.error(s"Cannot downcast value $v to the type $this") - } -} - -/** Descriptor of ErgoTree type `Short` - 16-bit signed integer. */ -case object SShort extends SPrimType with SEmbeddable with SNumericType with SMonoType { - override type WrappedType = Short - override val typeCode: TypeCode = 3: Byte - override val reprClass: RClass[_] = RClass(classOf[Short]) - override def typeId = typeCode - override def numericTypeIndex: Int = 1 - override def upcast(v: AnyVal): Short = v match { - case x: Byte => x.toShort - case x: Short => x - case _ => sys.error(s"Cannot upcast value $v to the type $this") - } - override def downcast(v: AnyVal): Short = v match { - case s: Short => s - case i: Int => i.toShortExact - case l: Long => l.toShortExact - case _ => sys.error(s"Cannot downcast value $v to the type $this") - } -} - -/** Descriptor of ErgoTree type `Int` - 32-bit signed integer. */ -case object SInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { - override type WrappedType = Int - override val typeCode: TypeCode = 4: Byte - override val reprClass: RClass[_] = RClass(classOf[Int]) - override def typeId = typeCode - override def numericTypeIndex: Int = 2 - override def upcast(v: AnyVal): Int = v match { - case x: Byte => x.toInt - case x: Short => x.toInt - case x: Int => x - case _ => sys.error(s"Cannot upcast value $v to the type $this") - } - override def downcast(v: AnyVal): Int = v match { - case b: Byte => b.toInt - case s: Short => s.toInt - case i: Int => i - case l: Long => l.toIntExact - case _ => sys.error(s"Cannot downcast value $v to the type $this") - } -} - -/** Descriptor of ErgoTree type `Long` - 64-bit signed integer. */ -case object SLong extends SPrimType with SEmbeddable with SNumericType with SMonoType { - override type WrappedType = Long - override val typeCode: TypeCode = 5: Byte - override val reprClass: RClass[_] = RClass(classOf[Long]) - override def typeId = typeCode - override def numericTypeIndex: Int = 3 - override def upcast(v: AnyVal): Long = v match { - case x: Byte => x.toLong - case x: Short => x.toLong - case x: Int => x.toLong - case x: Long => x - case _ => sys.error(s"Cannot upcast value $v to the type $this") - } - override def downcast(v: AnyVal): Long = v match { - case b: Byte => b.toLong - case s: Short => s.toLong - case i: Int => i.toLong - case l: Long => l - case _ => sys.error(s"Cannot downcast value $v to the type $this") - } -} - -/** Type of 256 bit integet values. Implemented using [[java.math.BigInteger]]. */ -case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SMonoType { - override type WrappedType = BigInt - override val typeCode: TypeCode = 6: Byte - override val reprClass: RClass[_] = RClass(classOf[BigInt]) - - override def typeId = typeCode - - /** Type of Relation binary op like GE, LE, etc. */ - val RelationOpType = SFunc(Array(SBigInt, SBigInt), SBoolean) - - /** The maximum size of BigInteger value in byte array representation. */ - val MaxSizeInBytes: Long = SigmaConstants.MaxBigIntSizeInBytes.value - - override def numericTypeIndex: Int = 4 - - override def upcast(v: AnyVal): BigInt = { - val bi = v match { - case x: Byte => BigInteger.valueOf(x.toLong) - case x: Short => BigInteger.valueOf(x.toLong) - case x: Int => BigInteger.valueOf(x.toLong) - case x: Long => BigInteger.valueOf(x) - case _ => sys.error(s"Cannot upcast value $v to the type $this") - } - SigmaDsl.BigInt(bi) - } - override def downcast(v: AnyVal): BigInt = { - val bi = v match { - case x: Byte => BigInteger.valueOf(x.toLong) - case x: Short => BigInteger.valueOf(x.toLong) - case x: Int => BigInteger.valueOf(x.toLong) - case x: Long => BigInteger.valueOf(x) - case _ => sys.error(s"Cannot downcast value $v to the type $this") - } - SigmaDsl.BigInt(bi) - } - - /** The following `modQ` methods are not fully implemented in v4.x and this descriptors. - * This descritors are remain here in the code and are waiting for full implementation - * is upcoming soft-forks at which point the cost parameters should be calculated and - * changed. - */ - val ModQMethod = SMethod(this, "modQ", SFunc(this, SBigInt), 1, FixedCost(JitCost(1))) - .withInfo(ModQ, "Returns this \\lst{mod} Q, i.e. remainder of division by Q, where Q is an order of the cryprographic group.") - val PlusModQMethod = SMethod(this, "plusModQ", SFunc(IndexedSeq(this, SBigInt), SBigInt), 2, FixedCost(JitCost(1))) - .withInfo(ModQArithOp.PlusModQ, "Adds this number with \\lst{other} by module Q.", ArgInfo("other", "Number to add to this.")) - val MinusModQMethod = SMethod(this, "minusModQ", SFunc(IndexedSeq(this, SBigInt), SBigInt), 3, FixedCost(JitCost(1))) - .withInfo(ModQArithOp.MinusModQ, "Subtracts \\lst{other} number from this by module Q.", ArgInfo("other", "Number to subtract from this.")) - val MultModQMethod = SMethod(this, "multModQ", SFunc(IndexedSeq(this, SBigInt), SBigInt), 4, FixedCost(JitCost(1))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "Multiply this number with \\lst{other} by module Q.", ArgInfo("other", "Number to multiply with this.")) - - protected override def getMethods() = super.getMethods() ++ Seq( -// ModQMethod, -// PlusModQMethod, -// MinusModQMethod, - // TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - // MultModQMethod, - ) -} - -/** Descriptor of type `String` which is not used in ErgoTree, but used in ErgoScript. - * NOTE: this descriptor both type and type companion */ -case object SString extends SProduct with SMonoType { - override type WrappedType = String - override val typeCode: TypeCode = 102: Byte - override def typeId = typeCode - override def reprClass: RClass[_] = RClass(classOf[String]) -} - -/** Descriptor of ErgoTree type `GroupElement`. - * NOTE: this descriptor both type and type companion */ -case object SGroupElement extends SProduct with SPrimType with SEmbeddable with SMonoType { - override type WrappedType = GroupElement - override val typeCode: TypeCode = 7: Byte - override val reprClass: RClass[_] = RClass(classOf[GroupElement]) - - override def typeId = typeCode - - /** Cost of: 1) serializing EcPointType to bytes 2) packing them in Coll. */ - val GetEncodedCostKind = FixedCost(JitCost(250)) - - /** The following SMethod instances are descriptors of methods defined in `GroupElement` type. */ - lazy val GetEncodedMethod: SMethod = SMethod( - this, "getEncoded", SFunc(Array(this), SByteArray), 2, GetEncodedCostKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, "Get an encoding of the point value.") - - lazy val ExponentiateMethod: SMethod = SMethod( - this, "exp", SFunc(Array(this, SBigInt), this), 3, Exponentiate.costKind) - .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")) - - lazy val MultiplyMethod: SMethod = SMethod( - this, "multiply", SFunc(Array(this, SGroupElement), this), 4, MultiplyGroup.costKind) - .withIRInfo({ case (builder, obj, _, Seq(arg), _) => - builder.mkMultiplyGroup(obj.asGroupElement, arg.asGroupElement) - }) - .withInfo(MultiplyGroup, "Group operation.", ArgInfo("other", "other element of the group")) - - /** Cost of: 1) calling EcPoint.negate 2) wrapping in GroupElement. */ - val Negate_CostKind = FixedCost(JitCost(45)) - - lazy val NegateMethod: SMethod = SMethod( - this, "negate", SFunc(this, this), 5, Negate_CostKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, "Inverse element of the group.") - - protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( - /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - SMethod(this, "isIdentity", SFunc(this, SBoolean), 1) - .withInfo(PropertyCall, "Checks if this value is identity element of the eliptic curve group."), - */ - GetEncodedMethod, - ExponentiateMethod, - MultiplyMethod, - NegateMethod - ) -} - -/** Descriptor of ErgoTree type `SigmaProp` which represent sigma-protocol propositions. */ -case object SSigmaProp extends SProduct with SPrimType with SEmbeddable with SLogical with SMonoType { - import SType._ - override type WrappedType = SigmaProp - override val typeCode: TypeCode = 8: Byte - override val reprClass: RClass[_] = RClass(classOf[SigmaProp]) - override def typeId = typeCode - - /** The maximum size of SigmaProp value in serialized byte array representation. */ - val MaxSizeInBytes: Long = SigmaConstants.MaxSigmaPropSizeInBytes.value - - val PropBytes = "propBytes" - val IsProven = "isProven" - lazy val PropBytesMethod = SMethod( - this, PropBytes, SFunc(this, SByteArray), 1, SigmaPropBytes.costKind) - .withInfo(SigmaPropBytes, "Serialized bytes of this sigma proposition taken as ErgoTree.") - - lazy val IsProvenMethod = SMethod(this, IsProven, SFunc(this, SBoolean), 2, null) - .withInfo(// available only at frontend of ErgoScript - "Verify that sigma proposition is proven.") - - protected override def getMethods() = super.getMethods() ++ Seq( - PropBytesMethod, IsProvenMethod - ) -} - -/** Any other type is implicitly subtype of this type. */ -case object SAny extends SPrimType { - override type WrappedType = Any - override val typeCode: TypeCode = 97: Byte -} - -/** The type with single inhabitant value `()` */ -case object SUnit extends SPrimType { - override type WrappedType = Unit - override val typeCode: TypeCode = 98: Byte -} - -/** Type description of optional values. Instances of `Option` - * are either constructed by `Some` or by `None` constructors. */ -case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct with SGenericType { - import SOption._ - override type WrappedType = Option[ElemType#WrappedType] - override val typeCode: TypeCode = SOption.OptionTypeCode - protected override def getMethods() = super.getMethods() ++ SOption.methods - override def toString = s"Option[$elemType]" - override def toTermString: String = s"Option[${elemType.toTermString}]" - override lazy val typeParams: Seq[STypeParam] = Array(SType.paramT) -} - -object SOption extends STypeCompanion { - /** Code of `Option[_]` type constructor. */ - val OptionTypeConstrId = 3 - /** Type code for `Option[T] for some T` type used in TypeSerializer. */ - val OptionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionTypeConstrId).toByte - /** Code of `Option[Coll[_]]` type constructor. */ - val OptionCollectionTypeConstrId = 4 - /** Type code for `Option[Coll[T]] for some T` type used in TypeSerializer. */ - val OptionCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * OptionCollectionTypeConstrId).toByte - - override def typeId = OptionTypeCode - - override val reprClass: RClass[_] = RClass(classOf[Option[_]]) - - type SBooleanOption = SOption[SBoolean.type] - type SByteOption = SOption[SByte.type] - type SShortOption = SOption[SShort.type] - type SIntOption = SOption[SInt.type] - type SLongOption = SOption[SLong.type] - type SBigIntOption = SOption[SBigInt.type] - type SGroupElementOption = SOption[SGroupElement.type] - type SBoxOption = SOption[SBox.type] - type SAvlTreeOption = SOption[SAvlTree.type] - - /** This descriptors are instantiated once here and then reused. */ - implicit val SByteOption = SOption(SByte) - implicit val SByteArrayOption = SOption(SByteArray) - implicit val SShortOption = SOption(SShort) - implicit val SIntOption = SOption(SInt) - implicit val SLongOption = SOption(SLong) - implicit val SBigIntOption = SOption(SBigInt) - implicit val SBooleanOption = SOption(SBoolean) - implicit val SAvlTreeOption = SOption(SAvlTree) - implicit val SGroupElementOption = SOption(SGroupElement) - implicit val SSigmaPropOption = SOption(SSigmaProp) - implicit val SBoxOption = SOption(SBox) - - val IsDefined = "isDefined" - val Get = "get" - val GetOrElse = "getOrElse" - - import SType.{tT, tR, paramT, paramR} - - /** Type descriptor of `this` argument used in the methods below. */ - val ThisType = SOption(tT) - - /** The following SMethod instances are descriptors of methods defined in `Option` type. */ - val IsDefinedMethod = SMethod( - this, IsDefined, SFunc(ThisType, SBoolean), 2, OptionIsDefined.costKind) - .withIRInfo({ - case (builder, obj, _, args, _) if args.isEmpty => builder.mkOptionIsDefined(obj.asValue[SOption[SType]]) - }) - .withInfo(OptionIsDefined, - "Returns \\lst{true} if the option is an instance of \\lst{Some}, \\lst{false} otherwise.") - - val GetMethod = SMethod(this, Get, SFunc(ThisType, tT), 3, OptionGet.costKind) - .withIRInfo({ - case (builder, obj, _, args, _) if args.isEmpty => builder.mkOptionGet(obj.asValue[SOption[SType]]) - }) - .withInfo(OptionGet, - """Returns the option's value. The option must be nonempty. Throws exception if the option is empty.""") - - lazy val GetOrElseMethod = SMethod( - this, GetOrElse, SFunc(Array(ThisType, tT), tT, Array[STypeParam](tT)), 4, OptionGetOrElse.costKind) - .withIRInfo(irBuilder = { - case (builder, obj, _, Seq(d), _) => builder.mkOptionGetOrElse(obj.asValue[SOption[SType]], d) - }) - .withInfo(OptionGetOrElse, - """Returns the option's value if the option is nonempty, otherwise - |return the result of evaluating \lst{default}. - """.stripMargin, ArgInfo("default", "the default value")) - -// TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 -// val FoldMethod = SMethod( -// this, Fold, SFunc(Array(ThisType, tR, SFunc(tT, tR)), tR, Array[STypeParam](tT, tR)), 5, FixedCost(JitCost(1))) -// .withInfo(MethodCall, -// """Returns the result of applying \lst{f} to this option's -// | value if the option is nonempty. Otherwise, evaluates -// | expression \lst{ifEmpty}. -// | This is equivalent to \lst{option map f getOrElse ifEmpty}. -// """.stripMargin, -// ArgInfo("ifEmpty", "the expression to evaluate if empty"), -// ArgInfo("f", "the function to apply if nonempty")) - - val MapMethod = SMethod(this, "map", - SFunc(Array(ThisType, SFunc(tT, tR)), SOption(tR), Array(paramT, paramR)), 7, FixedCost(JitCost(20))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """Returns a \lst{Some} containing the result of applying \lst{f} to this option's - | value if this option is nonempty. - | Otherwise return \lst{None}. - """.stripMargin, ArgInfo("f", "the function to apply")) - - val FilterMethod = SMethod(this, "filter", - SFunc(Array(ThisType, SFunc(tT, SBoolean)), ThisType, Array(paramT)), 8, FixedCost(JitCost(20))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """Returns this option if it is nonempty and applying the predicate \lst{p} to - | this option's value returns true. Otherwise, return \lst{None}. - """.stripMargin, ArgInfo("p", "the predicate used for testing")) - - val methods: Seq[SMethod] = Seq( - IsDefinedMethod, - GetMethod, - GetOrElseMethod, - /* TODO soft-fork: https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 - FoldMethod, - */ - MapMethod, - FilterMethod - ) - def apply[T <: SType](implicit elemType: T, ov: Overload1): SOption[T] = SOption(elemType) -} - -/** Base class for descriptors of `Coll[T]` ErgoTree type for some elemType T. */ -trait SCollection[T <: SType] extends SProduct with SGenericType { - def elemType: T - override type WrappedType = Coll[T#WrappedType] -} - -/** Descriptor of `Coll[T]` ErgoTree type for some elemType T. */ -case class SCollectionType[T <: SType](elemType: T) extends SCollection[T] { - override val typeCode: TypeCode = SCollectionType.CollectionTypeCode - override def typeParams: Seq[STypeParam] = SCollectionType.typeParams - protected override def getMethods() = super.getMethods() ++ SCollection.methods - override def toString = s"Coll[$elemType]" - override def toTermString = s"Coll[${elemType.toTermString}]" -} - -object SCollectionType { - /** Code of `Coll[_]` type constructor. */ - val CollectionTypeConstrId = 1 - - /** Type code for `Coll[T] for some T` type used in TypeSerializer. */ - val CollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * CollectionTypeConstrId).toByte - - /** Code of `Coll[Coll[_]]` type constructor. */ - val NestedCollectionTypeConstrId = 2 - - /** Type code for `Coll[Coll[T]] for some T` type used in TypeSerializer. */ - val NestedCollectionTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * NestedCollectionTypeConstrId).toByte - - /** Array of generic type parameters reused in all SCollectionType instances. */ - val typeParams: Seq[STypeParam] = Array(SType.paramIV) -} - -object SCollection extends STypeCompanion with MethodByNameUnapply { - override val reprClass: RClass[_] = RClass(classOf[Coll[_]]) - override def typeId = SCollectionType.CollectionTypeCode - - import SType.{tK, tV, paramIV, paramIVSeq, paramOV} - - /** Helper descriptors reused across different method descriptors. */ - def tIV = SType.tIV - def tOV = SType.tOV - - /** This descriptors are instantiated once here and then reused. */ - val ThisType = SCollection(tIV) - val tOVColl = SCollection(tOV) - val tPredicate = SFunc(tIV, SBoolean) - - /** The following SMethod instances are descriptors of methods defined in `Coll` type. */ - val SizeMethod = SMethod(this, "size", SFunc(ThisType, SInt), 1, SizeOf.costKind) - .withInfo(SizeOf, "The size of the collection in elements.") - - val GetOrElseMethod = SMethod( - this, "getOrElse", SFunc(Array(ThisType, SInt, tIV), tIV, paramIVSeq), 2, DynamicCost) - .withIRInfo({ case (builder, obj, _, Seq(index, defaultValue), _) => - val index1 = index.asValue[SInt.type] - val defaultValue1 = defaultValue.asValue[SType] - builder.mkByIndex(obj.asValue[SCollection[SType]], index1, Some(defaultValue1)) - }) - .withInfo(ByIndex, "Return the element of collection if \\lst{index} is in range \\lst{0 .. size-1}", - ArgInfo("index", "index of the element of this collection"), - ArgInfo("default", "value to return when \\lst{index} is out of range")) - - /** Implements evaluation of Coll.getOrElse method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def getOrElse_eval[A](mc: MethodCall, xs: Coll[A], i: Int, default: A)(implicit E: ErgoTreeEvaluator): A = { - E.addCost(ByIndex.costKind, mc.method.opDesc) - // the following lines should be semantically the same as in ByIndex.eval - Value.checkType(mc.args.last.tpe, default) - xs.getOrElse(i, default) - } - - val MapMethod = SMethod(this, "map", - SFunc(Array(ThisType, SFunc(tIV, tOV)), tOVColl, Array(paramIV, paramOV)), 3, MapCollection.costKind) - .withIRInfo({ - case (builder, obj, _, Seq(mapper), _) => builder.mkMapCollection(obj.asValue[SCollection[SType]], mapper.asFunc) - }) - .withInfo(MapCollection, - """ Builds a new collection by applying a function to all elements of this collection. - | Returns a new collection of type \lst{Coll[B]} resulting from applying the given function - | \lst{f} to each element of this collection and collecting the results. - """.stripMargin, - ArgInfo("f", "the function to apply to each element")) - - /** Implements evaluation of Coll.map method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def map_eval[A,B](mc: MethodCall, xs: Coll[A], f: A => B)(implicit E: ErgoTreeEvaluator): Coll[B] = { - val tpeB = mc.tpe.asInstanceOf[SCollection[SType]].elemType - val tB = Evaluation.stypeToRType(tpeB).asInstanceOf[RType[B]] - E.addSeqCostNoOp(MapCollection.costKind, xs.length, mc.method.opDesc) - xs.map(f)(tB) - } - - val ExistsMethod = SMethod(this, "exists", - SFunc(Array(ThisType, tPredicate), SBoolean, paramIVSeq), 4, Exists.costKind) - .withIRInfo({ - case (builder, obj, _, Seq(c), _) => builder.mkExists(obj.asValue[SCollection[SType]], c.asFunc) - }) - .withInfo(Exists, - """Tests whether a predicate holds for at least one element of this collection. - |Returns \lst{true} if the given predicate \lst{p} is satisfied by at least one element of this collection, otherwise \lst{false} - """.stripMargin, - ArgInfo("p", "the predicate used to test elements")) - - val FoldMethod = SMethod( - this, "fold", - SFunc(Array(ThisType, tOV, SFunc(Array(tOV, tIV), tOV)), tOV, Array(paramIV, paramOV)), - 5, Fold.costKind) - .withIRInfo({ - case (builder, obj, _, Seq(z, op), _) => builder.mkFold(obj.asValue[SCollection[SType]], z, op.asFunc) - }) - .withInfo(Fold, "Applies a binary operator to a start value and all elements of this collection, going left to right.", - ArgInfo("zero", "a starting value"), - ArgInfo("op", "the binary operator")) - - val ForallMethod = SMethod(this, "forall", - SFunc(Array(ThisType, tPredicate), SBoolean, paramIVSeq), 6, ForAll.costKind) - .withIRInfo({ - case (builder, obj, _, Seq(c), _) => builder.mkForAll(obj.asValue[SCollection[SType]], c.asFunc) - }) - .withInfo(ForAll, - """Tests whether a predicate holds for all elements of this collection. - |Returns \lst{true} if this collection is empty or the given predicate \lst{p} - |holds for all elements of this collection, otherwise \lst{false}. - """.stripMargin, - ArgInfo("p", "the predicate used to test elements")) - - val SliceMethod = SMethod(this, "slice", - SFunc(Array(ThisType, SInt, SInt), ThisType, paramIVSeq), 7, Slice.costKind) - .withIRInfo({ - case (builder, obj, _, Seq(from, until), _) => - builder.mkSlice(obj.asCollection[SType], from.asIntValue, until.asIntValue) - }) - .withInfo(Slice, - """Selects an interval of elements. The returned collection is made up - | of all elements \lst{x} which satisfy the invariant: - | \lst{ - | from <= indexOf(x) < until - | } - """.stripMargin, - ArgInfo("from", "the lowest index to include from this collection"), - ArgInfo("until", "the lowest index to EXCLUDE from this collection")) - - val FilterMethod = SMethod(this, "filter", - SFunc(Array(ThisType, tPredicate), ThisType, paramIVSeq), 8, Filter.costKind) - .withIRInfo({ - case (builder, obj, _, Seq(l), _) => builder.mkFilter(obj.asValue[SCollection[SType]], l.asFunc) - }) - .withInfo(Filter, - """Selects all elements of this collection which satisfy a predicate. - | Returns a new collection consisting of all elements of this collection that satisfy the given - | predicate \lst{p}. The order of the elements is preserved. - """.stripMargin, - ArgInfo("p", "the predicate used to test elements.")) - - val AppendMethod = SMethod(this, "append", - SFunc(Array(ThisType, ThisType), ThisType, paramIVSeq), 9, Append.costKind) - .withIRInfo({ - case (builder, obj, _, Seq(xs), _) => - builder.mkAppend(obj.asCollection[SType], xs.asCollection[SType]) - }) - .withInfo(Append, "Puts the elements of other collection after the elements of this collection (concatenation of 2 collections)", - ArgInfo("other", "the collection to append at the end of this")) - - val ApplyMethod = SMethod(this, "apply", - SFunc(Array(ThisType, SInt), tIV, Array[STypeParam](tIV)), 10, ByIndex.costKind) - .withInfo(ByIndex, - """The element at given index. - | Indices start at \lst{0}; \lst{xs.apply(0)} is the first element of collection \lst{xs}. - | Note the indexing syntax \lst{xs(i)} is a shorthand for \lst{xs.apply(i)}. - | Returns the element at the given index. - | Throws an exception if \lst{i < 0} or \lst{length <= i} - """.stripMargin, ArgInfo("i", "the index")) - - /** Cost of creating a collection of indices */ - val IndicesMethod_CostKind = PerItemCost( - baseCost = JitCost(20), perChunkCost = JitCost(2), chunkSize = 16) - - val IndicesMethod = SMethod( - this, "indices", SFunc(ThisType, SCollection(SInt)), 14, IndicesMethod_CostKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """Produces the range of all indices of this collection as a new collection - | containing [0 .. length-1] values. - """.stripMargin) - - /** Implements evaluation of Coll.indices method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def indices_eval[A, B](mc: MethodCall, xs: Coll[A]) - (implicit E: ErgoTreeEvaluator): Coll[Int] = { - val m = mc.method - E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => - xs.indices - } - } - /** BaseCost: - * 1) base cost of Coll.flatMap - * PerChunkCost: - * 1) cost of Coll.flatMap (per item) - * 2) new collection is allocated for each item - * 3) each collection is then appended to the resulting collection */ - val FlatMapMethod_CostKind = PerItemCost( - baseCost = JitCost(60), perChunkCost = JitCost(10), chunkSize = 8) - - val FlatMapMethod = SMethod(this, "flatMap", - SFunc(Array(ThisType, SFunc(tIV, tOVColl)), tOVColl, Array(paramIV, paramOV)), - 15, FlatMapMethod_CostKind) - .withIRInfo( - MethodCallIrBuilder, - javaMethodOf[Coll[_], Function1[_,_], RType[_]]("flatMap"), - { mtype => Array(mtype.tRange.asCollection[SType].elemType) }) - .withInfo(MethodCall, - """ Builds a new collection by applying a function to all elements of this collection - | and using the elements of the resulting collections. - | Function \lst{f} is constrained to be of the form \lst{x => x.someProperty}, otherwise - | it is illegal. - | Returns a new collection of type \lst{Coll[B]} resulting from applying the given collection-valued function - | \lst{f} to each element of this collection and concatenating the results. - """.stripMargin, ArgInfo("f", "the function to apply to each element.")) - - /** We assume all flatMap body patterns have similar executon cost. */ - final val CheckFlatmapBody_Info = OperationCostInfo( - PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(20), chunkSize = 1), - NamedDesc("CheckFlatmapBody")) - - - /** This patterns recognize all expressions, which are allowed as lambda body - * of flatMap. Other bodies are rejected with throwing exception. - */ - val flatMap_BodyPatterns = Array[PartialFunction[SValue, Int]]( - { case MethodCall(ValUse(id, tpe), m, args, _) if args.isEmpty => id }, - { case ExtractScriptBytes(ValUse(id, _)) => id }, - { case ExtractId(ValUse(id, _)) => id }, - { case SigmaPropBytes(ValUse(id, _)) => id }, - { case ExtractBytes(ValUse(id, _)) => id }, - { case ExtractBytesWithNoRef(ValUse(id, _)) => id } - ) - - /** Check the given expression is valid body of flatMap argument lambda. - * @param varId id of lambda variable (see [[FuncValue]].args) - * @param expr expression with is expected to use varId in ValUse node. - * @return true if the body is allowed - */ - def isValidPropertyAccess(varId: Int, expr: SValue) - (implicit E: ErgoTreeEvaluator): Boolean = { - var found = false - // NOTE: the cost depends on the position of the pattern since - // we are checking until the first matching pattern found. - E.addSeqCost(CheckFlatmapBody_Info) { () => - // the loop is bounded because flatMap_BodyPatterns is fixed - var i = 0 - val nPatterns = flatMap_BodyPatterns.length - while (i < nPatterns && !found) { - val p = flatMap_BodyPatterns(i) - found = p.lift(expr) match { - case Some(id) => id == varId // `id` in the pattern is equal to lambda `varId` - case None => false - } - i += 1 - } - i // how many patterns checked - } - found - } - - /** Operation descriptor for matching `flatMap` method calls with valid lambdas. */ - final val MatchSingleArgMethodCall_Info = OperationCostInfo( - FixedCost(JitCost(30)), NamedDesc("MatchSingleArgMethodCall")) - - /** Recognizer of `flatMap` method calls with valid lambdas. */ - object IsSingleArgMethodCall { - def unapply(mc:MethodCall) - (implicit E: ErgoTreeEvaluator): Nullable[(Int, SValue)] = { - var res: Nullable[(Int, SValue)] = Nullable.None - E.addFixedCost(MatchSingleArgMethodCall_Info) { - res = mc match { - case MethodCall(_, m, Seq(FuncValue(args, body)), _) if args.length == 1 => - val id = args(0)._1 - Nullable((id, body)) - case _ => - Nullable.None - } - } - res - } - } - - /** Checks that the given [[MethodCall]] operation is valid flatMap. */ - def checkValidFlatmap(mc: MethodCall)(implicit E: ErgoTreeEvaluator) = { - mc match { - case IsSingleArgMethodCall(varId, lambdaBody) - if isValidPropertyAccess(varId, lambdaBody) => - // ok, do nothing - case _ => - throwInvalidFlatmap(mc) - } - } - - def throwInvalidFlatmap(mc: MethodCall) = { - ErgoTreeEvaluator.error( - s"Unsupported lambda in flatMap: allowed usage `xs.flatMap(x => x.property)`: $mc") - } - - /** Implements evaluation of Coll.flatMap method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def flatMap_eval[A, B](mc: MethodCall, xs: Coll[A], f: A => Coll[B]) - (implicit E: ErgoTreeEvaluator): Coll[B] = { - val m = mc.method - var res: Coll[B] = null - E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], m.opDesc) { () => - val tpeB = mc.tpe.asInstanceOf[SCollection[SType]].elemType - val tB = Evaluation.stypeToRType(tpeB).asInstanceOf[RType[B]] - res = xs.flatMap(f)(tB) - res.length - } - res - } - - val PatchMethod = SMethod(this, "patch", - SFunc(Array(ThisType, SInt, ThisType, SInt), ThisType, paramIVSeq), - 19, PerItemCost(baseCost = JitCost(30), perChunkCost = JitCost(2), chunkSize = 10)) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - "Produces a new Coll where a slice of elements in this Coll is replaced by another Coll.") - - /** Implements evaluation of Coll.patch method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def patch_eval[A](mc: MethodCall, xs: Coll[A], from: Int, patch: Coll[A], replaced: Int) - (implicit E: ErgoTreeEvaluator): Coll[A] = { - val m = mc.method - val nItems = xs.length + patch.length - E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], nItems, m.opDesc) { () => - xs.patch(from, patch, replaced) - } - } - - val UpdatedMethod = SMethod(this, "updated", - SFunc(Array(ThisType, SInt, tIV), ThisType, paramIVSeq), - 20, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(1), chunkSize = 10)) - .withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Int, Any]("updated")) - .withInfo(MethodCall, - "A copy of this Coll with one single replaced element.") - - /** Implements evaluation of Coll.updated method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def updated_eval[A](mc: MethodCall, coll: Coll[A], index: Int, elem: A) - (implicit E: ErgoTreeEvaluator): Coll[A] = { - val m = mc.method - val costKind = m.costKind.asInstanceOf[PerItemCost] - E.addSeqCost(costKind, coll.length, m.opDesc) { () => - coll.updated(index, elem) - } - } - - val UpdateManyMethod = SMethod(this, "updateMany", - SFunc(Array(ThisType, SCollection(SInt), ThisType), ThisType, paramIVSeq), - 21, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(2), chunkSize = 10)) - .withIRInfo(MethodCallIrBuilder).withInfo(MethodCall, "") - - /** Implements evaluation of Coll.updateMany method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def updateMany_eval[A](mc: MethodCall, coll: Coll[A], indexes: Coll[Int], values: Coll[A]) - (implicit E: ErgoTreeEvaluator): Coll[A] = { - val costKind = mc.method.costKind.asInstanceOf[PerItemCost] - E.addSeqCost(costKind, coll.length, mc.method.opDesc) { () => - coll.updateMany(indexes, values) - } - } - - val IndexOfMethod = SMethod(this, "indexOf", - SFunc(Array(ThisType, tIV, SInt), SInt, paramIVSeq), - 26, PerItemCost(baseCost = JitCost(20), perChunkCost = JitCost(10), chunkSize = 2)) - .withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Any, Int]("indexOf")) - .withInfo(MethodCall, "") - - /** Implements evaluation of Coll.indexOf method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def indexOf_eval[A](mc: MethodCall, xs: Coll[A], elem: A, from: Int) - (implicit E: ErgoTreeEvaluator): Int = { - val costKind = mc.method.costKind.asInstanceOf[PerItemCost] - var res: Int = -1 - E.addSeqCost(costKind, mc.method.opDesc) { () => - // this loop is bounded because MaxArrayLength limit is enforced - val len = xs.length - val start = math.max(from, 0) - var i = start - var different = true - while (i < len && different) { - different = !DataValueComparer.equalDataValues(xs(i), elem) - i += 1 - } - if (!different) - res = i - 1 - i - start // return number of performed iterations - } - res - } - - /** Cost descriptor of Coll.zip operation. */ - val Zip_CostKind = PerItemCost( - baseCost = JitCost(10), perChunkCost = JitCost(1), chunkSize = 10) - - val ZipMethod = SMethod(this, "zip", - SFunc(Array(ThisType, tOVColl), SCollection(STuple(tIV, tOV)), Array[STypeParam](tIV, tOV)), - 29, Zip_CostKind) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, "") - - /** Implements evaluation of Coll.zip method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def zip_eval[A, B](mc: MethodCall, xs: Coll[A], ys: Coll[B]) - (implicit E: ErgoTreeEvaluator): Coll[(A,B)] = { - val m = mc.method - E.addSeqCost(m.costKind.asInstanceOf[PerItemCost], xs.length, m.opDesc) { () => - xs.zip(ys) - } - } - - override lazy val methods: Seq[SMethod] = Seq( - SizeMethod, - GetOrElseMethod, - MapMethod, - ExistsMethod, - FoldMethod, - ForallMethod, - SliceMethod, - FilterMethod, - AppendMethod, - ApplyMethod, - IndicesMethod, - FlatMapMethod, - PatchMethod, - UpdatedMethod, - UpdateManyMethod, - IndexOfMethod, - ZipMethod - ) - - /** Helper constructors. */ - def apply[T <: SType](elemType: T): SCollection[T] = SCollectionType(elemType) - def apply[T <: SType](implicit elemType: T, ov: Overload1): SCollection[T] = SCollectionType(elemType) - - type SBooleanArray = SCollection[SBoolean.type] - type SByteArray = SCollection[SByte.type] - type SShortArray = SCollection[SShort.type] - type SIntArray = SCollection[SInt.type] - type SLongArray = SCollection[SLong.type] - type SBigIntArray = SCollection[SBigInt.type] - type SGroupElementArray = SCollection[SGroupElement.type] - type SBoxArray = SCollection[SBox.type] - type SAvlTreeArray = SCollection[SAvlTree.type] - - /** This descriptors are instantiated once here and then reused. */ - val SBooleanArray = SCollection(SBoolean) - val SByteArray = SCollection(SByte) - val SByteArray2 = SCollection(SCollection(SByte)) - val SShortArray = SCollection(SShort) - val SIntArray = SCollection(SInt) - val SLongArray = SCollection(SLong) - val SBigIntArray = SCollection(SBigInt) - val SGroupElementArray = SCollection(SGroupElement) - val SSigmaPropArray = SCollection(SSigmaProp) - val SBoxArray = SCollection(SBox) - val SAvlTreeArray = SCollection(SAvlTree) - val SHeaderArray = SCollection(SHeader) -} - -/** Type descriptor of tuple type. */ -case class STuple(items: IndexedSeq[SType]) extends SCollection[SAny.type] { - import STuple._ - override val typeCode = STuple.TupleTypeCode - - /** Lazily computed value representing true | false | none. - * 0 - none, 1 - false, 2 - true - */ - @volatile - private var _isConstantSizeCode: Byte = 0.toByte - - override def elemType: SAny.type = SAny - - protected override def getMethods() = { - val tupleMethods = Array.tabulate(items.size) { i => - SMethod( - STuple, componentNameByIndex(i), SFunc(this, items(i)), - (i + 1).toByte, SelectField.costKind) - } - colMethods ++ tupleMethods - } - - override val typeParams = Nil - - override def toTermString = s"(${items.map(_.toTermString).mkString(",")})" - override def toString = s"(${items.mkString(",")})" -} - -object STuple extends STypeCompanion { - /** Code of `(_, T) for some embeddable T` type constructor. */ - val Pair1TypeConstrId = 5 - /** Type code for `(E, T) for some embeddable T` type used in TypeSerializer. */ - val Pair1TypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * Pair1TypeConstrId).toByte - - /** Code of `(T, _) for some embeddable T` type constructor. */ - val Pair2TypeConstrId = 6 - /** Type code for `(T, E) for some embeddable T` type used in TypeSerializer. */ - val Pair2TypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * Pair2TypeConstrId).toByte - val TripleTypeCode: TypeCode = Pair2TypeCode - - /** Type constructor code of symmetric pair `(T, T)` for some embeddable T. */ - val PairSymmetricTypeConstrId = 7 - /** Type code of symmetric pair `(T, T)` for some embeddable T. */ - val PairSymmetricTypeCode: TypeCode = ((SPrimType.MaxPrimTypeCode + 1) * PairSymmetricTypeConstrId).toByte - val QuadrupleTypeCode: TypeCode = PairSymmetricTypeCode - - /** Type code of generic tuple type. */ - val TupleTypeCode = ((SPrimType.MaxPrimTypeCode + 1) * 8).toByte - - override def typeId = TupleTypeCode - - override val reprClass: RClass[_] = RClass(classOf[Product2[_,_]]) - - /** A list of Coll methods inherited from Coll type and available as method of tuple. */ - lazy val colMethods: Seq[SMethod] = { - val subst = Map(SType.tIV -> SAny) - // TODO: implement other methods - val activeMethods = Set(1.toByte /*Coll.size*/, 10.toByte /*Coll.apply*/) - SCollection.methods.filter(m => activeMethods.contains(m.methodId)).map { m => - m.copy(stype = Terms.applySubst(m.stype, subst).asFunc) - } - } - - override def methods: Seq[SMethod] = sys.error(s"Shouldn't be called.") - - /** Helper factory method. */ - def apply(items: SType*): STuple = STuple(items.toArray) - - private val MaxTupleLength: Int = SigmaConstants.MaxTupleLength.value - private val componentNames = Array.tabulate(MaxTupleLength){ i => s"_${i + 1}" } - - /** Returns method name for the tuple component accessor (i.e. `_1`, `_2`, etc.) */ - def componentNameByIndex(i: Int): String = - try componentNames(i) - catch { - case e: IndexOutOfBoundsException => - throw new IllegalArgumentException( - s"Tuple component '_${i+1}' is not defined: valid range (1 .. $MaxTupleLength)", e) - } -} - -/** Helper constuctor/extractor for tuples of two types. */ -object SPair { - 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 - } -} - -/** Type descriptor of lambda types. */ -case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypeParam] = Nil) - extends SType with SGenericType -{ - override type WrappedType = Any => tRange.WrappedType - override val typeCode = SFunc.FuncTypeCode - override def toString = { - val args = if (tpeParams.isEmpty) "" else tpeParams.mkString("[", ",", "]") - s"$args(${tDom.mkString(",")}) => $tRange" - } - override def toTermString = { - val args = if (tpeParams.isEmpty) "" else tpeParams.mkString("[", ",", "]") - s"$args(${tDom.map(_.toTermString).mkString(",")}) => ${tRange.toTermString}" - } - import SFunc._ - override val typeParams: Seq[STypeParam] = tpeParams - - /** Generalize this type and return a new descriptor. */ - def getGenericType: SFunc = { - val typeParams: Seq[STypeParam] = tDom.zipWithIndex - .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) - } - - /** Transform function into method type by adding the given `objType` as the first - * argument type (aka method receiver type). - */ - def withReceiverType(objType: SType) = this.copy(tDom = objType +: tDom) -} - -object SFunc { - final val FuncTypeCode: TypeCode = OpCodes.FirstFuncType - def apply(tDom: SType, tRange: SType): SFunc = SFunc(Array(tDom), tRange) // HOTSPOT: - val identity = { x: Any => x } -} - -/** Used by ErgoScript compiler IR and eliminated during compilation. - * It is not used in ErgoTree. - */ -case class STypeApply(name: String, args: IndexedSeq[SType] = IndexedSeq()) extends SType { - override type WrappedType = Any - override val typeCode = STypeApply.TypeCode -} -object STypeApply { - val TypeCode = 94: Byte -} - -/** Type variable which is used in generic method/func signatures. - * Used by ErgoScript compiler IR and eliminated during compilation. - * It is not used in ErgoTree. - */ -case class STypeVar(name: String) extends SType { - require(name.length <= 255, "name is too long") - override type WrappedType = Any - override val typeCode = STypeVar.TypeCode - override def toString = name - override def toTermString: String = name -} -object STypeVar { - val TypeCode: TypeCode = 103: Byte - implicit def liftString(n: String): STypeVar = STypeVar(n) - - /** Immutable empty array, can be used to avoid repeated allocations. */ - val EmptyArray = Array.empty[STypeVar] - - /** Immutable empty IndexedSeq, can be used to avoid repeated allocations. */ - val EmptySeq: IndexedSeq[STypeVar] = EmptyArray -} - -/** Type descriptor of `Box` type of ErgoTree. */ -case object SBox extends SProduct with SPredefType with SMonoType { - import ErgoBox._ - override type WrappedType = Box - override val typeCode: TypeCode = 99: Byte - override val reprClass: RClass[_] = RClass(classOf[Box]) - override def typeId = typeCode - - import SType.{tT, paramT} - - /** Defined once here and then reused in SMethod descriptors. */ - lazy val GetRegFuncType = SFunc(Array(SBox), SOption(tT), Array(paramT)) - - /** Creates a descriptor for the given register method. (i.e. R1, R2, etc) */ - def registers(idOfs: Int): Seq[SMethod] = { - allRegisters.map { i => - i match { - case r: MandatoryRegisterId => - SMethod(this, s"R${i.asIndex}", - GetRegFuncType, (idOfs + i.asIndex + 1).toByte, ExtractRegisterAs.costKind) - .withInfo(ExtractRegisterAs, r.purpose) - case _ => - SMethod(this, s"R${i.asIndex}", - GetRegFuncType, (idOfs + i.asIndex + 1).toByte, ExtractRegisterAs.costKind) - .withInfo(ExtractRegisterAs, "Non-mandatory register") - } - } - } - - val PropositionBytes = "propositionBytes" - val Value = "value" - val Id = "id" - val Bytes = "bytes" - val BytesWithoutRef = "bytesWithoutRef" - val CreationInfo = "creationInfo" - val GetReg = "getReg" - - // should be lazy, otherwise lead to initialization error - lazy val ValueMethod = SMethod( - this, Value, SFunc(SBox, SLong), 1, ExtractAmount.costKind) - .withInfo(ExtractAmount, - "Mandatory: Monetary value, in Ergo tokens (NanoErg unit of measure)") - - lazy val PropositionBytesMethod = SMethod( - this, PropositionBytes, SFunc(SBox, SByteArray), 2, ExtractScriptBytes.costKind) - .withInfo(ExtractScriptBytes, - "Serialized bytes of guarding script, which should be evaluated to true in order to\n" + - " open this box. (aka spend it in a transaction)") - - lazy val BytesMethod = SMethod( - this, Bytes, SFunc(SBox, SByteArray), 3, ExtractBytes.costKind) - .withInfo(ExtractBytes, "Serialized bytes of this box's content, including proposition bytes.") - - lazy val BytesWithoutRefMethod = SMethod( - this, BytesWithoutRef, SFunc(SBox, SByteArray), 4, ExtractBytesWithNoRef.costKind) - .withInfo(ExtractBytesWithNoRef, - "Serialized bytes of this box's content, excluding transactionId and index of output.") - - lazy val IdMethod = SMethod(this, Id, SFunc(SBox, SByteArray), 5, ExtractId.costKind) - .withInfo(ExtractId, - "Blake2b256 hash of this box's content, basically equals to \\lst{blake2b256(bytes)}") - - lazy val creationInfoMethod = SMethod( - this, CreationInfo, ExtractCreationInfo.OpType, 6, ExtractCreationInfo.costKind) - .withInfo(ExtractCreationInfo, - """ If \lst{tx} is a transaction which generated this box, then \lst{creationInfo._1} - | is a height of the tx's block. The \lst{creationInfo._2} is a serialized transaction - | identifier followed by box index in the transaction outputs. - """.stripMargin ) // see ExtractCreationInfo - - lazy val getRegMethod = SMethod(this, "getReg", - SFunc(Array(SBox, SInt), SOption(tT), Array(paramT)), 7, ExtractRegisterAs.costKind) - .withInfo(ExtractRegisterAs, - """ Extracts register by id and type. - | Type param \lst{T} expected type of the register. - | Returns \lst{Some(value)} if the register is defined and has given type and \lst{None} otherwise - """.stripMargin, - ArgInfo("regId", "zero-based identifier of the register.")) - - lazy val tokensMethod = SMethod( - this, "tokens", SFunc(SBox, ErgoBox.STokensRegType), 8, FixedCost(JitCost(15))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, "Secondary tokens") - - - // should be lazy to solve recursive initialization - protected override def getMethods() = super.getMethods() ++ Array( - ValueMethod, // see ExtractAmount - PropositionBytesMethod, // see ExtractScriptBytes - BytesMethod, // see ExtractBytes - BytesWithoutRefMethod, // see ExtractBytesWithNoRef - IdMethod, // see ExtractId - creationInfoMethod, - getRegMethod, - tokensMethod - ) ++ registers(8) -} - -/** Type descriptor of `AvlTree` type of ErgoTree. */ -case object SAvlTree extends SProduct with SPredefType with SMonoType { - override type WrappedType = AvlTree - override val typeCode: TypeCode = 100: Byte - override val reprClass: RClass[_] = RClass(classOf[AvlTree]) - override def typeId = typeCode - - import SOption._ - lazy val TCollOptionCollByte = SCollection(SByteArrayOption) - lazy val CollKeyValue = SCollection(STuple(SByteArray, SByteArray)) - - type KeyValueColl = Coll[(Coll[Byte], Coll[Byte])] - - lazy val digestMethod = SMethod(this, "digest", SFunc(this, SByteArray), 1, FixedCost(JitCost(15))) - .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) - - /** Cost descriptor of `digest` method. */ - lazy val digest_Info = { - val m = digestMethod - OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) - } - - lazy val enabledOperationsMethod = SMethod( - this, "enabledOperations", SFunc(this, SByte), 2, FixedCost(JitCost(15))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ Flags of enabled operations packed in single byte. - | \lst{isInsertAllowed == (enabledOperations & 0x01) != 0}\newline - | \lst{isUpdateAllowed == (enabledOperations & 0x02) != 0}\newline - | \lst{isRemoveAllowed == (enabledOperations & 0x04) != 0} - """.stripMargin) - - lazy val keyLengthMethod = SMethod( - this, "keyLength", SFunc(this, SInt), 3, FixedCost(JitCost(15))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ - | - """.stripMargin) - - lazy val valueLengthOptMethod = SMethod( - this, "valueLengthOpt", SFunc(this, SIntOption), 4, FixedCost(JitCost(15))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ - | - """.stripMargin) - - lazy val isInsertAllowedMethod = SMethod( - this, "isInsertAllowed", SFunc(this, SBoolean), 5, FixedCost(JitCost(15))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ - | - """.stripMargin) - - lazy val isInsertAllowed_Info = { - val m = isInsertAllowedMethod - OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) - } - - lazy val isUpdateAllowedMethod = SMethod( - this, "isUpdateAllowed", SFunc(this, SBoolean), 6, FixedCost(JitCost(15))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ - | - """.stripMargin) - - lazy val isUpdateAllowed_Info = { - val m = isUpdateAllowedMethod - OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) - } - - lazy val isRemoveAllowedMethod = SMethod( - this, "isRemoveAllowed", SFunc(this, SBoolean), 7, FixedCost(JitCost(15))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(PropertyCall, - """ - | - """.stripMargin) - - lazy val isRemoveAllowed_Info = { - val m = isRemoveAllowedMethod - OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) - } - - lazy val updateOperationsMethod = SMethod(this, "updateOperations", - SFunc(Array(SAvlTree, SByte), SAvlTree), 8, FixedCost(JitCost(45))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | - """.stripMargin) - - lazy val containsMethod = SMethod(this, "contains", - SFunc(Array(SAvlTree, SByteArray, SByteArray), SBoolean), 9, DynamicCost) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | /** Checks if an entry with key `key` exists in this tree using proof `proof`. - | * Throws exception if proof is incorrect - | - | * @note CAUTION! Does not support multiple keys check, use [[getMany]] instead. - | * Return `true` if a leaf with the key `key` exists - | * Return `false` if leaf with provided key does not exist. - | * @param key a key of an element of this authenticated dictionary. - | * @param proof - | */ - | - """.stripMargin) - - /** The proof may contain keys, labels and values, we don't know for sure how many, - * but we assume the cost is O(proof.length). - * So the following is an approximation of the proof parsing cost. - */ - final val CreateAvlVerifier_Info = OperationCostInfo( - PerItemCost(baseCost = JitCost(110), perChunkCost = JitCost(20), chunkSize = 64), - NamedDesc("CreateAvlVerifier")) - - final val LookupAvlTree_Info = OperationCostInfo( - PerItemCost(baseCost = JitCost(40), perChunkCost = JitCost(10), chunkSize = 1), - NamedDesc("LookupAvlTree")) - - final val InsertIntoAvlTree_Info = OperationCostInfo( - PerItemCost(baseCost = JitCost(40), perChunkCost = JitCost(10), chunkSize = 1), - NamedDesc("InsertIntoAvlTree")) - - final val UpdateAvlTree_Info = OperationCostInfo( - PerItemCost(baseCost = JitCost(120), perChunkCost = JitCost(20), chunkSize = 1), - NamedDesc("UpdateAvlTree")) - - final val RemoveAvlTree_Info = OperationCostInfo( - PerItemCost(baseCost = JitCost(100), perChunkCost = JitCost(15), chunkSize = 1), - NamedDesc("RemoveAvlTree")) - - /** Creates [[AvlTreeVerifier]] for the given tree and proof. */ - def createVerifier(tree: AvlTree, proof: Coll[Byte])(implicit E: ErgoTreeEvaluator) = { - // the cost of tree reconstruction from proof is O(proof.length) - E.addSeqCost(CreateAvlVerifier_Info, proof.length) { () => - AvlTreeVerifier(tree, proof) - } - } - - /** Implements evaluation of AvlTree.contains method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def contains_eval(mc: MethodCall, tree: AvlTree, key: Coll[Byte], proof: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Boolean = { - val bv = createVerifier(tree, proof) - val nItems = bv.treeHeight - - var res = false - // the cost of tree lookup is O(bv.treeHeight) - E.addSeqCost(LookupAvlTree_Info, nItems) { () => - res = bv.performOneOperation(Lookup(ADKey @@ key.toArray)) match { - case Success(r) => r match { - case Some(_) => true - case _ => false - } - case Failure(_) => false - } - } - res - } - - lazy val getMethod = SMethod(this, "get", - SFunc(Array(SAvlTree, SByteArray, SByteArray), SByteArrayOption), 10, DynamicCost) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | /** Perform a lookup of key `key` in this tree using proof `proof`. - | * Throws exception if proof is incorrect - | * - | * @note CAUTION! Does not support multiple keys check, use [[getMany]] instead. - | * Return Some(bytes) of leaf with key `key` if it exists - | * Return None if leaf with provided key does not exist. - | * @param key a key of an element of this authenticated dictionary. - | * @param proof - | */ - | - """.stripMargin) - - /** Implements evaluation of AvlTree.get method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def get_eval(mc: MethodCall, tree: AvlTree, key: Coll[Byte], proof: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Option[Coll[Byte]] = { - val bv = createVerifier(tree, proof) - val nItems = bv.treeHeight - - // the cost of tree lookup is O(bv.treeHeight) - E.addSeqCost(LookupAvlTree_Info, nItems) { () => - bv.performOneOperation(Lookup(ADKey @@ key.toArray)) match { - case Success(r) => r match { - case Some(v) => Some(Colls.fromArray(v)) - case _ => None - } - case Failure(_) => Interpreter.error(s"Tree proof is incorrect $tree") - } - } - } - - lazy val getManyMethod = SMethod(this, "getMany", - SFunc(Array(SAvlTree, SByteArray2, SByteArray), TCollOptionCollByte), 11, DynamicCost) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | /** Perform a lookup of many keys `keys` in this tree using proof `proof`. - | * - | * @note CAUTION! Keys must be ordered the same way they were in lookup before proof was generated. - | * For each key return Some(bytes) of leaf if it exists and None if is doesn't. - | * @param keys keys of elements of this authenticated dictionary. - | * @param proof - | */ - | - """.stripMargin) - - /** Implements evaluation of AvlTree.getMany method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def getMany_eval(mc: MethodCall, tree: AvlTree, keys: Coll[Coll[Byte]], proof: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Coll[Option[Coll[Byte]]] = { - val bv = createVerifier(tree, proof) - val nItems = bv.treeHeight - keys.map { key => - // the cost of tree lookup is O(bv.treeHeight) - E.addSeqCost(LookupAvlTree_Info, nItems) { () => - bv.performOneOperation(Lookup(ADKey @@ key.toArray)) match { - case Success(r) => r match { - case Some(v) => Some(Colls.fromArray(v)) - case _ => None - } - case Failure(_) => Interpreter.error(s"Tree proof is incorrect $tree") - } - } - } - } - - lazy val insertMethod = SMethod(this, "insert", - SFunc(Array(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 12, DynamicCost) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | /** Perform insertions of key-value entries into this tree using proof `proof`. - | * Throws exception if proof is incorrect - | * - | * @note CAUTION! Pairs must be ordered the same way they were in insert ops before proof was generated. - | * Return Some(newTree) if successful - | * Return None if operations were not performed. - | * @param operations collection of key-value pairs to insert in this authenticated dictionary. - | * @param proof - | */ - | - """.stripMargin) - - /** Implements evaluation of AvlTree.insert method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def insert_eval(mc: MethodCall, tree: AvlTree, entries: KeyValueColl, proof: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Option[AvlTree] = { - E.addCost(isInsertAllowed_Info) - if (!tree.isInsertAllowed) { - None - } else { - val bv = createVerifier(tree, proof) - // when the tree is empty we still need to add the insert cost - val nItems = Math.max(bv.treeHeight, 1) - - entries.forall { case (key, value) => - var res = true - // the cost of tree lookup is O(bv.treeHeight) - E.addSeqCost(InsertIntoAvlTree_Info, nItems) { () => - val insert = Insert(ADKey @@ key.toArray, ADValue @@ value.toArray) - val insertRes = bv.performOneOperation(insert) - // TODO v6.0: throwing exception is not consistent with update semantics - // however it preserves v4.0 semantics (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908) - if (insertRes.isFailure) { - Interpreter.error(s"Incorrect insert for $tree (key: $key, value: $value, digest: ${tree.digest}): ${insertRes.failed.get}}") - } - res = insertRes.isSuccess - } - res - } - bv.digest match { - case Some(d) => - E.addCost(updateDigest_Info) - Some(tree.updateDigest(Colls.fromArray(d))) - case _ => None - } - } - } - - lazy val updateMethod = SMethod(this, "update", - SFunc(Array(SAvlTree, CollKeyValue, SByteArray), SAvlTreeOption), 13, DynamicCost) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | /** Perform updates of key-value entries into this tree using proof `proof`. - | * Throws exception if proof is incorrect - | * - | * @note CAUTION! Pairs must be ordered the same way they were in update ops before proof was generated. - | * Return Some(newTree) if successful - | * Return None if operations were not performed. - | * @param operations collection of key-value pairs to update in this authenticated dictionary. - | * @param proof - | */ - | - """.stripMargin) - - /** Implements evaluation of AvlTree.update method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def update_eval(mc: MethodCall, tree: AvlTree, - operations: KeyValueColl, proof: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Option[AvlTree] = { - E.addCost(isUpdateAllowed_Info) - if (!tree.isUpdateAllowed) { - None - } else { - val bv = createVerifier(tree, proof) - // when the tree is empty we still need to add the insert cost - val nItems = Math.max(bv.treeHeight, 1) - - // here we use forall as looping with fast break on first failed tree oparation - operations.forall { case (key, value) => - var res = true - // the cost of tree update is O(bv.treeHeight) - E.addSeqCost(UpdateAvlTree_Info, nItems) { () => - val op = Update(ADKey @@ key.toArray, ADValue @@ value.toArray) - val updateRes = bv.performOneOperation(op) - res = updateRes.isSuccess - } - res - } - bv.digest match { - case Some(d) => - E.addCost(updateDigest_Info) - Some(tree.updateDigest(Colls.fromArray(d))) - case _ => None - } - } - } - - lazy val removeMethod = SMethod(this, "remove", - SFunc(Array(SAvlTree, SByteArray2, SByteArray), SAvlTreeOption), 14, DynamicCost) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | /** Perform removal of entries into this tree using proof `proof`. - | * Throws exception if proof is incorrect - | * Return Some(newTree) if successful - | * Return None if operations were not performed. - | * - | * @note CAUTION! Keys must be ordered the same way they were in remove ops before proof was generated. - | * @param operations collection of keys to remove from this authenticated dictionary. - | * @param proof - | */ - | - """.stripMargin) - - /** Implements evaluation of AvlTree.remove method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod - */ - def remove_eval(mc: MethodCall, tree: AvlTree, - operations: Coll[Coll[Byte]], proof: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Option[AvlTree] = { - E.addCost(isRemoveAllowed_Info) - if (!tree.isRemoveAllowed) { - None - } else { - val bv = createVerifier(tree, proof) - // when the tree is empty we still need to add the insert cost - val nItems = Math.max(bv.treeHeight, 1) - - cfor(0)(_ < operations.length, _ + 1) { i => - E.addSeqCost(RemoveAvlTree_Info, nItems) { () => - val key = operations(i).toArray - bv.performOneOperation(Remove(ADKey @@ key)) - } - } - - E.addCost(digest_Info) - bv.digest match { - case Some(d) => - E.addCost(updateDigest_Info) - Some(tree.updateDigest(Colls.fromArray(d))) - case _ => None - } - } - } - - lazy val updateDigestMethod = SMethod(this, "updateDigest", - SFunc(Array(SAvlTree, SByteArray), SAvlTree), 15, FixedCost(JitCost(40))) - .withIRInfo(MethodCallIrBuilder) - .withInfo(MethodCall, - """ - | - """.stripMargin) - - lazy val updateDigest_Info = { - val m = updateDigestMethod - OperationCostInfo(m.costKind.asInstanceOf[FixedCost], m.opDesc) - } - - protected override def getMethods(): Seq[SMethod] = super.getMethods() ++ Seq( - digestMethod, - enabledOperationsMethod, - keyLengthMethod, - valueLengthOptMethod, - isInsertAllowedMethod, - isUpdateAllowedMethod, - isRemoveAllowedMethod, - updateOperationsMethod, - containsMethod, - getMethod, - getManyMethod, - insertMethod, - updateMethod, - removeMethod, - updateDigestMethod - ) -} - -/** Type descriptor of `Context` type of ErgoTree. */ -case object SContext extends SProduct with SPredefType with SMonoType { - override type WrappedType = Context - override val typeCode: TypeCode = 101: Byte - override def reprClass: RClass[_] = RClass(classOf[Context]) - override def typeId = typeCode - - /** Arguments on context operation such as getVar, DeserializeContext etc. - * This value can be reused where necessary to avoid allocations. */ - val ContextFuncDom: IndexedSeq[SType] = Array(SContext, SByte) - - import SType.{tT, paramT} - - lazy val dataInputsMethod = propertyCall("dataInputs", SBoxArray, 1, FixedCost(JitCost(15))) - lazy val headersMethod = propertyCall("headers", SHeaderArray, 2, FixedCost(JitCost(15))) - lazy val preHeaderMethod = propertyCall("preHeader", SPreHeader, 3, FixedCost(JitCost(15))) - 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 = propertyCall("selfBoxIndex", SInt, 8, FixedCost(JitCost(20))) - lazy val lastBlockUtxoRootHashMethod = property("LastBlockUtxoRootHash", SAvlTree, 9, LastBlockUtxoRootHash) - lazy val minerPubKeyMethod = property("minerPubKey", SByteArray, 10, MinerPubkey) - lazy val getVarMethod = SMethod( - this, "getVar", SFunc(ContextFuncDom, SOption(tT), Array(paramT)), 11, GetVar.costKind) - .withInfo(GetVar, "Get context variable with given \\lst{varId} and type.", - ArgInfo("varId", "\\lst{Byte} identifier of context variable")) - - protected override def getMethods() = super.getMethods() ++ Seq( - dataInputsMethod, headersMethod, preHeaderMethod, inputsMethod, outputsMethod, heightMethod, selfMethod, - selfBoxIndexMethod, lastBlockUtxoRootHashMethod, minerPubKeyMethod, getVarMethod - ) -} - -/** Type descriptor of `Header` type of ErgoTree. */ -case object SHeader extends SProduct with SPredefType with SMonoType { - override type WrappedType = Header - override val typeCode: TypeCode = 104: Byte - override val reprClass: RClass[_] = RClass(classOf[Header]) - override def typeId = typeCode - - lazy val idMethod = propertyCall("id", SByteArray, 1, FixedCost(JitCost(10))) - lazy val versionMethod = propertyCall("version", SByte, 2, FixedCost(JitCost(10))) - lazy val parentIdMethod = propertyCall("parentId", SByteArray, 3, FixedCost(JitCost(10))) - lazy val ADProofsRootMethod = propertyCall("ADProofsRoot", SByteArray, 4, FixedCost(JitCost(10))) - lazy val stateRootMethod = propertyCall("stateRoot", SAvlTree, 5, FixedCost(JitCost(10))) - lazy val transactionsRootMethod = propertyCall("transactionsRoot", SByteArray, 6, FixedCost(JitCost(10))) - lazy val timestampMethod = propertyCall("timestamp", SLong, 7, FixedCost(JitCost(10))) - lazy val nBitsMethod = propertyCall("nBits", SLong, 8, FixedCost(JitCost(10))) - lazy val heightMethod = propertyCall("height", SInt, 9, FixedCost(JitCost(10))) - lazy val extensionRootMethod = propertyCall("extensionRoot", SByteArray, 10, FixedCost(JitCost(10))) - lazy val minerPkMethod = propertyCall("minerPk", SGroupElement, 11, FixedCost(JitCost(10))) - lazy val powOnetimePkMethod = propertyCall("powOnetimePk", SGroupElement, 12, FixedCost(JitCost(10))) - lazy val powNonceMethod = propertyCall("powNonce", SByteArray, 13, FixedCost(JitCost(10))) - lazy val powDistanceMethod = propertyCall("powDistance", SBigInt, 14, FixedCost(JitCost(10))) - lazy val votesMethod = propertyCall("votes", SByteArray, 15, FixedCost(JitCost(10))) - - protected override def getMethods() = super.getMethods() ++ Seq( - idMethod, versionMethod, parentIdMethod, ADProofsRootMethod, stateRootMethod, transactionsRootMethod, - timestampMethod, nBitsMethod, heightMethod, extensionRootMethod, minerPkMethod, powOnetimePkMethod, - powNonceMethod, powDistanceMethod, votesMethod - ) -} - -/** Type descriptor of `PreHeader` type of ErgoTree. */ -case object SPreHeader extends SProduct with SPredefType with SMonoType { - override type WrappedType = PreHeader - override val typeCode: TypeCode = 105: Byte - override val reprClass: RClass[_] = RClass(classOf[PreHeader]) - override def typeId = typeCode - - lazy val versionMethod = propertyCall("version", SByte, 1, FixedCost(JitCost(10))) - lazy val parentIdMethod = propertyCall("parentId", SByteArray, 2, FixedCost(JitCost(10))) - lazy val timestampMethod = propertyCall("timestamp", SLong, 3, FixedCost(JitCost(10))) - lazy val nBitsMethod = propertyCall("nBits", SLong, 4, FixedCost(JitCost(10))) - lazy val heightMethod = propertyCall("height", SInt, 5, FixedCost(JitCost(10))) - lazy val minerPkMethod = propertyCall("minerPk", SGroupElement, 6, FixedCost(JitCost(10))) - lazy val votesMethod = propertyCall("votes", SByteArray, 7, FixedCost(JitCost(10))) - - protected override def getMethods() = super.getMethods() ++ Seq( - versionMethod, parentIdMethod, timestampMethod, nBitsMethod, heightMethod, minerPkMethod, votesMethod - ) -} - -/** This type is introduced to unify handling of global and non-global (i.e. methods) operations. - * It unifies implementation of global operation with implementation of methods and avoids code - * duplication (following DRY principle https://en.wikipedia.org/wiki/Don%27t_repeat_yourself). - * The WrappedType is `sigma.SigmaDslBuilder`, which is an interface implemented by - * the singleton sigmastate.eval.CostingSigmaDslBuilder - * - * The Constant(...) tree node of this type are not allowed, as well as using it in register and - * context variables (aka ContextExtension) - * - * When new methods are added to this type via a soft-fork, they will be serialized as part - * of ErgoTree using MethodCallSerializer, where SGlobal.typeCode will be used. - * - * @see sigmastate.lang.SigmaPredef - * */ -case object SGlobal extends SProduct with SPredefType with SMonoType { - override type WrappedType = SigmaDslBuilder - override val typeCode: TypeCode = 106: Byte - override val reprClass: RClass[_] = RClass(classOf[SigmaDslBuilder]) - override def typeId = typeCode - - import SType.tT - - lazy val groupGeneratorMethod = SMethod( - this, "groupGenerator", SFunc(this, SGroupElement), 1, GroupGenerator.costKind) - .withIRInfo({ case (builder, obj, method, args, tparamSubst) => GroupGenerator }) - .withInfo(GroupGenerator, "") - - lazy val xorMethod = SMethod( - this, "xor", SFunc(Array(this, SByteArray, SByteArray), SByteArray), 2, Xor.costKind) - .withIRInfo({ - case (_, _, _, Seq(l, r), _) => Xor(l.asByteArray, r.asByteArray) - }) - .withInfo(Xor, "Byte-wise XOR of two collections of bytes", - ArgInfo("left", "left operand"), ArgInfo("right", "right operand")) - - /** Implements evaluation of Global.xor method call ErgoTree node. - * Called via reflection based on naming convention. - * @see SMethod.evalMethod, Xor.eval, Xor.xorWithCosting - */ - def xor_eval(mc: MethodCall, G: SigmaDslBuilder, ls: Coll[Byte], rs: Coll[Byte]) - (implicit E: ErgoTreeEvaluator): Coll[Byte] = { - Xor.xorWithCosting(ls, rs) - } - - protected override def getMethods() = super.getMethods() ++ Seq( - groupGeneratorMethod, - xorMethod - ) -} - diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala b/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala index 9490f76965..54abc40f4e 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala +++ b/interpreter/shared/src/main/scala/sigmastate/utils/Extensions.scala @@ -3,7 +3,8 @@ package sigmastate.utils import org.ergoplatform.ErgoBox.TokenId import scorex.util.{ModifierId, idToBytes} import scorex.utils.{Ints, Longs, Shorts} -import sigmastate.eval.{Digest32Coll, SigmaDsl} +import sigma.data.Digest32Coll +import sigma.eval.SigmaDsl import sigma.{Coll, Colls} object Extensions { @@ -14,10 +15,6 @@ object Extensions { */ def toBytes: Coll[Byte] = SigmaDsl.Colls.fromItems(b) - /** Returns a big-endian representation of this numeric in a collection of Booleans. - * Each boolean corresponds to one bit. - */ - def toBits: Coll[Boolean] = ??? } implicit class ShortOpsForSigma(val x: Short) extends AnyVal { @@ -26,11 +23,6 @@ object Extensions { * byte array {@code {0x12, 0x13}}. */ def toBytes: Coll[Byte] = Colls.fromArray(Shorts.toByteArray(x)) - - /** Returns a big-endian representation of this numeric in a collection of Booleans. - * Each boolean corresponds to one bit. - */ - def toBits: Coll[Boolean] = ??? } implicit class IntOpsForSigma(val x: Int) extends AnyVal { @@ -39,11 +31,6 @@ object Extensions { * byte array {@code {0x12, 0x13, 0x14, 0x15}}. */ def toBytes: Coll[Byte] = Colls.fromArray(Ints.toByteArray(x)) - - /** Returns a big-endian representation of this numeric in a collection of Booleans. - * Each boolean corresponds to one bit. - */ - def toBits: Coll[Boolean] = ??? } implicit class LongOpsForSigma(val x: Long) extends AnyVal { @@ -52,13 +39,6 @@ object Extensions { * byte array {@code {0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19}}. */ def toBytes: Coll[Byte] = Colls.fromArray(Longs.toByteArray(x)) - - /** Returns a big-endian representation of this numeric in a collection of Booleans. - * Each boolean corresponds to one bit. - * - * @since 2.0 - */ - def toBits: Coll[Boolean] = ??? } /** Provides extension methods for `ModifierId` instances. diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala b/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala index e3407c57e8..53419a32b8 100644 --- a/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala +++ b/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala @@ -3,15 +3,10 @@ package sigmastate.utils import debox.cfor import io.circe.Decoder import org.ergoplatform.settings.ErgoAlgos -import sigma.data.{OverloadHack, RType} -import scorex.utils.Ints -import sigma.{Coll, Colls, Environment, GroupElement} -import sigmastate.eval.{CAnyValue, SigmaDsl} -import sigmastate.crypto.CryptoConstants.EcPointType - -import java.util -import java.util.concurrent.locks.Lock -import scala.reflect.ClassTag +import sigma.crypto.EcPointType +import sigma.{Coll, Colls, GroupElement} +import sigma.eval.SigmaDsl + import scala.util.{Either, Failure, Right, Success, Try} object Helpers { @@ -58,45 +53,6 @@ object Helpers { target } - /** Concatenates two arrays into a new resulting array. - * All items of both arrays are copied to the result using System.arraycopy. - */ - def concatArrays[T:ClassTag](arr1: Array[T], arr2: Array[T]): Array[T] = { - val l1 = arr1.length - val l2 = arr2.length - val length: Int = l1 + l2 - val result: Array[T] = new Array[T](length) - System.arraycopy(arr1, 0, result, 0, l1) - System.arraycopy(arr2, 0, result, l1, l2) - result - } - - def castArray[A, B >: A : ClassTag](array: Array[A]): Array[B] = { - val result: Array[B] = new Array[B](array.length) - System.arraycopy(array, 0, result, 0, array.length) - result - } - - def deepHashCode[T](arr: Array[T]): Int = arr match { - case arr: Array[AnyRef] => java.util.Arrays.deepHashCode(arr) - case arr: Array[Byte] => java.util.Arrays.hashCode(arr) - case arr: Array[Short] => java.util.Arrays.hashCode(arr) - case arr: Array[Int] => java.util.Arrays.hashCode(arr) - case arr: Array[Long] => java.util.Arrays.hashCode(arr) - case arr: Array[Char] => java.util.Arrays.hashCode(arr) - case arr: Array[Float] => java.util.Arrays.hashCode(arr) - case arr: Array[Double] => java.util.Arrays.hashCode(arr) - case arr: Array[Boolean] => java.util.Arrays.hashCode(arr) - } - - /** Optimized hashCode for array of bytes when it represents some hash thus it have - * enough randomness and we can use only first 4 bytes. - * @param id result of some hash function - */ - @inline final def safeIdHashCode(id: Array[Byte]): Int = - if (id != null && id.length >= 4) Ints.fromBytes(id(0), id(1), id(2), id(3)) - else java.util.Arrays.hashCode(id) - implicit class TryOps[+A](val source: Try[A]) extends AnyVal { def fold[B](onError: Throwable => B, onSuccess: A => B) = source match { case Success(value) => onSuccess(value) @@ -166,50 +122,5 @@ object Helpers { Colls.fromArray(bytes) } - /** - * Executes the given block with a reentrant mutual exclusion Lock with the same basic - * behavior and semantics as the implicit monitor lock accessed using synchronized - * methods and statements in Java. - * - * Note, using this method has an advantage of having this method in a stack trace in case of - * an exception in the block. - * @param l lock object which should be acquired by the current thread before block can start executing - * @param block block of code which will be executed retaining the lock - * @return the value produced by the block - */ - def withReentrantLock[A](l: Lock)(block: => A): A = { - l.lock() - val res = try - block - finally { - l.unlock() - } - res - } - - /** Encapsulate platform-specific logic of ensuring the value carries its precise type. - * For JVM this is identity function. - * For JS it can transform to AnyValue, if the type is numeric - */ - def ensureTypeCarringValue(v: Any, tT: RType[Any]): Any = - if (Environment.current.isJVM) v - else { // JS - v match { - case _: Byte | _: Short | _: Int => - // this is necessary for JS where Byte, Short, Int have the same runtime class - // and hence we need to pass the type information explicitly - CAnyValue(v)(tT, OverloadHack.overloaded1) - case _ => v - } - } } -object Overloading { - class Overload1 - class Overload2 - class Overload3 - - implicit val overload1: Overload1 = new Overload1 - implicit val overload2: Overload2 = new Overload2 - implicit val overload3: Overload3 = new Overload3 -} diff --git a/interpreter/shared/src/main/scala/sigmastate/utxo/ComplexityTable.scala b/interpreter/shared/src/main/scala/sigmastate/utxo/ComplexityTable.scala deleted file mode 100644 index 2613426257..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/utxo/ComplexityTable.scala +++ /dev/null @@ -1,161 +0,0 @@ -package sigmastate.utxo - -import org.ergoplatform._ -import sigmastate._ -import sigmastate.Values._ -import sigmastate.lang.Terms._ -import sigmastate.serialization.OpCodes.OpCode - -object ComplexityTable { - - val MinimalComplexity = 100 - - val OpCodeComplexity: Map[OpCode, Int] = Seq( - Fold.opCode -> 4034, // count = 122 - MapCollection.opCode -> 2514, // count = 402 - BinAnd.opCode -> 2000, // count = 21858 - BinOr.opCode -> 2000, // count = 9894 - Exists.opCode -> 1997, // count = 4131 - Apply.opCode -> 1592, // count = 327 - Append.opCode -> 1524, // count = 63 - ForAll.opCode -> 1451, // count = 7952 - XorOf.opCode -> 1273, // count = 2 - GroupGenerator.opCode -> 1212, // count = 10 - Filter.opCode -> 849, // count = 1656 - ByteArrayToBigInt.opCode -> 727, // count = 9 - LastBlockUtxoRootHash.opCode -> 726, // count = 3 - ModQ.opCode -> 690, // count = 1 - GetVar.opCode -> 687, // count = 1150 - Xor.opCode -> 632, // count = 9 - Tuple.opCode -> 625, // count = 26 - SubstConstants.opCode -> 621, // count = 131 - CalcSha256.opCode -> 505, // count = 6 - OptionGetOrElse.opCode -> 449, // count = 108 - ConcreteCollectionBooleanConstant.opCode -> 428, // count = 3 - CalcBlake2b256.opCode -> 381, // count = 609 - FuncValue.opCode -> 352, // count = 5687 - OptionIsDefined.opCode -> 343, // count = 58 - Negation.opCode -> 328, // count = 9 - ByteArrayToLong.opCode -> 284, // count = 3 - If.opCode -> 284, // count = 3918 - AtLeast.opCode -> 281, // count = 7540 - ConcreteCollection.opCode -> 279, // count = 7956 - BinXor.opCode -> 277, // count = 19 - OR.opCode -> 274, // count = 837 - Inputs.opCode -> 274, // count = 8961 - ModQArithOp.PlusModQ.opCode -> 272, // count = 1 - ExtractCreationInfo.opCode -> 266, // count = 430 - Exponentiate.opCode -> 253, // count = 7 - OptionGet.opCode -> 238, // count = 29116 - AND.opCode -> 230, // count = 10153 - EQ.opCode -> 227, // count = 33055 - ArithOp.Min.opCode -> 227, // count = 30 - Outputs.opCode -> 215, // count = 29061 - ModQArithOp.MinusModQ.opCode -> 211, // count = 1 - LongToByteArray.opCode -> 209, // count = 15 - SelectField.opCode -> 205, // count = 1217 - ExtractRegisterAs.opCode -> 197, // count = 28059 - ArithOp.Modulo.opCode -> 186, // count = 34 - ExtractId.opCode -> 186, // count = 66 - ArithOp.Max.opCode -> 185, // count = 70 - DecodePoint.opCode -> 184, // count = 133 - SigmaOr.opCode -> 183, // count = 4666 - SigmaAnd.opCode -> 177, // count = 4467 - MultiplyGroup.opCode -> 176, // count = 3 - ByIndex.opCode -> 174, // count = 32408 - ExtractBytes.opCode -> 174, // count = 17 - Downcast.opCode -> 168, // count = 79 - CreateProveDHTuple.opCode -> 164, // count = 27 - SizeOf.opCode -> 151, // count = 4952 - Slice.opCode -> 143, // count = 580 - Self.opCode -> 117, // count = 18395 - ExtractBytesWithNoRef.opCode -> 116, // count = 1129 - MinerPubkey.opCode -> 107, // count = 131 - GT.opCode -> 101, // count = 7137 - ExtractScriptBytes.opCode -> 100, // count = 13780 - ArithOp.Plus.opCode -> 99, // count = 12850 - LE.opCode -> 96, // count = 3549 - NEQ.opCode -> 96, // count = 2079 - GE.opCode -> 95, // count = 10941 - ArithOp.Minus.opCode -> 94, // count = 18200 - ArithOp.Multiply.opCode -> 94, // count = 12955 - Upcast.opCode -> 94, // count = 15608 - SigmaPropBytes.opCode -> 94, // count = 4135 - ArithOp.Division.opCode -> 92, // count = 6809 - LT.opCode -> 87, // count = 8715 - TrueLeaf.opCode -> 86, // count = 6764 - BoolToSigmaProp.opCode -> 84, // count = 11765 - FalseLeaf.opCode -> 80, // count = 4825 - Height.opCode -> 80, // count = 30856 - Constant.opCode -> 80, // count = 251669 - SigmaPropIsProven.opCode -> 78, // count = 20566 - CreateProveDlog.opCode -> 76, // count = 147 - BlockValue.opCode -> 74, // count = 895 - ExtractAmount.opCode -> 74, // count = 14650 - LogicalNot.opCode -> 56, // count = 1420 - Global.opCode -> 7, // count = 3 - ValUse.opCode -> 3, // count = 18771 - Context.opCode -> 1 // count = 72 - ).toMap - - val MethodCallComplexity: Map[(Byte, Byte), Int] = Seq( - (100.toByte, 13.toByte) -> 3911, // count = 1, AvlTree.update - (36.toByte, 7.toByte) -> 2183, // count = 7, SOption.map - (7.toByte, 2.toByte) -> 2107, // count = 2, GroupElement.getEncoded - (100.toByte, 11.toByte) -> 1960, // count = 9, AvlTree.getMany - (12.toByte, 15.toByte) -> 1853, // count = 820, SCollection.flatMap - (36.toByte, 8.toByte) -> 1719, // count = 7, SOption.filter - (12.toByte, 29.toByte) -> 1588, // count = 19, SCollection.zip - (100.toByte, 12.toByte) -> 1460, // count = 8, AvlTree.insert - (100.toByte, 4.toByte) -> 1343, // count = 5, AvlTree.valueLengthOpt - (100.toByte, 10.toByte) -> 1331, // count = 22, AvlTree.get - (100.toByte, 14.toByte) -> 1229, // count = 5, AvlTree.remove - (105.toByte, 1.toByte) -> 1214, // count = 3, PreHeader.version - (104.toByte, 1.toByte) -> 1157, // count = 3, Header.id - (12.toByte, 21.toByte) -> 966, // count = 5, SCollection.updateMany - (101.toByte, 1.toByte) -> 900, // count = 25, Context.dataInputs - (100.toByte, 9.toByte) -> 809, // count = 13, AvlTree.contains - (12.toByte, 26.toByte) -> 788, // count = 6, SCollection.indexOf - (100.toByte, 7.toByte) -> 694, // count = 1, AvlTree.isRemoveAllowed - (100.toByte, 5.toByte) -> 671, // count = 1, AvlTree.isInsertAllowed - (12.toByte, 19.toByte) -> 557, // count = 5, SCollection.patch - (101.toByte, 8.toByte) -> 510, // count = 1, Context.selfBoxIndex - (100.toByte, 2.toByte) -> 452, // count = 3, AvlTree.enabledOperations - (105.toByte, 7.toByte) -> 451, // count = 3, PreHeader.votes - (105.toByte, 6.toByte) -> 447, // count = 3, PreHeader.minerPk - (7.toByte, 5.toByte) -> 444, // count = 1, GroupElement.negate - (104.toByte, 14.toByte) -> 412, // count = 3, Header.powDistance - (100.toByte, 1.toByte) -> 384, // count = 6, AvlTree.digest - (104.toByte, 13.toByte) -> 378, // count = 3, Header.powNonce - (101.toByte, 3.toByte) -> 357, // count = 17, Context.preHeader - (104.toByte, 7.toByte) -> 343, // count = 3, Header.timestamp - (105.toByte, 3.toByte) -> 339, // count = 3, PreHeader.timestamp - (104.toByte, 10.toByte) -> 335, // count = 3, Header.extensionRoot - (12.toByte, 20.toByte) -> 329, // count = 5, SCollection.updated - (100.toByte, 3.toByte) -> 328, // count = 5, AvlTree.keyLength - (105.toByte, 5.toByte) -> 328, // count = 5, PreHeader.height - (104.toByte, 9.toByte) -> 326, // count = 3, Header.height - (105.toByte, 2.toByte) -> 323, // count = 3, PreHeader.parentId - (105.toByte, 4.toByte) -> 319, // count = 3, PreHeader.nBits - (104.toByte, 8.toByte) -> 317, // count = 3, Header.nBits - (104.toByte, 2.toByte) -> 316, // count = 3, Header.version - (101.toByte, 2.toByte) -> 315, // count = 33, Context.headers - (104.toByte, 4.toByte) -> 313, // count = 3, Header.ADProofsRoot - (104.toByte, 12.toByte) -> 303, // count = 3, Header.powOnetimePk - (104.toByte, 11.toByte) -> 303, // count = 3, Header.minerPk - (104.toByte, 15.toByte) -> 302, // count = 3, Header.votes - (104.toByte, 3.toByte) -> 299, // count = 3, Header.parentId - (104.toByte, 6.toByte) -> 296, // count = 3, Header.transactionsRoot - (101.toByte, 4.toByte) -> 293, // count = 4, Context.INPUTS - (100.toByte, 6.toByte) -> 288, // count = 6, AvlTree.isUpdateAllowed - (104.toByte, 5.toByte) -> 284, // count = 3, Header.stateRoot - (101.toByte, 7.toByte) -> 278, // count = 1, Context.SELF - (101.toByte, 5.toByte) -> 276, // count = 1, Context.OUTPUTS - (99.toByte, 8.toByte) -> 269, // count = 163, Box.tokens - (101.toByte, 10.toByte) -> 249, // count = 2, Context.minerPubKey - (12.toByte, 14.toByte) -> 238, // count = 1725, SCollection.indices - (101.toByte, 9.toByte) -> 182, // count = 2, Context.LastBlockUtxoRootHash - (106.toByte, 1.toByte) -> 169, // count = 3, SigmaDslBuilder.groupGenerator - (101.toByte, 6.toByte) -> 146 // count = 1, Context.HEIGHT - ).toMap -} diff --git a/interpreter/shared/src/main/scala/sigmastate/utxo/ComplexityTableStat.scala b/interpreter/shared/src/main/scala/sigmastate/utxo/ComplexityTableStat.scala deleted file mode 100644 index e595fa2b6a..0000000000 --- a/interpreter/shared/src/main/scala/sigmastate/utxo/ComplexityTableStat.scala +++ /dev/null @@ -1,94 +0,0 @@ -package sigmastate.utxo - -import sigmastate.serialization.ValueSerializer.getSerializer -import sigmastate.serialization.OpCodes.OpCode -import sigma.util.Extensions.ByteOps -import sigmastate.SMethod -import sigmastate.serialization.OpCodes - -import scala.collection.mutable - -object ComplexityTableStat { - // NOTE: this class is mutable so better to keep it private - private class StatItem( - /** How many times the operation has been executed */ - var count: Long, - /** Sum of all execution times */ - var sum: Long - ) - - /** Timings of op codes */ - private val opStat = mutable.HashMap[OpCode, StatItem]() - - /** Timings of method calls */ - private val mcStat = mutable.HashMap[(Byte, Byte), StatItem]() - - def addOpTime(op: OpCode, time: Long) = { - opStat.get(op) match { - case Some(item) => - item.count += 1 - item.sum += time - case None => - opStat(op) = new StatItem(1, time) - } - } - - def addMcTime(typeId: Byte, methodId: Byte, time: Long) = { - mcStat.get((typeId, methodId)) match { - case Some(item) => - item.count += 1 - item.sum += time - case None => - mcStat((typeId, methodId)) = new StatItem(1, time) - } - } - - /** Prints the complexity table - * */ - def complexityTableString: String = { - val opCodeLines = opStat.map { case (opCode, item) => - val avgTime = item.sum / item.count - val time = avgTime / 1000 - val ser = getSerializer(opCode) - val opName = ser.opDesc.typeName - (opName, (opCode.toUByte - OpCodes.LastConstantCode).toString, time, item.count.toString) - }.toList.sortBy(_._3)(Ordering[Long].reverse) - - val mcLines = mcStat.map { case (id @ (typeId, methodId), item) => - val avgTime = item.sum / item.count - val time = avgTime / 1000 - val m = SMethod.fromIds(typeId, methodId) - val typeName = m.objType.typeName - (s"$typeName.${m.name}", typeId, methodId, time, item.count.toString) - }.toList.sortBy(r => (r._2,r._3))(Ordering[(Byte,Byte)].reverse) - -// val lines = (("Op", "OpCode", "Avg Time,us", "Count") :: opCodeLines ::: mcLines) -// .map { case (opName, opCode, time, count) => -// s"${opName.padTo(30, ' ')}\t${opCode.padTo(7, ' ')}\t${time.padTo(9, ' ')}\t${count}" -// } -// .mkString("\n") - - val rows = (opCodeLines) - .map { case (opName, opCode, time, count) => - val key = s"$opName.opCode".padTo(30, ' ') - s"$key -> $time, // count = $count " - } - .mkString("\n") - - val mcRows = (mcLines) - .map { case (opName, typeId, methodId, time, count) => - val key = s"($typeId.toByte, $methodId.toByte)".padTo(25, ' ') - s"$key -> $time, // count = $count, $opName " - } - .mkString("\n") - -// val total = opStat.values.foldLeft(0L) { (acc, item) => acc + item.sum } - s""" - |----------- - |$rows - |----------- - |$mcRows - |----------- - """.stripMargin - } -} diff --git a/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala b/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala index 1e30653d9b..b223355c5b 100644 --- a/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala +++ b/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala @@ -1,5 +1,7 @@ package org.ergoplatform.validation +import sigma.validation.SigmaValidationSettings + trait ValidationSpecification { implicit val vs: SigmaValidationSettings = ValidationRules.currentSettings } diff --git a/interpreter/shared/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala b/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala similarity index 95% rename from interpreter/shared/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala rename to interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala index 657ded51ce..acf8e74bd2 100644 --- a/interpreter/shared/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala +++ b/interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala @@ -1,17 +1,15 @@ -package sigmastate.lang +package sigma.ast import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigma.data.{Nullable, RType} -import sigma.{Environment, VersionContext} -import sigmastate.Values._ +import sigma.Extensions.ArrayOps +import sigma.data.{CAnyValue, CAvlTree, CBox, Nullable, RType} +import sigma.eval.SigmaDsl +import sigma.exceptions.ConstraintFailed +import sigma.serialization.OpCodes +import sigma.{Environment, SigmaTestingData, VersionContext} import sigmastate._ -import sigmastate.eval.Extensions.ArrayOps -import sigmastate.eval.{CAnyValue, CAvlTree, CostingBox, SigmaDsl} -import sigmastate.exceptions.ConstraintFailed -import sigmastate.serialization.OpCodes -import sigma.SigmaTestingData import java.math.BigInteger @@ -79,7 +77,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma an[ConstraintFailed] should be thrownBy mkDivide(LongConstant(1), IntConstant(1)) an[ConstraintFailed] should be thrownBy mkModulo(LongConstant(1), IntConstant(1)) } - import Platform.liftToConstant + import sigma.Platform.liftToConstant def testSuccess(v: Any, c: Constant[SType]): Unit = { liftToConstant(v, TransformingSigmaBuilder) shouldBe Nullable(c) @@ -207,7 +205,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma } property("liftToConstant ErgoBox") { - val v = TestData.b2.asInstanceOf[CostingBox].wrappedValue + val v = TestData.b2.asInstanceOf[CBox].wrappedValue val c = BoxConstant(TestData.b2) testSuccess(v, c) // TODO v6.0: ErgoBox should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905) testFailure(Array.fill(10)(v)) diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/AndSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/AndSerializerSpecification.scala similarity index 82% rename from interpreter/shared/src/test/scala/sigmastate/serialization/AndSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/AndSerializerSpecification.scala index bfa922ab69..bc7e678062 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/AndSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/AndSerializerSpecification.scala @@ -1,10 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{BooleanConstant, Constant, IntConstant} -import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.serialization.OpCodes._ +import sigma.ast.{AND, BooleanConstant, Constant, EQ, IntConstant, SBoolean, SCollection, SCollectionType, SInt} +import sigma.serialization.OpCodes._ import scorex.util.encode.ZigZagEncoder.encodeZigZagInt +import sigma.Extensions.ArrayOps +import sigma.ast.syntax.BooleanConstant class AndSerializerSpecification extends TableSerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/AvlTreeSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/AvlTreeSpecification.scala similarity index 89% rename from interpreter/shared/src/test/scala/sigmastate/serialization/AvlTreeSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/AvlTreeSpecification.scala index 8a0b4b9255..1bd3b47983 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/AvlTreeSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/AvlTreeSpecification.scala @@ -1,14 +1,14 @@ -package sigmastate.serialization +package sigma.serialization -import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert} +import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} import sigma.Colls -import sigmastate.Values.AvlTreeConstant -import sigmastate.AvlTreeFlags -import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.eval._ +import sigma.data.{AvlTreeData, AvlTreeFlags} +import sigma.ast.AvlTreeConstant +import sigma.Extensions.ArrayOps +import sigma.eval.SigmaDsl +import sigmastate.eval.Extensions.AvlTreeOps class AvlTreeSpecification extends SerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/BlockSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/BlockSerializerSpecification.scala similarity index 87% rename from interpreter/shared/src/test/scala/sigmastate/serialization/BlockSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/BlockSerializerSpecification.scala index c07081b816..4a242b6fb8 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/BlockSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/BlockSerializerSpecification.scala @@ -1,9 +1,8 @@ -package sigmastate.serialization +package sigma.serialization import org.scalacheck.Gen -import sigmastate.SType -import sigmastate.Values.Constant -import sigmastate.lang.{SigmaBuilder, DeserializationSigmaBuilder} +import sigma.ast.{DeserializationSigmaBuilder, SType, SigmaBuilder} +import sigma.ast.Constant class BlockSerializerSpecification extends SerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/ConcreteCollectionSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/ConcreteCollectionSerializerSpecification.scala similarity index 84% rename from interpreter/shared/src/test/scala/sigmastate/serialization/ConcreteCollectionSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/ConcreteCollectionSerializerSpecification.scala index a0812597e5..0c795ea4f4 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/ConcreteCollectionSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/ConcreteCollectionSerializerSpecification.scala @@ -1,10 +1,9 @@ -package sigmastate.serialization - -import sigmastate.Values.{FalseLeaf, Constant, TrueLeaf, IntConstant, TaggedInt, ConcreteCollection} -import sigmastate._ -import sigmastate.eval.Evaluation -import sigmastate.lang.Terms._ +package sigma.serialization +import sigma.Evaluation +import sigma.ast.syntax.{IntConstant, OptionValueOps} +import sigma.ast._ +import sigma.ast.syntax._ import scala.util.Random class ConcreteCollectionSerializerSpecification extends TableSerializationSpecification { @@ -37,8 +36,8 @@ class ConcreteCollectionSerializerSpecification extends TableSerializationSpecif } property("ConcreteCollection: Serializer round trip with different types seq") { - forAll { (i: IntConstant, ti: TaggedInt) => - val seq = Random.shuffle(Seq(i.asIntValue, ti.asIntValue)).toArray + forAll(intConstGen, getVarIntGen) { (i: IntConstant, ti: GetVar[SInt.type]) => + val seq = Random.shuffle(Seq(i.asIntValue, ti.get)).toArray roundTripTest(ConcreteCollection.fromSeq(seq)) } } diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/ConstantSerializerSpecification.scala similarity index 88% rename from interpreter/shared/src/test/scala/sigmastate/serialization/ConstantSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/ConstantSerializerSpecification.scala index 04b016376a..7bc73643f2 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/ConstantSerializerSpecification.scala @@ -1,23 +1,22 @@ -package sigmastate.serialization +package sigma.serialization import java.math.BigInteger import org.ergoplatform._ import org.scalacheck.Arbitrary._ -import sigma.data.RType -import sigmastate.SCollection.SByteArray -import sigmastate.Values.{BigIntConstant, ByteArrayConstant, Constant, FalseLeaf, GroupGenerator, LongConstant, SValue, TrueLeaf} -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate._ +import sigma.data.{RType, SigmaBoolean, TupleColl} +import sigma.ast.SCollection.SByteArray +import sigma.ast.{BigIntConstant, ByteArrayConstant, Constant, DeserializationSigmaBuilder, FalseLeaf, GroupGenerator, LongConstant, TrueLeaf} import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigmastate.Values._ -import sigmastate.eval.Evaluation -import sigma.{AvlTree, Colls} -import SType.AnyOps +import sigma.Extensions.ArrayOps +import sigma.ast._ +import sigma.{AvlTree, Colls, Evaluation} +import sigma.ast.SType.AnyOps import scorex.util.encode.Base16 -import sigmastate.exceptions.SerializerException -import sigmastate.lang.DeserializationSigmaBuilder - +import sigma.ast.BoolArrayConstant.BoolArrayTypeCode +import sigma.ast.ByteArrayConstant.ByteArrayTypeCode +import sigma.ast.syntax.{BoolValue, SValue} +import sigma.crypto.EcPointType +import sigma.util.Extensions.{BigIntegerOps, EcpOps, SigmaBooleanOps} import scala.annotation.nowarn class ConstantSerializerSpecification extends TableSerializationSpecification { @@ -67,9 +66,9 @@ class ConstantSerializerSpecification extends TableSerializationSpecification { forAll { x: Boolean => roundTripTest(BooleanConstant.fromBoolean(x)) } forAll { x: Long => roundTripTest(Constant[SLong.type](x, SLong)) } forAll { x: String => roundTripTest(Constant[SString.type](x, SString)) } - forAll { x: BigInteger => roundTripTest(Constant[SBigInt.type](x, SBigInt)) } - forAll { x: EcPointType => roundTripTest(Constant[SGroupElement.type](x, SGroupElement)) } - forAll { x: SigmaBoolean => roundTripTest(Constant[SSigmaProp.type](x, SSigmaProp)) } + forAll { x: BigInteger => roundTripTest(Constant[SBigInt.type](x.toBigInt, SBigInt)) } + forAll { x: EcPointType => roundTripTest(Constant[SGroupElement.type](x.toGroupElement, SGroupElement)) } + forAll { x: SigmaBoolean => roundTripTest(Constant[SSigmaProp.type](x.toSigmaProp, SSigmaProp)) } forAll { x: ErgoBox => roundTripTest(Constant[SBox.type](x, SBox)) } forAll { x: AvlTree => roundTripTest(Constant[SAvlTree.type](x, SAvlTree)) } forAll { x: Array[Byte] => roundTripTest(Constant[SByteArray](x.toColl, SByteArray)) } diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantStoreSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/ConstantStoreSpecification.scala similarity index 88% rename from interpreter/shared/src/test/scala/sigmastate/serialization/ConstantStoreSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/ConstantStoreSpecification.scala index 70764b5e1f..f8ebeb34a6 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantStoreSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/ConstantStoreSpecification.scala @@ -1,8 +1,7 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{Constant, IntConstant} -import sigmastate._ -import sigmastate.lang.{SigmaBuilder, DeserializationSigmaBuilder} +import sigma.ast.{DeserializationSigmaBuilder, SType, SigmaBuilder} +import sigma.ast.{Constant, IntConstant} class ConstantStoreSpecification extends SerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/DataSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala similarity index 79% rename from interpreter/shared/src/test/scala/sigmastate/serialization/DataSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala index c02df973dc..ecb2d2ef70 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/DataSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala @@ -1,24 +1,26 @@ -package sigmastate.serialization +package sigma.serialization import java.math.BigInteger import org.ergoplatform.ErgoBox import org.scalacheck.Arbitrary._ -import sigma.data.RType -import sigmastate.SCollection.SByteArray -import sigmastate.Values.{ErgoTree, SigmaBoolean} +import sigma.data.{DataValueComparer, RType, SigmaBoolean, TupleColl} +import sigma.ast.SCollection.SByteArray import sigmastate._ -import sigmastate.eval.Evaluation import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigmastate.crypto.CryptoConstants.EcPointType -import sigma.{AvlTree, Colls} -import SType.AnyOps -import sigmastate.exceptions.SerializerException -import sigmastate.interpreter.{CostAccumulator, ErgoTreeEvaluator} -import sigmastate.interpreter.ErgoTreeEvaluator.DefaultProfiler +import sigma.{AvlTree, Colls, Evaluation} +import sigma.ast.SType.AnyOps +import sigma.ast._ +import org.scalacheck.Gen +import sigma.Extensions.ArrayOps +import sigma.crypto.EcPointType +import sigma.eval.SigmaDsl +import sigma.util.Extensions.{BigIntegerOps, EcpOps, SigmaBooleanOps} +import sigmastate.interpreter.{CostAccumulator, CErgoTreeEvaluator} +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultProfiler import sigmastate.utils.Helpers import scala.annotation.nowarn +import scala.reflect.ClassTag class DataSerializerSpecification extends SerializationSpecification { @@ -30,11 +32,11 @@ class DataSerializerSpecification extends SerializationSpecification { val res = DataSerializer.deserialize(tpe, r) res shouldBe obj - val es = ErgoTreeEvaluator.DefaultEvalSettings + val es = CErgoTreeEvaluator.DefaultEvalSettings val accumulator = new CostAccumulator( initialCost = JitCost(0), costLimit = Some(JitCost.fromBlockCost(es.scriptCostLimitInEvaluator))) - val evaluator = new ErgoTreeEvaluator( + val evaluator = new CErgoTreeEvaluator( context = null, constants = ErgoTree.EmptyConstants, coster = accumulator, DefaultProfiler, es) @@ -76,9 +78,10 @@ class DataSerializerSpecification extends SerializationSpecification { } def testTuples[T <: SType](tpe: T) = { - implicit val wWrapped = wrappedTypeGen(tpe) - @nowarn implicit val tag = tpe.classTag[T#WrappedType] - implicit val tAny: RType[Any] = sigma.AnyType + implicit val wWrapped: Gen[T#WrappedType] = wrappedTypeGen(tpe) + val tT = Evaluation.stypeToRType(tpe) + @nowarn implicit val tag: ClassTag[T#WrappedType] = tT.classTag + implicit val tAny : RType[Any] = sigma.AnyType forAll { in: (T#WrappedType, T#WrappedType) => val (x,y) = (in._1, in._2) roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe)) @@ -95,9 +98,9 @@ class DataSerializerSpecification extends SerializationSpecification { forAll { x: Boolean => roundtrip[SBoolean.type](x, SBoolean) } forAll { x: Long => roundtrip[SLong.type](x, SLong) } forAll { x: String => roundtrip[SString.type](x, SString) } - forAll { x: BigInteger => roundtrip[SBigInt.type](x, SBigInt) } - forAll { x: EcPointType => roundtrip[SGroupElement.type](x, SGroupElement) } - forAll { x: SigmaBoolean => roundtrip[SSigmaProp.type](x, SSigmaProp) } + forAll { x: BigInteger => roundtrip[SBigInt.type](x.toBigInt, SBigInt) } + forAll { x: EcPointType => roundtrip[SGroupElement.type](x.toGroupElement, SGroupElement) } + forAll { x: SigmaBoolean => roundtrip[SSigmaProp.type](x.toSigmaProp, SSigmaProp) } forAll { x: ErgoBox => roundtrip[SBox.type](x, SBox) } forAll { x: AvlTree => roundtrip[SAvlTree.type](x, SAvlTree) } forAll { x: Array[Byte] => roundtrip[SByteArray](x.toColl, SByteArray) } diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/GroupElementSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/GroupElementSerializerSpecification.scala similarity index 76% rename from interpreter/shared/src/test/scala/sigmastate/serialization/GroupElementSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/GroupElementSerializerSpecification.scala index 04c4a8e128..aaaeee2725 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/GroupElementSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/GroupElementSerializerSpecification.scala @@ -1,8 +1,7 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.crypto.CryptoConstants -import sigmastate.crypto.CryptoFacade -import sigmastate.eval._ +import sigma.crypto.{CryptoConstants, CryptoFacade} +import sigma.util.Extensions.{EcpOps, GroupElementOps} class GroupElementSerializerSpecification extends SerializationSpecification { @@ -13,18 +12,18 @@ class GroupElementSerializerSpecification extends SerializationSpecification { val bytes = GroupElementSerializer.toBytes(identity) bytes.length shouldBe CryptoConstants.EncodedGroupElementLength bytes.forall(_ == 0) shouldBe true - GroupElementSerializer.parse(SigmaSerializer.startReader(bytes, 0)).isIdentity shouldBe true + GroupElementSerializer.parse(SigmaSerializer.startReader(bytes, 0)).toGroupElement.isIdentity shouldBe true } property("point roundtrip") { forAll(groupElementConstGen){ge => - val bytes = GroupElementSerializer.toBytes(ge.value) + val bytes = GroupElementSerializer.toBytes(ge.value.toECPoint) bytes.length shouldBe CryptoConstants.EncodedGroupElementLength val restored = GroupElementSerializer.parse(SigmaSerializer.startReader(bytes, 0)) CryptoFacade.getAffineXCoord(CryptoFacade.normalizePoint(restored)) shouldBe - CryptoFacade.getAffineXCoord(CryptoFacade.normalizePoint(ge.value)) + CryptoFacade.getAffineXCoord(CryptoFacade.normalizePoint(ge.value.toECPoint)) CryptoFacade.getAffineYCoord(CryptoFacade.normalizePoint(restored)) shouldBe - CryptoFacade.getAffineYCoord(CryptoFacade.normalizePoint(ge.value)) + CryptoFacade.getAffineYCoord(CryptoFacade.normalizePoint(ge.value.toECPoint)) } } } diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala similarity index 53% rename from interpreter/shared/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala index e84f2f64a4..ac9c997d98 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/MethodCallSerializerSpecification.scala @@ -1,16 +1,12 @@ -package sigmastate.serialization +package sigma.serialization -import org.ergoplatform.Outputs -import sigmastate.Values.{FuncValue, ValUse} -import sigmastate.lang.Terms.MethodCall -import sigmastate.utxo.ExtractScriptBytes -import sigmastate._ +import sigma.ast._ class MethodCallSerializerSpecification extends SerializationSpecification { property("MethodCall deserialization round trip") { val expr = MethodCall(Outputs, - SCollection.FlatMapMethod.withConcreteTypes(Map(SCollection.tIV -> SBox, SCollection.tOV -> SByte)), + SCollectionMethods.FlatMapMethod.withConcreteTypes(Map(SCollection.tIV -> SBox, SCollection.tOV -> SByte)), Vector(FuncValue(1, SBox, ExtractScriptBytes(ValUse(1, SBox)))), Map() ) @@ -19,7 +15,7 @@ class MethodCallSerializerSpecification extends SerializationSpecification { property("MethodCall deserialization round trip (non-generic method)") { val expr = MethodCall(Outputs, - SCollection.SizeMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SCollectionMethods.SizeMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), Vector(), Map() ) diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/ModQSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/ModQSerializerSpecification.scala similarity index 75% rename from interpreter/shared/src/test/scala/sigmastate/serialization/ModQSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/ModQSerializerSpecification.scala index cfeb881fc6..13cb1b07a2 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/ModQSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/ModQSerializerSpecification.scala @@ -1,7 +1,7 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.BigIntConstant -import sigmastate._ +import sigma.ast.ModQ +import sigma.ast.syntax.BigIntConstant class ModQSerializerSpecification extends SerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/OrSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/OrSerializerSpecification.scala similarity index 82% rename from interpreter/shared/src/test/scala/sigmastate/serialization/OrSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/OrSerializerSpecification.scala index b7fbf54c66..ad61df8a71 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/OrSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/OrSerializerSpecification.scala @@ -1,10 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{BooleanConstant, Constant, IntConstant} -import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.serialization.OpCodes._ +import sigma.ast.{BooleanConstant, Constant, EQ, IntConstant, OR, SBoolean, SCollection, SCollectionType, SInt} +import sigma.serialization.OpCodes._ import scorex.util.encode.ZigZagEncoder.encodeZigZagInt +import sigma.Extensions.ArrayOps +import sigma.ast.syntax.BooleanConstant class OrSerializerSpecification extends TableSerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/PDHTSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/PDHTSerializerSpecification.scala similarity index 83% rename from interpreter/shared/src/test/scala/sigmastate/serialization/PDHTSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/PDHTSerializerSpecification.scala index dbca6fb190..bc57decbf8 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/PDHTSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/PDHTSerializerSpecification.scala @@ -1,12 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.crypto.ProveDHTuple +import sigma.data.ProveDHTuple +import sigma.eval.Extensions.SigmaBooleanOps class PDHTSerializerSpecification extends SerializationSpecification { property("ProveDiffieHellmanTupleSerializer: Serializer round trip") { forAll { i: ProveDHTuple => - roundTripTest(i.toSigmaProp) + roundTripTest(i.toSigmaPropValue) } // In IntelliJ IDEA this test is executed last, at this point all statistics has been collected diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/ProveDlogSerializerSpec.scala b/interpreter/shared/src/test/scala/sigma/serialization/ProveDlogSerializerSpec.scala similarity index 53% rename from interpreter/shared/src/test/scala/sigmastate/serialization/ProveDlogSerializerSpec.scala rename to interpreter/shared/src/test/scala/sigma/serialization/ProveDlogSerializerSpec.scala index df791f248a..cce194b3ce 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/ProveDlogSerializerSpec.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/ProveDlogSerializerSpec.scala @@ -1,12 +1,13 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.crypto.DLogProtocol.ProveDlog +import sigma.data.ProveDlog +import sigma.eval.Extensions.SigmaBooleanOps class ProveDlogSerializerSpec extends SerializationSpecification { property("ProveDlog: Serializer round trip") { forAll { pd: ProveDlog => - roundTripTest(pd.toSigmaProp) + roundTripTest(pd.toSigmaPropValue) } } diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/RelationsSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/RelationsSpecification.scala similarity index 92% rename from interpreter/shared/src/test/scala/sigmastate/serialization/RelationsSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/RelationsSpecification.scala index fc9d18330f..44bdabebf8 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/RelationsSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/RelationsSpecification.scala @@ -1,10 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values._ -import sigmastate._ -import sigmastate.serialization.OpCodes._ -import sigmastate.serialization.ValueSerializer._ +import sigma.ast._ +import sigma.serialization.OpCodes._ +import sigma.serialization.ValueSerializer._ import scorex.util.encode.ZigZagEncoder.encodeZigZagLong +import sigma.ast.{SInt, SLong} class RelationsSpecification extends TableSerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/SelectFieldSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala similarity index 79% rename from interpreter/shared/src/test/scala/sigmastate/serialization/SelectFieldSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala index 678ff1a241..fb3bbcd326 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/SelectFieldSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SelectFieldSerializerSpecification.scala @@ -1,9 +1,9 @@ -package sigmastate.serialization +package sigma.serialization import org.scalacheck.Gen -import sigmastate.Values.{FalseLeaf, IntConstant, Tuple} -import sigmastate.serialization.OpCodes.{SelectFieldCode, TupleCode} -import sigmastate.utxo.SelectField +import sigma.ast.syntax.CollectionOps +import sigma.ast.{FalseLeaf, IntConstant, SelectField, Tuple} +import sigma.serialization.OpCodes.{SelectFieldCode, TupleCode} class SelectFieldSerializerSpecification extends TableSerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/SerializationSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SerializationSpecification.scala similarity index 93% rename from interpreter/shared/src/test/scala/sigmastate/serialization/SerializationSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/SerializationSpecification.scala index 03b8801459..30ae6af19b 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/SerializationSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SerializationSpecification.scala @@ -1,4 +1,4 @@ -package sigmastate.serialization +package sigma.serialization import org.ergoplatform.validation.ValidationSpecification import org.scalacheck.Gen @@ -8,10 +8,10 @@ import org.scalacheck.Arbitrary._ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.{ScalaCheckDrivenPropertyChecks, ScalaCheckPropertyChecks} -import sigmastate.Values._ -import sigmastate.SType +import sigma.ast.SType +import sigma.ast._ import sigmastate.helpers.NegativeTesting -import sigmastate.serialization.generators._ +import sigma.serialization.generators._ trait SerializationSpecification extends AnyPropSpec with ScalaCheckPropertyChecks diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SigSerializerSpecification.scala similarity index 98% rename from interpreter/shared/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/SigSerializerSpecification.scala index 52aaa89070..69d85f16f2 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SigSerializerSpecification.scala @@ -1,19 +1,20 @@ -package sigmastate.serialization +package sigma.serialization import java.math.BigInteger import org.ergoplatform.settings.ErgoAlgos import org.scalacheck.{Arbitrary, Gen} import org.scalatest.Assertion -import sigmastate.Values.SigmaBoolean +import sigma.Extensions.ArrayOps +import sigma.data.{AvlTreeData, CAND, COR, CTHRESHOLD, ProveDHTuple, ProveDlog, SigmaBoolean, TrivialProp} +import sigma.eval.Extensions.SigmaBooleanOps import sigmastate._ -import sigmastate.crypto.DLogProtocol.{ProveDlog, SecondDLogProverMessage} +import sigmastate.crypto.DLogProtocol.SecondDLogProverMessage import sigmastate.crypto.VerifierMessage.Challenge -import sigmastate.crypto.{ProveDHTuple, SecondDHTupleProverMessage} +import sigmastate.crypto.SecondDHTupleProverMessage import sigmastate.crypto.GF2_192_Poly -import sigmastate.eval.Extensions.ArrayOps import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTransactionTesting, TestingCommons} import sigmastate.interpreter.Interpreter -import sigmastate.serialization.generators.ObjectGenerators +import sigma.serialization.generators.ObjectGenerators import sigmastate.utils.Helpers import scala.util.Random @@ -72,7 +73,7 @@ class SigSerializerSpecification extends TestingCommons property("SigSerializer round trip") { forAll(configParams = MinSuccessful(100)) { sb: SigmaBoolean => - val expr = sb.toSigmaProp + val expr = sb.toSigmaPropValue val challenge = Array.fill(32)(Random.nextInt(100).toByte) val ctx = ErgoLikeContextTesting( diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/SubstConstantsSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/SubstConstantsSerializerSpecification.scala similarity index 65% rename from interpreter/shared/src/test/scala/sigmastate/serialization/SubstConstantsSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/SubstConstantsSerializerSpecification.scala index 0a6e73dac1..9c56fe2d48 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/SubstConstantsSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/SubstConstantsSerializerSpecification.scala @@ -1,8 +1,10 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{ConcreteCollection, IntConstant, IntArrayConstant, IntValue} -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.{CrossVersionProps, SInt, EQ, SubstConstants} +import sigma.ast.{EQ, SInt, SubstConstants} +import sigma.ast.syntax.IntValue +import sigma.ast.{ConcreteCollection, IntArrayConstant, IntConstant} +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer +import sigmastate.CrossVersionProps class SubstConstantsSerializerSpecification extends SerializationSpecification with CrossVersionProps { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/TableSerializationSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TableSerializationSpecification.scala similarity index 85% rename from interpreter/shared/src/test/scala/sigmastate/serialization/TableSerializationSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/TableSerializationSpecification.scala index 543be44806..6774d03f15 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/TableSerializationSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TableSerializationSpecification.scala @@ -1,8 +1,8 @@ -package sigmastate.serialization +package sigma.serialization import org.scalatest.prop.TableFor2 -import sigmastate.Values._ -import sigmastate.SType +import sigma.ast.SType +import sigma.ast._ trait TableSerializationSpecification extends SerializationSpecification { def objects: TableFor2[_ <: Value[_ <: SType], Array[Byte]] diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala b/interpreter/shared/src/test/scala/sigma/serialization/TransformersSerializationSpec.scala similarity index 98% rename from interpreter/shared/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala rename to interpreter/shared/src/test/scala/sigma/serialization/TransformersSerializationSpec.scala index 1239495bba..7de1f89512 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TransformersSerializationSpec.scala @@ -1,7 +1,6 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate._ -import sigmastate.utxo._ +import sigma.ast._ class TransformersSerializationSpec extends SerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/TupleSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala similarity index 85% rename from interpreter/shared/src/test/scala/sigmastate/serialization/TupleSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala index c38a6fe94d..e72890cbb7 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/TupleSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TupleSerializerSpecification.scala @@ -1,6 +1,6 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{FalseLeaf, IntConstant, Tuple} +import sigma.ast.{FalseLeaf, IntConstant, Tuple} class TupleSerializerSpecification extends TableSerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/TwoArgumentSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TwoArgumentSerializerSpecification.scala similarity index 89% rename from interpreter/shared/src/test/scala/sigmastate/serialization/TwoArgumentSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/TwoArgumentSerializerSpecification.scala index 8d7a4dfa88..e0c04e33b8 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/TwoArgumentSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TwoArgumentSerializerSpecification.scala @@ -1,12 +1,12 @@ -package sigmastate.serialization +package sigma.serialization -import sigmastate.Values.{BigIntConstant, ByteArrayConstant, GroupElementConstant, LongConstant} -import sigmastate._ -import sigmastate.Values._ -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.utxo.Append -import OpCodes._ import scorex.util.encode.ZigZagEncoder.encodeZigZagLong +import sigma.ast.ByteArrayConstant.ByteArrayTypeCode +import sigma.ast.syntax.{BigIntConstant, GroupElementConstant, LongConstant} +import sigma.ast._ +import sigmastate._ +import sigma.serialization.OpCodes._ +import sigma.serialization.ValueCodes.OpCode class TwoArgumentSerializerSpecification extends TableSerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/TypeSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala similarity index 98% rename from interpreter/shared/src/test/scala/sigmastate/serialization/TypeSerializerSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala index 6521ec55f7..6419faf364 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/TypeSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/TypeSerializerSpecification.scala @@ -1,8 +1,8 @@ -package sigmastate.serialization +package sigma.serialization import org.scalacheck.Arbitrary._ import org.scalatest.Assertion -import sigmastate._ +import sigma.ast._ class TypeSerializerSpecification extends SerializationSpecification { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/UpcastOnDeserializationSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/UpcastOnDeserializationSpecification.scala similarity index 74% rename from interpreter/shared/src/test/scala/sigmastate/serialization/UpcastOnDeserializationSpecification.scala rename to interpreter/shared/src/test/scala/sigma/serialization/UpcastOnDeserializationSpecification.scala index de54b62295..5a2f4dc1eb 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/UpcastOnDeserializationSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/UpcastOnDeserializationSpecification.scala @@ -1,10 +1,6 @@ -package sigmastate.serialization +package sigma.serialization -import org.ergoplatform.Outputs -import sigmastate.Values.{ByteConstant, IntConstant, LongConstant} -import sigmastate.lang.CheckingSigmaBuilder -import sigmastate.utxo.ByIndex -import sigmastate.{SInt, SLong, Upcast} +import sigma.ast._ class UpcastOnDeserializationSpecification extends SerializationSpecification { import CheckingSigmaBuilder._ diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ConcreteCollectionGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/ConcreteCollectionGenerators.scala similarity index 88% rename from interpreter/shared/src/test/scala/sigmastate/serialization/generators/ConcreteCollectionGenerators.scala rename to interpreter/shared/src/test/scala/sigma/serialization/generators/ConcreteCollectionGenerators.scala index 08ae08bd3f..a8d8c8bbf0 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ConcreteCollectionGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/ConcreteCollectionGenerators.scala @@ -1,8 +1,8 @@ -package sigmastate.serialization.generators +package sigma.serialization.generators import org.scalacheck.{Arbitrary, Gen} -import sigmastate._ -import sigmastate.Values.{ConcreteCollection, Value, IntConstant} +import sigma.ast.syntax.IntConstant +import sigma.ast.{ConcreteCollection, SBoolean, SInt, SSigmaProp, SType, Value} trait ConcreteCollectionGenerators { self: ObjectGenerators => val minCollLength = 1 diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala similarity index 93% rename from interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala rename to interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala index 4c57f86246..d4971f88c2 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala @@ -1,29 +1,35 @@ -package sigmastate.serialization.generators +package sigma.serialization.generators import org.ergoplatform.ErgoBox._ -import org.ergoplatform.SigmaConstants.MaxPropositionBytes +import sigma.data.SigmaConstants.MaxPropositionBytes import org.ergoplatform.validation._ import org.ergoplatform._ import org.scalacheck.Arbitrary._ import org.scalacheck.Gen.{choose, frequency} import org.scalacheck.util.Buildable import org.scalacheck.{Arbitrary, Gen} -import sigma.data.RType +import sigma.data._ import scorex.crypto.authds.{ADDigest, ADKey} import scorex.util.encode.{Base58, Base64} import scorex.util.{ModifierId, bytesToId} -import sigmastate.Values._ -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.{CryptoConstants, ProveDHTuple} -import sigmastate.eval.Extensions._ -import sigmastate.eval.{CostingBox, SigmaDsl, _} -import sigmastate.crypto.CryptoConstants.{EcPointType, dlogGroup} -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.lang.TransformingSigmaBuilder._ +import sigma.ast._ +import sigma.ast.syntax._ +import sigmastate.eval._ +import sigma.crypto.CryptoConstants.dlogGroup +import TransformingSigmaBuilder._ import sigmastate._ -import sigmastate.utxo._ import sigma.Coll +import sigma.Extensions.ArrayOps import sigma._ +import sigma.ast.syntax.SigmaPropValue +import sigma.crypto.{CryptoConstants, EcPointType} +import sigma.util.Extensions.EcpOps +import sigma.validation.{ChangedRule, DisabledRule, EnabledRule, ReplacedRule, RuleStatus} +import sigma.validation.ValidationRules.FirstRuleId +import ErgoTree.ZeroHeader +import sigma.eval.Extensions.{EvalIterableOps, SigmaBooleanOps} +import sigma.eval.SigmaDsl +import sigma.interpreter.{ContextExtension, ProverResult} import java.math.BigInteger import scala.collection.compat.immutable.ArraySeq @@ -70,11 +76,9 @@ trait ObjectGenerators extends TypeGenerators implicit lazy val arbBoxConstant: Arbitrary[BoxConstant] = Arbitrary(boxConstantGen) implicit lazy val arbAvlTreeConstant: Arbitrary[AvlTreeConstant] = Arbitrary(avlTreeConstantGen) implicit lazy val arbBigIntConstant: Arbitrary[BigIntConstant] = Arbitrary(bigIntConstGen) - implicit lazy val arbTaggedInt: Arbitrary[TaggedInt] = Arbitrary(taggedVar[SInt.type]) - implicit lazy val arbTaggedLong: Arbitrary[TaggedLong] = Arbitrary(taggedVar[SLong.type]) - implicit lazy val arbTaggedBox: Arbitrary[TaggedBox] = Arbitrary(taggedVar[SBox.type]) - implicit lazy val arbTaggedAvlTree: Arbitrary[TaggedAvlTree] = Arbitrary(taggedAvlTreeGen) - implicit lazy val arbProveDlog: Arbitrary[ProveDlog] = Arbitrary(proveDlogGen) + implicit lazy val arbGetVarBox: Arbitrary[BoxValue] = Arbitrary(getVar[SBox.type]) + implicit lazy val arbGetVarAvlTree : Arbitrary[AvlTreeValue] = Arbitrary(getVarAvlTreeGen) + implicit lazy val arbProveDlog : Arbitrary[ProveDlog] = Arbitrary(proveDlogGen) implicit lazy val arbProveDHT: Arbitrary[ProveDHTuple] = Arbitrary(proveDHTGen) implicit lazy val arbRegisterIdentifier: Arbitrary[RegisterId] = Arbitrary(registerIdentifierGen) implicit lazy val arbBigInteger: Arbitrary[BigInteger] = Arbitrary(Arbitrary.arbBigInt.arbitrary.map(_.bigInteger)) @@ -155,17 +159,17 @@ trait ObjectGenerators extends TypeGenerators lazy val groupElementConstGen: Gen[GroupElementConstant] = for { p <- groupElementGen - } yield mkConstant[SGroupElement.type](p, SGroupElement) + } yield mkConstant[SGroupElement.type](p.toGroupElement, SGroupElement) lazy val constantGen: Gen[Constant[SType]] = Gen.oneOf(booleanConstGen, byteConstGen, shortConstGen, intConstGen, longConstGen, bigIntConstGen, byteArrayConstGen, intArrayConstGen, groupElementConstGen).asInstanceOf[Gen[Constant[SType]]] - def taggedVar[T <: SType](implicit aT: Arbitrary[T]): Gen[TaggedVariable[T]] = for { + def getVar[T <: SType](implicit aT: Arbitrary[T]): Gen[Value[T]] = for { t <- aT.arbitrary id <- arbByte.arbitrary - } yield mkTaggedVariable(id, t) + } yield mkGetVar(id, t).get lazy val proveDlogGen: Gen[ProveDlog] = for {v <- groupElementGen} yield ProveDlog(v) @@ -204,8 +208,8 @@ trait ObjectGenerators extends TypeGenerators lazy val registerIdentifierGen: Gen[RegisterId] = Gen.oneOf(R0, R1, R2, R3, R4, R5, R6, R7, R8, R9) - lazy val taggedAvlTreeGen: Gen[TaggedAvlTree] = - arbByte.arbitrary.map { v => TaggedAvlTree(v).asInstanceOf[TaggedAvlTree] } + lazy val getVarAvlTreeGen: Gen[AvlTreeValue] = + arbByte.arbitrary.map { v => mkGetVar(v, SAvlTree).get } lazy val evaluatedValueGen: Gen[EvaluatedValue[SType]] = Gen.oneOf(booleanConstGen.asInstanceOf[Gen[EvaluatedValue[SType]]], byteArrayConstGen, longConstGen) @@ -321,19 +325,19 @@ trait ObjectGenerators extends TypeGenerators booleanConstGen, bigIntConstGen, groupElementConstGen, - taggedVar[SInt.type], - taggedVar[SLong.type], - taggedVar[SBox.type], - taggedVar(Arbitrary(sTupleGen(2, 10))) + getVar[SInt.type], + getVar[SLong.type], + getVar[SBox.type], + getVar(Arbitrary(sTupleGen(2, 10))) )) } yield mkTuple(values).asInstanceOf[Tuple] lazy val modifierIdGen: Gen[ModifierId] = - arrayOfN(CryptoConstants.hashLength, arbByte.arbitrary) + arrayOfN(sigma.crypto.hashLength, arbByte.arbitrary) .map(bytesToId) lazy val modifierIdBytesGen: Gen[Coll[Byte]] = - collOfN(CryptoConstants.hashLength, arbByte.arbitrary) + collOfN(sigma.crypto.hashLength, arbByte.arbitrary) val MaxTokens = 10 @@ -372,7 +376,7 @@ trait ObjectGenerators extends TypeGenerators creationHeight <- heightGen } yield new ErgoBoxCandidate(l, b, creationHeight, tokens, ar) - lazy val boxConstantGen: Gen[BoxConstant] = ergoBoxGen.map { v => BoxConstant(CostingBox(v)) } + lazy val boxConstantGen: Gen[BoxConstant] = ergoBoxGen.map { v => BoxConstant(CBox(v)) } lazy val digest32Gen: Gen[TokenId] = for { bytes <- collOfN(TokenId.size, arbByte.arbitrary) @@ -477,23 +481,23 @@ trait ObjectGenerators extends TypeGenerators } yield mkSizeOf(input).asInstanceOf[SizeOf[SInt.type]] lazy val extractAmountGen: Gen[ExtractAmount] = - arbTaggedBox.arbitrary.map { b => mkExtractAmount(b).asInstanceOf[ExtractAmount] } + arbGetVarBox.arbitrary.map { b => mkExtractAmount(b).asInstanceOf[ExtractAmount] } lazy val extractScriptBytesGen: Gen[ExtractScriptBytes] = - arbTaggedBox.arbitrary.map { b => mkExtractScriptBytes(b).asInstanceOf[ExtractScriptBytes] } + arbGetVarBox.arbitrary.map { b => mkExtractScriptBytes(b).asInstanceOf[ExtractScriptBytes] } lazy val extractBytesGen: Gen[ExtractBytes] = - arbTaggedBox.arbitrary.map { b => mkExtractBytes(b).asInstanceOf[ExtractBytes] } + arbGetVarBox.arbitrary.map { b => mkExtractBytes(b).asInstanceOf[ExtractBytes] } lazy val extractBytesWithNoRefGen: Gen[ExtractBytesWithNoRef] = - arbTaggedBox.arbitrary.map { b => + arbGetVarBox.arbitrary.map { b => mkExtractBytesWithNoRef(b).asInstanceOf[ExtractBytesWithNoRef] } lazy val extractIdGen: Gen[ExtractId] = - arbTaggedBox.arbitrary.map { b => mkExtractId(b).asInstanceOf[ExtractId] } + arbGetVarBox.arbitrary.map { b => mkExtractId(b).asInstanceOf[ExtractId] } lazy val extractRegisterAsGen: Gen[ExtractRegisterAs[SInt.type]] = for { - input <- arbTaggedBox.arbitrary + input <- arbGetVarBox.arbitrary r <- arbRegisterIdentifier.arbitrary } yield ExtractRegisterAs(input, r)(SInt) lazy val extractCreationInfoGen: Gen[ExtractCreationInfo] = - arbTaggedBox.arbitrary.map { b => mkExtractCreationInfo(b).asInstanceOf[ExtractCreationInfo] } + arbGetVarBox.arbitrary.map { b => mkExtractCreationInfo(b).asInstanceOf[ExtractCreationInfo] } lazy val deserializeContextGen: Gen[DeserializeContext[SBoolean.type]] = Arbitrary.arbitrary[Byte].map(b => @@ -678,16 +682,16 @@ trait ObjectGenerators extends TypeGenerators lazy val ergoTreeGen: Gen[ErgoTree] = for { sigmaBoolean <- Gen.delay(sigmaBooleanGen) propWithConstants <- Gen.delay(logicalExprTreeNodeGen(Seq(AND.apply, OR.apply, XorOf.apply)).map(_.toSigmaProp)) - prop <- Gen.oneOf(propWithConstants, sigmaBoolean.toSigmaProp) - treeBuilder <- Gen.oneOf(Seq[SigmaPropValue => ErgoTree](ErgoTree.withSegregation, - ErgoTree.withoutSegregation)) + prop <- Gen.oneOf(propWithConstants, sigmaBoolean.toSigmaPropValue) + treeBuilder <- Gen.oneOf(Seq[SigmaPropValue => ErgoTree](ErgoTree.withSegregation(ZeroHeader, _), + ErgoTree.withoutSegregation(ZeroHeader, _))) } yield treeBuilder(prop) lazy val ergoTreeWithSegregationGen: Gen[ErgoTree] = for { sigmaBoolean <- Gen.delay(sigmaBooleanGen) propWithConstants <- Gen.delay(logicalExprTreeNodeGen(Seq(AND.apply, OR.apply, XorOf.apply)).map(_.toSigmaProp)) - prop <- Gen.oneOf(propWithConstants, sigmaBoolean.toSigmaProp) - } yield ErgoTree.withSegregation(prop) + prop <- Gen.oneOf(propWithConstants, sigmaBoolean.toSigmaPropValue) + } yield ErgoTree.withSegregation(ZeroHeader, prop) def headerGen(stateRoot: AvlTree, parentId: Coll[Byte]): Gen[Header] = for { id <- modifierIdBytesGen @@ -704,7 +708,7 @@ trait ObjectGenerators extends TypeGenerators powDistance <- arbBigInt.arbitrary votes <- minerVotesGen } yield CHeader(id, version, parentId, adProofsRoot, stateRoot, transactionRoot, timestamp, nBits, - height, extensionRoot, minerPk, powOnetimePk, powNonce, powDistance, votes) + height, extensionRoot, minerPk.toGroupElement, powOnetimePk.toGroupElement, powNonce, powDistance, votes) lazy val headerGen: Gen[Header] = for { stateRoot <- avlTreeGen @@ -734,7 +738,7 @@ trait ObjectGenerators extends TypeGenerators height <- heightGen minerPk <- groupElementGen votes <- minerVotesGen - } yield CPreHeader(version, parentId, timestamp, nBits, height, minerPk, votes) + } yield CPreHeader(version, parentId, timestamp, nBits, height, minerPk.toGroupElement, votes) lazy val preHeaderGen: Gen[PreHeader] = for { parentId <- modifierIdBytesGen diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/OpcodesGen.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/OpcodesGen.scala similarity index 59% rename from interpreter/shared/src/test/scala/sigmastate/serialization/generators/OpcodesGen.scala rename to interpreter/shared/src/test/scala/sigma/serialization/generators/OpcodesGen.scala index f0b3ef092b..c0bf5b97a2 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/OpcodesGen.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/OpcodesGen.scala @@ -1,7 +1,8 @@ -package sigmastate.serialization.generators +package sigma.serialization.generators import org.scalacheck.Gen -import sigmastate.serialization.OpCodes._ +import sigma.serialization.OpCodes._ +import sigma.serialization.ValueCodes.OpCode trait OpcodesGen { diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/RelationGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/RelationGenerators.scala similarity index 78% rename from interpreter/shared/src/test/scala/sigmastate/serialization/generators/RelationGenerators.scala rename to interpreter/shared/src/test/scala/sigma/serialization/generators/RelationGenerators.scala index fc7193a4f5..0637f211d7 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/RelationGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/RelationGenerators.scala @@ -1,14 +1,14 @@ -package sigmastate.serialization.generators +package sigma.serialization.generators import org.scalacheck.{Arbitrary, Gen} -import sigmastate.Values.{FalseLeaf, TrueLeaf} -import sigmastate.{If, SInt, TreeLookup} +import sigma.ast.{If, SInt, TreeLookup} +import sigma.ast.{FalseLeaf, TrueLeaf} trait RelationGenerators { this: ObjectGenerators with ConcreteCollectionGenerators => val treeLookupGen: Gen[TreeLookup] = for { - t <- arbTaggedAvlTree.arbitrary + t <- arbGetVarAvlTree.arbitrary b1 <- arbByteArrayConstant.arbitrary b2 <- arbByteArrayConstant.arbitrary } yield TreeLookup(t, b1, b2) diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TransformerGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/TransformerGenerators.scala similarity index 81% rename from interpreter/shared/src/test/scala/sigmastate/serialization/generators/TransformerGenerators.scala rename to interpreter/shared/src/test/scala/sigma/serialization/generators/TransformerGenerators.scala index 87256fea4e..22b51763fd 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TransformerGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/TransformerGenerators.scala @@ -1,4 +1,4 @@ -package sigmastate.serialization.generators +package sigma.serialization.generators import org.ergoplatform.validation.ValidationSpecification diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala similarity index 94% rename from interpreter/shared/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala rename to interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala index 2582d1305c..81073c4849 100644 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/generators/TypeGenerators.scala @@ -1,8 +1,8 @@ -package sigmastate.serialization.generators +package sigma.serialization.generators -import org.scalacheck.{Gen, Arbitrary} +import org.scalacheck.{Arbitrary, Gen} import org.scalacheck.Arbitrary.arbString -import sigmastate._ +import sigma.ast._ trait TypeGenerators { implicit val booleanTypeGen: Gen[SBoolean.type] = Gen.const(SBoolean) diff --git a/interpreter/shared/src/test/scala/sigmastate/CalcSha256Specification.scala b/interpreter/shared/src/test/scala/sigmastate/CalcSha256Specification.scala index 9fc8333784..ce070c6760 100644 --- a/interpreter/shared/src/test/scala/sigmastate/CalcSha256Specification.scala +++ b/interpreter/shared/src/test/scala/sigmastate/CalcSha256Specification.scala @@ -2,7 +2,9 @@ package sigmastate import org.scalatest.prop.TableFor2 import scorex.util.encode.Base16 -import sigmastate.Values.{ByteArrayConstant, CollectionConstant} +import sigma.ast.{ByteArrayConstant, CalcSha256, EQ, SByte} +import sigma.ast.syntax.CollectionConstant +import sigma.data.TrivialProp import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, TestingCommons} class CalcSha256Specification extends TestingCommons diff --git a/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala b/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala index 0c4056dcee..87101a1f71 100644 --- a/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala +++ b/interpreter/shared/src/test/scala/sigmastate/CrossVersionProps.scala @@ -2,18 +2,20 @@ package sigmastate import debox.cfor import org.scalactic.source.Position + import scala.util.DynamicVariable import org.scalatest.Tag -import sigmastate.eval.Profiler +import sigmastate.eval.CProfiler import org.scalatest.propspec.AnyPropSpecLike +import sigma.VersionContext trait CrossVersionProps extends AnyPropSpecLike with TestsBase { /** Number of times each test property is warmed up (i.e. executed before final execution). */ def perTestWarmUpIters: Int = 0 - private[sigmastate] val _warmupProfiler = new DynamicVariable[Option[Profiler]](None) + private[sigmastate] val _warmupProfiler = new DynamicVariable[Option[CProfiler]](None) - def warmupProfiler: Option[Profiler] = _warmupProfiler.value + def warmupProfiler: Option[CProfiler] = _warmupProfiler.value override protected def property(testName: String, testTags: Tag*) (testFun: => Any) @@ -21,7 +23,7 @@ trait CrossVersionProps extends AnyPropSpecLike with TestsBase { super.property(testName, testTags: _*) { // do warmup if necessary if (perTestWarmUpIters > 0) { - _warmupProfiler.withValue(Some(new Profiler)) { + _warmupProfiler.withValue(Some(new CProfiler)) { cfor(0)(_ < perTestWarmUpIters, _ + 1) { _ => testFun_Run(testName, testFun) } @@ -29,7 +31,9 @@ trait CrossVersionProps extends AnyPropSpecLike with TestsBase { System.gc() } forEachScriptAndErgoTreeVersion(activatedVersions, ergoTreeVersions) { - testFun_Run(testName, testFun) + VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { + testFun_Run(testName, testFun) + } } } } diff --git a/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala index 49fa4534bd..375ef3f4e5 100644 --- a/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala +++ b/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala @@ -5,7 +5,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scorex.util.encode.Base16 -import sigmastate.crypto.CryptoFacade +import sigma.crypto.CryptoFacade import java.math.BigInteger diff --git a/interpreter/shared/src/test/scala/sigmastate/JitCostSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/JitCostSpecification.scala index 4a434624a8..82ebe9a297 100644 --- a/interpreter/shared/src/test/scala/sigmastate/JitCostSpecification.scala +++ b/interpreter/shared/src/test/scala/sigmastate/JitCostSpecification.scala @@ -1,5 +1,6 @@ package sigmastate +import sigma.ast.JitCost import sigmastate.helpers.TestingCommons import scala.util.{Failure, Try} diff --git a/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala b/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala index 57ecd5e5e8..af9675c7f8 100644 --- a/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala +++ b/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala @@ -1,9 +1,12 @@ package sigmastate import org.scalatest.matchers.should.Matchers -import sigmastate.Values.{ErgoTree, SigmaBoolean, SigmaPropValue} +import sigma.ast.syntax.SigmaPropValue import org.ergoplatform.ErgoTreePredef import sigma.VersionTesting +import sigma.ast.ErgoTree +import sigma.data.SigmaBoolean +import sigma.ast.ErgoTree.{HeaderType, ZeroHeader} trait TestsBase extends Matchers with VersionTesting { /** Set this to true to enable debug console output in tests */ @@ -15,7 +18,7 @@ trait TestsBase extends Matchers with VersionTesting { /** Current ErgoTree header flags assigned dynamically using [[CrossVersionProps]] and * ergoTreeVersionInTests. */ - def ergoTreeHeaderInTests: Byte = ErgoTree.headerWithVersion(ergoTreeVersionInTests) + def ergoTreeHeaderInTests: HeaderType = ErgoTree.headerWithVersion(ZeroHeader, ergoTreeVersionInTests) /** Obtains [[ErgoTree]] which corresponds to True proposition using current * ergoTreeHeaderInTests. */ diff --git a/interpreter/shared/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala index d177a26e4c..a5762444f6 100644 --- a/interpreter/shared/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala +++ b/interpreter/shared/src/test/scala/sigmastate/crypto/GroupLawsSpecification.scala @@ -2,10 +2,9 @@ package sigmastate.crypto import java.math.BigInteger import org.scalacheck.Gen -import sigmastate.crypto.CryptoConstants -import CryptoConstants.EcPointType import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigma.crypto.{CryptoConstants, CryptoFacade, EcPointType, Ecp} import sigmastate.TestsBase import sigmastate.utils.Helpers diff --git a/interpreter/shared/src/test/scala/sigmastate/crypto/SigningSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/crypto/SigningSpecification.scala index 724a0171dc..16999e7457 100644 --- a/interpreter/shared/src/test/scala/sigmastate/crypto/SigningSpecification.scala +++ b/interpreter/shared/src/test/scala/sigmastate/crypto/SigningSpecification.scala @@ -2,13 +2,13 @@ package sigmastate.crypto import org.scalacheck.Gen import scorex.util.encode.Base16 -import sigmastate.{AtLeast, COR, CAND} -import sigmastate.Values.SigmaBoolean +import sigma.ast.{AtLeast, ErgoTree} +import sigma.data.{CAND, COR, ProveDHTuple, SigmaBoolean} +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.ProveDHTupleSerializer import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.helpers.{ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter, TestingCommons} -import sigmastate.interpreter.{HintsBag, ContextExtension, ProverResult} -import sigmastate.serialization.transformers.ProveDHTupleSerializer -import sigmastate.crypto.ProveDHTuple +import sigmastate.interpreter.HintsBag class SigningSpecification extends TestingCommons { @@ -24,7 +24,9 @@ class SigningSpecification extends TestingCommons { // check that signature is correct val verifier = new ErgoLikeTestInterpreter val proverResult = ProverResult(signature, ContextExtension.empty) - verifier.verify(sk.publicImage, fakeContext, proverResult, msg).get._1 shouldBe true + verifier.verify( + ErgoTree.fromSigmaBoolean(sk.publicImage), + fakeContext, proverResult, msg).get._1 shouldBe true // print one more random vector for debug purposes printSimpleSignature(msg: Array[Byte]) @@ -38,7 +40,7 @@ class SigningSpecification extends TestingCommons { // check that signature is correct val verifier = new ErgoLikeTestInterpreter val proverResult = ProverResult(signature, ContextExtension.empty) - verifier.verify(pdht, fakeContext, proverResult, msg).get._1 shouldBe true + verifier.verify(ErgoTree.fromSigmaBoolean(pdht), fakeContext, proverResult, msg).get._1 shouldBe true } property("handle improper signature") { @@ -59,7 +61,7 @@ class SigningSpecification extends TestingCommons { val verifier = new ErgoLikeTestInterpreter val proverResult = ProverResult(signature, ContextExtension.empty) val sigmaTree: SigmaBoolean = CAND(Seq(sk1.publicImage, sk2.publicImage)) - verifier.verify(sigmaTree, fakeContext, proverResult, msg).get._1 shouldBe true + verifier.verify(ErgoTree.fromSigmaBoolean(sigmaTree), fakeContext, proverResult, msg).get._1 shouldBe true } property("OR signature test vector") { @@ -71,7 +73,9 @@ class SigningSpecification extends TestingCommons { val verifier = new ErgoLikeTestInterpreter val proverResult = ProverResult(signature, ContextExtension.empty) val sigmaTree: SigmaBoolean = COR(Seq(sk1.publicImage, sk2.publicImage)) - verifier.verify(sigmaTree, fakeContext, proverResult, msg).get._1 shouldBe true + verifier.verify( + ErgoTree.fromSigmaBoolean(sigmaTree), + fakeContext, proverResult, msg).get._1 shouldBe true } property("OR with ProveDHT signature test vector") { @@ -84,7 +88,9 @@ class SigningSpecification extends TestingCommons { val verifier = new ErgoLikeTestInterpreter val proverResult = ProverResult(signature, ContextExtension.empty) val sigmaTree: SigmaBoolean = COR(Seq(sk1.publicImage, pdht)) - verifier.verify(sigmaTree, fakeContext, proverResult, msg).get._1 shouldBe true + verifier.verify( + ErgoTree.fromSigmaBoolean(sigmaTree), + fakeContext, proverResult, msg).get._1 shouldBe true } property("AND with OR signature test vector") { @@ -97,7 +103,9 @@ class SigningSpecification extends TestingCommons { val verifier = new ErgoLikeTestInterpreter val proverResult = ProverResult(signature, ContextExtension.empty) val sigmaTree: SigmaBoolean = CAND(Seq(sk1.publicImage, COR(Seq(sk2.publicImage, sk3.publicImage)))) - verifier.verify(sigmaTree, fakeContext, proverResult, msg).get._1 shouldBe true + verifier.verify( + ErgoTree.fromSigmaBoolean(sigmaTree), + fakeContext, proverResult, msg).get._1 shouldBe true } property("OR with AND signature test vector") { @@ -110,7 +118,9 @@ class SigningSpecification extends TestingCommons { val verifier = new ErgoLikeTestInterpreter val proverResult = ProverResult(signature, ContextExtension.empty) val sigmaTree: SigmaBoolean = COR(Seq(sk1.publicImage, CAND(Seq(sk2.publicImage, sk3.publicImage)))) - verifier.verify(sigmaTree, fakeContext, proverResult, msg).get._1 shouldBe true + verifier.verify( + ErgoTree.fromSigmaBoolean(sigmaTree), + fakeContext, proverResult, msg).get._1 shouldBe true } property("threshold signature test vector") { diff --git a/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala b/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala index 07b8fdcddb..ba996b0246 100644 --- a/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala +++ b/interpreter/shared/src/test/scala/sigmastate/eval/BasicOpsTests.scala @@ -2,18 +2,19 @@ package sigmastate.eval import org.scalatest.funsuite.AnyFunSuite import org.scalatest.matchers.should.Matchers +import sigma.crypto.SecP256K1Group +import sigma.data.{CSigmaDslBuilder, TrivialProp} +import sigma.util.Extensions.SigmaBooleanOps import java.math.BigInteger -import sigmastate.TrivialProp -import sigmastate.crypto.SecP256K1Group import sigma.{ContractsTestkit, SigmaDslBuilder, SigmaProp} import scala.language.implicitConversions class BasicOpsTests extends AnyFunSuite with ContractsTestkit with Matchers { - override val SigmaDsl: SigmaDslBuilder = CostingSigmaDslBuilder + override val SigmaDsl: SigmaDslBuilder = CSigmaDslBuilder - implicit def boolToSigma(b: Boolean): SigmaProp = TrivialProp(b) + implicit def boolToSigma(b: Boolean): SigmaProp = TrivialProp(b).toSigmaProp test("atLeast") { val props = Colls.fromArray(Array[SigmaProp](false, true, true, false)) diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingProverInterpreter.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingProverInterpreter.scala index f88d30ddcc..2eaac82790 100644 --- a/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingProverInterpreter.scala +++ b/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingProverInterpreter.scala @@ -1,9 +1,10 @@ package sigmastate.helpers -import sigmastate.SType -import sigmastate.Values.{ErgoTree, EvaluatedValue} +import sigma.ast.{ErgoTree, SType} +import sigma.ast.EvaluatedValue +import sigma.interpreter.{ContextExtension, CostedProverResult} import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.interpreter.{ContextExtension, CostedProverResult, HintsBag, ProverInterpreter} +import sigmastate.interpreter.{HintsBag, ProverInterpreter} import scala.util.Try diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingTestProvingInterpreter.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingTestProvingInterpreter.scala index 641142f43c..0d4c1904ec 100644 --- a/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingTestProvingInterpreter.scala +++ b/interpreter/shared/src/test/scala/sigmastate/helpers/ContextEnrichingTestProvingInterpreter.scala @@ -1,8 +1,7 @@ package sigmastate.helpers import scorex.utils.Random -import sigmastate.SType -import sigmastate.Values._ +import sigma.ast._ import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.crypto.DiffieHellmanTupleProverInput diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala index 70f644bd66..a353ff5e46 100644 --- a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala +++ b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala @@ -2,14 +2,17 @@ package sigmastate.helpers import org.ergoplatform.ErgoLikeContext.Height import org.ergoplatform._ -import org.ergoplatform.validation.{SigmaValidationSettings, ValidationRules} -import sigmastate.AvlTreeData -import sigmastate.crypto.CryptoConstants -import sigmastate.eval._ -import sigmastate.interpreter.ContextExtension -import sigmastate.interpreter.ErgoTreeEvaluator.DefaultEvalSettings -import sigmastate.serialization.{GroupElementSerializer, SigmaSerializer} +import org.ergoplatform.validation.ValidationRules +import sigma.crypto.CryptoConstants +import sigma.data.{AvlTreeData, CSigmaDslBuilder} +import sigma.interpreter.ContextExtension +import sigma.serialization.GroupElementSerializer +import sigma.util.Extensions.EcpOps +import sigma.validation.SigmaValidationSettings import sigma.{Box, Coll, Colls, Header, PreHeader} +import sigmastate.eval._ +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings +import sigma.serialization.SigmaSerializer object ErgoLikeContextTesting { /* NO HF PROOF: @@ -22,14 +25,14 @@ object ErgoLikeContextTesting { val dummyPubkey: Array[Byte] = GroupElementSerializer.toBytes(CryptoConstants.dlogGroup.generator) val noBoxes: IndexedSeq[ErgoBox] = IndexedSeq.empty[ErgoBox] - val noHeaders: Coll[Header] = CostingSigmaDslBuilder.Colls.emptyColl[Header] + val noHeaders: Coll[Header] = CSigmaDslBuilder.Colls.emptyColl[Header] def dummyPreHeader(currentHeight: Height, minerPk: Array[Byte]): PreHeader = CPreHeader(0, parentId = Colls.emptyColl[Byte], timestamp = 3, nBits = 0, height = currentHeight, - minerPk = GroupElementSerializer.parse(SigmaSerializer.startReader(minerPk)), + minerPk = GroupElementSerializer.parse(SigmaSerializer.startReader(minerPk)).toGroupElement, votes = Colls.emptyColl[Byte] ) diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoTransactionValidator.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoTransactionValidator.scala index 3203dfc13f..afacb3e6ee 100644 --- a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoTransactionValidator.scala +++ b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoTransactionValidator.scala @@ -1,8 +1,8 @@ package sigmastate.helpers import org.ergoplatform._ -import sigmastate.interpreter.ErgoTreeEvaluator.DefaultEvalSettings -import sigmastate.interpreter.EvalSettings +import sigma.eval.EvalSettings +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import scala.util.{Failure, Success} diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/TestingCommons.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/TestingCommons.scala index 19a8efe608..d92ad87ccd 100644 --- a/interpreter/shared/src/test/scala/sigmastate/helpers/TestingCommons.scala +++ b/interpreter/shared/src/test/scala/sigmastate/helpers/TestingCommons.scala @@ -1,15 +1,15 @@ package sigmastate.helpers -import sigmastate.Values.GroupElementConstant import org.scalatest.propspec.AnyPropSpec import sigmastate.TestsBase import sigmastate.helpers.TestingHelpers.createBox -import org.scalatestplus.scalacheck.{ScalaCheckPropertyChecks, ScalaCheckDrivenPropertyChecks} -import sigmastate.eval.SigmaDsl +import org.scalatestplus.scalacheck.{ScalaCheckDrivenPropertyChecks, ScalaCheckPropertyChecks} +import sigma.eval.SigmaDsl import scorex.crypto.hash.Blake2b256 import org.ergoplatform.{ErgoBox, ErgoLikeContext} import org.scalatest.matchers.should.Matchers -import sigmastate.crypto.CryptoConstants.EcPointType +import sigma.ast.syntax.GroupElementConstant +import sigma.crypto.EcPointType trait TestingCommons extends AnyPropSpec with ScalaCheckPropertyChecks diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala index c0255b6adc..392e319773 100644 --- a/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala +++ b/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala @@ -1,17 +1,13 @@ package sigmastate.helpers import org.ergoplatform.ErgoBox.{AdditionalRegisters, Token, allZerosModifierId} -import org.ergoplatform.validation.SigmaValidationSettings import org.ergoplatform._ import scorex.util.ModifierId -import sigma.data.{CollOverArray, PairOfCols} -import sigmastate.AvlTreeData -import sigmastate.Values.ErgoTree -import sigmastate.eval._ -import sigmastate.interpreter.ContextExtension -import sigma.Coll -import sigma.{Header, PreHeader} - +import sigma.ast.ErgoTree +import sigma.data.{AvlTreeData, CSigmaDslBuilder, CollOverArray, PairOfCols} +import sigma.interpreter.ContextExtension +import sigma.validation.SigmaValidationSettings +import sigma.{Coll, Colls, Header, PreHeader} import scala.collection.compat.immutable.ArraySeq // TODO refactor: unification is required between two hierarchies of tests @@ -28,7 +24,7 @@ object TestingHelpers { transactionId: ModifierId = allZerosModifierId, boxIndex: Short = 0): ErgoBox = new ErgoBox(value, ergoTree, - CostingSigmaDslBuilder.Colls.fromArray(additionalTokens.toArray[Token]), + CSigmaDslBuilder.Colls.fromArray(additionalTokens.toArray[Token]), additionalRegisters, transactionId, boxIndex, creationHeight) @@ -49,7 +45,7 @@ object TestingHelpers { */ def cloneColl[A](c: Coll[A]): Coll[A] = (c match { case c: CollOverArray[_] => - new CollOverArray(c.toArray.clone(), SigmaDsl.Colls)(c.tItem) + new CollOverArray(c.toArray.clone(), Colls)(c.tItem) case ps: PairOfCols[_,_] => new PairOfCols(cloneColl(ps.ls), cloneColl(ps.rs)) }).asInstanceOf[Coll[A]] diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/TaggedVariableSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/serialization/TaggedVariableSerializerSpecification.scala deleted file mode 100644 index af0e04cf3b..0000000000 --- a/interpreter/shared/src/test/scala/sigmastate/serialization/TaggedVariableSerializerSpecification.scala +++ /dev/null @@ -1,24 +0,0 @@ -package sigmastate.serialization - -import sigmastate.Values._ -import OpCodes._ -import sigmastate.SBox - -class TaggedVariableSerializerSpecification extends SerializationSpecification { - - property("TaggedVariable: TaggedInt serializer round trip") { - forAll { ti: TaggedInt => - roundTripTest(ti) - } - } - - property("TaggedVariable: TaggedBox serializer round trip") { - forAll { tb: TaggedBox => - roundTripTest(tb) - } - } - - property("TaggedVariable deserialize from predefined bytes") { - predefinedBytesTest(TaggedBox(10), Array(TaggedVariableCode, 10, SBox.typeCode)) - } -} diff --git a/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala b/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala index 3325035bbd..b02c489b08 100644 --- a/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala +++ b/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala @@ -1,11 +1,11 @@ package sigmastate.utils -import sigmastate.serialization.generators.ObjectGenerators +import sigma.serialization.generators.ObjectGenerators import Helpers._ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigmastate.eval.Extensions.ArrayOps +import sigma.Extensions.ArrayOps class HelpersTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with ObjectGenerators { property("xorU") { diff --git a/interpreter/shared/src/test/scala/sigmastate/utils/SparseArrayContainerSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/utils/SparseArrayContainerSpecification.scala index 0f6b69b78f..e45bc00e70 100644 --- a/interpreter/shared/src/test/scala/sigmastate/utils/SparseArrayContainerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigmastate/utils/SparseArrayContainerSpecification.scala @@ -4,7 +4,8 @@ import org.scalacheck.{Arbitrary, Gen} import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigmastate.serialization.generators.ObjectGenerators +import sigma.serialization.generators.ObjectGenerators +import sigma.utils.SparseArrayContainer class SparseArrayContainerSpecification extends AnyPropSpec with ObjectGenerators diff --git a/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala index 1334ee520f..8b560c523d 100644 --- a/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala +++ b/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala @@ -2,11 +2,12 @@ package sigmastate.utxo import org.ergoplatform.ErgoLikeInterpreter import scorex.crypto.hash.Blake2b256 -import sigmastate.Values.SigmaBoolean +import sigma.crypto.SecP256K1Group +import sigma.data.{CAND, COR, CTHRESHOLD, SigmaBoolean, TrivialProp} +import sigma.exceptions.InterpreterException import sigmastate._ import sigmastate.crypto.DLogProtocol.FirstDLogProverMessage -import sigmastate.crypto.{FirstDHTupleProverMessage, SecP256K1Group} -import sigmastate.exceptions.InterpreterException +import sigmastate.crypto.FirstDHTupleProverMessage import sigmastate.helpers.{ErgoLikeTestProvingInterpreter, TestingCommons} import sigmastate.interpreter.{HintsBag, ProverInterpreter} diff --git a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala index a1226b1d03..720223ee3a 100644 --- a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala +++ b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala @@ -1,10 +1,10 @@ package sigma -import sigma.data.RType -import sigmastate.Values.ErgoTree -import sigmastate.{AvlTreeData, Values} +import sigma.Extensions.ArrayOps +import sigma.ast.ErgoTree +import sigma.ast.syntax.TrueSigmaProp +import sigma.data.{AvlTreeData, RType} import sigmastate.eval._ -import sigmastate.eval.Extensions._ import sigmastate.helpers.TestingHelpers._ import sigma.data._ @@ -22,7 +22,7 @@ trait ContractsTestkit { val R8 = 8.toByte; val R9 = 9.toByte; val Colls = new CollOverArrayBuilder - val SigmaDsl: SigmaDslBuilder = CostingSigmaDslBuilder + val SigmaDsl: SigmaDslBuilder = CSigmaDslBuilder val noRegisters = collection[AnyValue]() val noBytes = collection[Byte]() val noInputs = Array[Box]() @@ -30,7 +30,7 @@ trait ContractsTestkit { val dummyPubkey: Array[Byte] = Array.fill(32)(0: Byte) val dummyADDigest: Coll[Byte] = Colls.fromArray(Array.fill(33)(0: Byte)) val emptyAvlTree = new CAvlTree(AvlTreeData.dummy) - val noHeaders = CostingSigmaDslBuilder.Colls.emptyColl[Header] + val noHeaders = CSigmaDslBuilder.Colls.emptyColl[Header] val dummyPreHeader: PreHeader = null /** Create collection from array of items */ @@ -62,16 +62,16 @@ trait ContractsTestkit { def newAliceBox(@nowarn id: Byte, value: Long): Box = { val ergoBox = testBox(value, - ErgoTree.fromProposition(Values.TrueSigmaProp), + ErgoTree.fromProposition(TrueSigmaProp), creationHeight = 0, additionalTokens = Seq(), additionalRegisters = Map()) - new CostingBox(ergoBox) + new CBox(ergoBox) } def testContext( inputs: Array[Box], outputs: Array[Box], height: Int, self: Box, tree: AvlTree, minerPk: Array[Byte], activatedScriptVersion: Byte, currErgoTreeVersion: Byte, vars: Array[AnyValue]) = - new CostingDataContext( + new CContext( noInputs.toColl, noHeaders, dummyPreHeader, inputs.toColl, outputs.toColl, height, self, inputs.indexOf(self), tree, minerPk.toColl, vars.toColl, activatedScriptVersion, currErgoTreeVersion) @@ -81,13 +81,13 @@ trait ContractsTestkit { self: Box, activatedScriptVersion: Byte, currErgoTreeVersion: Byte, - vars: AnyValue*): CostingDataContext = { + vars: AnyValue*): CContext = { testContext( noInputs, noOutputs, height, self, emptyAvlTree, dummyPubkey, activatedScriptVersion, currErgoTreeVersion, vars.toArray) } - implicit class TestContextOps(ctx: CostingDataContext) { + implicit class TestContextOps(ctx: CContext) { def withInputs(inputs: Box*) = ctx.copy(inputs = inputs.toArray.toColl) def withOutputs(outputs: Box*) = ctx.copy(outputs = outputs.toArray.toColl) diff --git a/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala b/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala index f5e695e32d..d33f09dd80 100644 --- a/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala +++ b/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala @@ -6,23 +6,21 @@ import org.scalacheck.Arbitrary.arbitrary import org.scalacheck.Gen.containerOfN import org.scalacheck.util.Buildable import org.scalacheck.{Arbitrary, Gen} -import sigma.data.RType +import sigma.data._ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId -import sigmastate.Values.{ByteArrayConstant, ConcreteCollection, ConstantPlaceholder, ErgoTree, FalseLeaf, IntConstant, LongConstant, SigmaPropConstant, TrueLeaf} -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.ProveDHTuple -import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigmastate.eval.{CAvlTree, CBigInt, CHeader, CPreHeader, CSigmaProp, CostingBox, CostingSigmaDslBuilder, SigmaDsl} +import sigma.ast._ +import sigma.Extensions.ArrayOps +import sigmastate.eval.{CHeader, CPreHeader} import sigmastate.helpers.TestingCommons -import sigmastate.serialization.ErgoTreeSerializer -import sigmastate.serialization.generators.ObjectGenerators +import sigma.serialization.ErgoTreeSerializer +import sigma.serialization.generators.ObjectGenerators import sigmastate.utils.Helpers -import sigmastate._ -import sigma.Coll +import sigma.ast.{SBoolean, SSigmaProp} +import sigma.crypto.EcPointType +import ErgoTree.HeaderType +import sigma.eval.SigmaDsl import java.math.BigInteger import scala.reflect.ClassTag @@ -30,7 +28,7 @@ import scala.reflect.ClassTag trait SigmaTestingData extends TestingCommons with ObjectGenerators { /** Creates a [[sigma.Coll]] with the given `items`. */ def Coll[T](items: T*)(implicit cT: RType[T]): Coll[T] = - CostingSigmaDslBuilder.Colls.fromItems(items: _*) + CSigmaDslBuilder.Colls.fromItems(items: _*) /** Generator of random collection with `n` elements. */ def collOfN[T: RType : Arbitrary](n: Int) @@ -208,11 +206,11 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators { ) ) - val b1_instances = new CloneSet(1000, CostingBox( + val b1_instances = new CloneSet(1000, CBox( new ErgoBox( 9223372036854775807L, new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array( SigmaPropConstant( CSigmaProp( @@ -246,11 +244,11 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators { val b1: Box = create_b1() - val b2: Box = CostingBox( + val b2: Box = CBox( new ErgoBox( 12345L, new ErgoTree( - 0.toByte, + HeaderType @@ 0.toByte, Vector(), Right( BoolToSigmaProp( diff --git a/parsers/shared/src/main/scala/sigmastate/lang/ContractParser.scala b/parsers/shared/src/main/scala/sigmastate/lang/ContractParser.scala new file mode 100644 index 0000000000..87dd20ab0c --- /dev/null +++ b/parsers/shared/src/main/scala/sigmastate/lang/ContractParser.scala @@ -0,0 +1,196 @@ +package sigmastate.lang + +import fastparse._ +import fastparse.NoWhitespace._ +import SigmaParser._ +import sigma.ast.SType +import sigma.ast.syntax.SValue +import sigmastate.lang.parsers.Basic + +/** + * Represents a docstring line. + */ +case class DocumentationToken(kind: DocumentationToken.Kind, name: Option[String], body: Option[String]) + +/** + * Companion object containing the classes required for describing an docstring token. + */ +object DocumentationToken { + def apply(kind: Kind): DocumentationToken = new DocumentationToken(kind, None, None) + + def apply(kind: Kind, body: String): DocumentationToken = new DocumentationToken(kind, None, Option(body)) + + def apply(kind: TagKind, name: String, body: String): DocumentationToken = + new DocumentationToken(kind, Option(name), Option(body)) + + /** + * Represents a documentation remark. + */ + sealed abstract class Kind + + /** + * Documents an untagged doc description, this is simply a line of text anywhere in the docstring. + */ + case object Description extends Kind + + /** + * Represents an empty doc line. + */ + case object EmptyLine extends Kind + + /** + * Represents a line starting with a tag (@) but is not supported. + */ + case object UnsupportedTag extends Kind + + /** + * Represents a labeled documentation remark. + */ + sealed abstract class TagKind(val label: String) extends Kind + + /** + * Documents a specific value parameter of the contract template. + */ + case object Param extends TagKind("@param") + + /** + * Documents the return value of the contract template. + */ + case object Return extends TagKind("@returns") +} + +/** + * Holds values extracted from a `@param` tag line in docstrings. + * + * @param name Name of the parameter. + * @param description Description of the parameter. + */ +case class ParameterDoc(name: String, description: String) + +/** + * Contract template documentation extracted from the preceding docstring. + * + * @param description Top level contract template description. + * @param params Contract template parameters as defined in the docstring. + */ +case class ContractDoc(description: String, params: Seq[ParameterDoc]) + +object ContractDoc { + def apply(tokens: Seq[DocumentationToken]): ContractDoc = { + val nonEmptyTokens = tokens.dropWhile(_.kind == DocumentationToken.EmptyLine) + + val (description, paramTokens) = nonEmptyTokens.span(_.kind == DocumentationToken.Description) + val descriptionText = description.flatMap(_.body).mkString(" ") + + def extractParamDocs(tokens: Seq[DocumentationToken]): Seq[ParameterDoc] = { + tokens match { + case DocumentationToken(kind: DocumentationToken.TagKind, Some(name), Some(body)) +: tail if kind == DocumentationToken.Param => + // grab the succeeding description tokens if there are any, this is multiline description of params + val (descTokens, remainingTokens) = tail.span(_.kind == DocumentationToken.Description) + val fullDescription = (body +: descTokens.flatMap(_.body)).mkString(" ") + ParameterDoc(name, fullDescription) +: extractParamDocs(remainingTokens) + case _ +: tail => extractParamDocs(tail) + case Seq() => Seq() + } + } + + ContractDoc(descriptionText, extractParamDocs(paramTokens)) + } +} + +/** + * Represents a parsed parameter defined in a contracts signature. + * + * @param name The name of the parameter. + * @param tpe The type of the parameter. + * @param defaultValue The default value assigned to the parameter, if it exists. + */ +case class ContractParam(name: String, tpe: SType, defaultValue: Option[SType#WrappedType]) + +/** + * Represents the signature of a contract. + * + * @param name The name of the contract. + * @param params The parameters for the contract. + */ +case class ContractSignature(name: String, params: Seq[ContractParam]) + +/** + * The result of parsing a contract template. + * Includes the parsed docstring preceding the contract as well as the contract signature. + * + * @param docs Docstring parsed from the contract. + * @param signature Signature of the contract. + * @param body Parsed SValue of the contract. + */ +case class ParsedContractTemplate(docs: ContractDoc, signature: ContractSignature, body: SValue) + +/** + * Parsers that handle parsing contract definitions excluding the contract body which is handled + * by [[SigmaParser]]. + */ +object ContractParser { + def parse(source: String): Parsed[ParsedContractTemplate] = fastparse.parse(source, parse(_)) + + /** + * Parse a contract up until the open brace (i.e the beginning of the contract logic). + */ + def parse[_: P]: P[ParsedContractTemplate] = P(Docs.parse ~ Basic.Newline ~ Signature.parse ~ WL.? ~ "=" ~ WL.? ~ AnyChar.rep(1).!).map(s => ParsedContractTemplate(s._1, s._2, SigmaParser(s._3).get.value)) + + /** + * Parsers for contract docstrings. + * Contract docstrings follow similiar structure as scaladocs except only support a subset + * of documentation tags such as @param & @returns. + */ + object Docs { + + import DocumentationToken._ + + def parse(source: String): Parsed[ContractDoc] = { + fastparse.parse(source, parse(_)) + } + + def parse[_: P]: P[ContractDoc] = P(" ".rep.? ~ "/*" ~ docLine.rep ~ " ".rep.? ~ "*/").map(ContractDoc.apply) + + def linePrefix[_: P] = P(WL.? ~ "*" ~ " ".rep.? ~ !"/") + + def word[_: P] = CharsWhile(c => c != ' ') + + def charUntilNewLine[_: P] = CharsWhile(c => c != '\n') + + def unsupportedTag[_: P] = P("@" ~ charUntilNewLine.?).map(_ => DocumentationToken(UnsupportedTag)) + + def returnTag[_: P] = P("@returns").map(_ => DocumentationToken(Return)) + + def paramTag[_: P] = P("@param" ~ WL ~ word.! ~ WL ~ charUntilNewLine.!).map(s => DocumentationToken(Param, s._1, s._2)) + + def tag[_: P] = P(returnTag | paramTag | unsupportedTag) + + def emptyLine[_: P] = P(("" | " ".rep.?) ~ &(Basic.Newline)).map(_ => DocumentationToken(EmptyLine)) + + def description[_: P] = P(!"@" ~ charUntilNewLine.!).map(s => DocumentationToken(Description, s)) + + def docLine[_: P] = P(linePrefix ~ (emptyLine | description | tag) ~ Basic.Newline) + } + + /** + * Parsers for contract signatures. + * A contract signature has essentially the same shape as a scala function signature. + */ + object Signature { + + def parse(source: String): Parsed[ContractSignature] = { + fastparse.parse(source, parse(_)) + } + + def parse[_: P]: P[ContractSignature] = P(annotation ~ WL.? ~ `def` ~ WL.? ~ Id.! ~ params).map(s => ContractSignature(s._1, s._2.getOrElse(Seq()))) + + def annotation[_: P] = P("@contract") + + def paramDefault[_: P] = P(WL.? ~ `=` ~ WL.? ~ ExprLiteral).map(s => s.asWrappedType) + + def param[_: P] = P(WL.? ~ Id.! ~ ":" ~ Type ~ paramDefault.?).map(s => ContractParam(s._1, s._2, s._3)) + + def params[_: P] = P("(" ~ param.rep(1, ",").? ~ ")") + } +} diff --git a/parsers/shared/src/main/scala/sigmastate/lang/SigmaParser.scala b/parsers/shared/src/main/scala/sigmastate/lang/SigmaParser.scala index 155e4ab879..3e7a5a313f 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/SigmaParser.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/SigmaParser.scala @@ -1,19 +1,18 @@ package sigmastate.lang import fastparse.internal.Logger -import sigmastate._ -import Values._ +import sigma.ast._ import sigma.data.Nullable -import sigmastate.lang.Terms._ -import sigmastate.lang.syntax.Basic._ -import sigmastate.lang.syntax.{Core, Exprs} +import sigma.ast.syntax._ +import sigmastate.lang.parsers.{Basic, Core, Exprs} import scala.collection.mutable import scala.util.DynamicVariable /** Main facade to ErgoScript parser implementation. */ -object SigmaParser extends Exprs with Types with Core { - import fastparse._; import ScalaWhitespace._ +object SigmaParser extends Exprs with Types with Core { parser => + import fastparse._ + import ScalaWhitespace._ import builder._ private val currentInput = new DynamicVariable[String]("") @@ -30,7 +29,7 @@ object SigmaParser extends Exprs with Types with Core { mkVal(n, t.getOrElse(NoType), body) } case (index, pat,_,_) => - error(s"Only single name patterns supported but was $pat", Some(srcCtx(index))) + Basic.error(s"Only single name patterns supported but was $pat", Some(srcCtx(index))) } override def BlockDef[_:P] = P( Dcl ) @@ -47,7 +46,7 @@ object SigmaParser extends Exprs with Types with Core { mkConstant[SInt.type](-value, SInt) case LongConstant(value) => mkConstant[SLong.type](-value, SLong) - case _ => error(s"cannot prefix $arg with op $opName", arg.sourceContext) + case _ => Basic.error(s"cannot prefix $arg with op $opName", arg.sourceContext) } case "!" => mkLogicalNot(arg.asBoolValue) @@ -56,16 +55,16 @@ object SigmaParser extends Exprs with Types with Core { if (arg.tpe.isNumTypeOrNoType) mkNegation(arg.asNumValue) else - error(s"Numeric argument expected for '$opName' operation: $arg", arg.sourceContext) + Basic.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) + Basic.error(s"Numeric argument expected for '$opName' operation: $arg", arg.sourceContext) case _ => - error(s"Unknown prefix operation $opName for $arg", arg.sourceContext) + Basic.error(s"Unknown prefix operation $opName for $arg", arg.sourceContext) } } @@ -86,18 +85,18 @@ object SigmaParser extends Exprs with Types with Core { 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) + Basic.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) + Basic.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.asNumValue, r.asNumValue) case "%" => mkModulo(l.asNumValue, r.asNumValue) - case _ => error(s"Unknown binary operation $opName", l.sourceContext) + case _ => Basic.error(s"Unknown binary operation $opName", l.sourceContext) } } diff --git a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala index e4869e4190..06683f6e96 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala @@ -2,11 +2,9 @@ package sigmastate.lang import fastparse._ import ScalaWhitespace._ -import sigmastate._ -import Values._ -import sigmastate.lang.Terms.Ident -import sigmastate.lang.syntax.Core -import syntax.Basic.error +import sigmastate.lang.parsers.Core +import parsers.Basic.error +import sigma.ast._ //noinspection ForwardReference /** Parsers of type terms. Can produce values of SType. */ diff --git a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Basic.scala b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Basic.scala similarity index 96% rename from parsers/shared/src/main/scala/sigmastate/lang/syntax/Basic.scala rename to parsers/shared/src/main/scala/sigmastate/lang/parsers/Basic.scala index 55bdd4e399..152de08780 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Basic.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Basic.scala @@ -1,11 +1,11 @@ -package sigmastate.lang.syntax +package sigmastate.lang.parsers import fastparse._ import NoWhitespace._ import fastparse.CharPredicates._ +import sigma.ast.SourceContext import sigma.data.Nullable -import sigmastate.lang.SourceContext -import sigmastate.exceptions.CompilerException +import sigma.exceptions.CompilerException /** Basic lexical parsers for ErgoScript. */ object Basic { diff --git a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Core.scala b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Core.scala similarity index 93% rename from parsers/shared/src/main/scala/sigmastate/lang/syntax/Core.scala rename to parsers/shared/src/main/scala/sigmastate/lang/parsers/Core.scala index 05f0952b20..a2d7781b87 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Core.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Core.scala @@ -1,11 +1,11 @@ -package sigmastate.lang.syntax +package sigmastate.lang.parsers -import sigmastate._ -import sigmastate.Values._ -import sigmastate.lang.syntax +import sigma.ast.syntax.SValue +import sigma.ast._ +import sigmastate.lang.parsers /** Keywords and identifiers used in expressions. */ -trait Core extends syntax.Literals { +trait Core extends parsers.Literals { import fastparse._ import ScalaWhitespace._ diff --git a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Exprs.scala b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Exprs.scala similarity index 95% rename from parsers/shared/src/main/scala/sigmastate/lang/syntax/Exprs.scala rename to parsers/shared/src/main/scala/sigmastate/lang/parsers/Exprs.scala index f2b8c68b12..3d02cb3b14 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Exprs.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Exprs.scala @@ -1,13 +1,12 @@ -package sigmastate.lang.syntax +package sigmastate.lang.parsers import fastparse._ import ScalaWhitespace._ -import sigmastate._ -import sigmastate.Values._ -import sigmastate.lang.Terms.{Val, Ident, ValueOps} +import sigma.ast._ +import sigma.ast.syntax.{SValue, ValueOps} import sigmastate.lang._ -import sigmastate.lang.SigmaPredef._ -import sigmastate.lang.syntax.Basic._ +import SigmaPredef._ +import sigmastate.lang.parsers.Basic._ import scala.annotation.tailrec import scala.collection.compat.immutable.ArraySeq @@ -25,11 +24,11 @@ trait Exprs extends Core with Types { // varies. // Expressions used as statements, directly within a {block} - object StatCtx extends WsCtx(semiInference=true, arrowTypeAscriptions=false) + object StatCtx extends WsCtx(semiInference=true) // Expressions nested within other expressions - object ExprCtx extends WsCtx(semiInference=false, arrowTypeAscriptions=true) + object ExprCtx extends WsCtx(semiInference=false) // Expressions directly within a `val x = ...` or `def x = ...` - object FreeCtx extends WsCtx(semiInference=true, arrowTypeAscriptions=true) + object FreeCtx extends WsCtx(semiInference=true) override def TypeExpr[_:P]: P[Value[SType]] = ExprCtx.Expr @@ -38,7 +37,7 @@ trait Exprs extends Core with Types { //noinspection TypeAnnotation,ForwardReference /** Parsing context of expressions (see derived classes). */ - class WsCtx(semiInference: Boolean, arrowTypeAscriptions: Boolean) { + class WsCtx(semiInference: Boolean) { private def OneSemiMax[_:P]: P[Unit] = if (semiInference) OneNLMax else Pass private def NoSemis[_:P]: P[Unit] = if (semiInference) NotNewline else Pass @@ -76,7 +75,7 @@ trait Exprs extends Core with Types { } private def SuperPostfixSuffix[_:P] = P( (`=` ~/ Expr).? ) - private def ExprPrefix[_:P] = P( WL ~ CharPred("-+!~".contains(_)).! ~~ !syntax.Basic.OpChar ~ WS) + private def ExprPrefix[_:P] = P( WL ~ CharPred("-+!~".contains(_)).! ~~ !parsers.Basic.OpChar ~ WS) private def ExprSuffix[_:P] = P( (WL ~ "." ~/ (Index ~ Id.!).map{ case (i, s) => atSrcPos(i) { mkIdent(s, NoType)} } | WL ~ TypeArgs.map(items => STypeApply("", items.toIndexedSeq)) @@ -198,7 +197,7 @@ trait Exprs extends Core with Types { case STypeApply("", targs) => mkApplyTypes(acc, targs) case arg: SValue => acc match { case Ident(name, _) if name == ZKProofFunc.name => arg match { - case Terms.Block(_, body) => + case Block(_, body) => mkApply(mkIdent(ZKProofFunc.name, ZKProofFunc.declaration.tpe), Array(body)) case nonBlock => error(s"expected block parameter for ZKProof, got $nonBlock", nonBlock.sourceContext) diff --git a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Identifiers.scala b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Identifiers.scala similarity index 96% rename from parsers/shared/src/main/scala/sigmastate/lang/syntax/Identifiers.scala rename to parsers/shared/src/main/scala/sigmastate/lang/parsers/Identifiers.scala index 65acacc765..f8e3e4ccbd 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Identifiers.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Identifiers.scala @@ -1,9 +1,9 @@ -package sigmastate.lang.syntax +package sigmastate.lang.parsers import fastparse.CharPredicates.{isDigit, isLetter} import fastparse._ import NoWhitespace._ -import sigmastate.lang.syntax.Basic._ +import sigmastate.lang.parsers.Basic._ //noinspection ForwardReference /** Identifiers and keywords */ diff --git a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Literals.scala b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Literals.scala similarity index 96% rename from parsers/shared/src/main/scala/sigmastate/lang/syntax/Literals.scala rename to parsers/shared/src/main/scala/sigmastate/lang/parsers/Literals.scala index 007e4057b5..6beff079e4 100644 --- a/parsers/shared/src/main/scala/sigmastate/lang/syntax/Literals.scala +++ b/parsers/shared/src/main/scala/sigmastate/lang/parsers/Literals.scala @@ -1,13 +1,12 @@ -package sigmastate.lang.syntax +package sigmastate.lang.parsers -import fastparse._; import NoWhitespace._ -import Identifiers._ -import sigmastate._ -import Values._ -import java.lang.Long.parseLong +import fastparse.NoWhitespace._ +import fastparse._ +import sigma.ast.syntax.BooleanConstant +import sigma.ast._ +import sigmastate.lang.parsers.Identifiers._ import java.lang.Integer.parseInt - -import sigmastate.lang.{SigmaBuilder, SourceContext, StdSigmaBuilder} +import java.lang.Long.parseLong /** Parsers of literal expressions. */ trait Literals { l => diff --git a/parsers/shared/src/test/scala/sigmastate/lang/ContractParserSpec.scala b/parsers/shared/src/test/scala/sigmastate/lang/ContractParserSpec.scala new file mode 100644 index 0000000000..9a412b7100 --- /dev/null +++ b/parsers/shared/src/test/scala/sigmastate/lang/ContractParserSpec.scala @@ -0,0 +1,50 @@ +package sigmastate.lang + +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigma.ast._ + +class ContractParserSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { + property("parses docstring") { + val doc = + """/** This is my contracts description. + |* Here is another line describing what it does in more detail. + |* + |* @param p1 describe p1 + |* @param p2 description of the 2nd parameter + |* which is pretty complex and on many + |* lines to describe functions + |* @param p3 the final parameter + |* @return + |*/""".stripMargin + val contractDoc = ContractParser.Docs.parse(doc).get.value + + contractDoc.description shouldBe "This is my contracts description. Here is another line describing what it does in more detail." + contractDoc.params should contain theSameElementsInOrderAs Seq( + ParameterDoc("p1", "describe p1"), + ParameterDoc("p2", "description of the 2nd parameter which is pretty complex and on many lines to describe functions"), + ParameterDoc("p3", "the final parameter") + ) + } + + property("parses contract signature") { + val source = "@contract def contractName(p1: Int = 5, p2: String = \"default string\", param3: Long)" + val parsed = ContractParser.Signature.parse(source).get.value + + parsed.name shouldBe "contractName" + parsed.params should contain theSameElementsInOrderAs Seq( + ContractParam("p1", SInt, Some(IntConstant(5).asWrappedType)), + ContractParam("p2", SString, Some(StringConstant("default string").asWrappedType)), + ContractParam("param3", SLong, None) + ) + } + + property("parses contract signature no params") { + val source = "@contract def contractName()" + val parsed = ContractParser.Signature.parse(source).get.value + + parsed.name shouldBe "contractName" + parsed.params should contain theSameElementsInOrderAs Seq() + } +} diff --git a/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala b/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala index a1da3d0766..498c3934bf 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/LangTests.scala @@ -1,19 +1,18 @@ package sigmastate.lang import org.scalatest.matchers.should.Matchers -import sigmastate.lang.Terms.{Ident, MethodCallLike} -import sigmastate.Values.{ConcreteCollection, LongConstant, SValue, SigmaBoolean, Value} -import sigmastate._ +import sigma.{Coll, _} +import sigma.ast.SCollection.SByteArray +import sigma.ast.syntax.{SValue, ValueOps} +import sigma.ast._ +import sigma.crypto.CryptoConstants +import sigma.data.{CAnyValue, CSigmaDslBuilder, ProveDHTuple, ProveDlog, SigmaBoolean} +import sigma.util.Extensions.BigIntegerOps +import sigmastate.helpers.NegativeTesting +import sigmastate.interpreter.Interpreter.ScriptEnv +import sigma.ast.{Ident, MethodCallLike} import java.math.BigInteger -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.SCollection.SByteArray -import sigmastate.crypto.{ProveDHTuple, CryptoConstants} -import sigmastate.interpreter.Interpreter.ScriptEnv -import sigma._ -import sigmastate.eval._ -import sigmastate.helpers.NegativeTesting -import sigma.Coll trait LangTests extends Matchers with NegativeTesting { @@ -37,13 +36,13 @@ trait LangTests extends Matchers with NegativeTesting { val ecp2 = dlog.multiplyGroupElements(ecp1, ecp1) val ecp3 = dlog.multiplyGroupElements(ecp2, ecp2) val ecp4 = dlog.multiplyGroupElements(ecp3, ecp3) - val g1 = CostingSigmaDslBuilder.GroupElement(ecp1) - val g2 = CostingSigmaDslBuilder.GroupElement(ecp2) - val g3 = CostingSigmaDslBuilder.GroupElement(ecp3) - val g4 = CostingSigmaDslBuilder.GroupElement(ecp4) + val g1 = CSigmaDslBuilder.GroupElement(ecp1) + val g2 = CSigmaDslBuilder.GroupElement(ecp2) + val g3 = CSigmaDslBuilder.GroupElement(ecp3) + val g4 = CSigmaDslBuilder.GroupElement(ecp4) - protected val n1: BigInt = BigInt(10).underlying() - protected val n2: BigInt = BigInt(20).underlying() + protected val n1: BigInt = BigInt(10).underlying().toBigInt + protected val n2: BigInt = BigInt(20).underlying().toBigInt protected val bigIntegerArr1: Coll[BigInt] = Colls.fromItems(n1, n2) protected val big: BigInteger = BigInt(Long.MaxValue).underlying().pow(2) protected val p1: SigmaBoolean = ProveDlog(ecp1) diff --git a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala index 42d02fefb5..7213088a05 100644 --- a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala +++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala @@ -6,13 +6,14 @@ import org.ergoplatform.ErgoBox import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigmastate.SCollection._ -import sigmastate.Values._ +import sigma.ast.SCollection.{SByteArray, SLongArray} +import sigma.ast._ +import sigma.ast.syntax.SValue import sigmastate._ -import sigmastate.lang.SigmaPredef.PredefinedFuncRegistry -import sigmastate.lang.Terms._ -import sigmastate.lang.syntax.ParserException -import sigmastate.serialization.OpCodes +import SigmaPredef.PredefinedFuncRegistry +import sigma.ast.syntax._ +import sigmastate.lang.parsers.ParserException +import sigma.serialization.OpCodes class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests { import StdSigmaBuilder._ @@ -538,7 +539,7 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat property("Box properties") { parse("{ (box: Box) => box.value }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), "value")) - parse("{ (box: Box) => box.propositionBytes }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), SBox.PropositionBytes)) + parse("{ (box: Box) => box.propositionBytes }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), SBoxMethods.PropositionBytes)) parse("{ (box: Box) => box.bytes }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), "bytes")) parse("{ (box: Box) => box.id }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), "id")) } diff --git a/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala b/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala index d428a75294..8fe26a30b0 100644 --- a/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala +++ b/sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala @@ -1,15 +1,16 @@ package sigmastate.lang.js import org.ergoplatform.ErgoAddressEncoder -import org.ergoplatform.sdk.js.Isos.isoValueToConstant import org.scalablytyped.runtime.StringDictionary +import sigma.ast +import sigma.ast.js.{ErgoTree, isoValueToConstant} import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel -import org.ergoplatform.sdk.js.{ErgoTree, Value} -import sigmastate.Values +import sigma.js.Value +import sigma.ast.ErgoTree.HeaderType import sigmastate.eval.CompiletimeIRContext -import sigmastate.lang.Terms.ValueOps +import sigma.ast.syntax.ValueOps /** Wrapper exported to JS. */ @@ -27,7 +28,7 @@ class SigmaCompiler(_compiler: sigmastate.lang.SigmaCompiler) extends js.Object def compile( namedConstants: StringDictionary[Value], segregateConstants: Boolean, - additionalHeaderFlags: Byte, ergoScript: String): ErgoTree = { + treeHeader: Byte, ergoScript: String): ErgoTree = { val env = StringDictionary .wrapStringDictionary(namedConstants) .view.mapValues(v => isoValueToConstant.to(v)).toMap @@ -36,15 +37,15 @@ class SigmaCompiler(_compiler: sigmastate.lang.SigmaCompiler) extends js.Object require(prop.tpe.isSigmaProp, s"Expected SigmaProp expression type bue got ${prop.tpe}: $prop") val tree = if (segregateConstants) { - Values.ErgoTree.withSegregation(additionalHeaderFlags, prop.asSigmaProp) + ast.ErgoTree.withSegregation(HeaderType @@ treeHeader, prop.asSigmaProp) } else { - Values.ErgoTree.withoutSegregation(additionalHeaderFlags, prop.asSigmaProp) + ast.ErgoTree.withoutSegregation(HeaderType @@ treeHeader, prop.asSigmaProp) } new ErgoTree(tree) } } -@JSExportTopLevel("SigmaCompilerObj") +@JSExportTopLevel("SigmaCompiler$") object SigmaCompiler extends js.Object { /** Creates a new instance of SigmaCompiler for the mainnet. */ def forMainnet(): SigmaCompiler = create(ErgoAddressEncoder.MainnetNetworkPrefix) @@ -58,7 +59,7 @@ object SigmaCompiler extends js.Object { * @return SigmaCompiler instance */ private def create(networkPrefix: Byte): SigmaCompiler = { - val compiler = new sigmastate.lang.SigmaCompiler(networkPrefix) + val compiler = sigmastate.lang.SigmaCompiler(networkPrefix) new SigmaCompiler(compiler) } } diff --git a/sc/jvm/src/test/scala/sigmastate/ErgoTreeBenchmarks.scala b/sc/jvm/src/test/scala/sigmastate/ErgoTreeBenchmarks.scala index f5741dcb42..2b23d85846 100644 --- a/sc/jvm/src/test/scala/sigmastate/ErgoTreeBenchmarks.scala +++ b/sc/jvm/src/test/scala/sigmastate/ErgoTreeBenchmarks.scala @@ -3,15 +3,16 @@ package sigmastate import debox.cfor import org.scalameter.api.Bench import sigma.BenchmarkGens -import sigmastate.Values.{IntConstant, SValue} -import sigmastate.serialization.OpCodes.PlusCode +import sigma.ast.{ArithOp, EQ, IntConstant, SType} +import sigma.ast.syntax.SValue +import sigma.serialization.OpCodes.PlusCode object ErgoTreeBenchmarks extends Bench.LocalTime with BenchmarkGens { suite: Bench[Double] => override def maxSize: Int = 10000 /** Expected approximate results: - * ::Benchmark allocation of sigmastate.Values.ArithOp, EQ, IntConstant:: + * ::Benchmark allocation of sigma.ast.ArithOp, EQ, IntConstant:: * name: OpenJDK 64-Bit Server VM * osArch: x86_64 * osName: Mac OS X @@ -22,7 +23,7 @@ object ErgoTreeBenchmarks extends Bench.LocalTime with BenchmarkGens { suite: Be * Parameters(size -> 1000): 0.696851 ms * Parameters(size -> 10000): 5.687967 ms */ - performance of "allocation of sigmastate.Values" in { + performance of "allocation of sigma.ast" in { measure method "ArithOp, EQ, IntConstant" in { using(sizes) in { size => val arr = new Array[SValue](size) @@ -57,7 +58,7 @@ object ErgoTreeBenchmarks extends Bench.LocalTime with BenchmarkGens { suite: Be measure method "isCorrectType" in { using(sizes) in { size => cfor(0)(_ < size, _ + 1) { i => - sigmastate.crypto.Platform.isCorrectType(i, SType.allPredefTypes(i % 10)) + sigma.crypto.Platform.isCorrectType(i, SType.allPredefTypes(i % 10)) } } } diff --git a/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala b/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala index 28b2733c31..1375b48d99 100644 --- a/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala +++ b/sc/jvm/src/test/scala/sigmastate/ReflectionGenerator.scala @@ -34,7 +34,7 @@ object ReflectionGenerator { "sigma.", "sigma.", "special.wrappers.", - "sigmastate.Values.", + "sigma.ast.", "sigmastate.lang.Terms.", "sigmastate.interpreter.", "sigmastate.utxo.", diff --git a/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala b/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala index 5a2c65d565..54c0f652dc 100644 --- a/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala +++ b/sc/jvm/src/test/scala/sigmastate/helpers/SigmaPPrintSpec.scala @@ -1,18 +1,17 @@ package sigmastate.helpers import org.ergoplatform.settings.ErgoAlgos -import org.ergoplatform.{ErgoBox, Outputs} -import sigma.data.{CollType, RType} +import org.ergoplatform.ErgoBox import scorex.util.ModifierId -import sigmastate.Values._ -import sigmastate._ -import sigmastate.eval.Extensions.ArrayOps +import sigma.Extensions.ArrayOps +import sigma.SigmaDslTesting +import sigma.ast._ +import sigma.data.{AvlTreeData, AvlTreeFlags, CBox, CollType, Digest32Coll} +import ErgoTree.HeaderType import sigmastate.eval._ -import sigmastate.lang.Terms.MethodCall -import sigmastate.serialization.OpCodes +import sigma.ast.MethodCall +import sigma.serialization.OpCodes import sigmastate.utils.Helpers -import sigmastate.utxo.SelectField -import sigma.SigmaDslTesting import java.math.BigInteger import scala.collection.mutable.ArrayBuffer @@ -88,7 +87,7 @@ class SigmaPPrintSpec extends SigmaDslTesting { |)""".stripMargin) test( new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Vector(IntArrayConstant(Array(10, 20))), Right(BoolToSigmaProp(TrueLeaf)) ), @@ -98,10 +97,10 @@ class SigmaPPrintSpec extends SigmaDslTesting { | Right(BoolToSigmaProp(TrueLeaf)) |)""".stripMargin) test( - CostingBox( + CBox( new ErgoBox( 9223372036854775807L, - new ErgoTree(0.toByte, Vector(), Right(BoolToSigmaProp(FalseLeaf))), + new ErgoTree(HeaderType @@ 0.toByte, Vector(), Right(BoolToSigmaProp(FalseLeaf))), Coll( (Digest32Coll @@ (ErgoAlgos.decodeUnsafe("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001").toColl), 10000000L) ), @@ -111,7 +110,7 @@ class SigmaPPrintSpec extends SigmaDslTesting { 1000000 ) ), - """CostingBox( + """CBox( | new ErgoBox( | 9223372036854775807L, | new ErgoTree(0.toByte, Vector(), Right(BoolToSigmaProp(FalseLeaf))), @@ -149,13 +148,13 @@ class SigmaPPrintSpec extends SigmaDslTesting { test( MethodCall.typed[Value[SCollection[SBox.type]]]( ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + SContextMethods.getMethodByName("dataInputs"), Vector(), Map() ), """MethodCall.typed[Value[SCollection[SBox.type]]]( | ValUse(1, SContext), - | SContext.getMethodByName("dataInputs"), + | SContextMethods.getMethodByName("dataInputs"), | Vector(), | Map() |)""".stripMargin) @@ -165,13 +164,13 @@ class SigmaPPrintSpec extends SigmaDslTesting { test( MethodCall.typed[Value[SCollection[SInt.type]]]( ValUse(1, SCollectionType(SBox)), - SCollection.IndicesMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SCollectionMethods.IndicesMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), Vector(), Map() ), """MethodCall.typed[Value[SCollection[SInt.type]]]( | ValUse(1, SCollectionType(SBox)), - | SCollection.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SBox)), + | SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SBox)), | Vector(), | Map() |)""".stripMargin) diff --git a/sc/jvm/src/test/scala/sigmastate/utils/GenSerializers.scala b/sc/jvm/src/test/scala/sigmastate/utils/GenSerializers.scala index 75e39111ab..1aa3d81c33 100644 --- a/sc/jvm/src/test/scala/sigmastate/utils/GenSerializers.scala +++ b/sc/jvm/src/test/scala/sigmastate/utils/GenSerializers.scala @@ -3,8 +3,8 @@ package sigmastate.utils import sigma.util.Extensions.ByteOps import sigma.util.FileUtil import sigma.util.PrintExtensions._ -import sigmastate.lang.Terms.{MethodCall, PropertyCall} -import sigmastate.serialization.ValueSerializer._ +import sigma.ast.{MethodCall, PropertyCall} +import sigma.serialization.ValueSerializer._ /** Generate contents of ErgoTree serializer format specification. * To generate serialization formats, it is necessary that all branches of serializers diff --git a/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala b/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala index b8c0db379a..faded6e28c 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/ErgoScriptPredef.scala @@ -1,18 +1,19 @@ package org.ergoplatform -import sigmastate.SType import sigmastate.lang.SigmaCompiler import sigmastate.eval.IRContext import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix -import sigmastate.Values.{SigmaPropValue, Value} -import sigmastate.lang.Terms.ValueOps +import sigma.ast.SType +import sigma.ast.syntax.SigmaPropValue +import sigma.ast.Value +import sigma.ast.syntax.ValueOps object ErgoScriptPredef { import sigmastate.interpreter.Interpreter._ /** Compiles the given ErgoScript `code` into ErgoTree expression. */ def compileWithCosting(env: ScriptEnv, code: String, networkPrefix: NetworkPrefix)(implicit IR: IRContext): Value[SType] = { - val compiler = new SigmaCompiler(networkPrefix) + val compiler = SigmaCompiler(networkPrefix) val res = compiler.compile(env, code) res.buildTree } diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala index 5950b8fcdb..484b9e2728 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSpec.scala @@ -1,20 +1,20 @@ package org.ergoplatform.dsl import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} -import sigmastate.interpreter.{CostedProverResult, ProverResult} -import sigma.data.RType +import sigma.interpreter.{CostedProverResult, ProverResult} +import sigma.data.{CSigmaDslBuilder, RType} import org.ergoplatform.{ErgoBox, ErgoLikeContext} -import sigma.{AnyValue, Coll, SigmaDslBuilder, SigmaProp} -import sigmastate.Values.ErgoTree -import sigmastate.eval.{CostingSigmaDslBuilder, IRContext} +import sigma.{Coll, SigmaDslBuilder, SigmaProp} +import sigmastate.eval.IRContext import scala.util.Try import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition, Token} +import sigma.ast.{ErgoTree, EvaluatedValue, SType} import scala.language.implicitConversions trait ContractSpec { - val dsl: SigmaDslBuilder = CostingSigmaDslBuilder + val dsl: SigmaDslBuilder = CSigmaDslBuilder val Colls = dsl.Colls implicit def Coll[T](items: Array[T])(implicit cT: RType[T]): Coll[T] = Colls.fromArray(items) @@ -49,7 +49,7 @@ trait ContractSpec { /** Generate proof for the given `inBox`. The input box has attached guarding proposition, * which is executed in the Context, specifically created for `inBox`.*/ - def prove(inBox: InputBox, extensions: Map[Byte, AnyValue] = Map()): Try[CostedProverResult] + def prove(inBox: InputBox, extensions: Map[Byte, EvaluatedValue[_ <: SType]] = Map()): Try[CostedProverResult] } object ProvingParty { def apply(name: String): ProvingParty = mkProvingParty(name) @@ -68,7 +68,7 @@ trait ContractSpec { trait InputBox { def tx: TransactionCandidate def utxoBox: OutBox - def runDsl(extensions: Map[Byte, AnyValue] = Map()): SigmaProp + def runDsl(extensions: Map[Byte, EvaluatedValue[_ <: SType]] = Map()): SigmaProp private [dsl] def toErgoContext: ErgoLikeContext } diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala index de3012ff8f..11cbaff739 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ContractSyntax.scala @@ -1,17 +1,21 @@ package org.ergoplatform.dsl +import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.TokenId -import sigma.data.RType -import sigmastate.SType -import sigmastate.SType.AnyOps +import sigma.data.{AvlTreeData, CSigmaDslBuilder, RType, SigmaBoolean} import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition} -import sigmastate.eval.{CostingSigmaDslBuilder, Evaluation} +import org.ergoplatform.sdk.JavaHelpers.collRType import sigmastate.interpreter.Interpreter.ScriptEnv -import sigma.{SigmaProp, SigmaContract, Context, SigmaDslBuilder} +import sigma._ +import sigma.ast.{SType, syntax} +import sigma.ast.SType.AnyOps + +import scala.reflect.ClassTag +import scala.util.Try /** Defines methods to be used in contract implementations based on [[SigmaContract]]. */ trait ContractSyntax { contract: SigmaContract => - override def builder: SigmaDslBuilder = CostingSigmaDslBuilder + override def builder: SigmaDslBuilder = CSigmaDslBuilder /** Instance of contract specification DSL, which can be imported in the body of * [[SigmaContract]] implementations. */ @@ -29,6 +33,41 @@ trait ContractSyntax { contract: SigmaContract => /** Helper method to support Scala <-> ErgoScript equivalence. */ def Coll[T](items: T*)(implicit cT: RType[T]) = builder.Colls.fromItems(items:_*) + /** Tries to reconstruct RType of the given value. + * If not successfull returns failure. */ + def rtypeOf(value: Any): Try[RType[_]] = Try { + value match { + case arr if arr.getClass.isArray => + val itemClass = arr.getClass.getComponentType + if (itemClass.isPrimitive) { + val itemTag = ClassTag[Any](itemClass) + RType.fromClassTag(itemTag) + } else + sys.error(s"Cannot compute rtypeOf($value): non-primitive type of array items") + case coll: Coll[_] => collRType(coll.tItem) + + // all primitive types + case _: Boolean => BooleanType + case _: Byte => ByteType + case _: Short => ShortType + case _: Int => IntType + case _: Long => LongType + case _: String => StringType + case _: Unit => UnitType + case _: sigma.BigInt => BigIntRType + case _: GroupElement => GroupElementRType + case _: ErgoBox => syntax.ErgoBoxRType // TODO remove this RType + case _: Box => BoxRType + case _: AvlTreeData => syntax.AvlTreeDataRType // TODO remove this RType + case _: AvlTree => AvlTreeRType + case _: SigmaBoolean => SigmaBooleanRType // TODO remove this RType + case _: SigmaProp => SigmaPropRType + case _: Context => ContextRType + case _ => + sys.error(s"Don't know how to compute typeOf($value)") + } + } + /** Call this function in [[SigmaContract]] implementations to define propositions. * * @param name name of the proposition (aka contract name) @@ -44,7 +83,7 @@ trait ContractSyntax { contract: SigmaContract => scriptCode: String, scriptVersion: Option[Byte] = None): spec.PropositionSpec = { val env = contractEnv.map { case (k, v) => - val tV = Evaluation.rtypeOf(v).get + val tV = rtypeOf(v).get val elemTpe = Evaluation.rtypeToSType(tV) k -> spec.IR.builder.mkConstant[SType](v.asWrappedType, elemTpe) }.toMap diff --git a/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala b/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala index c1b0b40381..2c31304b70 100644 --- a/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala +++ b/sc/shared/src/main/scala/org/ergoplatform/dsl/ErgoContractSpec.scala @@ -1,10 +1,10 @@ package org.ergoplatform.dsl import sigma.Coll -import sigmastate.interpreter.CostedProverResult import sigmastate.eval.IRContext import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition, Token} import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, TokenId} +import sigma.interpreter.CostedProverResult class ErgoContractSpec(implicit val IR: IRContext) extends ContractSpec { diff --git a/sc/shared/src/main/scala/scalan/GraphIRReflection.scala b/sc/shared/src/main/scala/scalan/GraphIRReflection.scala index f7218afc92..0eaba9d8a3 100644 --- a/sc/shared/src/main/scala/scalan/GraphIRReflection.scala +++ b/sc/shared/src/main/scala/scalan/GraphIRReflection.scala @@ -11,6 +11,10 @@ import special.wrappers.{OptionWrapSpec, RTypeWrapSpec} import wrappers.scalan.WRTypes /** Registrations of reflection metadata for graph-ir module (see README.md). + * Such metadata is only used on JS platform to support reflection-like interfaces of + * RClass, RMethod, RConstructor. These interfaces implemented on JVM using Java + * reflection. + * * For each class of this module that needs reflection metadata, * we register a class entry with the necessary information. * Only information that is needed at runtime is registered. @@ -26,10 +30,10 @@ object GraphIRReflection { mkMethod(clazz, "filter", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.WOption[Any]].filter(args(0).asInstanceOf[ctx.Ref[Any => Boolean]]) }, - mkMethod(clazz, "get", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "get", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.WOption[_]].get }, - mkMethod(clazz, "isDefined", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "isDefined", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.WOption[_]].isDefined }, mkMethod(clazz, "getOrElse", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => @@ -76,10 +80,10 @@ object GraphIRReflection { mkMethod(clazz, "$bar$bar", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.SigmaProp].$bar$bar(args(0).asInstanceOf[ctx.Ref[ctx.SigmaProp]]) }, - mkMethod(clazz, "isValid", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "isValid", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.SigmaProp].isValid }, - mkMethod(clazz, "propBytes", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "propBytes", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.SigmaProp].propBytes }, mkMethod(clazz, "$amp$amp", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => @@ -221,26 +225,26 @@ object GraphIRReflection { obj.asInstanceOf[ctx.AvlTree].update(args(0).asInstanceOf[ctx.Ref[ctx.Coll[(ctx.Coll[Byte], ctx.Coll[Byte])]]], args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]]) }, - mkMethod(clazz, "keyLength", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "keyLength", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.AvlTree].keyLength }, - mkMethod(clazz, "enabledOperations", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "enabledOperations", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.AvlTree].enabledOperations }, mkMethod(clazz, "updateDigest", Array[Class[_]](classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.AvlTree].updateDigest(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]]) }, - mkMethod(clazz, "digest", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "digest", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.AvlTree].digest }, mkMethod(clazz, "insert", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) => obj.asInstanceOf[ctx.AvlTree].insert(args(0).asInstanceOf[ctx.Ref[ctx.Coll[(ctx.Coll[Byte], ctx.Coll[Byte])]]], args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]]) }, - mkMethod(clazz, "isRemoveAllowed", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "isRemoveAllowed", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.AvlTree].isRemoveAllowed }, - mkMethod(clazz, "valueLengthOpt", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "valueLengthOpt", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.AvlTree].valueLengthOpt }, mkMethod(clazz, "get", Array[Class[_]](classOf[Base#Ref[_]], classOf[Base#Ref[_]])) { (obj, args) => @@ -254,10 +258,10 @@ object GraphIRReflection { obj.asInstanceOf[ctx.AvlTree].contains(args(0).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]], args(1).asInstanceOf[ctx.Ref[ctx.Coll[Byte]]]) }, - mkMethod(clazz, "isUpdateAllowed", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "isUpdateAllowed", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.AvlTree].isUpdateAllowed }, - mkMethod(clazz, "isInsertAllowed", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "isInsertAllowed", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.AvlTree].isInsertAllowed } ) @@ -268,22 +272,22 @@ object GraphIRReflection { val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only registerClassEntry(clazz, methods = Map( - mkMethod(clazz, "value", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "value", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.Box].value }, - mkMethod(clazz, "id", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "id", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.Box].id }, - mkMethod(clazz, "creationInfo", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "creationInfo", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.Box].creationInfo }, - mkMethod(clazz, "bytes", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "bytes", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.Box].bytes }, mkMethod(clazz, "getReg", Array[Class[_]](classOf[Base#Ref[_]], classOf[TypeDescs#Elem[_]])) { (obj, args) => obj.asInstanceOf[ctx.Box].getReg(args(0).asInstanceOf[ctx.Ref[Int]])(args(1).asInstanceOf[ctx.Elem[_]]) }, - mkMethod(clazz, "tokens", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "tokens", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.Box].tokens }, mkMethod(clazz, "bytesWithoutRef", Array[Class[_]]()) { (obj, args) => @@ -415,25 +419,25 @@ object GraphIRReflection { val ctx = null.asInstanceOf[SigmaLibrary] // ok! type level only registerClassEntry(clazz, methods = Map( - mkMethod(clazz, "minerPk", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "minerPk", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.PreHeader].minerPk }, - mkMethod(clazz, "votes", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "votes", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.PreHeader].votes }, - mkMethod(clazz, "nBits", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "nBits", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.PreHeader].nBits }, - mkMethod(clazz, "version", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "version", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.PreHeader].version }, - mkMethod(clazz, "timestamp", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "timestamp", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.PreHeader].timestamp }, - mkMethod(clazz, "parentId", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "parentId", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.PreHeader].parentId }, - mkMethod(clazz, "height", Array[Class[_]]()) { (obj, args) => + mkMethod(clazz, "height", Array[Class[_]]()) { (obj, _) => obj.asInstanceOf[ctx.PreHeader].height } ) diff --git a/sc/shared/src/main/scala/scalan/TypeDescs.scala b/sc/shared/src/main/scala/scalan/TypeDescs.scala index cb5f954109..fdef77a108 100644 --- a/sc/shared/src/main/scala/scalan/TypeDescs.scala +++ b/sc/shared/src/main/scala/scalan/TypeDescs.scala @@ -3,7 +3,6 @@ package scalan import scala.language.implicitConversions import scala.annotation.implicitNotFound import scala.collection.immutable.ListMap -import sigma.data.RType._ import scala.collection.mutable import debox.cfor diff --git a/sc/shared/src/main/scala/scalan/primitives/Functions.scala b/sc/shared/src/main/scala/scalan/primitives/Functions.scala index e5cd6f345e..31a6ca8d81 100644 --- a/sc/shared/src/main/scala/scalan/primitives/Functions.scala +++ b/sc/shared/src/main/scala/scalan/primitives/Functions.scala @@ -332,7 +332,7 @@ trait Functions extends Base with ProgramGraphs { self: Scalan => val m = new java.util.HashMap[Sym, Sym](100) m.put(lam.x, s) val subst = new MapTransformer(m) - val t = DefaultMirror.mirrorSymbols(subst, NoRewriting, lam, body) + val t = DefaultMirror.mirrorSymbols(subst, NoRewriting, body) t(lam.y) } diff --git a/sc/shared/src/main/scala/scalan/primitives/Thunks.scala b/sc/shared/src/main/scala/scalan/primitives/Thunks.scala index ce51dc5638..f9f843664d 100644 --- a/sc/shared/src/main/scala/scalan/primitives/Thunks.scala +++ b/sc/shared/src/main/scala/scalan/primitives/Thunks.scala @@ -323,7 +323,7 @@ trait Thunks extends Functions { self: Scalan => */ def forceThunkDefByMirror[A](th: ThunkDef[A], subst: MapTransformer = MapTransformer.empty()): Ref[A] = { val body = th.scheduleIds - val t = DefaultMirror.mirrorSymbols(subst, NoRewriting, th, body) + val t = DefaultMirror.mirrorSymbols(subst, NoRewriting, body) t(th.root) } diff --git a/sc/shared/src/main/scala/scalan/primitives/Tuples.scala b/sc/shared/src/main/scala/scalan/primitives/Tuples.scala index c22b8a31f4..fd201b9574 100644 --- a/sc/shared/src/main/scala/scalan/primitives/Tuples.scala +++ b/sc/shared/src/main/scala/scalan/primitives/Tuples.scala @@ -58,8 +58,6 @@ trait Tuples extends Base { self: Scalan => case Tup(a, b) => (a, b) case _ => p.elem match { case pe: PairElem[_, _] => - implicit val eA = pe.eFst - implicit val eB = pe.eSnd if (cachePairs) { if (!tuplesCache.containsKey(p)) { tuplesCache.put(p, (First(p), Second(p))) @@ -74,8 +72,6 @@ trait Tuples extends Base { self: Scalan => } implicit def zipPair[A, B](p: (Ref[A], Ref[B])): Ref[(A, B)] = { - implicit val ea = p._1.elem - implicit val eb = p._2.elem Tup(p._1, p._2) } diff --git a/sc/shared/src/main/scala/scalan/staged/ProgramGraphs.scala b/sc/shared/src/main/scala/scalan/staged/ProgramGraphs.scala index 19d762c5bb..ccb351e7a2 100644 --- a/sc/shared/src/main/scala/scalan/staged/ProgramGraphs.scala +++ b/sc/shared/src/main/scala/scalan/staged/ProgramGraphs.scala @@ -63,28 +63,6 @@ trait ProgramGraphs extends AstGraphs { self: Scalan => sch } - /** Mirror all the nodes of this graph applying transformer and performing rewriting. - * @param m mirror instance to be used for mirroring of nodes - * @param rw rewriter to be tried for each new created mirrored node - * @param t transformer of symbols, to be used for substitution of symbols in the new nodes. - * @return new graph which is not necessary clone of this graph, but should be semantically - * equivalent to this graph (provided all rw rules preserve equivalence). - * If rw is identity, then the resulting graph is alpha-equivalent to this graph - * as long as t is bijection. - */ - def transform(m: Mirror, rw: Rewriter, t: Transformer): ProgramGraph = { - val t0 = mapping match { - case Nullable(mapping) => t merge mapping - case _ => t - } - val t1 = m.mirrorSymbols(t0, rw, this, scheduleIds) - val newRoots = roots map { t1(_) } - new ProgramGraph(newRoots, Nullable(t1), filterNode) - } - - /** Remove transformer component of the graph. */ - def withoutContext = ProgramGraph(roots, Nullable.None, filterNode) - override def toString: String = { val mappingStr = if (mapping.isEmpty) "None" else mapping.toString val filterNodeStr = if (filterNode.isDefined) filterNode.toString else "None" @@ -92,11 +70,4 @@ trait ProgramGraphs extends AstGraphs { self: Scalan => } } - object ProgramGraph { - def transform[A](s: Ref[A], rw: Rewriter = NoRewriting, t: MapTransformer = MapTransformer.empty()): Ref[A] = { - val g = ProgramGraph(List(s), Nullable.None, Nullable.None) - val g1 = g.transform(DefaultMirror, rw, t) - g1.roots(0).asInstanceOf[Ref[A]] - } - } } diff --git a/sc/shared/src/main/scala/scalan/staged/Transforming.scala b/sc/shared/src/main/scala/scalan/staged/Transforming.scala index 045340e503..cd1fac9fd0 100644 --- a/sc/shared/src/main/scala/scalan/staged/Transforming.scala +++ b/sc/shared/src/main/scala/scalan/staged/Transforming.scala @@ -65,10 +65,6 @@ trait Transforming { self: Scalan => def beginPass(pass: Pass): Unit = { _currentPass = pass } - /** Called to let this IR context to finalized the given pass. */ - def endPass(pass: Pass): Unit = { - _currentPass = Pass.defaultPass - } /** Concrete and default implementation of Transformer using underlying HashMap. * HOTSPOT: don't beatify the code */ @@ -152,7 +148,7 @@ trait Transforming { self: Scalan => protected def mirrorElem(node: Sym): Elem[_] = node.elem // every mirrorXXX method should return a pair (t + (v -> v1), v1) - protected def mirrorVar[A](t: Transformer, rewriter: Rewriter, v: Ref[A]): Transformer = { + protected def mirrorVar[A](t: Transformer, v: Ref[A]): Transformer = { val newVar = variable(Lazy(mirrorElem(v))) t + (v, newVar) } @@ -173,7 +169,7 @@ trait Transforming { self: Scalan => protected def mirrorLambda[A, B](t: Transformer, rewriter: Rewriter, node: Ref[A => B], lam: Lambda[A, B]): Transformer = { var tRes: Transformer = t - val t1 = mirrorNode(t, rewriter, lam, lam.x) + val t1 = mirrorNode(t, rewriter, lam.x) // original root val originalRoot = lam.y @@ -190,7 +186,7 @@ trait Transforming { self: Scalan => // lambdaStack = newLambdaCandidate :: lambdaStack val newRoot = { // reifyEffects block val schedule = lam.scheduleIds - val t2 = mirrorSymbols(t1, rewriter, lam, schedule) + val t2 = mirrorSymbols(t1, rewriter, schedule) tRes = t2 tRes(originalRoot) // this will be a new root } @@ -214,7 +210,7 @@ trait Transforming { self: Scalan => val newScope = thunkStack.beginScope(newThunkSym) val schedule = thunk.scheduleIds - val t1 = mirrorSymbols(t, rewriter, thunk, schedule) + val t1 = mirrorSymbols(t, rewriter, schedule) thunkStack.endScope() val newRoot = t1(thunk.root) @@ -230,12 +226,12 @@ trait Transforming { self: Scalan => protected def isMirrored(t: Transformer, node: Sym): Boolean = t.isDefinedAt(node) - def mirrorNode(t: Transformer, rewriter: Rewriter, g: AstGraph, node: Sym): Transformer = { + def mirrorNode(t: Transformer, rewriter: Rewriter, node: Sym): Transformer = { if (isMirrored(t, node)) t else { node.node match { - case v: Variable[_] => - mirrorVar(t, rewriter, node) + case _: Variable[_] => + mirrorVar(t, node) case lam: Lambda[a, b] => mirrorLambda(t, rewriter, node.asInstanceOf[Ref[a => b]], lam) case th: ThunkDef[a] => @@ -247,12 +243,12 @@ trait Transforming { self: Scalan => } /** HOTSPOT: */ - def mirrorSymbols(t0: Transformer, rewriter: Rewriter, g: AstGraph, nodes: DBuffer[Int]) = { + def mirrorSymbols(t0: Transformer, rewriter: Rewriter, nodes: DBuffer[Int]): Transformer = { var t: Transformer = t0 cfor(0)(_ < nodes.length, _ + 1) { i => val n = nodes(i) val s = getSym(n) - t = mirrorNode(t, rewriter, g, s) + t = mirrorNode(t, rewriter, s) } t } diff --git a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala index 99467289f2..9c9fa5ffe1 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala @@ -2,20 +2,20 @@ package sigmastate.eval import org.ergoplatform._ import scalan.MutableLazy +import sigma.{SigmaException, ast} +import sigma.ast.TypeCodes.LastConstantCode +import sigma.ast.Value.Typed +import sigma.ast._ +import sigma.ast.syntax.{SValue, ValueOps} +import sigma.crypto.EcPointType import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} import sigma.data.ExactOrdering.{ByteIsExactOrdering, IntIsExactOrdering, LongIsExactOrdering, ShortIsExactOrdering} +import sigma.data.{CSigmaDslBuilder, ExactIntegral, ExactNumeric, ExactOrdering, Lazy, Nullable} import sigma.util.Extensions.ByteOps -import sigma.data.{ExactIntegral, ExactNumeric, ExactOrdering, Lazy, Nullable} -import sigmastate.Values.Value.Typed -import sigmastate.Values._ import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.lang.{SourceContext, Terms} -import sigmastate.lang.Terms.{Ident, Select, Val, ValueOps} -import sigmastate.serialization.OpCodes -import sigmastate.utxo._ -import sigmastate._ -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.exceptions.{GraphBuildingException, SigmaException} +import sigma.ast.{Ident, Select, Val} +import sigma.exceptions.GraphBuildingException +import sigma.serialization.OpCodes import scala.collection.mutable.ArrayBuffer @@ -43,10 +43,9 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => import WOption._ /** Should be specified in the final cake */ - val builder: sigmastate.lang.SigmaBuilder + val builder: SigmaBuilder import builder._ - val okMeasureOperationTime: Boolean = false this.isInlineThunksOnForce = true // this required for splitting of cost graph @@ -60,16 +59,6 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => /** Whether to save calcF and costF graphs in the file given by ScriptNameProp environment variable */ var saveGraphsInFile: Boolean = false - // /** Pass configuration which is used by default in IRContext. */ - // val calcPass = new DefaultPass("calcPass", Pass.defaultPassConfig.copy(constantPropagation = true)) - // - // /** Pass configuration which is used during splitting cost function out of cost graph. - // * @see `RuntimeCosting.split2` */ - // val costPass = new DefaultPass("costPass", Pass.defaultPassConfig.copy(constantPropagation = true)) - - /** To enable specific configuration uncomment one of the lines above and use it in the beginPass below. */ - // beginPass(costPass) - /** Check the tuple type is valid. * In v5.x this code is taken from CheckTupleType validation rule which is no longer * part of consensus. @@ -173,7 +162,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case ThunkForce(Def(ThunkDef(root, sch))) if sch.isEmpty => root // Rule: l.isValid op Thunk {... root} => (l op TrivialSigma(root)).isValid - case ApplyBinOpLazy(op, SigmaM.isValid(l), Def(ThunkDef(root, sch))) if root.elem == BooleanElement => + case ApplyBinOpLazy(op, SigmaM.isValid(l), Def(ThunkDef(root, _))) if root.elem == BooleanElement => // don't need new Thunk because sigma logical ops always strict val r = asRep[SigmaProp](sigmaDslBuilder.sigmaProp(asRep[Boolean](root))) val res = if (op == And) @@ -298,7 +287,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case _: BigIntElem[_] => SBigInt case _: GroupElementElem[_] => SGroupElement case _: AvlTreeElem[_] => SAvlTree - case oe: WOptionElem[_, _] => sigmastate.SOption(elemToSType(oe.eItem)) + case oe: WOptionElem[_, _] => SOption(elemToSType(oe.eItem)) case _: BoxElem[_] => SBox case _: ContextElem[_] => SContext case _: SigmaDslBuilderElem[_] => SGlobal @@ -311,8 +300,6 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case _ => error(s"Don't know how to convert Elem $e to SType") } - import Liftables._ - /** Translates Elem to the corresponding Liftable instance. * @param eWT type descriptor */ @@ -339,7 +326,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => FuncIsLiftable(la, lb) }).asInstanceOf[Liftable[_,WT]] - import NumericOps._ + import sigma.data.NumericOps._ private lazy val elemToExactNumericMap = Map[Elem[_], ExactNumeric[_]]( (ByteElement, ByteIsExactIntegral), (ShortElement, ShortIsExactIntegral), @@ -390,12 +377,12 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case OpCodes.LtCode => OrderingLT[A](elemToExactOrdering(eA)) case OpCodes.GeCode => OrderingGTEQ[A](elemToExactOrdering(eA)) case OpCodes.LeCode => OrderingLTEQ[A](elemToExactOrdering(eA)) - case _ => error(s"Cannot find BinOp for opcode newOpCode(${opCode.toUByte-OpCodes.LastConstantCode}) and type $eA") + case _ => error(s"Cannot find BinOp for opcode newOpCode(${opCode.toUByte - LastConstantCode}) and type $eA") } import sigmastate._ - protected implicit def groupElementToECPoint(g: sigma.GroupElement): EcPointType = CostingSigmaDslBuilder.toECPoint(g).asInstanceOf[EcPointType] + protected implicit def groupElementToECPoint(g: sigma.GroupElement): EcPointType = CSigmaDslBuilder.toECPoint(g).asInstanceOf[EcPointType] def error(msg: String) = throw new GraphBuildingException(msg, None) def error(msg: String, srcCtx: Option[SourceContext]) = throw new GraphBuildingException(msg, srcCtx) @@ -484,7 +471,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val resV = toRep(v)(e) resV } - case org.ergoplatform.Context => ctx + case sigma.ast.Context => ctx case Global => sigmaDslBuilder case Height => ctx.HEIGHT case Inputs => ctx.INPUTS @@ -496,10 +483,10 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case Ident(n, _) => env.getOrElse(n, !!!(s"Variable $n not found in environment $env")) - case sigmastate.Upcast(Constant(value, _), toTpe: SNumericType) => + case ast.Upcast(Constant(value, _), toTpe: SNumericType) => eval(mkConstant(toTpe.upcast(value.asInstanceOf[AnyVal]), toTpe)) - case sigmastate.Downcast(Constant(value, _), toTpe: SNumericType) => + case ast.Downcast(Constant(value, _), toTpe: SNumericType) => eval(mkConstant(toTpe.downcast(value.asInstanceOf[AnyVal]), toTpe)) // Rule: col.size --> SizeOf(col) @@ -510,11 +497,11 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => error(s"The type of $obj is expected to be Collection to select 'size' property", obj.sourceContext.toOption) // Rule: proof.isProven --> IsValid(proof) - case Select(p, SSigmaProp.IsProven, _) if p.tpe == SSigmaProp => + case Select(p, SSigmaPropMethods.IsProven, _) if p.tpe == SSigmaProp => eval(SigmaPropIsProven(p.asSigmaProp)) // Rule: prop.propBytes --> SigmaProofBytes(prop) - case Select(p, SSigmaProp.PropBytes, _) if p.tpe == SSigmaProp => + case Select(p, SSigmaPropMethods.PropBytes, _) if p.tpe == SSigmaProp => eval(SigmaPropBytes(p.asSigmaProp)) // box.R$i[valType] => @@ -525,12 +512,12 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case sel @ Select(obj, field, _) if obj.tpe == SBox => (obj.asValue[SBox.type], field) match { - case (box, SBox.Value) => eval(mkExtractAmount(box)) - case (box, SBox.PropositionBytes) => eval(mkExtractScriptBytes(box)) - case (box, SBox.Id) => eval(mkExtractId(box)) - case (box, SBox.Bytes) => eval(mkExtractBytes(box)) - case (box, SBox.BytesWithoutRef) => eval(mkExtractBytesWithNoRef(box)) - case (box, SBox.CreationInfo) => eval(mkExtractCreationInfo(box)) + case (box, SBoxMethods.Value) => eval(mkExtractAmount(box)) + case (box, SBoxMethods.PropositionBytes) => eval(mkExtractScriptBytes(box)) + case (box, SBoxMethods.Id) => eval(mkExtractId(box)) + case (box, SBoxMethods.Bytes) => eval(mkExtractBytes(box)) + case (box, SBoxMethods.BytesWithoutRef) => eval(mkExtractBytesWithNoRef(box)) + case (box, SBoxMethods.CreationInfo) => eval(mkExtractCreationInfo(box)) case _ => error(s"Invalid access to Box property in $sel: field $field is not found", sel.sourceContext.toOption) } @@ -539,7 +526,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => eval(mkSelectField(tuple.asTuple, index)) case Select(obj, method, Some(tRes: SNumericType)) - if obj.tpe.isNumType && obj.asNumValue.tpe.isCastMethod(method) => + if obj.tpe.isNumType && SNumericTypeMethods.isCastMethod(method) => val numValue = obj.asNumValue if (numValue.tpe == tRes) eval(numValue) @@ -548,7 +535,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => else eval(mkUpcast(numValue, tRes)) - case Terms.Apply(col, Seq(index)) if col.tpe.isCollection => + case sigma.ast.Apply(col, Seq(index)) if col.tpe.isCollection => eval(mkByIndex(col.asCollection[SType], index.asValue[SInt.type], None)) case GetVar(id, optTpe) => @@ -558,7 +545,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => case ValUse(valId, _) => env.getOrElse(valId, !!!(s"ValUse $valId not found in environment $env")) - case Terms.Block(binds, res) => + case Block(binds, res) => var curEnv = env for (v @ Val(n, _, b) <- binds) { if (curEnv.contains(n)) @@ -591,37 +578,37 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val vv = asRep[GroupElement](_vv) sigmaDslBuilder.proveDHTuple(gv, hv, uv, vv) - case sigmastate.Exponentiate(In(l), In(r)) => + case Exponentiate(In(l), In(r)) => val lV = asRep[GroupElement](l) val rV = asRep[BigInt](r) lV.exp(rV) - case sigmastate.MultiplyGroup(In(_l), In(_r)) => + case MultiplyGroup(In(_l), In(_r)) => val l = asRep[GroupElement](_l) val r = asRep[GroupElement](_r) l.multiply(r) - case Values.GroupGenerator => + case GroupGenerator => sigmaDslBuilder.groupGenerator - case sigmastate.ByteArrayToBigInt(In(arr)) => + case ByteArrayToBigInt(In(arr)) => val arrV = asRep[Coll[Byte]](arr) sigmaDslBuilder.byteArrayToBigInt(arrV) - case sigmastate.LongToByteArray(In(x)) => + case LongToByteArray(In(x)) => val xV = asRep[Long](x) sigmaDslBuilder.longToByteArray(xV) // opt.get - case utxo.OptionGet(In(opt: ROption[_]@unchecked)) => + case OptionGet(In(opt: ROption[_]@unchecked)) => opt.get // opt.isDefined - case utxo.OptionIsDefined(In(opt: ROption[_]@unchecked)) => + case OptionIsDefined(In(opt: ROption[_]@unchecked)) => opt.isDefined // opt.getOrElse(default) - case utxo.OptionGetOrElse(In(opt: ROption[a]@unchecked), In(default)) => + case OptionGetOrElse(In(opt: ROption[a]@unchecked), In(default)) => opt.getOrElse(asRep[a](default)) // tup._1 or tup._2 @@ -630,16 +617,14 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => checkTupleType(IR)(eTuple) eTuple match { case pe: PairElem[a,b] => - assert(fieldIndex == 1 || fieldIndex == 2, s"Invalid field index $fieldIndex of the pair ${tup}: $pe") - implicit val ea = pe.eFst - implicit val eb = pe.eSnd + assert(fieldIndex == 1 || fieldIndex == 2, s"Invalid field index $fieldIndex of the pair $tup: $pe") val pair = asRep[(a,b)](tup) val res = if (fieldIndex == 1) pair._1 else pair._2 res } // (x, y) - case Values.Tuple(InSeq(Seq(x, y))) => + case Tuple(InSeq(Seq(x, y))) => Pair(x, y) // xs.exists(predicate) or xs.forall(predicate) @@ -705,7 +690,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val pV = asRep[Any => Boolean](eval(p)) inputV.filter(pV) - case Terms.Apply(f, Seq(x)) if f.tpe.isFunc => + case sigma.ast.Apply(f, Seq(x)) if f.tpe.isFunc => val fV = asRep[Any => Coll[Any]](eval(f)) val xV = asRep[Any](eval(x)) Apply(fV, xV, mayInline = false) @@ -720,7 +705,7 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val res = sigmaDslBuilder.sha256(inputV) res - case utxo.SizeOf(In(xs)) => + case ast.SizeOf(In(xs)) => xs.elem.asInstanceOf[Any] match { case _: CollElem[a,_] => val xsV = asRep[Coll[a]](xs) @@ -749,26 +734,26 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val pV = asRep[SigmaProp](eval(p)) pV.propBytes - case utxo.ExtractId(In(box: Ref[Box]@unchecked)) => + case ExtractId(In(box: Ref[Box]@unchecked)) => box.id - case utxo.ExtractBytesWithNoRef(In(box: Ref[Box]@unchecked)) => + case ExtractBytesWithNoRef(In(box: Ref[Box]@unchecked)) => box.bytesWithoutRef - case utxo.ExtractAmount(In(box)) => + case ExtractAmount(In(box)) => val boxV = asRep[Box](box) boxV.value - case utxo.ExtractScriptBytes(In(box: Ref[Box]@unchecked)) => + case ExtractScriptBytes(In(box: Ref[Box]@unchecked)) => box.propositionBytes - case utxo.ExtractBytes(In(box: Ref[Box]@unchecked)) => + case ExtractBytes(In(box: Ref[Box]@unchecked)) => box.bytes - case utxo.ExtractCreationInfo(In(box: Ref[Box]@unchecked)) => + case ExtractCreationInfo(In(box: Ref[Box]@unchecked)) => box.creationInfo - case utxo.ExtractRegisterAs(In(box: Ref[Box]@unchecked), regId, optTpe) => + case ExtractRegisterAs(In(box: Ref[Box]@unchecked), regId, optTpe) => val elem = stypeToElem(optTpe.elemType).asInstanceOf[Elem[Any]] val i: Ref[Int] = regId.number.toInt box.getReg(i)(elem) @@ -886,14 +871,14 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val y = eval(rel.right) binop.apply(x, asRep[t#WrappedType](y)) - case Terms.Lambda(_, Seq((n, argTpe)), _, Some(body)) => + case sigma.ast.Lambda(_, Seq((n, argTpe)), _, Some(body)) => val eArg = stypeToElem(argTpe).asInstanceOf[Elem[Any]] val f = fun(removeIsProven({ x: Ref[Any] => buildNode(ctx, env + (n -> x), body) }))(Lazy(eArg)) f - case Terms.Lambda(_, Seq((accN, accTpe), (n, tpe)), _, Some(body)) => + case sigma.ast.Lambda(_, Seq((accN, accTpe), (n, tpe)), _, Some(body)) => (stypeToElem(accTpe), stypeToElem(tpe)) match { case (eAcc: Elem[s], eA: Elem[a]) => val eArg = pairElement(eAcc, eA) val f = fun { x: Ref[(s, a)] => @@ -914,11 +899,11 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => val values = colBuilder.fromItems(vs: _*)(eAny) values - case sigmastate.Upcast(In(input), tpe) => + case ast.Upcast(In(input), tpe) => val elem = stypeToElem(tpe.asNumType) upcast(input)(elem) - case sigmastate.Downcast(In(input), tpe) => + case ast.Downcast(In(input), tpe) => val elem = stypeToElem(tpe.asNumType) downcast(input)(elem) @@ -936,221 +921,221 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => sigmaDslBuilder.decodePoint(bytes) // fallback rule for MethodCall, should be the last case in the list - case Terms.MethodCall(obj, method, args, _) => + case sigma.ast.MethodCall(obj, method, args, _) => val objV = eval(obj) val argsV = args.map(eval) (objV, method.objType) match { - case (xs: RColl[t]@unchecked, SCollection) => method.name match { - case SCollection.IndicesMethod.name => + case (xs: RColl[t]@unchecked, SCollectionMethods) => method.name match { + case SCollectionMethods.IndicesMethod.name => xs.indices - case SCollection.PatchMethod.name => + case SCollectionMethods.PatchMethod.name => val from = asRep[Int](argsV(0)) val patch = asRep[Coll[t]](argsV(1)) val replaced = asRep[Int](argsV(2)) xs.patch(from, patch, replaced) - case SCollection.UpdatedMethod.name => + case SCollectionMethods.UpdatedMethod.name => val index = asRep[Int](argsV(0)) val value = asRep[t](argsV(1)) xs.updated(index, value) - case SCollection.AppendMethod.name => + case SCollectionMethods.AppendMethod.name => val ys = asRep[Coll[t]](argsV(0)) xs.append(ys) - case SCollection.SliceMethod.name => + case SCollectionMethods.SliceMethod.name => val from = asRep[Int](argsV(0)) val until = asRep[Int](argsV(1)) xs.slice(from, until) - case SCollection.UpdateManyMethod.name => + case SCollectionMethods.UpdateManyMethod.name => val indexes = asRep[Coll[Int]](argsV(0)) val values = asRep[Coll[t]](argsV(1)) xs.updateMany(indexes, values) - case SCollection.IndexOfMethod.name => + case SCollectionMethods.IndexOfMethod.name => val elem = asRep[t](argsV(0)) val from = asRep[Int](argsV(1)) xs.indexOf(elem, from) - case SCollection.ZipMethod.name => + case SCollectionMethods.ZipMethod.name => val ys = asRep[Coll[Any]](argsV(0)) xs.zip(ys) - case SCollection.FlatMapMethod.name => + case SCollectionMethods.FlatMapMethod.name => val f = asRep[Any => Coll[Any]](argsV(0)) xs.flatMap(f) - case SCollection.MapMethod.name => + case SCollectionMethods.MapMethod.name => val f = asRep[Any => Any](argsV(0)) xs.map(f) - case SCollection.FilterMethod.name => + case SCollectionMethods.FilterMethod.name => val p = asRep[Any => Boolean](argsV(0)) xs.filter(p) - case SCollection.ForallMethod.name => + case SCollectionMethods.ForallMethod.name => val p = asRep[Any => Boolean](argsV(0)) xs.forall(p) - case SCollection.ExistsMethod.name => + case SCollectionMethods.ExistsMethod.name => val p = asRep[Any => Boolean](argsV(0)) xs.exists(p) - case SCollection.FoldMethod.name => + case SCollectionMethods.FoldMethod.name => val zero = asRep[Any](argsV(0)) val op = asRep[((Any, Any)) => Any](argsV(1)) xs.foldLeft(zero, op) - case SCollection.GetOrElseMethod.name => + case SCollectionMethods.GetOrElseMethod.name => val i = asRep[Int](argsV(0)) val d = asRep[t](argsV(1)) xs.getOrElse(i, d) case _ => throwError } - case (opt: ROption[t]@unchecked, SOption) => method.name match { - case SOption.GetMethod.name => + case (opt: ROption[t]@unchecked, SOptionMethods) => method.name match { + case SOptionMethods.GetMethod.name => opt.get - case SOption.GetOrElseMethod.name => + case SOptionMethods.GetOrElseMethod.name => val defaultTh = asRep[t](argsV(0)) opt.getOrElse(Thunk(defaultTh)) - case SOption.IsDefinedMethod.name => + case SOptionMethods.IsDefinedMethod.name => opt.isDefined - case SOption.MapMethod.name => + case SOptionMethods.MapMethod.name => opt.map(asRep[t => Any](argsV(0))) - case SOption.FilterMethod.name => + case SOptionMethods.FilterMethod.name => opt.filter(asRep[t => Boolean](argsV(0))) case _ => throwError } - case (ge: Ref[GroupElement]@unchecked, SGroupElement) => method.name match { - case SGroupElement.GetEncodedMethod.name => + case (ge: Ref[GroupElement]@unchecked, SGroupElementMethods) => method.name match { + case SGroupElementMethods.GetEncodedMethod.name => ge.getEncoded - case SGroupElement.NegateMethod.name => + case SGroupElementMethods.NegateMethod.name => ge.negate - case SGroupElement.MultiplyMethod.name => + case SGroupElementMethods.MultiplyMethod.name => val g2 = asRep[GroupElement](argsV(0)) ge.multiply(g2) - case SGroupElement.ExponentiateMethod.name => + case SGroupElementMethods.ExponentiateMethod.name => val k = asRep[BigInt](argsV(0)) ge.exp(k) case _ => throwError } - case (box: Ref[Box]@unchecked, SBox) => method.name match { - case SBox.tokensMethod.name => + case (box: Ref[Box]@unchecked, SBoxMethods) => method.name match { + case SBoxMethods.tokensMethod.name => box.tokens case _ => throwError } - case (ctx: Ref[Context]@unchecked, SContext) => method.name match { - case SContext.dataInputsMethod.name => + case (ctx: Ref[Context]@unchecked, SContextMethods) => method.name match { + case SContextMethods.dataInputsMethod.name => ctx.dataInputs - case SContext.headersMethod.name => + case SContextMethods.headersMethod.name => ctx.headers - case SContext.preHeaderMethod.name => + case SContextMethods.preHeaderMethod.name => ctx.preHeader - case SContext.inputsMethod.name => + case SContextMethods.inputsMethod.name => ctx.INPUTS - case SContext.outputsMethod.name => + case SContextMethods.outputsMethod.name => ctx.OUTPUTS - case SContext.heightMethod.name => + case SContextMethods.heightMethod.name => ctx.HEIGHT - case SContext.selfMethod.name => + case SContextMethods.selfMethod.name => ctx.SELF - case SContext.selfBoxIndexMethod.name => + case SContextMethods.selfBoxIndexMethod.name => ctx.selfBoxIndex - case SContext.lastBlockUtxoRootHashMethod.name => + case SContextMethods.lastBlockUtxoRootHashMethod.name => ctx.LastBlockUtxoRootHash - case SContext.minerPubKeyMethod.name => + case SContextMethods.minerPubKeyMethod.name => ctx.minerPubKey case _ => throwError } - case (tree: Ref[AvlTree]@unchecked, SAvlTree) => method.name match { - case SAvlTree.digestMethod.name => + case (tree: Ref[AvlTree]@unchecked, SAvlTreeMethods) => method.name match { + case SAvlTreeMethods.digestMethod.name => tree.digest - case SAvlTree.keyLengthMethod.name => + case SAvlTreeMethods.keyLengthMethod.name => tree.keyLength - case SAvlTree.valueLengthOptMethod.name => + case SAvlTreeMethods.valueLengthOptMethod.name => tree.valueLengthOpt - case SAvlTree.enabledOperationsMethod.name => + case SAvlTreeMethods.enabledOperationsMethod.name => tree.enabledOperations - case SAvlTree.isInsertAllowedMethod.name => + case SAvlTreeMethods.isInsertAllowedMethod.name => tree.isInsertAllowed - case SAvlTree.isRemoveAllowedMethod.name => + case SAvlTreeMethods.isRemoveAllowedMethod.name => tree.isRemoveAllowed - case SAvlTree.isUpdateAllowedMethod.name => + case SAvlTreeMethods.isUpdateAllowedMethod.name => tree.isUpdateAllowed - case SAvlTree.updateDigestMethod.name => + case SAvlTreeMethods.updateDigestMethod.name => val digest = asRep[Coll[Byte]](argsV(0)) tree.updateDigest(digest) - case SAvlTree.updateOperationsMethod.name => + case SAvlTreeMethods.updateOperationsMethod.name => val operations = asRep[Byte](argsV(0)) tree.updateOperations(operations) - case SAvlTree.getMethod.name => + case SAvlTreeMethods.getMethod.name => val key = asRep[Coll[Byte]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.get(key, proof) - case SAvlTree.getManyMethod.name => + case SAvlTreeMethods.getManyMethod.name => val keys = asRep[Coll[Coll[Byte]]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.getMany(keys, proof) - case SAvlTree.containsMethod.name => + case SAvlTreeMethods.containsMethod.name => val key = asRep[Coll[Byte]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.contains(key, proof) - case SAvlTree.insertMethod.name => + case SAvlTreeMethods.insertMethod.name => val operations = asRep[Coll[(Coll[Byte], Coll[Byte])]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.insert(operations, proof) - case SAvlTree.removeMethod.name => + case SAvlTreeMethods.removeMethod.name => val operations = asRep[Coll[Coll[Byte]]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.remove(operations, proof) - case SAvlTree.updateMethod.name => + case SAvlTreeMethods.updateMethod.name => val operations = asRep[Coll[(Coll[Byte], Coll[Byte])]](argsV(0)) val proof = asRep[Coll[Byte]](argsV(1)) tree.update(operations, proof) case _ => throwError } - case (ph: Ref[PreHeader]@unchecked, SPreHeader) => method.name match { - case SPreHeader.versionMethod.name => + case (ph: Ref[PreHeader]@unchecked, SPreHeaderMethods) => method.name match { + case SPreHeaderMethods.versionMethod.name => ph.version - case SPreHeader.parentIdMethod.name => + case SPreHeaderMethods.parentIdMethod.name => ph.parentId - case SPreHeader.timestampMethod.name => + case SPreHeaderMethods.timestampMethod.name => ph.timestamp - case SPreHeader.nBitsMethod.name => + case SPreHeaderMethods.nBitsMethod.name => ph.nBits - case SPreHeader.heightMethod.name => + case SPreHeaderMethods.heightMethod.name => ph.height - case SPreHeader.minerPkMethod.name => + case SPreHeaderMethods.minerPkMethod.name => ph.minerPk - case SPreHeader.votesMethod.name => + case SPreHeaderMethods.votesMethod.name => ph.votes case _ => throwError } - case (h: Ref[Header]@unchecked, SHeader) => method.name match { - case SHeader.idMethod.name => + case (h: Ref[Header]@unchecked, SHeaderMethods) => method.name match { + case SHeaderMethods.idMethod.name => h.id - case SHeader.versionMethod.name => + case SHeaderMethods.versionMethod.name => h.version - case SHeader.parentIdMethod.name => + case SHeaderMethods.parentIdMethod.name => h.parentId - case SHeader.ADProofsRootMethod.name => + case SHeaderMethods.ADProofsRootMethod.name => h.ADProofsRoot - case SHeader.stateRootMethod.name => + case SHeaderMethods.stateRootMethod.name => h.stateRoot - case SHeader.transactionsRootMethod.name => + case SHeaderMethods.transactionsRootMethod.name => h.transactionsRoot - case SHeader.timestampMethod.name => + case SHeaderMethods.timestampMethod.name => h.timestamp - case SHeader.nBitsMethod.name => + case SHeaderMethods.nBitsMethod.name => h.nBits - case SHeader.heightMethod.name => + case SHeaderMethods.heightMethod.name => h.height - case SHeader.extensionRootMethod.name => + case SHeaderMethods.extensionRootMethod.name => h.extensionRoot - case SHeader.minerPkMethod.name => + case SHeaderMethods.minerPkMethod.name => h.minerPk - case SHeader.powOnetimePkMethod.name => + case SHeaderMethods.powOnetimePkMethod.name => h.powOnetimePk - case SHeader.powNonceMethod.name => + case SHeaderMethods.powNonceMethod.name => h.powNonce - case SHeader.powDistanceMethod.name => + case SHeaderMethods.powDistanceMethod.name => h.powDistance - case SHeader.votesMethod.name => + case SHeaderMethods.votesMethod.name => h.votes case _ => throwError } - case (g: Ref[SigmaDslBuilder]@unchecked, SGlobal) => method.name match { - case SGlobal.groupGeneratorMethod.name => + case (g: Ref[SigmaDslBuilder]@unchecked, SGlobalMethods) => method.name match { + case SGlobalMethods.groupGeneratorMethod.name => g.groupGenerator - case SGlobal.xorMethod.name => + case SGlobalMethods.xorMethod.name => val c1 = asRep[Coll[Byte]](argsV(0)) val c2 = asRep[Coll[Byte]](argsV(1)) g.xor(c1, c2) diff --git a/sc/shared/src/main/scala/sigmastate/eval/IRContext.scala b/sc/shared/src/main/scala/sigmastate/eval/IRContext.scala index a3338eaae6..b84098f2e7 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/IRContext.scala +++ b/sc/shared/src/main/scala/sigmastate/eval/IRContext.scala @@ -1,6 +1,7 @@ package sigmastate.eval -import sigmastate.lang.TransformingSigmaBuilder +import sigma.ast.TransformingSigmaBuilder +import sigma.data.CSigmaDslBuilder import java.util.concurrent.locks.ReentrantLock import scala.util.Try @@ -28,7 +29,7 @@ trait IRContext extends TreeBuilding with GraphBuilding { Pass.defaultPassConfig.copy(constantPropagation = false)) /** The value of Global ErgoTree operation */ - val sigmaDslBuilderValue = CostingSigmaDslBuilder + val sigmaDslBuilderValue = CSigmaDslBuilder /** Finds SigmaProp.isProven method calls in the given Lambda `f` */ def findIsProven[T](f: Ref[Context => T]): Option[Sym] = { diff --git a/sc/shared/src/main/scala/sigmastate/eval/TreeBuilding.scala b/sc/shared/src/main/scala/sigmastate/eval/TreeBuilding.scala index 13fc8c7bd1..26bc33956f 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/sc/shared/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -1,18 +1,16 @@ package sigmastate.eval -import sigmastate.Values._ +import sigma.ast._ import org.ergoplatform._ -import sigmastate._ -import sigmastate.lang.Terms.ValueOps -import sigmastate.serialization.OpCodes._ -import sigmastate.serialization.ConstantStore +import sigma.ast.syntax.ValueOps +import sigma.serialization.OpCodes._ +import sigma.serialization.ConstantStore +import sigma.ast.syntax._ +import sigma.data.{ProveDHTuple, ProveDlog} import scala.collection.mutable.ArrayBuffer -import SType._ -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.ProveDHTuple -import sigmastate.lang.Terms +import sigma.serialization.ValueCodes.OpCode /** Implementation of IR-graph to ErgoTree expression translation. * This, in a sense, is inverse to [[GraphBuilding]], however roundtrip identity is not @@ -159,7 +157,7 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => def recurse[T <: SType](s: Sym) = buildValue(ctx, mainG, env, s, defId, constantsProcessing).asValue[T] object In { def unapply(s: Sym): Option[SValue] = Some(buildValue(ctx, mainG, env, s, defId, constantsProcessing)) } s match { - case _ if s == ctx => org.ergoplatform.Context + case _ if s == ctx => sigma.ast.Context case _ if env.contains(s) => val (id, tpe) = env(s) ValUse(id, tpe) // recursion base @@ -242,7 +240,7 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => val col = recurse(colSym) mkByIndex(col, index.asIntValue, None) case CollM.length(col) => - utxo.SizeOf(recurse(col).asCollection[SType]) + sigma.ast.SizeOf(recurse(col).asCollection[SType]) case CollM.exists(colSym, pSym) => val Seq(col, p) = Seq(colSym, pSym).map(recurse) mkExists(col.asCollection[SType], p.asFunc) @@ -273,15 +271,15 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => val args = argsSyms.map(_.asInstanceOf[Sym]).map(recurse) val col = recurse(colSym).asCollection[SType] val colTpe = col.tpe - val method = SCollection.methods.find(_.name == m.getName).getOrElse(error(s"unknown method Coll.${m.getName}")) + val method = SCollectionMethods.methods.find(_.name == m.getName).getOrElse(error(s"unknown method Coll.${m.getName}")) val typeSubst = (method, args) match { - case (mth @ SCollection.FlatMapMethod, Seq(f)) => + case (_ @ SCollectionMethods.FlatMapMethod, Seq(f)) => val typeSubst = Map(SCollection.tOV -> f.asFunc.tpe.tRange.asCollection.elemType) typeSubst - case (mth @ SCollection.ZipMethod, Seq(coll)) => + case (_ @ SCollectionMethods.ZipMethod, Seq(coll)) => val typeSubst = Map(SCollection.tOV -> coll.asCollection[SType].tpe.elemType) typeSubst - case (mth, _) => Terms.EmptySubst + case (_, _) => EmptySubst } val specMethod = method.withConcreteTypes(typeSubst + (SCollection.tIV -> colTpe.elemType)) builder.mkMethodCall(col, specMethod, args.toIndexedSeq, Map()) @@ -402,10 +400,13 @@ trait TreeBuilding extends SigmaLibrary { IR: IRContext => case Def(MethodCall(objSym, m, argSyms, _)) => val obj = recurse[SType](objSym) val args = argSyms.collect { case argSym: Sym => recurse[SType](argSym) } - val method = obj.tpe.asProduct.method(m.getName) - .getOrElse(error(s"Cannot find method ${m.getName} in object $obj")) - val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)) - builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map()) + MethodsContainer.getMethod(obj.tpe, m.getName) match { + case Some(method) => + val specMethod = method.specializeFor(obj.tpe, args.map(_.tpe)) + builder.mkMethodCall(obj, specMethod, args.toIndexedSeq, Map()) + case None => + error(s"Cannot find method ${m.getName} in object $obj") + } case Def(d) => !!!(s"Don't know how to buildValue($mainG, $s -> $d, $env, $defId)") diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala b/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala index 3295429ffe..f93e31703d 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala +++ b/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala @@ -2,14 +2,15 @@ package sigmastate.lang import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix import org.ergoplatform._ +import sigma.ast.NoType import sigma.data.Nullable import sigma.kiama.rewriting.CallbackRewriter -import sigmastate.Values._ -import sigmastate._ +import sigma.ast._ +import sigma.ast.syntax.SValue import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.lang.SigmaPredef.PredefinedFuncRegistry -import sigmastate.lang.Terms._ -import sigmastate.exceptions.{BinderException, InvalidArguments} +import SigmaPredef.PredefinedFuncRegistry +import sigma.ast.syntax._ +import sigma.exceptions.{BinderException, InvalidArguments} object SrcCtxCallbackRewriter extends CallbackRewriter { override def rewriting[T](oldTerm: T, newTerm: T): T = (oldTerm, newTerm) match { diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaCompiler.scala b/sc/shared/src/main/scala/sigmastate/lang/SigmaCompiler.scala index d37a037a58..849167e159 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaCompiler.scala +++ b/sc/shared/src/main/scala/sigmastate/lang/SigmaCompiler.scala @@ -4,21 +4,23 @@ import fastparse.Parsed import fastparse.Parsed.Success import sigma.kiama.rewriting.Rewriter.{everywherebu, rewrite, rule} import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix -import org.ergoplatform.Global -import sigmastate.Values.{SValue, Value} +import scalan.GraphIRReflection +import sigma.ast.{Exponentiate, MultiplyGroup, SCollectionMethods, SGlobalMethods, SGroupElementMethods, Value, Xor} import sigmastate.eval.IRContext import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.lang.SigmaPredef.PredefinedFuncRegistry -import sigmastate.lang.Terms.MethodCall -import sigmastate.lang.syntax.ParserException -import sigmastate.utxo._ -import sigmastate.{Exponentiate, MultiplyGroup, SCollection, SGlobal, SGroupElement, SType, STypeVar, Xor} +import sigma.ast.SigmaPredef.PredefinedFuncRegistry +import sigma.ast.MethodCall +import sigmastate.lang.parsers.ParserException +import sigma.ast._ +import sigma.ast.syntax.SValue +import SCollectionMethods.{ExistsMethod, ForallMethod, MapMethod} +import sigmastate.InterpreterReflection /** * @param networkPrefix network prefix to decode an ergo address from string (PK op) * @param builder used to create ErgoTree nodes * @param lowerMethodCalls if true, then MethodCall nodes are lowered to ErgoTree nodes - * when [[sigmastate.SMethod.irInfo.irBuilder]] is defined. For + * when [[sigma.ast.SMethod.irInfo.irBuilder]] is defined. For * example, in the `coll.map(x => x+1)` code, the `map` method * call can be lowered to MapCollection node. * The lowering if preferable, because it is more compact (1 byte @@ -33,9 +35,7 @@ case class CompilerSettings( /** Result of ErgoScript source code compilation. * @param env compiler environment used to compile the code * @param code ErgoScript source code - * @param calcF graph obtained by using old AOT costing based compiler * @param compiledGraph graph obtained by using new [[GraphBuilding]] - * @param calcTree ErgoTree expression obtained from calcF graph. * @param buildTree ErgoTree expression obtained from graph created by [[GraphBuilding]] */ case class CompilerResult[Ctx <: IRContext]( @@ -49,7 +49,7 @@ case class CompilerResult[Ctx <: IRContext]( /** Compiler which compiles ErgoScript source code into ErgoTree. * @param settings compilation parameters \ */ -class SigmaCompiler(settings: CompilerSettings) { +class SigmaCompiler private(settings: CompilerSettings) { /** Constructs an instance for the given network type and with default settings. */ def this(networkPrefix: Byte) = this( CompilerSettings(networkPrefix, TransformingSigmaBuilder, lowerMethodCalls = true) @@ -96,6 +96,12 @@ class SigmaCompiler(settings: CompilerSettings) { CompilerResult(env, "", compiledGraph, compiledTree) } + /** Compiles the given parsed contract source. */ + def compileParsed(env: ScriptEnv, parsedExpr: SValue)(implicit IR: IRContext): CompilerResult[IR.type] = { + val typed = typecheck(env, parsedExpr) + compileTyped(env, typed) + } + /** Unlowering transformation, which replaces some operations with equivalent MethodCall * node. This replacement is only defined for some operations. * This is inverse to `lowering` which is performed during compilation. @@ -104,9 +110,9 @@ class SigmaCompiler(settings: CompilerSettings) { import SCollection._ val r = rule[Any]({ case MultiplyGroup(l, r) => - MethodCall(l, SGroupElement.MultiplyMethod, Vector(r), Map()) + MethodCall(l, SGroupElementMethods.MultiplyMethod, Vector(r), Map()) case Exponentiate(l, r) => - MethodCall(l, SGroupElement.ExponentiateMethod, Vector(r), Map()) + MethodCall(l, SGroupElementMethods.ExponentiateMethod, Vector(r), Map()) case ForAll(xs, p) => MethodCall(xs, ForallMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType)), Vector(p), Map()) case Exists(xs, p) => @@ -117,21 +123,21 @@ class SigmaCompiler(settings: CompilerSettings) { Vector(f), Map()) case Fold(xs, z, op) => MethodCall(xs, - SCollection.FoldMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType, tOV -> z.tpe)), + SCollectionMethods.FoldMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType, tOV -> z.tpe)), Vector(z, op), Map()) case Slice(xs, from, until) => MethodCall(xs, - SCollection.SliceMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType)), + SCollectionMethods.SliceMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType)), Vector(from, until), Map()) case Append(xs, ys) => MethodCall(xs, - SCollection.AppendMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType)), + SCollectionMethods.AppendMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType)), Vector(ys), Map()) case Xor(l, r) => - MethodCall(Global, SGlobal.xorMethod, Vector(l, r), Map()) + MethodCall(Global, SGlobalMethods.xorMethod, Vector(l, r), Map()) case ByIndex(xs, index, Some(default)) => MethodCall(xs, - SCollection.GetOrElseMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType)), + SCollectionMethods.GetOrElseMethod.withConcreteTypes(Map(tIV -> xs.tpe.elemType)), Vector(index, default), Map()) }) rewrite(everywherebu(r))(expr) @@ -139,6 +145,14 @@ class SigmaCompiler(settings: CompilerSettings) { } object SigmaCompiler { + /** Force initialization of reflection before any instance of SigmaCompiler is used. */ + val _ = (InterpreterReflection, GraphIRReflection) + + /** Constructs an instance for the given settings. */ def apply(settings: CompilerSettings): SigmaCompiler = new SigmaCompiler(settings) + + /** Constructs an instance for the given network type. */ + def apply(networkPrefix: Byte): SigmaCompiler = + new SigmaCompiler(networkPrefix) } diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaTemplateCompiler.scala b/sc/shared/src/main/scala/sigmastate/lang/SigmaTemplateCompiler.scala new file mode 100644 index 0000000000..ec94bdfd3a --- /dev/null +++ b/sc/shared/src/main/scala/sigmastate/lang/SigmaTemplateCompiler.scala @@ -0,0 +1,58 @@ +package sigmastate.lang + +import fastparse.Parsed +import org.ergoplatform.sdk.ContractTemplate +import sigmastate.eval.CompiletimeIRContext +import org.ergoplatform.sdk.Parameter +import sigma.ast.SourceContext +import sigma.ast.syntax.SValue +import sigmastate.interpreter.Interpreter.ScriptEnv +import sigmastate.lang.parsers.ParserException + +/** Compiler which compiles Ergo contract templates into a [[ContractTemplate]]. */ +class SigmaTemplateCompiler(networkPrefix: Byte) { + val sigmaCompiler = new SigmaCompiler(networkPrefix) + + /** + * Compiles the provided contract source code into a [[ContractTemplate]]. + * + * @param source The ErgoScript contract source code. + * @return The contract template. + */ + def compile(env: ScriptEnv, source: String): ContractTemplate = { + ContractParser.parse(source) match { + case Parsed.Success(template, _) => { + implicit val ir = new CompiletimeIRContext + val result = sigmaCompiler.compileParsed(env, template.body) + assemble(template, result.buildTree) + } + case f: Parsed.Failure => + throw new ParserException(s"Contract template syntax error: $f", Some(SourceContext.fromParserFailure(f))) + } + } + + private def assemble(parsed: ParsedContractTemplate, expr: SValue): ContractTemplate = { + val (constTypes, constValueOpts) = parsed.signature.params.map(param => (param.tpe, param.defaultValue)).unzip + val allConstValuesAreNone = constValueOpts.forall(_.isEmpty) + val constValues = + if (allConstValuesAreNone) None + else Some(constValueOpts.toIndexedSeq) + val contractParams = parsed.signature.params.zipWithIndex.map { case (param, idx) => + val desc: String = parsed.docs.params.find(docParam => docParam.name == param.name).map(_.description).getOrElse("") + Parameter(param.name, desc, idx) + } + + ContractTemplate( + name = parsed.signature.name, + description = parsed.docs.description, + constTypes = constTypes.toIndexedSeq, + constValues = constValues, + parameters = contractParams.toIndexedSeq, + expressionTree = expr.toSigmaProp + ) + } +} + +object SigmaTemplateCompiler { + def apply(networkPrefix: Byte): SigmaTemplateCompiler = new SigmaTemplateCompiler(networkPrefix) +} diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala b/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala index beb17a98b0..7fd6dadd45 100644 --- a/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala @@ -1,17 +1,16 @@ package sigmastate.lang import org.ergoplatform._ -import sigmastate.SCollection._ -import sigmastate.Values._ -import sigmastate._ -import SCollection.SBooleanArray -import sigma.data.Nullable +import sigma.ast.SCollection.{SBooleanArray, SByteArray} +import sigma.ast._ +import sigma.ast.syntax.SValue +import sigma.data.{Nullable, SigmaBoolean} import sigma.util.Extensions.Ensuring -import sigmastate.lang.Terms._ import sigmastate.exceptions._ -import sigmastate.lang.SigmaPredef._ -import sigmastate.serialization.OpCodes -import sigmastate.utxo._ +import SigmaPredef._ +import sigma.ast.syntax._ +import sigma.exceptions.TyperException +import sigma.serialization.OpCodes import scala.collection.mutable.ArrayBuffer @@ -46,9 +45,7 @@ class SigmaTyper(val builder: SigmaBuilder, * Rewrite tree to typed tree. Checks constituent names and types. Uses * the env map to resolve bound variables and their types. */ - def assignType(env: Map[String, SType], - bound: SValue, - expected: Option[SType] = None): SValue = ( bound match { + def assignType(env: Map[String, SType], bound: SValue): SValue = ( bound match { case Block(bs, res) => var curEnv = env val bs1 = ArrayBuffer[Val]() @@ -74,7 +71,7 @@ class SigmaTyper(val builder: SigmaBuilder, env.get(n) match { case Some(t) => mkIdent(n, t) case None => - SGlobal.method(n) match { + SGlobalMethods.method(n) match { case Some(method) if method.stype.tDom.length == 1 => // this is like `groupGenerator` without parentheses val srcCtx = i.sourceContext processGlobalMethod(srcCtx, method, IndexedSeq()) @@ -87,11 +84,10 @@ class SigmaTyper(val builder: SigmaBuilder, val newObj = assignType(env, obj) newObj.tpe match { case tNewObj: SProduct => - val iField = tNewObj.methodIndex(n) - val method = if (iField != -1) { - tNewObj.methods(iField) - } else - throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type with methods ${tNewObj.methods}", obj.sourceContext.toOption) + val method = MethodsContainer.getMethod(tNewObj, n).getOrElse( + throw new MethodNotFound( + s"Cannot find method '$n' in in the object $obj of Product type with methods", + obj.sourceContext.toOption)) val tMeth = method.stype val tRes = tMeth match { case SFunc(args, _, _) => @@ -138,7 +134,7 @@ class SigmaTyper(val builder: SigmaBuilder, val newArgs = args.map(assignType(env, _)) obj.tpe match { case p: SProduct => - p.method(n) match { + MethodsContainer.getMethod(p, n) match { case Some(method @ SMethod(_, _, genFunTpe @ SFunc(_, _, _), _, _, _, _, _)) => val subst = Map(genFunTpe.tpeParams.head.ident -> rangeTpe) val concrFunTpe = applySubst(genFunTpe, subst) @@ -159,7 +155,7 @@ class SigmaTyper(val builder: SigmaBuilder, case Some(method) => error(s"Don't know how to handle method $method in obj $p", sel.sourceContext) case None => - throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type with methods ${p.methods}", obj.sourceContext.toOption) + throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type $p", obj.sourceContext.toOption) } case _ => error(s"Cannot get field '$n' in in the object $obj of non-product type ${obj.tpe}", sel.sourceContext) @@ -176,7 +172,7 @@ class SigmaTyper(val builder: SigmaBuilder, unifyTypeLists(argTypes, newArgTypes) match { case Some(subst) => val concrFunTpe = applySubst(genFunTpe, subst) - newObj.tpe.asInstanceOf[SProduct].method(n) match { + MethodsContainer.getMethod(newObj.tpe.asInstanceOf[SProduct], n) match { case Some(method) if method.irInfo.irBuilder.isDefined => val expectedArgs = concrFunTpe.asFunc.tDom if (expectedArgs.length != newArgTypes.length @@ -198,8 +194,8 @@ class SigmaTyper(val builder: SigmaBuilder, mkApply(newSel, newArgs.toArray[SValue]) } - case a @ Apply(ident: Ident, args) if SGlobal.hasMethod(ident.name) => // example: groupGenerator() - val method = SGlobal.method(ident.name).get + case a @ Apply(ident: Ident, args) if SGlobalMethods.hasMethod(ident.name) => // example: groupGenerator() + val method = SGlobalMethods.method(ident.name).get val srcCtx = a.sourceContext val newArgs = args.map(assignType(env, _)) processGlobalMethod(srcCtx, method, newArgs) @@ -212,7 +208,7 @@ class SigmaTyper(val builder: SigmaBuilder, if (args.length != argTypes.length) error(s"Invalid argument type of application $app: invalid number of arguments", app.sourceContext) val typedArgs = args.zip(argTypes).map { - case (arg, expectedType) => assignType(env, arg, Some(expectedType)) + case (arg, expectedType) => assignType(env, arg) } val adaptedTypedArgs = (new_f, typedArgs) match { case (AllOfFunc.sym | AnyOfFunc.sym, _) => @@ -280,7 +276,7 @@ class SigmaTyper(val builder: SigmaBuilder, mkAppend(newObj.asCollection[a], r.asCollection[a]) else error(s"Invalid argument type for $m, expected $tColl but was ${r.tpe}", r.sourceContext) - case (SCollection(method), _) => + case (SCollectionMethods(method), _) => val typeSubst = method.stype match { case sfunc @ SFunc(_, _, _) => val newArgsTypes = newArgs.map(_.tpe) @@ -335,7 +331,7 @@ class SigmaTyper(val builder: SigmaBuilder, case SSigmaProp => (m, newArgs) match { case ("||" | "&&" | "^", Seq(r)) => r.tpe match { case SBoolean => - val (a, b) = (Select(newObj, SSigmaProp.IsProven, Some(SBoolean)).asBoolValue, r.asBoolValue) + val (a, b) = (Select(newObj, SSigmaPropMethods.IsProven, Some(SBoolean)).asBoolValue, r.asBoolValue) val res = m match { case "||" => mkBinOr(a, b) case "&&" => mkBinAnd(a, b) @@ -364,7 +360,7 @@ class SigmaTyper(val builder: SigmaBuilder, case "^" => mkBinXor(newObj.asBoolValue, r.asBoolValue) } case SSigmaProp => - val (a, b) = (newObj.asBoolValue, Select(r, SSigmaProp.IsProven, Some(SBoolean)).asBoolValue) + val (a, b) = (newObj.asBoolValue, Select(r, SSigmaPropMethods.IsProven, Some(SBoolean)).asBoolValue) val res = m match { case "||" => mkBinOr(a, b) case "&&" => mkBinAnd(a, b) @@ -505,7 +501,6 @@ class SigmaTyper(val builder: SigmaBuilder, case Inputs => Inputs case Outputs => Outputs case LastBlockUtxoRootHash => LastBlockUtxoRootHash - case v: ContextVariable[_] => v case v: GetVar[_] => v case v: OptionGet[_] => v case v: EvaluatedValue[_] => v diff --git a/sc/shared/src/main/scala/special/collection/CollsUnit.scala b/sc/shared/src/main/scala/special/collection/CollsUnit.scala index 02e62aeb4d..19ae1f5e48 100644 --- a/sc/shared/src/main/scala/special/collection/CollsUnit.scala +++ b/sc/shared/src/main/scala/special/collection/CollsUnit.scala @@ -9,9 +9,6 @@ package sigma { * for details. */ trait Colls extends Base { self: Library => - import Coll._; - import CollBuilder._; - import WOption._; trait Coll[A] extends Def[Coll[A]] { implicit def eA: Elem[A]; def length: Ref[Int]; diff --git a/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala b/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala index 8da36ce6cf..037505b531 100644 --- a/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala +++ b/sc/shared/src/main/scala/special/sigma/impl/SigmaDslImpl.scala @@ -22,19 +22,16 @@ import BigInt._ import Box._ import Coll._ import CollBuilder._ -import Context._ import GroupElement._ import Header._ import PreHeader._ -import SigmaDslBuilder._ import SigmaProp._ import WOption._ -import WRType._ + object BigInt extends EntityObject("BigInt") { // entityConst: single const for each entity import Liftables._ - import scala.reflect.{ClassTag, classTag} type SBigInt = sigma.BigInt case class BigIntConst( constValue: SBigInt diff --git a/sc/shared/src/main/scala/wrappers/scalan/WRTypes.scala b/sc/shared/src/main/scala/wrappers/scalan/WRTypes.scala index 0d55f5dd9e..8c6bc48f8e 100644 --- a/sc/shared/src/main/scala/wrappers/scalan/WRTypes.scala +++ b/sc/shared/src/main/scala/wrappers/scalan/WRTypes.scala @@ -1,18 +1,9 @@ package wrappers.scalan { import scalan._ - - import impl._ - - import sigma.data.RType - import special.wrappers.WrappersModule - import special.wrappers.RTypeWrapSpec - - import scala.collection.mutable.WrappedArray trait WRTypes extends Base { self: WrappersModule => - import WRType._; trait WRType[A] extends Def[WRType[A]] { implicit def eA: Elem[A]; def name: Ref[String] diff --git a/sc/shared/src/main/scala/wrappers/special/WSpecialPredefs.scala b/sc/shared/src/main/scala/wrappers/special/WSpecialPredefs.scala index 42bbbb4373..5abbab95e2 100644 --- a/sc/shared/src/main/scala/wrappers/special/WSpecialPredefs.scala +++ b/sc/shared/src/main/scala/wrappers/special/WSpecialPredefs.scala @@ -4,8 +4,6 @@ package wrappers.special { import special.wrappers.WrappersModule trait WSpecialPredefs extends Base { self: WrappersModule => - import WOption._; - import WSpecialPredef._; trait WSpecialPredef extends Def[WSpecialPredef]; trait WSpecialPredefCompanion { def some[A](x: Ref[A]): Ref[WOption[A]]; diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala index 73156ca1b8..d8ee6e3707 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala @@ -1,32 +1,36 @@ package org.ergoplatform import org.ergoplatform.ErgoAddressEncoder.{MainnetNetworkPrefix, TestnetNetworkPrefix, hash256} -import org.ergoplatform.validation.{ValidationException, ValidationRules} +import org.scalatest.propspec.AnyPropSpecLike import org.scalatest.{Assertion, TryValues} import scorex.crypto.hash.Blake2b256 import scorex.util.encode.Base58 -import sigmastate.Values.{ByteArrayConstant, Constant, ErgoTree, IntConstant, UnparsedErgoTree} -import sigmastate.crypto.DLogProtocol -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.eval.InvalidType +import sigma.ast.{ByteArrayConstant, Constant, ErgoTree, IntConstant, SigmaAnd, UnparsedErgoTree} +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers._ -import sigmastate.interpreter.ContextExtension.VarBinding -import sigmastate.crypto.CryptoConstants.dlogGroup -import sigmastate.exceptions.CostLimitException +import sigma.interpreter.ContextExtension.VarBinding +import sigma.crypto.CryptoConstants.dlogGroup import sigmastate.interpreter.Interpreter.{ScriptEnv, ScriptNameProp} -import sigmastate.interpreter.{ContextExtension, CostedProverResult} -import sigmastate.lang.Terms.ValueOps -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.serialization.{GroupElementSerializer, ValueSerializer} +import sigma.ast.syntax.ValueOps +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer +import sigma.serialization.ValueSerializer import sigmastate.utils.Helpers._ -import sigmastate.{CompilerCrossVersionProps, SType, SigmaAnd} -import sigma.SigmaDslTesting +import sigmastate.CompilerCrossVersionProps +import sigma.{SigmaDslTesting, VersionContext} +import sigma.ast.ErgoTree.{ZeroHeader, setConstantSegregation} +import sigma.ast.SType +import sigma.data.ProveDlog +import sigma.exceptions.{CostLimitException, InvalidType} +import sigma.interpreter.{ContextExtension, CostedProverResult} +import sigma.serialization.GroupElementSerializer +import sigma.validation.ValidationException +import sigma.validation.ValidationRules.CheckTypeCode import java.math.BigInteger class ErgoAddressSpecification extends SigmaDslTesting - with TryValues with CompilerCrossVersionProps { + with TryValues with CompilerCrossVersionProps with AnyPropSpecLike { private implicit val ergoAddressEncoder: ErgoAddressEncoder = new ErgoAddressEncoder(TestnetNetworkPrefix) @@ -76,12 +80,12 @@ class ErgoAddressSpecification extends SigmaDslTesting def testFromProposition(scriptVersion: Byte, expectedP2S: String, expectedP2SH: String, expectedP2PK: String) = { - val pk: DLogProtocol.ProveDlog = DLogProverInput(BigInteger.ONE).publicImage - val pk10: DLogProtocol.ProveDlog = DLogProverInput(BigInteger.TEN).publicImage + val pk: ProveDlog = DLogProverInput(BigInteger.ONE).publicImage + val pk10: ProveDlog = DLogProverInput(BigInteger.TEN).publicImage val p2s: Pay2SAddress = Pay2SAddress( ErgoTree.fromProposition( - ErgoTree.headerWithVersion(scriptVersion), + ErgoTree.headerWithVersion(ZeroHeader, scriptVersion), SigmaAnd(pk, pk10))) val p2sh: Pay2SHAddress = Pay2SHAddress(pk) val p2pk: P2PKAddress = P2PKAddress(pk) @@ -125,7 +129,7 @@ class ErgoAddressSpecification extends SigmaDslTesting val pk = dlogGroup.exponentiate(g, w) val pkBytes = GroupElementSerializer.toBytes(pk) val encoder = new ErgoAddressEncoder(MainnetNetworkPrefix) - val p2pk = new P2PKAddress(ProveDlog(pk), pkBytes)(encoder) + val p2pk = P2PKAddress(ProveDlog(pk))(encoder) val addrStr = p2pk.toString val prefix = (encoder.networkPrefix + P2PKAddress.addressTypePrefix).toByte @@ -148,6 +152,11 @@ class ErgoAddressSpecification extends SigmaDslTesting expectedP2S = "2MzJLjzX6UNfJHSVvioB6seYZ99FpWHB4Ds1gekHPv5KtNmLJUecgRWwvcGEqbt8ZAokUxGvKMuNgMZFzkPPdTGiYzPQoSR55NT5isCidMywgp52LYV", expectedP2SH = "qETVgcEctaXurNbFRgGUcZEGg4EKa8R4a5UNHY7", expectedP2PK = "3WwXpssaZwcNzaGMv3AgxBdTPJQBt5gCmqBsg3DykQ39bYdhJBsN") + + testFromProposition(scriptVersion = 2, + expectedP2S = "2N1Egpu5R9XtomV7x343LTXGrBLEkC8pvMVtjm6V3iHryxVfc6LUJhd1JsswhXMpPXUMatoBgnJ4qMGAC7dha27WkjqVBUsebWBDhig97zhmKS8T4YS", + expectedP2SH = "qETVgcEctaXurNbFRgGUcZEGg4EKa8R4a5UNHY7", + expectedP2PK = "3WwXpssaZwcNzaGMv3AgxBdTPJQBt5gCmqBsg3DykQ39bYdhJBsN") } property("decode with wrong address") { @@ -175,7 +184,7 @@ class ErgoAddressSpecification extends SigmaDslTesting } { - val pk: DLogProtocol.ProveDlog = DLogProverInput(BigInteger.ONE).publicImage + val pk: ProveDlog = DLogProverInput(BigInteger.ONE).publicImage val p2pk = P2PKAddress(pk)(ergoAddressEncoder) val invalidAddrType = 4.toByte @@ -192,9 +201,9 @@ class ErgoAddressSpecification extends SigmaDslTesting { val unparsedTree = new ErgoTree( - (ErgoTree.ConstantSegregationHeader | ergoTreeHeaderInTests).toByte, + setConstantSegregation(ergoTreeHeaderInTests), Array[Constant[SType]](), - Left(UnparsedErgoTree(Array[Byte](), ValidationException("", ValidationRules.CheckTypeCode, Seq()))) + Left(UnparsedErgoTree(Array[Byte](), ValidationException("", CheckTypeCode, Seq()))) ) assertExceptionThrown( ergoAddressEncoder.fromProposition(unparsedTree).getOrThrow, @@ -204,7 +213,7 @@ class ErgoAddressSpecification extends SigmaDslTesting { val invalidTree = new ErgoTree( - (ErgoTree.ConstantSegregationHeader | ergoTreeHeaderInTests).toByte, + setConstantSegregation(ergoTreeHeaderInTests), Array[Constant[SType]](), Right(IntConstant(10).asSigmaProp) ) @@ -243,12 +252,16 @@ class ErgoAddressSpecification extends SigmaDslTesting verifier.verify(env + (ScriptNameProp -> s"verify_ext"), address.script, ctx, pr.proof, fakeMessage).getOrThrow._1 shouldBe true } - property("spending a box protected by P2SH contract") { + def createPropAndScriptBytes() = { implicit lazy val IR = new TestingIRContext - val script = "{ 1 < 2 }" val prop = compile(Map.empty, script).asBoolValue.toSigmaProp val scriptBytes = ValueSerializer.serialize(prop) + (prop, scriptBytes) + } + + property("spending a box protected by P2SH contract") { + val (prop, scriptBytes) = createPropAndScriptBytes() testPay2SHAddress(Pay2SHAddress(prop), scriptBytes) @@ -256,8 +269,24 @@ class ErgoAddressSpecification extends SigmaDslTesting testPay2SHAddress(Pay2SHAddress(tree), scriptBytes) // NOTE: same scriptBytes regardless of ErgoTree version } + property("Pay2SHAddress.script should create ErgoTree v0") { + val (prop, _) = createPropAndScriptBytes() + + val address = VersionContext.withVersions(activatedVersionInTests, ergoTreeVersionInTests) { + Pay2SHAddress(prop) + } + address.script.version shouldBe 0 + } + + // create non-versioned property which is executed under default version context + // see VersionContext._defaultContext + super[AnyPropSpecLike].property("using default VersionContext still creates ErgoTree v0") { + val (prop, _) = createPropAndScriptBytes() + val address = Pay2SHAddress(prop) + address.script.version shouldBe 0 + } + property("negative cases: deserialized script + costing exceptions") { - implicit lazy val IR = new TestingIRContext def testPay2SHAddress(address: Pay2SHAddress, script: VarBinding, costLimit: Int = scriptCostLimitInTests): CostedProverResult = { val boxToSpend = testBox(10, address.script, creationHeight = 5) @@ -272,24 +301,23 @@ class ErgoAddressSpecification extends SigmaDslTesting } val scriptVarId = Pay2SHAddress.scriptId - val script = "{ 1 < 2 }" - val prop = compile(Map.empty, script).asBoolValue.toSigmaProp - val scriptBytes = ValueSerializer.serialize(prop) + val (prop, scriptBytes) = createPropAndScriptBytes() val addr = Pay2SHAddress(prop) // when everything is ok testPay2SHAddress(addr, script = scriptVarId -> ByteArrayConstant(scriptBytes)) + val expectedCost = 88 + // when limit is low { val deliberatelySmallLimit = 24 - assertExceptionThrown( testPay2SHAddress(addr, script = scriptVarId -> ByteArrayConstant(scriptBytes), costLimit = deliberatelySmallLimit), rootCauseLike[CostLimitException]( - s"Estimated execution cost 88 exceeds the limit $deliberatelySmallLimit") + s"Estimated execution cost $expectedCost exceeds the limit $deliberatelySmallLimit") ) } @@ -305,7 +333,7 @@ class ErgoAddressSpecification extends SigmaDslTesting costLimit = deliberatelySmallLimit) }, rootCauseLike[CostLimitException]( - s"Estimated execution cost 88 exceeds the limit $deliberatelySmallLimit") + s"Estimated execution cost $expectedCost exceeds the limit $deliberatelySmallLimit") ) } diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 1661f43803..6a7ef5a512 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -5,16 +5,17 @@ import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.sdk.JsonCodecs import org.ergoplatform.settings.ErgoAlgos import scorex.util.encode.Base16 -import scorex.util.{Random, ModifierId} -import sigmastate.SCollection.SByteArray -import sigmastate.{SSigmaProp, TrivialProp, SType, SPair, SInt} -import sigmastate.Values._ -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.serialization.SigmaSerializer -import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigmastate._ -import sigmastate.SType._ +import scorex.util.{ModifierId, Random} +import sigma.Extensions._ +import sigma.SigmaDslTesting +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType._ +import sigma.ast.syntax.{ErgoBoxCandidateRType, TrueSigmaProp} +import sigma.ast._ +import sigma.data.{CSigmaProp, Digest32Coll, TrivialProp} +import sigma.eval.Extensions.{EvalCollOps, EvalIterableOps} +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.SigmaSerializer import sigmastate.helpers.TestingHelpers.copyTransaction import sigmastate.utils.Helpers import sigma.SigmaDslTesting @@ -28,7 +29,7 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { val token2 = "a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600" val b1 = new ErgoBoxCandidate( 10L, - ErgoTree(ErgoTree.DefaultHeader, Vector(), TrueSigmaProp), + ErgoTree(ErgoTree.ZeroHeader, Vector(), TrueSigmaProp), 100, Coll( (Digest32Coll @@@ (ErgoAlgos.decodeUnsafe(token1).toColl)) -> 10000000L, @@ -37,7 +38,7 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting with JsonCodecs { ) val b1_clone = new ErgoBoxCandidate( 10L, - ErgoTree(ErgoTree.DefaultHeader, Vector(), TrueSigmaProp), + ErgoTree(ErgoTree.ZeroHeader, Vector(), TrueSigmaProp), 100, Coll( Digest32Coll @@ (ErgoAlgos.decodeUnsafe(token1).toColl) -> 10000000L, diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala index d017b6ca35..eb086e88c8 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala @@ -8,18 +8,18 @@ import org.scalacheck.Gen import scorex.crypto.hash.Blake2b256 import scorex.util.Random import sigma.Colls +import sigma.ast._ +import sigma.ast.syntax.CollectionConstant +import sigma.data.{AvlTreeData, Digest32Coll, ProveDlog, TrivialProp} import sigma.util.BenchmarkUtil.measure -import sigmastate.Values.{ByteArrayConstant, CollectionConstant, ErgoTree, IntConstant, SigmaPropConstant} import sigmastate._ -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.lang.Terms.ValueOps -import sigmastate.serialization.ValueSerializer -import sigmastate.utxo.{ByIndex, ExtractCreationInfo, SelectField} -import sigmastate.eval.Digest32Coll +import sigma.ast.syntax.ValueOps +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.ValueSerializer import sigmastate.utils.Helpers._ import scala.util.Try @@ -92,7 +92,9 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio // unable to satisfy R4 conditions checkSpending(remaining(height), height, prop, R4Prop(false)).isFailure shouldBe true // incorrect new script - checkSpending(remaining(height), height, TrivialProp.TrueProp, R4Prop(true)).isFailure shouldBe true + checkSpending(remaining(height), height, + ErgoTree.fromProposition(TrivialProp.TrueProp), + R4Prop(true)).isFailure shouldBe true // collect less coins then possible checkSpending(remaining(height) + 1, height, prop, R4Prop(true)).isSuccess shouldBe true // collect more coins then possible @@ -188,7 +190,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio createRewardTx(currentRate, height, correctProp).isSuccess shouldBe true createRewardTx(currentRate, height, incorrectDelay).isFailure shouldBe true createRewardTx(currentRate, height, incorrectPk).isFailure shouldBe true - createRewardTx(currentRate, height, minerPk).isFailure shouldBe true + createRewardTx(currentRate, height, + ErgoTree.fromSigmaBoolean(minerPk)).isFailure shouldBe true } def createRewardTx(emissionAmount: Long, nextHeight: Int, minerProp: ErgoTree): Try[ErgoLikeTransaction] = { diff --git a/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala index 46668c3b1a..26e8b08b14 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/dsl/TestContractSpec.scala @@ -3,33 +3,34 @@ package org.ergoplatform.dsl import sigmastate.interpreter.Interpreter.ScriptNameProp import scala.collection.mutable -import sigmastate.interpreter.{CostedProverResult, ProverResult} - +import sigma.interpreter.{CostedProverResult, ProverResult} import scala.collection.mutable.ArrayBuffer import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} -import sigma.data.Nullable +import sigma.data.{AvlTreeData, CAnyValue, CSigmaProp, Nullable} import scala.util.Try import org.ergoplatform.{ErgoBox, ErgoLikeContext} import org.ergoplatform.dsl.ContractSyntax.{ErgoScript, Proposition, Token} -import sigmastate.{AvlTreeData, SType} -import sigmastate.Values.{ErgoTree, EvaluatedValue} -import sigmastate.eval.{CSigmaProp, Evaluation, IRContext, CAnyValue} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} +import sigma.ast.{ErgoTree, SType} +import sigma.ast.EvaluatedValue +import sigma.interpreter.ContextExtension +import sigmastate.eval.IRContext +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.lang.Terms.ValueOps -import sigma.{AnyValue, SigmaProp} +import sigma.ast.syntax.ValueOps +import sigma.{AnyValue, Evaluation, SigmaProp} +import ErgoTree.ZeroHeader case class TestContractSpec(testSuite: CompilerTestingCommons)(implicit val IR: IRContext) extends ContractSpec { case class TestPropositionSpec(name: String, dslSpec: Proposition, scriptSpec: ErgoScript) extends PropositionSpec { lazy val ergoTree: ErgoTree = { val value = testSuite.compile(scriptSpec.env, scriptSpec.code) - val headerFlags = scriptSpec.scriptVersion match { - case Some(version) => ErgoTree.headerWithVersion(version) + val header = scriptSpec.scriptVersion match { + case Some(version) => ErgoTree.headerWithVersion(ZeroHeader, version) case None => testSuite.ergoTreeHeaderInTests } - val tree: ErgoTree = ErgoTree.fromProposition(headerFlags, value.asSigmaProp) + val tree: ErgoTree = ErgoTree.fromProposition(header, value.asSigmaProp) tree } } @@ -44,16 +45,13 @@ case class TestContractSpec(testSuite: CompilerTestingCommons)(implicit val IR: val pubKey: SigmaProp = CSigmaProp(prover.dlogSecrets.head.publicImage) import SType.AnyOps - def prove(inBox: InputBox, extensions: Map[Byte, AnyValue] = Map()): Try[CostedProverResult] = { + def prove(inBox: InputBox, extensions: Map[Byte, EvaluatedValue[_ <: SType]] = Map()): Try[CostedProverResult] = { val boxToSpend = inBox.utxoBox val propSpec: PropositionSpec = boxToSpend.propSpec - val bindings = extensions.mapValues { case v: CAnyValue[a] => - IR.builder.mkConstant(v.value.asWrappedType, Evaluation.rtypeToSType(v.tA)) - } val ctx = inBox.toErgoContext val env = propSpec.scriptSpec.env + (ScriptNameProp -> (propSpec.name + "_prove")) val prop = propSpec.ergoTree - val p = bindings.foldLeft(prover) { (p, b) => p.withContextExtender(b._1, b._2) } + val p = extensions.foldLeft(prover) { (p, b) => p.withContextExtender(b._1, b._2) } val proof = p.prove(env, prop, ctx, testSuite.fakeMessage) proof } @@ -92,8 +90,8 @@ case class TestContractSpec(testSuite: CompilerTestingCommons)(implicit val IR: .withErgoTreeVersion(testSuite.ergoTreeVersionInTests) ctx } - def runDsl(extensions: Map[Byte, AnyValue] = Map()): SigmaProp = { - val ctx = toErgoContext.toSigmaContext(extensions) + def runDsl(extensions: Map[Byte, EvaluatedValue[_ <: SType]] = Map()): SigmaProp = { + val ctx = toErgoContext.withExtension(ContextExtension(extensions)).toSigmaContext() val res = utxoBox.propSpec.dslSpec(ctx) res } @@ -170,7 +168,6 @@ case class TestContractSpec(testSuite: CompilerTestingCommons)(implicit val IR: case class BBlockCandidate(height: Int) extends BlockCandidate { def newTransaction() = MockTransaction(this) -// def onTopOf(chain: ChainBlock*) } def candidateBlock(height: Int): BlockCandidate = BBlockCandidate(height) diff --git a/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala index 9909f485b5..da7d74e79b 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala @@ -1,7 +1,8 @@ package org.ergoplatform.validation +import sigma.validation.{ReplacedRule, RuleStatus} import sigmastate.helpers.CompilerTestingCommons -import sigmastate.serialization.{SigmaSerializer, SerializationSpecification} +import sigma.serialization.{SerializationSpecification, SigmaSerializer} class RuleStatusSerializerSpec extends SerializationSpecification with CompilerTestingCommons { diff --git a/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala index c4162103c6..ee1c1a339a 100644 --- a/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala +++ b/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala @@ -1,9 +1,10 @@ package org.ergoplatform.validation -import org.ergoplatform.validation.ValidationRules.{FirstRuleId, currentSettings} -import org.scalatest.Assertion +import org.ergoplatform.validation.ValidationRules.currentSettings +import sigma.validation.{DisabledRule, SigmaValidationSettings} +import sigma.validation.ValidationRules.FirstRuleId import sigmastate.helpers.CompilerTestingCommons -import sigmastate.serialization.SerializationSpecification +import sigma.serialization.SerializationSpecification class SigmaValidationSettingsSerializerSpec extends SerializationSpecification with CompilerTestingCommons { diff --git a/sc/shared/src/test/scala/sigma/DataValueComparerSpecification.scala b/sc/shared/src/test/scala/sigma/DataValueComparerSpecification.scala index df3c5dc7ed..b82a747069 100644 --- a/sc/shared/src/test/scala/sigma/DataValueComparerSpecification.scala +++ b/sc/shared/src/test/scala/sigma/DataValueComparerSpecification.scala @@ -1,14 +1,13 @@ package sigma import org.scalatest.BeforeAndAfterAll -import sigma.data.RType +import sigma.data.{CSigmaProp, DataValueComparer, RType, TrivialProp} import sigma.util.BenchmarkUtil -import sigmastate.{DataValueComparer, JitCost, TrivialProp} -import sigmastate.Values.ErgoTree -import sigmastate.eval.{CSigmaProp, Profiler, SigmaDsl} +import sigmastate.eval.CProfiler import sigmastate.helpers.SigmaPPrint -import sigmastate.interpreter.{CostAccumulator, ErgoTreeEvaluator, EvalSettings, TracedCost} -import sigma.Coll +import sigmastate.interpreter.{CostAccumulator, CErgoTreeEvaluator} +import sigma.ast.{ErgoTree, JitCost} +import sigma.eval.{EvalSettings, SigmaDsl, TracedCost} import scala.util.{Success, Try} @@ -18,7 +17,7 @@ class DataValueComparerSpecification extends SigmaDslTesting implicit override val generatorDrivenConfig = PropertyCheckConfiguration(minSuccessful = 30) implicit override val evalSettings: EvalSettings = - ErgoTreeEvaluator.DefaultEvalSettings.copy( + CErgoTreeEvaluator.DefaultEvalSettings.copy( isMeasureOperationTime = true, isMeasureScriptTime = true, isLogEnabled = false, // don't commit the `true` value (CI log is too high) @@ -29,15 +28,15 @@ class DataValueComparerSpecification extends SigmaDslTesting val nWarmUpIterations = 100 - implicit val suiteProfiler = new Profiler + implicit val suiteProfiler = new CProfiler import TestData._ - def createEvaluator(settings: EvalSettings, profiler: Profiler): ErgoTreeEvaluator = { + def createEvaluator(settings: EvalSettings, profiler: CProfiler): CErgoTreeEvaluator = { val accumulator = new CostAccumulator( initialCost = JitCost(0), costLimit = Some(JitCost.fromBlockCost(settings.scriptCostLimitInEvaluator))) - val evaluator = new ErgoTreeEvaluator( + val evaluator = new CErgoTreeEvaluator( context = null, constants = ErgoTree.EmptyConstants, coster = accumulator, profiler, settings) @@ -51,7 +50,7 @@ class DataValueComparerSpecification extends SigmaDslTesting * @param x computation which produced first argument * @param y computation which produced second argument */ - def check(x: => Any, y: => Any, expected: Boolean)(implicit settings: EvalSettings, profiler: Profiler) = { + def check(x: => Any, y: => Any, expected: Boolean)(implicit settings: EvalSettings, profiler: CProfiler) = { val _x = x // force computation and obtain value val _y = y withClue(s"EQ.equalDataValues(${_x}, ${_y})") { @@ -96,7 +95,7 @@ class DataValueComparerSpecification extends SigmaDslTesting override protected def beforeAll(): Unit = { // this method warms up the code in DataValueComparer - val warmUpProfiler = new Profiler + val warmUpProfiler = new CProfiler warmUpBeforeAllTest(nTotalIters = nWarmUpIterations) { runBaseCases(warmUpProfiler)(evalSettings = evalSettings.copy(isLogEnabled = false)) } @@ -116,7 +115,7 @@ class DataValueComparerSpecification extends SigmaDslTesting * This method is used: * 1) to warm up DataValueComparer in the beforeAll method * 2) to profile DataValueComparer operations */ - def runBaseCases(profiler: Profiler)(implicit evalSettings: EvalSettings) = { + def runBaseCases(profiler: CProfiler)(implicit evalSettings: EvalSettings) = { implicit val suiteProfiler = profiler // hide suite's profiler and use explicitly passed ones.foreach { x => ones.foreach { y => diff --git a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala index f5c47a055f..4dd576f03a 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslSpecification.scala @@ -1,48 +1,42 @@ package special.sigma -import java.math.BigInteger +import org.ergoplatform.ErgoBox.AdditionalRegisters import org.ergoplatform._ import org.ergoplatform.settings.ErgoAlgos +import org.scalacheck.Arbitrary._ import org.scalacheck.{Arbitrary, Gen} -import sigma.data.{ExactIntegral, ExactNumeric, ExactOrdering, RType} +import org.scalatest.BeforeAndAfterAll import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} -import sigma.util.Extensions._ -import sigmastate.utils.Extensions._ -import sigmastate.SCollection._ -import sigmastate.Values.IntConstant +import scorex.util.ModifierId +import sigma.Extensions.{ArrayOps, CollOps} +import sigma.ast.SCollection._ +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.data.RType._ +import sigma.data._ +import sigma.util.Extensions.{BooleanOps, IntOps, LongOps} +import sigma.{VersionContext, ast, data, _} +import ErgoTree.{HeaderType, ZeroHeader} +import sigma.eval.{CostDetails, EvalSettings, Profiler, SigmaDsl, TracedCost} import sigmastate._ -import sigmastate.crypto.DLogProtocol._ -import sigmastate.Values._ -import sigmastate.lang.Terms.Apply -import sigmastate.eval.Extensions._ +import sigmastate.eval.Extensions.AvlTreeOps +import sigma.eval.Extensions.{ByteExt, IntExt, LongExt, ShortExt} +import OrderingOps._ import sigmastate.eval._ -import sigmastate.lang.Terms.{MethodCall, PropertyCall} -import sigmastate.utxo._ -import sigma._ -import sigma.Extensions._ -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.utils.Helpers -import sigmastate.utils.Helpers._ import sigmastate.helpers.TestingHelpers._ - -import scala.util.{Failure, Success, Try} -import OrderingOps._ -import org.ergoplatform.ErgoBox.AdditionalRegisters -import org.scalacheck.Arbitrary._ -import org.scalacheck.Gen.frequency -import org.scalatest.{BeforeAndAfterAll, Tag} -import sigma.data.RType._ -import scorex.util.ModifierId -import sigmastate.crypto.ProveDHTuple import sigmastate.interpreter._ -import org.scalactic.source.Position -import sigma.VersionContext -import sigmastate.helpers.SigmaPPrint -import sigmastate.exceptions.GraphBuildingException +import sigma.ast.{Apply, MethodCall, PropertyCall} +import sigma.exceptions.InvalidType +import sigma.serialization.ValueCodes.OpCode +import sigmastate.utils.Extensions._ +import sigmastate.utils.Helpers +import sigmastate.utils.Helpers._ +import java.math.BigInteger import scala.collection.compat.immutable.ArraySeq +import scala.util.{Failure, Success} /** This suite tests every method of every SigmaDsl type to be equivalent to * the evaluation of the corresponding ErgoScript operation. @@ -86,7 +80,7 @@ class SigmaDslSpecification extends SigmaDslTesting implicit override val generatorDrivenConfig = PropertyCheckConfiguration(minSuccessful = 30) - val evalSettingsInTests = ErgoTreeEvaluator.DefaultEvalSettings.copy( + val evalSettingsInTests = CErgoTreeEvaluator.DefaultEvalSettings.copy( isMeasureOperationTime = true, isMeasureScriptTime = true, isLogEnabled = false, // don't commit the `true` value (travis log is too high) @@ -97,7 +91,7 @@ class SigmaDslSpecification extends SigmaDslTesting */ costTracingEnabled = true, - profilerOpt = Some(ErgoTreeEvaluator.DefaultProfiler), + profilerOpt = Some(CErgoTreeEvaluator.DefaultProfiler), isTestRun = true ) @@ -180,7 +174,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FuncValue.AddToEnvironmentDesc_CostKind), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FuncValue.AddToEnvironmentDesc_CostKind), @@ -674,7 +668,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(LogicalNot), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -1066,7 +1060,6 @@ class SigmaDslSpecification extends SigmaDslTesting property("Byte methods equivalence (new features)") { lazy val toBytes = newFeature((x: Byte) => x.toBytes, "{ (x: Byte) => x.toBytes }") - lazy val toBits = newFeature((x: Byte) => x.toBits, "{ (x: Byte) => x.toBits }") lazy val toAbs = newFeature((x: Byte) => x.toAbs, "{ (x: Byte) => x.toAbs }") lazy val compareTo = newFeature( (x: (Byte, Byte)) => x._1.compareTo(x._2), @@ -1081,7 +1074,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: (Byte, Byte)) => (x._1 & x._2).toByteExact }") forAll { x: Byte => - Seq(toBytes, toBits, toAbs).foreach(f => f.checkEquality(x)) + Seq(toBytes, toAbs).foreach(f => f.checkEquality(x)) } forAll { x: (Byte, Byte) => @@ -1371,7 +1364,6 @@ class SigmaDslSpecification extends SigmaDslTesting property("Short methods equivalence (new features)") { lazy val toBytes = newFeature((x: Short) => x.toBytes, "{ (x: Short) => x.toBytes }") - lazy val toBits = newFeature((x: Short) => x.toBits, "{ (x: Short) => x.toBits }") lazy val toAbs = newFeature((x: Short) => x.toAbs, "{ (x: Short) => x.toAbs }") lazy val compareTo = newFeature((x: (Short, Short)) => x._1.compareTo(x._2), @@ -1386,7 +1378,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: (Short, Short)) => x._1 & x._2 }") forAll { x: Short => - Seq(toBytes, toBits, toAbs).foreach(_.checkEquality(x)) + Seq(toBytes, toAbs).foreach(_.checkEquality(x)) } forAll { x: (Short, Short) => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) @@ -1675,7 +1667,6 @@ class SigmaDslSpecification extends SigmaDslTesting property("Int methods equivalence (new features)") { lazy val toBytes = newFeature((x: Int) => x.toBytes, "{ (x: Int) => x.toBytes }") - lazy val toBits = newFeature((x: Int) => x.toBits, "{ (x: Int) => x.toBits }") lazy val toAbs = newFeature((x: Int) => x.toAbs, "{ (x: Int) => x.toAbs }") lazy val compareTo = newFeature((x: (Int, Int)) => x._1.compareTo(x._2), "{ (x: (Int, Int)) => x._1.compareTo(x._2) }") @@ -1689,7 +1680,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: (Int, Int)) => x._1 & x._2 }") forAll { x: Int => - Seq(toBytes, toBits, toAbs).foreach(_.checkEquality(x)) + Seq(toBytes, toAbs).foreach(_.checkEquality(x)) } forAll { x: (Int, Int) => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) @@ -1995,9 +1986,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("Long methods equivalence (new features)") { lazy val toBytes = newFeature((x: Long) => x.toBytes, "{ (x: Long) => x.toBytes }") - lazy val toBits = newFeature((x: Long) => x.toBits, "{ (x: Long) => x.toBits }") lazy val toAbs = newFeature((x: Long) => x.toAbs, "{ (x: Long) => x.toAbs }") - lazy val compareTo = newFeature((x: (Long, Long)) => x._1.compareTo(x._2), "{ (x: (Long, Long)) => x._1.compareTo(x._2) }") @@ -2010,7 +1999,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: (Long, Long)) => x._1 & x._2 }") forAll { x: Long => - Seq(toBytes, toBits, toAbs).foreach(_.checkEquality(x)) + Seq(toBytes, toAbs).foreach(_.checkEquality(x)) } forAll { x: (Long, Long) => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) @@ -2309,7 +2298,6 @@ class SigmaDslSpecification extends SigmaDslTesting FuncValue(Vector((1, SBigInt)), Downcast(ValUse(1, SBigInt), SLong))) lazy val toBytes = newFeature((x: BigInt) => x.toBytes, "{ (x: BigInt) => x.toBytes }") - lazy val toBits = newFeature((x: BigInt) => x.toBits, "{ (x: BigInt) => x.toBits }") lazy val toAbs = newFeature((x: BigInt) => x.toAbs, "{ (x: BigInt) => x.toAbs }") lazy val compareTo = newFeature((x: (BigInt, BigInt)) => x._1.compareTo(x._2), @@ -2322,7 +2310,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: (BigInt, BigInt)) => x._1 & x._2 }") forAll { x: BigInt => - Seq(toByte, toShort, toInt, toLong, toBytes, toBits, toAbs).foreach(_.checkEquality(x)) + Seq(toByte, toShort, toInt, toLong, toBytes, toAbs).foreach(_.checkEquality(x)) } forAll { x: (BigInt, BigInt) => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) @@ -2354,7 +2342,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("NEQ of pre-defined types") { verifyNeq(ge1, ge2, 1783, Array[CostItem](FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172)))), 1783)(_.asInstanceOf[CGroupElement].copy()) verifyNeq(t1, t2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_AvlTree"), FixedCost(JitCost(6)))), 1767)(_.asInstanceOf[CAvlTree].copy()) - verifyNeq(b1, b2, 1767, Array[CostItem](), 1767)(_.asInstanceOf[CostingBox].copy()) + verifyNeq(b1, b2, 1767, Array[CostItem](), 1767)(_.asInstanceOf[CBox].copy()) verifyNeq(preH1, preH2, 1766, Array[CostItem](FixedCostItem(NamedDesc("EQ_PreHeader"), FixedCost(JitCost(4)))), 1766)(_.asInstanceOf[CPreHeader].copy()) verifyNeq(h1, h2, 1767, Array[CostItem](FixedCostItem(NamedDesc("EQ_Header"), FixedCost(JitCost(6)))), 1767)(_.asInstanceOf[CHeader].copy()) } @@ -2504,14 +2492,14 @@ class SigmaDslSpecification extends SigmaDslTesting ) val collNeqCost2 = Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 0) + ast.SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 0) ) implicit val evalSettings = suite.evalSettings.copy(isMeasureOperationTime = false) verifyNeq(Coll[Byte](), Coll(1.toByte), 1766, collNeqCost1, 1766)(cloneColl(_)) verifyNeq(Coll[Byte](0, 1), Coll(1.toByte, 1.toByte), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Byte"), PerItemCost(JitCost(15), JitCost(2), 128), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_Byte"), PerItemCost(JitCost(15), JitCost(2), 128), 1)), 1768 )(cloneColl(_)) @@ -2519,7 +2507,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[Short](0), Coll(1.toShort), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Short"), PerItemCost(JitCost(15), JitCost(2), 96), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_Short"), PerItemCost(JitCost(15), JitCost(2), 96), 1)), 1768 )(cloneColl(_)) @@ -2527,7 +2515,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[Int](0), Coll(1), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1)), 1768 )(cloneColl(_)) @@ -2535,7 +2523,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[Long](0), Coll(1.toLong), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Long"), PerItemCost(JitCost(15), JitCost(2), 48), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_Long"), PerItemCost(JitCost(15), JitCost(2), 48), 1)), 1768 )(cloneColl(_)) @@ -2544,7 +2532,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[BigInt](0.toBigInt), Coll(1.toBigInt), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_BigInt"), PerItemCost(JitCost(15), JitCost(7), 5), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_BigInt"), PerItemCost(JitCost(15), JitCost(7), 5), 1)), 1768 )(cloneColl(_)) @@ -2553,7 +2541,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[GroupElement](ge1), Coll(ge2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_GroupElement"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_GroupElement"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), 1768 )(cloneColl(_)) @@ -2562,7 +2550,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[AvlTree](t1), Coll(t2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_AvlTree"), PerItemCost(JitCost(15), JitCost(5), 2), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_AvlTree"), PerItemCost(JitCost(15), JitCost(5), 2), 1)), 1768 )(cloneColl(_)) @@ -2583,7 +2571,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[Box](b1), Coll(b2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_Box"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), 1768 )(cloneColl(_), generateCases = false) } @@ -2593,7 +2581,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[PreHeader](preH1), Coll(preH2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_PreHeader"), PerItemCost(JitCost(15), JitCost(3), 1), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_PreHeader"), PerItemCost(JitCost(15), JitCost(3), 1), 1)), 1768 )(cloneColl(_)) @@ -2602,7 +2590,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyNeq(Coll[Header](h1), Coll(h2), 1768, Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Header"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), + ast.SeqCostItem(NamedDesc("EQ_COA_Header"), PerItemCost(JitCost(15), JitCost(5), 1), 1)), 1768 )(cloneColl(_)) } @@ -2619,13 +2607,13 @@ class SigmaDslSpecification extends SigmaDslTesting val nestedNeq2 = Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) val nestedNeq3 = Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), - SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) + ast.SeqCostItem(NamedDesc("EQ_COA_Int"), PerItemCost(JitCost(15), JitCost(2), 64), 1), + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) verifyNeq(Coll[Coll[Int]](), Coll(Coll[Int]()), 1766, nestedNeq1, 1766)(cloneColl(_)) verifyNeq(Coll(Coll[Int]()), Coll(Coll[Int](1)), 1767, nestedNeq2, 1767)(cloneColl(_)) @@ -2641,8 +2629,8 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_Prim"), FixedCost(JitCost(3))), FixedCostItem(NamedDesc("EQ_BigInt"), FixedCost(JitCost(5))), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) val nestedNeq5 = Array( FixedCostItem(NamedDesc("MatchType"), FixedCost(JitCost(1))), @@ -2651,9 +2639,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_Prim"), FixedCost(JitCost(3))), FixedCostItem(NamedDesc("EQ_BigInt"), FixedCost(JitCost(5))), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) val nestedNeq6 = Array( FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), @@ -2669,9 +2657,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(NamedDesc("EQ_Tuple"), FixedCost(JitCost(4))), FixedCostItem(NamedDesc("EQ_Prim"), FixedCost(JitCost(3))), FixedCostItem(NamedDesc("EQ_BigInt"), FixedCost(JitCost(5))), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 2), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), - SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 2), + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1), + ast.SeqCostItem(NamedDesc("EQ_Coll"), PerItemCost(JitCost(10), JitCost(2), 1), 1) ) verifyNeq(Coll(Coll((1, 10.toBigInt))), Coll(Coll((1, 11.toBigInt))), 1770, nestedNeq4, 1770)(cloneColl(_)) verifyNeq(Coll(Coll(Coll((1, 10.toBigInt)))), Coll(Coll(Coll((1, 11.toBigInt)))), 1771, nestedNeq5, 1771)(cloneColl(_)) @@ -2695,7 +2683,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("GroupElement.getEncoded equivalence") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1790, methodCostDetails(SGroupElement.GetEncodedMethod, 250), 1790) + def success[T](v: T) = Expected(Success(v), 1790, methodCostDetails(SGroupElementMethods.GetEncodedMethod, 250), 1790) Seq( (ge1, success(Helpers.decodeBytes(ge1str))), (ge2, success(Helpers.decodeBytes(ge2str))), @@ -2710,7 +2698,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: GroupElement) => x.getEncoded }", FuncValue( Vector((1, SGroupElement)), - MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("getEncoded"), Vector(), Map()) + MethodCall(ValUse(1, SGroupElement), SGroupElementMethods.getMethodByName("getEncoded"), Vector(), Map()) ))) } @@ -2720,7 +2708,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = TracedCost( traceBase ++ Array( FixedCostItem(PropertyCall), - FixedCostItem(MethodDesc(SGroupElement.GetEncodedMethod), FixedCost(JitCost(250))), + FixedCostItem(MethodDesc(SGroupElementMethods.GetEncodedMethod), FixedCost(JitCost(250))), FixedCostItem(DecodePoint), FixedCostItem(ValUse), FixedCostItem(NamedDesc("EQ_GroupElement"), FixedCost(JitCost(172))) @@ -2743,7 +2731,7 @@ class SigmaDslSpecification extends SigmaDslTesting DecodePoint( MethodCall.typed[Value[SCollection[SByte.type]]]( ValUse(1, SGroupElement), - SGroupElement.getMethodByName("getEncoded"), + SGroupElementMethods.getMethodByName("getEncoded"), Vector(), Map() ) @@ -2756,7 +2744,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("GroupElement.negate equivalence") { verifyCases( { - def success[T](v: T) = Expected(Success(v), 1785, methodCostDetails(SGroupElement.NegateMethod, 45), 1785) + def success[T](v: T) = Expected(Success(v), 1785, methodCostDetails(SGroupElementMethods.NegateMethod, 45), 1785) Seq( (ge1, success(Helpers.decodeGroupElement("02358d53f01276211f92d0aefbd278805121d4ff6eb534b777af1ee8abae5b2056"))), (ge2, success(Helpers.decodeGroupElement("03dba7b94b111f3894e2f9120b577da595ec7d58d488485adf73bf4e153af63575"))), @@ -2769,7 +2757,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: GroupElement) => x.negate }", FuncValue( Vector((1, SGroupElement)), - MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("negate"), Vector(), Map()) + MethodCall(ValUse(1, SGroupElement), SGroupElementMethods.getMethodByName("negate"), Vector(), Map()) ))) } @@ -2822,7 +2810,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(MethodCall), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SGroupElement.ExponentiateMethod, FixedCost(JitCost(900))) + FixedCostItem(SGroupElementMethods.ExponentiateMethod, FixedCost(JitCost(900))) ) ) verifyCases(cases(1873, costDetails), @@ -2836,7 +2824,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, STuple(Vector(SGroupElement, SBigInt))), 1.toByte ), - SGroupElement.getMethodByName("exp"), + SGroupElementMethods.getMethodByName("exp"), Vector( SelectField.typed[Value[SBigInt.type]]( ValUse(1, STuple(Vector(SGroupElement, SBigInt))), @@ -2868,7 +2856,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(MethodCall), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SGroupElement.MultiplyMethod, FixedCost(JitCost(40))) + FixedCostItem(SGroupElementMethods.MultiplyMethod, FixedCost(JitCost(40))) ) ) ) @@ -2908,7 +2896,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SGroupElement, SGroupElement)), 1.toByte ), - SGroupElement.getMethodByName("multiply"), + SGroupElementMethods.getMethodByName("multiply"), Vector( SelectField.typed[Value[SGroupElement.type]]( ValUse(1, SPair(SGroupElement, SGroupElement)), @@ -2927,7 +2915,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SAvlTree)), MethodCall( ValUse(1, SAvlTree), - SAvlTree.getMethodByName(propName), + SAvlTreeMethods.getMethodByName(propName), Vector(), Map() ) @@ -2935,7 +2923,7 @@ class SigmaDslSpecification extends SigmaDslTesting } verifyCases( { - def success[T](v: T) = Expected(Success(v), 1767, methodCostDetails(SAvlTree.digestMethod, 15), 1767) + def success[T](v: T) = Expected(Success(v), 1767, methodCostDetails(SAvlTreeMethods.digestMethod, 15), 1767) Seq( (t1, success(Helpers.decodeBytes("000183807f66b301530120ff7fc6bd6601ff01ff7f7d2bedbbffff00187fe89094"))), (t2, success(Helpers.decodeBytes("ff000d937f80ffd731ed802d24358001ff8080ff71007f00ad37e0a7ae43fff95b"))), @@ -2948,7 +2936,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTree.enabledOperationsMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.enabledOperationsMethod, 15), 1765) Seq( (t1, success(6.toByte)), (t2, success(0.toByte)), @@ -2961,7 +2949,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTree.keyLengthMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.keyLengthMethod, 15), 1765) Seq( (t1, success(1)), (t2, success(32)), @@ -2974,7 +2962,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, methodCostDetails(SAvlTree.valueLengthOptMethod, 15), newCost) + def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, methodCostDetails(SAvlTreeMethods.valueLengthOptMethod, 15), newCost) Seq( (t1, success(Some(1), 1766)), (t2, success(Some(64), 1766)), @@ -2987,7 +2975,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTree.isInsertAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isInsertAllowedMethod, 15), 1765) Seq( (t1, success(false)), (t2, success(false)), @@ -3000,7 +2988,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTree.isUpdateAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isUpdateAllowedMethod, 15), 1765) Seq( (t1, success(true)), (t2, success(false)), @@ -3013,7 +3001,7 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( { - def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTree.isRemoveAllowedMethod, 15), 1765) + def success[T](v: T) = Expected(Success(v), 1765, methodCostDetails(SAvlTreeMethods.isRemoveAllowedMethod, 15), 1765) Seq( (t1, success(true)), (t2, success(false)), @@ -3055,7 +3043,7 @@ class SigmaDslSpecification extends SigmaDslTesting ), 1.toByte ), - SAvlTree.getMethodByName("contains"), + SAvlTreeMethods.getMethodByName("contains"), Vector( SelectField.typed[Value[SCollection[SByte.type]]]( ValUse(3, STuple(Vector(SCollectionType(SByte), SCollectionType(SByte)))), @@ -3099,7 +3087,7 @@ class SigmaDslSpecification extends SigmaDslTesting ), 1.toByte ), - SAvlTree.getMethodByName("get"), + SAvlTreeMethods.getMethodByName("get"), Vector( SelectField.typed[Value[SCollection[SByte.type]]]( ValUse(3, STuple(Vector(SCollectionType(SByte), SCollectionType(SByte)))), @@ -3162,7 +3150,7 @@ class SigmaDslSpecification extends SigmaDslTesting ), 1.toByte ), - SAvlTree.getMethodByName("getMany"), + SAvlTreeMethods.getMethodByName("getMany"), Vector( SelectField.typed[Value[SCollection[SCollection[SByte.type]]]]( ValUse(3, STuple(Vector(SCollectionType(SCollectionType(SByte)), SCollectionType(SByte)))), @@ -3188,7 +3176,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, STuple(Vector(SAvlTree, SCollectionType(SByte)))), 1.toByte ), - SAvlTree.getMethodByName("updateDigest"), + SAvlTreeMethods.getMethodByName("updateDigest"), Vector( SelectField.typed[Value[SCollection[SByte.type]]]( ValUse(1, STuple(Vector(SAvlTree, SCollectionType(SByte)))), @@ -3205,7 +3193,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, STuple(Vector(SAvlTree, SByte)))), MethodCall.typed[Value[SAvlTree.type]]( SelectField.typed[Value[SAvlTree.type]](ValUse(1, STuple(Vector(SAvlTree, SByte))), 1.toByte), - SAvlTree.getMethodByName("updateOperations"), + SAvlTreeMethods.getMethodByName("updateOperations"), Vector( SelectField.typed[Value[SByte.type]](ValUse(1, STuple(Vector(SAvlTree, SByte))), 2.toByte) ), @@ -3248,7 +3236,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -3259,12 +3247,12 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 456), - SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), - SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), - SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), - SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), - SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 456), + ast.SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), + ast.SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), + ast.SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), + ast.SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3), + ast.SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 3) ) ) @@ -3295,7 +3283,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -3306,8 +3294,8 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), i), - SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 1) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), i), + ast.SeqCostItem(NamedDesc("LookupAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 1) ) ) @@ -3323,7 +3311,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(MethodCall), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SAvlTree.updateDigestMethod, FixedCost(JitCost(40))) + FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40))) ) ) @@ -3339,7 +3327,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(MethodCall), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SAvlTree.updateOperationsMethod, FixedCost(JitCost(45))) + FixedCostItem(SAvlTreeMethods.updateOperationsMethod, FixedCost(JitCost(45))) ) ) @@ -3473,7 +3461,7 @@ class SigmaDslSpecification extends SigmaDslTesting ), 1.toByte ), - SAvlTree.getMethodByName("insert"), + SAvlTreeMethods.getMethodByName("insert"), Vector( SelectField.typed[Value[SCollection[STuple]]]( ValUse( @@ -3501,7 +3489,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -3512,14 +3500,14 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SAvlTree.isInsertAllowedMethod, FixedCost(JitCost(15))) + FixedCostItem(SAvlTreeMethods.isInsertAllowedMethod, FixedCost(JitCost(15))) ) val costDetails1 = TracedCost(testTraceBase) val costDetails2 = TracedCost( testTraceBase ++ Array( - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 70), - SeqCostItem(NamedDesc("InsertIntoAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 1), - FixedCostItem(SAvlTree.updateDigestMethod, FixedCost(JitCost(40))) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 70), + ast.SeqCostItem(NamedDesc("InsertIntoAvlTree"), PerItemCost(JitCost(40), JitCost(10), 1), 1), + FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40))) ) ) @@ -3611,7 +3599,7 @@ class SigmaDslSpecification extends SigmaDslTesting ), 1.toByte ), - SAvlTree.getMethodByName("update"), + SAvlTreeMethods.getMethodByName("update"), Vector( SelectField.typed[Value[SCollection[STuple]]]( ValUse( @@ -3639,7 +3627,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -3650,20 +3638,20 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SAvlTree.isUpdateAllowedMethod, FixedCost(JitCost(15))) + FixedCostItem(SAvlTreeMethods.isUpdateAllowedMethod, FixedCost(JitCost(15))) ) val costDetails1 = TracedCost(testTraceBase) val costDetails2 = TracedCost( testTraceBase ++ Array( - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111), - SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1), - FixedCostItem(SAvlTree.updateDigestMethod, FixedCost(JitCost(40))) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111), + ast.SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1), + FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40))) ) ) val costDetails3 = TracedCost( testTraceBase ++ Array( - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111), - SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 111), + ast.SeqCostItem(NamedDesc("UpdateAvlTree"), PerItemCost(JitCost(120), JitCost(20), 1), 1) ) ) @@ -3746,7 +3734,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, STuple(Vector(SAvlTree, STuple(Vector(SByteArray2, SByteArray))))), 1.toByte ), - SAvlTree.getMethodByName("remove"), + SAvlTreeMethods.getMethodByName("remove"), Vector( SelectField.typed[Value[SCollection[SCollection[SByte.type]]]]( ValUse(3, STuple(Vector(SByteArray2, SByteArray))), @@ -3768,7 +3756,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -3779,31 +3767,31 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SAvlTree.isRemoveAllowedMethod, FixedCost(JitCost(15))) + FixedCostItem(SAvlTreeMethods.isRemoveAllowedMethod, FixedCost(JitCost(15))) ) val costDetails1 = TracedCost( testTraceBase ++ Array( - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 436), - SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 3), - SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 3), - FixedCostItem(SAvlTree.digestMethod, FixedCost(JitCost(15))), - FixedCostItem(SAvlTree.updateDigestMethod, FixedCost(JitCost(40))) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 436), + ast.SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 3), + ast.SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 3), + FixedCostItem(SAvlTreeMethods.digestMethod, FixedCost(JitCost(15))), + FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40))) ) ) val costDetails2 = TracedCost( testTraceBase ++ Array( - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 140), - SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 1), - FixedCostItem(SAvlTree.digestMethod, FixedCost(JitCost(15))), - FixedCostItem(SAvlTree.updateDigestMethod, FixedCost(JitCost(40))) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 140), + ast.SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 1), + FixedCostItem(SAvlTreeMethods.digestMethod, FixedCost(JitCost(15))), + FixedCostItem(SAvlTreeMethods.updateDigestMethod, FixedCost(JitCost(40))) ) ) val costDetails3 = TracedCost(testTraceBase) val costDetails4 = TracedCost( testTraceBase ++ Array( - SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 140), - SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 1), - FixedCostItem(SAvlTree.digestMethod, FixedCost(JitCost(15))) + ast.SeqCostItem(NamedDesc("CreateAvlVerifier"), PerItemCost(JitCost(110), JitCost(20), 64), 140), + ast.SeqCostItem(NamedDesc("RemoveAvlTree"), PerItemCost(JitCost(100), JitCost(15), 1), 1), + FixedCostItem(SAvlTreeMethods.digestMethod, FixedCost(JitCost(15))) ) ) @@ -4047,8 +4035,8 @@ class SigmaDslSpecification extends SigmaDslTesting b1 -> Expected(Success(Coll[(Coll[Byte], Long)]( (Helpers.decodeBytes("6e789ab7b2fffff12280a6cd01557f6fb22b7f80ff7aff8e1f7f15973d7f0001"), 10000000L), (Helpers.decodeBytes("a3ff007f00057600808001ff8f8000019000ffdb806fff7cc0b6015eb37fa600"), 500L) - ).map(identity)), 1772, methodCostDetails(SBox.tokensMethod, 15), 1772), - b2 -> Expected(Success(Coll[(Coll[Byte], Long)]().map(identity)), 1766, methodCostDetails(SBox.tokensMethod, 15), 1766) + ).map(identity)), 1772, methodCostDetails(SBoxMethods.tokensMethod, 15), 1772), + b2 -> Expected(Success(Coll[(Coll[Byte], Long)]().map(identity)), 1766, methodCostDetails(SBoxMethods.tokensMethod, 15), 1766) ), existingFeature({ (x: Box) => x.tokens }, "{ (x: Box) => x.tokens }", @@ -4056,7 +4044,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SBox)), MethodCall.typed[Value[SCollection[STuple]]]( ValUse(1, SBox), - SBox.getMethodByName("tokens"), + SBoxMethods.getMethodByName("tokens"), Vector(), Map() ) @@ -4096,7 +4084,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(ExtractRegisterAs), FixedCost(JitCost(50))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -4114,7 +4102,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(ExtractRegisterAs), FixedCost(JitCost(50))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -4169,14 +4157,14 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(ExtractRegisterAs), FixedCost(JitCost(50))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(OptionIsDefined), FixedCost(JitCost(10))), FixedCostItem(CompanionDesc(If), FixedCost(JitCost(10))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -4198,14 +4186,14 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(ExtractRegisterAs), FixedCost(JitCost(50))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(OptionIsDefined), FixedCost(JitCost(10))), FixedCostItem(CompanionDesc(If), FixedCost(JitCost(10))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -4230,14 +4218,14 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(ExtractRegisterAs), FixedCost(JitCost(50))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(CompanionDesc(OptionIsDefined), FixedCost(JitCost(10))), FixedCostItem(CompanionDesc(If), FixedCost(JitCost(10))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -4481,11 +4469,11 @@ class SigmaDslSpecification extends SigmaDslTesting val tA = RType[A] val typeName = tA.name val stypeA = Evaluation.rtypeToSType(tA) - val typeCompanion = stypeA.asInstanceOf[STypeCompanion] + val m = MethodsContainer.getMethod(stypeA, propName).get existingFeature(scalaFunc, s"{ (x: $typeName) => x.$propName }", FuncValue(Vector((1, stypeA)), - MethodCall(ValUse(1, stypeA), typeCompanion.getMethodByName(propName), Vector(), Map() ) + MethodCall(ValUse(1, stypeA), m, Vector(), Map() ) )) } @@ -4493,35 +4481,35 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( - Seq((preH1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SPreHeader.versionMethod, 10), 1765))), + Seq((preH1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SPreHeaderMethods.versionMethod, 10), 1765))), existingPropTest("version", { (x: PreHeader) => x.version })) verifyCases( Seq((preH1, Expected(Success( Helpers.decodeBytes("7fff7fdd6f62018bae0001006d9ca888ff7f56ff8006573700a167f17f2c9f40")), - cost = 1766, methodCostDetails(SPreHeader.parentIdMethod, 10), 1766))), + cost = 1766, methodCostDetails(SPreHeaderMethods.parentIdMethod, 10), 1766))), existingPropTest("parentId", { (x: PreHeader) => x.parentId })) verifyCases( - Seq((preH1, Expected(Success(6306290372572472443L), cost = 1765, methodCostDetails(SPreHeader.timestampMethod, 10), 1765))), + Seq((preH1, Expected(Success(6306290372572472443L), cost = 1765, methodCostDetails(SPreHeaderMethods.timestampMethod, 10), 1765))), existingPropTest("timestamp", { (x: PreHeader) => x.timestamp })) verifyCases( - Seq((preH1, Expected(Success(-3683306095029417063L), cost = 1765, methodCostDetails(SPreHeader.nBitsMethod, 10), 1765))), + Seq((preH1, Expected(Success(-3683306095029417063L), cost = 1765, methodCostDetails(SPreHeaderMethods.nBitsMethod, 10), 1765))), existingPropTest("nBits", { (x: PreHeader) => x.nBits })) verifyCases( - Seq((preH1, Expected(Success(1), cost = 1765, methodCostDetails(SPreHeader.heightMethod, 10), 1765))), + Seq((preH1, Expected(Success(1), cost = 1765, methodCostDetails(SPreHeaderMethods.heightMethod, 10), 1765))), existingPropTest("height", { (x: PreHeader) => x.height })) verifyCases( Seq((preH1, Expected(Success( Helpers.decodeGroupElement("026930cb9972e01534918a6f6d6b8e35bc398f57140d13eb3623ea31fbd069939b")), - cost = 1782, methodCostDetails(SPreHeader.minerPkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SPreHeaderMethods.minerPkMethod, 10), 1782))), existingPropTest("minerPk", { (x: PreHeader) => x.minerPk })) verifyCases( - Seq((preH1, Expected(Success(Helpers.decodeBytes("ff8087")), cost = 1766, methodCostDetails(SPreHeader.votesMethod,10), 1766))), + Seq((preH1, Expected(Success(Helpers.decodeBytes("ff8087")), cost = 1766, methodCostDetails(SPreHeaderMethods.votesMethod,10), 1766))), existingPropTest("votes", { (x: PreHeader) => x.votes })) } @@ -4529,90 +4517,90 @@ class SigmaDslSpecification extends SigmaDslTesting verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("957f008001808080ffe4ffffc8f3802401df40006aa05e017fa8d3f6004c804a")), - cost = 1766, methodCostDetails(SHeader.idMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.idMethod, 10), 1766))), existingPropTest("id", { (x: Header) => x.id })) verifyCases( - Seq((h1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SHeader.versionMethod, 10), 1765))), + Seq((h1, Expected(Success(0.toByte), cost = 1765, methodCostDetails(SHeaderMethods.versionMethod, 10), 1765))), existingPropTest("version", { (x: Header) => x.version })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("0180dd805b0000ff5400b997fd7f0b9b00de00fb03c47e37806a8186b94f07ff")), - cost = 1766, methodCostDetails(SHeader.parentIdMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.parentIdMethod, 10), 1766))), existingPropTest("parentId", { (x: Header) => x.parentId })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("01f07f60d100ffb970c3007f60ff7f24d4070bb8fffa7fca7f34c10001ffe39d")), - cost = 1766, methodCostDetails(SHeader.ADProofsRootMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.ADProofsRootMethod, 10), 1766))), existingPropTest("ADProofsRoot", { (x: Header) => x.ADProofsRoot})) verifyCases( - Seq((h1, Expected(Success(CAvlTree(createAvlTreeData())), cost = 1765, methodCostDetails(SHeader.stateRootMethod, 10), 1765))), + Seq((h1, Expected(Success(CAvlTree(createAvlTreeData())), cost = 1765, methodCostDetails(SHeaderMethods.stateRootMethod, 10), 1765))), existingPropTest("stateRoot", { (x: Header) => x.stateRoot })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("804101ff01000080a3ffbd006ac080098df132a7017f00649311ec0e00000100")), - cost = 1766, methodCostDetails(SHeader.transactionsRootMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.transactionsRootMethod, 10), 1766))), existingPropTest("transactionsRoot", { (x: Header) => x.transactionsRoot })) verifyCases( - Seq((h1, Expected(Success(1L), cost = 1765, methodCostDetails(SHeader.timestampMethod, 10), 1765))), + Seq((h1, Expected(Success(1L), cost = 1765, methodCostDetails(SHeaderMethods.timestampMethod, 10), 1765))), existingPropTest("timestamp", { (x: Header) => x.timestamp })) verifyCases( - Seq((h1, Expected(Success(-1L), cost = 1765, methodCostDetails(SHeader.nBitsMethod, 10), 1765))), + Seq((h1, Expected(Success(-1L), cost = 1765, methodCostDetails(SHeaderMethods.nBitsMethod, 10), 1765))), existingPropTest("nBits", { (x: Header) => x.nBits })) verifyCases( - Seq((h1, Expected(Success(1), cost = 1765, methodCostDetails(SHeader.heightMethod, 10), 1765))), + Seq((h1, Expected(Success(1), cost = 1765, methodCostDetails(SHeaderMethods.heightMethod, 10), 1765))), existingPropTest("height", { (x: Header) => x.height })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("e57f80885601b8ff348e01808000bcfc767f2dd37f0d01015030ec018080bc62")), - cost = 1766, methodCostDetails(SHeader.extensionRootMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.extensionRootMethod, 10), 1766))), existingPropTest("extensionRoot", { (x: Header) => x.extensionRoot })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeGroupElement("039bdbfa0b49cc6bef58297a85feff45f7bbeb500a9d2283004c74fcedd4bd2904")), - cost = 1782, methodCostDetails(SHeader.minerPkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SHeaderMethods.minerPkMethod, 10), 1782))), existingPropTest("minerPk", { (x: Header) => x.minerPk })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeGroupElement("0361299207fa392231e23666f6945ae3e867b978e021d8d702872bde454e9abe9c")), - cost = 1782, methodCostDetails(SHeader.powOnetimePkMethod, 10), 1782))), + cost = 1782, methodCostDetails(SHeaderMethods.powOnetimePkMethod, 10), 1782))), existingPropTest("powOnetimePk", { (x: Header) => x.powOnetimePk })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("7f4f09012a807f01")), - cost = 1766, methodCostDetails(SHeader.powNonceMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.powNonceMethod, 10), 1766))), existingPropTest("powNonce", { (x: Header) => x.powNonce })) verifyCases( Seq((h1, Expected(Success( CBigInt(new BigInteger("-e24990c47e15ed4d0178c44f1790cc72155d516c43c3e8684e75db3800a288", 16))), - cost = 1765, methodCostDetails(SHeader.powDistanceMethod, 10), 1765))), + cost = 1765, methodCostDetails(SHeaderMethods.powDistanceMethod, 10), 1765))), existingPropTest("powDistance", { (x: Header) => x.powDistance })) verifyCases( Seq((h1, Expected(Success( Helpers.decodeBytes("7f0180")), - cost = 1766, methodCostDetails(SHeader.votesMethod, 10), 1766))), + cost = 1766, methodCostDetails(SHeaderMethods.votesMethod, 10), 1766))), existingPropTest("votes", { (x: Header) => x.votes })) } def contextData() = { - val input = CostingBox( + val input = CBox( new ErgoBox( 80946L, new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Vector( SigmaPropConstant( CSigmaProp( @@ -4638,11 +4626,11 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) - val dataBox = CostingBox( + val dataBox = CBox( new ErgoBox( -1L, new ErgoTree( - 0.toByte, + HeaderType @@ 0.toByte, Vector(), Right(SigmaPropConstant(CSigmaProp(ProveDlog(Helpers.decodeECPoint("02af645874c3b53465a5e9d820eb207d6001258c3b708f0d31d7c2e342833dce64"))))) ), @@ -4690,7 +4678,7 @@ class SigmaDslSpecification extends SigmaDslTesting Helpers.decodeBytes("01ff13") ) - val ctx = CostingDataContext( + val ctx = CContext( _dataInputs = Coll[Box](dataBox), headers = Coll[Header](header), preHeader = CPreHeader( @@ -4704,11 +4692,11 @@ class SigmaDslSpecification extends SigmaDslTesting ), inputs = Coll[Box](input), outputs = Coll[Box]( - CostingBox( + CBox( new ErgoBox( 1000000L, new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Vector( SigmaPropConstant( CSigmaProp( @@ -4741,11 +4729,11 @@ class SigmaDslSpecification extends SigmaDslTesting 11 ) ), - CostingBox( + CBox( new ErgoBox( 2769336982721999022L, new ErgoTree( - 0.toByte, + HeaderType @@ 0.toByte, Vector(), Right(SigmaPropConstant(CSigmaProp(ProveDlog(Helpers.decodeECPoint("02d13e1a8c31f32194761adc1cdcbaa746b3e049e682bba9308d8ee84576172991"))))) ), @@ -4783,19 +4771,19 @@ class SigmaDslSpecification extends SigmaDslTesting (input, dataBox, header, ctx, ctx2, ctx3) } - def ctxWithRegsInOutput(ctx: CostingDataContext, regs: AdditionalRegisters) = { + def ctxWithRegsInOutput(ctx: CContext, regs: AdditionalRegisters) = { ctx.copy( outputs = Coll({ - val box = ctx.outputs(0).asInstanceOf[CostingBox] + val box = ctx.outputs(0).asInstanceOf[CBox] box.copy(ebox = copyBox(box.ebox)(additionalRegisters = regs)) }) ) } - def ctxWithRegsInDataInput(ctx: CostingDataContext, regs: AdditionalRegisters) = { + def ctxWithRegsInDataInput(ctx: CContext, regs: AdditionalRegisters) = { ctx.copy( _dataInputs = Coll({ - val box = ctx.dataInputs(0).asInstanceOf[CostingBox] + val box = ctx.dataInputs(0).asInstanceOf[CBox] box.copy(ebox = copyBox(box.ebox)(additionalRegisters = regs)) }) ) @@ -4809,7 +4797,7 @@ class SigmaDslSpecification extends SigmaDslTesting val testTraceBase = traceBase ++ Array( FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex) ) @@ -4827,7 +4815,7 @@ class SigmaDslSpecification extends SigmaDslTesting ByIndex( MethodCall.typed[Value[SCollection[SBox.type]]]( ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + SContextMethods.getMethodByName("dataInputs"), Vector(), Map() ), @@ -4852,7 +4840,7 @@ class SigmaDslSpecification extends SigmaDslTesting ByIndex( MethodCall.typed[Value[SCollection[SBox.type]]]( ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + SContextMethods.getMethodByName("dataInputs"), Vector(), Map() ), @@ -4919,7 +4907,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(Inputs), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount))) @@ -4933,7 +4921,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(Inputs), FixedCostItem(MethodCall), FixedCostItem(FuncValue), - SeqCostItem(MethodDesc(SCollection.MapMethod), PerItemCost(JitCost(20), JitCost(1), 10), 1), + ast.SeqCostItem(MethodDesc(SCollectionMethods.MapMethod), PerItemCost(JitCost(20), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount) @@ -4954,7 +4942,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SContext)), MethodCall.typed[Value[SCollection[SLong.type]]]( Inputs, - SCollection.getMethodByName("map").withConcreteTypes( + SCollectionMethods.getMethodByName("map").withConcreteTypes( Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SLong) ), Vector(FuncValue(Array((3, SBox)), ExtractAmount(ValUse(3, SBox)))), @@ -4975,9 +4963,9 @@ class SigmaDslSpecification extends SigmaDslTesting if (lowerMethodCallsInTests) Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -4988,9 +4976,9 @@ class SigmaDslSpecification extends SigmaDslTesting Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - SeqCostItem(MethodDesc(SCollection.MapMethod), PerItemCost(JitCost(20), JitCost(1), 10), 1), + ast.SeqCostItem(MethodDesc(SCollectionMethods.MapMethod), PerItemCost(JitCost(20), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5025,7 +5013,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SContext)), MethodCall.typed[Value[SCollection[STuple]]]( Inputs, - SCollection.getMethodByName("map").withConcreteTypes( + SCollectionMethods.getMethodByName("map").withConcreteTypes( Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SPair(SLong, SLong)) ), Vector( @@ -5082,7 +5070,7 @@ class SigmaDslSpecification extends SigmaDslTesting val selfCostDetails = TracedCost( traceBase ++ Array( FixedCostItem(PropertyCall), - FixedCostItem(SContext.selfBoxIndexMethod, FixedCost(JitCost(20))) + FixedCostItem(SContextMethods.selfBoxIndexMethod, FixedCost(JitCost(20))) ) ) verifyCases( @@ -5103,7 +5091,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SContext)), MethodCall.typed[Value[SInt.type]]( ValUse(1, SContext), - SContext.getMethodByName("selfBoxIndex"), + SContextMethods.getMethodByName("selfBoxIndex"), Vector(), Map() ) @@ -5122,16 +5110,16 @@ class SigmaDslSpecification extends SigmaDslTesting } verifyCases( - Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash), cost = 1766, methodCostDetails(SContext.lastBlockUtxoRootHashMethod, 15), 1766)), + Seq(ctx -> Expected(Success(ctx.LastBlockUtxoRootHash), cost = 1766, methodCostDetails(SContextMethods.lastBlockUtxoRootHashMethod, 15), 1766)), existingPropTest("LastBlockUtxoRootHash", { (x: Context) => x.LastBlockUtxoRootHash }), preGeneratedSamples = Some(samples)) val isUpdateAllowedCostDetails = TracedCost( traceBase ++ Array( FixedCostItem(PropertyCall), - FixedCostItem(SContext.lastBlockUtxoRootHashMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.lastBlockUtxoRootHashMethod, FixedCost(JitCost(15))), FixedCostItem(PropertyCall), - FixedCostItem(SAvlTree.isUpdateAllowedMethod, FixedCost(JitCost(15))) + FixedCostItem(SAvlTreeMethods.isUpdateAllowedMethod, FixedCost(JitCost(15))) ) ) verifyCases( @@ -5144,11 +5132,11 @@ class SigmaDslSpecification extends SigmaDslTesting MethodCall.typed[Value[SBoolean.type]]( MethodCall.typed[Value[SAvlTree.type]]( ValUse(1, SContext), - SContext.getMethodByName("LastBlockUtxoRootHash"), + SContextMethods.getMethodByName("LastBlockUtxoRootHash"), Vector(), Map() ), - SAvlTree.getMethodByName("isUpdateAllowed"), + SAvlTreeMethods.getMethodByName("isUpdateAllowed"), Vector(), Map() ) @@ -5156,7 +5144,7 @@ class SigmaDslSpecification extends SigmaDslTesting preGeneratedSamples = Some(samples)) verifyCases( - Seq(ctx -> Expected(Success(ctx.minerPubKey), cost = 1767, methodCostDetails(SContext.minerPubKeyMethod, 20), 1767)), + Seq(ctx -> Expected(Success(ctx.minerPubKey), cost = 1767, methodCostDetails(SContextMethods.minerPubKeyMethod, 20), 1767)), existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey }), preGeneratedSamples = Some(samples)) @@ -5213,10 +5201,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(ExtractRegisterAs), @@ -5260,7 +5248,7 @@ class SigmaDslSpecification extends SigmaDslTesting ByIndex( MethodCall.typed[Value[SCollection[SBox.type]]]( ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + SContextMethods.getMethodByName("dataInputs"), Vector(), Map() ), @@ -5332,7 +5320,7 @@ class SigmaDslSpecification extends SigmaDslTesting ByIndex( MethodCall.typed[Value[SCollection[SBox.type]]]( ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + SContextMethods.getMethodByName("dataInputs"), Vector(), Map() ), @@ -5374,7 +5362,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(Outputs), FixedCostItem(Constant), FixedCostItem(ByIndex), @@ -5385,7 +5373,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5409,7 +5397,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(Outputs), FixedCostItem(Constant), FixedCostItem(ByIndex), @@ -5420,7 +5408,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5442,7 +5430,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(Outputs), FixedCostItem(Constant), FixedCostItem(ByIndex), @@ -5543,7 +5531,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(Outputs), FixedCostItem(Constant), FixedCostItem(ByIndex), @@ -5554,7 +5542,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5581,7 +5569,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(Outputs), FixedCostItem(Constant), FixedCostItem(ByIndex), @@ -5592,7 +5580,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5616,7 +5604,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(Outputs), FixedCostItem(Constant), FixedCostItem(ByIndex), @@ -5627,7 +5615,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5649,7 +5637,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(Outputs), FixedCostItem(Constant), FixedCostItem(ByIndex), @@ -5772,10 +5760,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5785,7 +5773,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5809,10 +5797,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5822,7 +5810,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5843,10 +5831,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5856,7 +5844,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5878,10 +5866,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -5959,7 +5947,7 @@ class SigmaDslSpecification extends SigmaDslTesting ByIndex( MethodCall.typed[Value[SCollection[SBox.type]]]( ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + SContextMethods.getMethodByName("dataInputs"), Vector(), Map() ), @@ -6000,10 +5988,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -6013,7 +6001,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -6040,10 +6028,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -6053,7 +6041,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -6077,10 +6065,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -6090,7 +6078,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(OptionIsDefined), FixedCostItem(If), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -6112,10 +6100,10 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.dataInputsMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.dataInputsMethod, FixedCost(JitCost(15))), FixedCostItem(Constant), FixedCostItem(ByIndex), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -6192,7 +6180,7 @@ class SigmaDslSpecification extends SigmaDslTesting ByIndex( MethodCall.typed[Value[SCollection[SBox.type]]]( ValUse(1, SContext), - SContext.getMethodByName("dataInputs"), + SContextMethods.getMethodByName("dataInputs"), Vector(), Map() ), @@ -6237,7 +6225,7 @@ class SigmaDslSpecification extends SigmaDslTesting } property("xorOf equivalence") { - def costDetails(i: Int) = TracedCost(traceBase :+ SeqCostItem(CompanionDesc(XorOf), PerItemCost(JitCost(20), JitCost(5), 32), i)) + def costDetails(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(XorOf), PerItemCost(JitCost(20), JitCost(5), 32), i)) verifyCases( { def successNew[T](v: T, c: Int, newV: T, cd: CostDetails) = Expected( @@ -6395,7 +6383,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(Global), FixedCostItem(PropertyCall), - FixedCostItem(SGlobal.groupGeneratorMethod, FixedCost(JitCost(10))) + FixedCostItem(SGlobalMethods.groupGeneratorMethod, FixedCost(JitCost(10))) ) ) verifyCases( @@ -6411,7 +6399,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SInt)), MethodCall.typed[Value[SGroupElement.type]]( Global, - SGlobal.getMethodByName("groupGenerator"), + SGlobalMethods.getMethodByName("groupGenerator"), Vector(), Map() ) @@ -6430,7 +6418,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SInt)), MethodCall.typed[Value[SGroupElement.type]]( Global, - SGlobal.getMethodByName("groupGenerator"), + SGlobalMethods.getMethodByName("groupGenerator"), Vector(), Map() ) @@ -6468,7 +6456,7 @@ class SigmaDslSpecification extends SigmaDslTesting Exponentiate( MethodCall.typed[Value[SGroupElement.type]]( Global, - SGlobal.getMethodByName("groupGenerator"), + SGlobalMethods.getMethodByName("groupGenerator"), Vector(), Map() ), @@ -6486,7 +6474,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(CompanionDesc(Xor), PerItemCost(JitCost(10), JitCost(2), 128), i) + ast.SeqCostItem(CompanionDesc(Xor), PerItemCost(JitCost(10), JitCost(2), 128), i) ) ) else @@ -6503,7 +6491,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(CompanionDesc(Xor), PerItemCost(JitCost(10), JitCost(2), 128), i) + ast.SeqCostItem(CompanionDesc(Xor), PerItemCost(JitCost(10), JitCost(2), 128), i) ) ) } @@ -6554,7 +6542,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SPair(SByteArray, SByteArray))), MethodCall.typed[Value[SCollection[SByte.type]]]( Global, - SGlobal.getMethodByName("xor"), + SGlobalMethods.getMethodByName("xor"), Vector( SelectField.typed[Value[SCollection[SByte.type]]]( ValUse(1, SPair(SByteArray, SByteArray)), @@ -6573,11 +6561,11 @@ class SigmaDslSpecification extends SigmaDslTesting def sampleCollBoxes = genSamples[Coll[Box]](collOfN[Box](5, arbitrary[Box]), MinSuccessful(20)) - def create_b1 = CostingBox( + def create_b1 = CBox( new ErgoBox( 1L, new ErgoTree( - 0.toByte, + HeaderType @@ 0.toByte, Vector(), Right( SigmaPropConstant( @@ -6609,11 +6597,11 @@ class SigmaDslSpecification extends SigmaDslTesting ) ) - def create_b2 = CostingBox( + def create_b2 = CBox( new ErgoBox( 1000000000L, new ErgoTree( - 0.toByte, + HeaderType @@ 0.toByte, Vector(), Right(BoolToSigmaProp(OR(ConcreteCollection(Array(FalseLeaf, AND(ConcreteCollection(Array(FalseLeaf, FalseLeaf), SBoolean))), SBoolean)))) ), @@ -6633,13 +6621,13 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), @@ -6650,7 +6638,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails3 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), @@ -6693,7 +6681,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 0) + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 0) ) ) val costDetails2 = TracedCost( @@ -6703,7 +6691,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractScriptBytes), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 135) + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 135) ) ) val costDetails3 = TracedCost( @@ -6716,7 +6704,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractScriptBytes), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 147) + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 147) ) ) verifyCases( @@ -6737,7 +6725,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SCollectionType(SBox))), MethodCall.typed[Value[SCollection[SByte.type]]]( ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("flatMap").withConcreteTypes( + SCollectionMethods.getMethodByName("flatMap").withConcreteTypes( Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SByte) ), Vector(FuncValue(Vector((3, SBox)), ExtractScriptBytes(ValUse(3, SBox)))), @@ -6755,7 +6743,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(ValUse), - SeqCostItem(MethodDesc(SCollection.ZipMethod), PerItemCost(JitCost(10), JitCost(1), 10), zipElements) + ast.SeqCostItem(MethodDesc(SCollectionMethods.ZipMethod), PerItemCost(JitCost(10), JitCost(1), 10), zipElements) ) ) @@ -6774,7 +6762,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SCollectionType(SBox))), MethodCall.typed[Value[SCollection[STuple]]]( ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("zip").withConcreteTypes( + SCollectionMethods.getMethodByName("zip").withConcreteTypes( Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SBox) ), Vector(ValUse(1, SCollectionType(SBox))), @@ -6811,7 +6799,7 @@ class SigmaDslSpecification extends SigmaDslTesting def costDetails(indicesCount: Int) = TracedCost( traceBase ++ Array( FixedCostItem(PropertyCall), - SeqCostItem(MethodDesc(SCollection.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), indicesCount) + ast.SeqCostItem(MethodDesc(SCollectionMethods.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), indicesCount) ) ) verifyCases( @@ -6829,7 +6817,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SCollectionType(SBox))), MethodCall.typed[Value[SCollection[SInt.type]]]( ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SBox)), + SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SBox)), Vector(), Map() ) @@ -6845,13 +6833,13 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails1 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), @@ -6862,7 +6850,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails3 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), @@ -6900,7 +6888,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SCollectionType(SBox))), MethodCall.typed[Value[SBoolean.type]]( ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("forall").withConcreteTypes(Map(STypeVar("IV") -> SBox)), + SCollectionMethods.getMethodByName("forall").withConcreteTypes(Map(STypeVar("IV") -> SBox)), Vector(FuncValue(Array((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L)))), Map() ) @@ -6916,13 +6904,13 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails1 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), @@ -6933,7 +6921,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails3 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(ExtractAmount), @@ -6976,7 +6964,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SCollectionType(SBox))), MethodCall.typed[Value[SBoolean.type]]( ValUse(1, SCollectionType(SBox)), - SCollection.getMethodByName("exists").withConcreteTypes(Map(STypeVar("IV") -> SBox)), + SCollectionMethods.getMethodByName("exists").withConcreteTypes(Map(STypeVar("IV") -> SBox)), Vector(FuncValue(Array((3, SBox)), GT(ExtractAmount(ValUse(3, SBox)), LongConstant(1L)))), Map() ) @@ -6990,13 +6978,13 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails1 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -7008,7 +6996,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails3 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -7022,7 +7010,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails4 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(Exists), PerItemCost(JitCost(3), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -7082,7 +7070,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SCollectionType(SBigInt))), MethodCall.typed[Value[SBoolean.type]]( ValUse(1, SCollectionType(SBigInt)), - SCollection.getMethodByName("exists").withConcreteTypes(Map(STypeVar("IV") -> SBigInt)), + SCollectionMethods.getMethodByName("exists").withConcreteTypes(Map(STypeVar("IV") -> SBigInt)), Vector( FuncValue( Array((3, SBigInt)), @@ -7104,13 +7092,13 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails1 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -7122,7 +7110,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails3 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -7136,7 +7124,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails4 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -7197,7 +7185,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SCollectionType(SBigInt))), MethodCall.typed[Value[SBoolean.type]]( ValUse(1, SCollectionType(SBigInt)), - SCollection.getMethodByName("forall").withConcreteTypes(Map(STypeVar("IV") -> SBigInt)), + SCollectionMethods.getMethodByName("forall").withConcreteTypes(Map(STypeVar("IV") -> SBigInt)), Vector( FuncValue( Array((3, SBigInt)), @@ -7225,7 +7213,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 0) + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 0) ) ) val costDetails1 = TracedCost( @@ -7233,8 +7221,8 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(MethodCall), FixedCostItem(FuncValue), FixedCostItem(NamedDesc("MatchSingleArgMethodCall"), FixedCost(JitCost(30))), - SeqCostItem(NamedDesc("CheckFlatmapBody"), PerItemCost(JitCost(20), JitCost(20), 1), 1), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 0) + ast.SeqCostItem(NamedDesc("CheckFlatmapBody"), PerItemCost(JitCost(20), JitCost(20), 1), 1), + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 0) ) ) val costDetails2 = TracedCost( @@ -7244,12 +7232,12 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SGroupElement.GetEncodedMethod, FixedCost(JitCost(250))), + FixedCostItem(SGroupElementMethods.GetEncodedMethod, FixedCost(JitCost(250))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SGroupElement.GetEncodedMethod, FixedCost(JitCost(250))), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 66) + FixedCostItem(SGroupElementMethods.GetEncodedMethod, FixedCost(JitCost(250))), + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 66) ) ) val costDetails3 = TracedCost( @@ -7259,16 +7247,16 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SGroupElement.GetEncodedMethod, FixedCost(JitCost(250))), + FixedCostItem(SGroupElementMethods.GetEncodedMethod, FixedCost(JitCost(250))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SGroupElement.GetEncodedMethod, FixedCost(JitCost(250))), + FixedCostItem(SGroupElementMethods.GetEncodedMethod, FixedCost(JitCost(250))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SGroupElement.GetEncodedMethod, FixedCost(JitCost(250))), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 99) + FixedCostItem(SGroupElementMethods.GetEncodedMethod, FixedCost(JitCost(250))), + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 99) ) ) val costDetails4 = TracedCost( @@ -7278,16 +7266,16 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SGroupElement.GetEncodedMethod, FixedCost(JitCost(250))), + FixedCostItem(SGroupElementMethods.GetEncodedMethod, FixedCost(JitCost(250))), FixedCostItem(PropertyCall), - SeqCostItem(MethodDesc(SCollection.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), 33), + ast.SeqCostItem(MethodDesc(SCollectionMethods.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), 33), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SGroupElement.GetEncodedMethod, FixedCost(JitCost(250))), + FixedCostItem(SGroupElementMethods.GetEncodedMethod, FixedCost(JitCost(250))), FixedCostItem(PropertyCall), - SeqCostItem(MethodDesc(SCollection.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), 33), - SeqCostItem(MethodDesc(SCollection.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 66) + ast.SeqCostItem(MethodDesc(SCollectionMethods.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), 33), + ast.SeqCostItem(MethodDesc(SCollectionMethods.FlatMapMethod), PerItemCost(JitCost(60), JitCost(10), 8), 66) ) ) @@ -7324,7 +7312,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SCollectionType(SGroupElement))), MethodCall.typed[Value[SCollection[SByte.type]]]( ValUse(1, SCollectionType(SGroupElement)), - SCollection.getMethodByName("flatMap").withConcreteTypes( + SCollectionMethods.getMethodByName("flatMap").withConcreteTypes( Map(STypeVar("IV") -> SGroupElement, STypeVar("OV") -> SByte) ), Vector( @@ -7332,7 +7320,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((3, SGroupElement)), MethodCall.typed[Value[SCollection[SByte.type]]]( ValUse(3, SGroupElement), - SGroupElement.getMethodByName("getEncoded"), + SGroupElementMethods.getMethodByName("getEncoded"), Vector(), Map() ) @@ -7366,7 +7354,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SCollectionType(SGroupElement))), MethodCall.typed[Value[SCollection[SInt.type]]]( ValUse(1, SCollectionType(SGroupElement)), - SCollection.getMethodByName("flatMap").withConcreteTypes( + SCollectionMethods.getMethodByName("flatMap").withConcreteTypes( Map(STypeVar("IV") -> SGroupElement, STypeVar("OV") -> SInt) ), Vector( @@ -7375,11 +7363,11 @@ class SigmaDslSpecification extends SigmaDslTesting MethodCall.typed[Value[SCollection[SInt.type]]]( MethodCall.typed[Value[SCollection[SByte.type]]]( ValUse(3, SGroupElement), - SGroupElement.getMethodByName("getEncoded"), + SGroupElementMethods.getMethodByName("getEncoded"), Vector(), Map() ), - SCollection.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SByte)), + SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SByte)), Vector(), Map() ) @@ -7402,7 +7390,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -7416,7 +7404,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(MethodDesc(SCollection.PatchMethod), PerItemCost(JitCost(30), JitCost(2), 10), i) + ast.SeqCostItem(MethodDesc(SCollectionMethods.PatchMethod), PerItemCost(JitCost(30), JitCost(2), 10), i) ) ) @@ -7472,7 +7460,7 @@ class SigmaDslSpecification extends SigmaDslTesting ), MethodCall.typed[Value[SCollection[SInt.type]]]( ValUse(3, SCollectionType(SInt)), - SCollection.getMethodByName("patch").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + SCollectionMethods.getMethodByName("patch").withConcreteTypes(Map(STypeVar("IV") -> SInt)), Vector( SelectField.typed[Value[SInt.type]](ValUse(4, SPair(SInt, SInt)), 1.toByte), ValUse(3, SCollectionType(SInt)), @@ -7494,7 +7482,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -7505,7 +7493,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(MethodDesc(SCollection.UpdatedMethod), PerItemCost(JitCost(20), JitCost(1), 10), i) + ast.SeqCostItem(MethodDesc(SCollectionMethods.UpdatedMethod), PerItemCost(JitCost(20), JitCost(1), 10), i) ) ) verifyCases( @@ -7544,7 +7532,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), 1.toByte ), - SCollection.getMethodByName("updated").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + SCollectionMethods.getMethodByName("updated").withConcreteTypes(Map(STypeVar("IV") -> SInt)), Vector( SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) @@ -7571,7 +7559,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -7582,7 +7570,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(MethodDesc(SCollection.UpdateManyMethod), PerItemCost(JitCost(20), JitCost(2), 10), i) + ast.SeqCostItem(MethodDesc(SCollectionMethods.UpdateManyMethod), PerItemCost(JitCost(20), JitCost(2), 10), i) ) ) verifyCases( @@ -7632,7 +7620,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SCollectionType(SInt), SPair(SCollectionType(SInt), SCollectionType(SInt)))), 1.toByte ), - SCollection.getMethodByName("updateMany").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + SCollectionMethods.getMethodByName("updateMany").withConcreteTypes(Map(STypeVar("IV") -> SInt)), Vector( SelectField.typed[Value[SCollection[SInt.type]]]( ValUse(3, SPair(SCollectionType(SInt), SCollectionType(SInt))), @@ -7688,7 +7676,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( @@ -7697,7 +7685,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(SelectField), @@ -7713,7 +7701,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(SelectField), @@ -7736,7 +7724,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 3), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 3), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(SelectField), @@ -7805,7 +7793,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SPair(SByteArray, SInt))), MethodCall.typed[Value[SInt.type]]( SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), - SCollection.getMethodByName("fold").withConcreteTypes(Map(STypeVar("IV") -> SByte, STypeVar("OV") -> SInt)), + SCollectionMethods.getMethodByName("fold").withConcreteTypes(Map(STypeVar("IV") -> SByte, STypeVar("OV") -> SInt)), Vector( SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte), FuncValue( @@ -7835,7 +7823,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( @@ -7844,9 +7832,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -7869,9 +7857,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -7892,9 +7880,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -7910,7 +7898,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), TypeBasedCostItem(ArithOp.Plus, SInt), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -7933,9 +7921,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -7951,7 +7939,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), TypeBasedCostItem(ArithOp.Plus, SInt), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -7972,9 +7960,9 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 3), + ast.SeqCostItem(CompanionDesc(Fold), PerItemCost(JitCost(3), JitCost(1), 10), 3), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -7990,7 +7978,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ValUse), TypeBasedCostItem(ArithOp.Plus, SInt), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -8004,7 +7992,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(If), FixedCostItem(ValUse), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 2), FixedCostItem(ValUse), FixedCostItem(SelectField), TypeBasedCostItem(Upcast, SInt), @@ -8083,7 +8071,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SPair(SByteArray, SInt))), MethodCall.typed[Value[SInt.type]]( SelectField.typed[Value[SCollection[SByte.type]]](ValUse(1, SPair(SByteArray, SInt)), 1.toByte), - SCollection.getMethodByName("fold").withConcreteTypes(Map(STypeVar("IV") -> SByte, STypeVar("OV") -> SInt)), + SCollectionMethods.getMethodByName("fold").withConcreteTypes(Map(STypeVar("IV") -> SByte, STypeVar("OV") -> SInt)), Vector( SelectField.typed[Value[SInt.type]](ValUse(1, SPair(SByteArray, SInt)), 2.toByte), FuncValue( @@ -8127,7 +8115,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -8140,7 +8128,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField) ) ++ Array.fill(i)(FixedCostItem(NamedDesc("EQ_Prim"), FixedCost(JitCost(3)))) - :+ SeqCostItem(MethodDesc(SCollection.IndexOfMethod), PerItemCost(JitCost(20), JitCost(10), 2), i) + :+ ast.SeqCostItem(MethodDesc(SCollectionMethods.IndexOfMethod), PerItemCost(JitCost(20), JitCost(10), 2), i) ) verifyCases( // (coll, (elem: Byte, from: Int)) @@ -8189,7 +8177,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SByteArray, SPair(SByte, SInt))), 1.toByte ), - SCollection.getMethodByName("indexOf").withConcreteTypes(Map(STypeVar("IV") -> SByte)), + SCollectionMethods.getMethodByName("indexOf").withConcreteTypes(Map(STypeVar("IV") -> SByte)), Vector( SelectField.typed[Value[SByte.type]](ValUse(3, SPair(SByte, SInt)), 1.toByte), SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SByte, SInt)), 2.toByte) @@ -8247,7 +8235,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -8268,7 +8256,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - FixedCostItem(SCollection.GetOrElseMethod, FixedCost(JitCost(30))) + FixedCostItem(SCollectionMethods.GetOrElseMethod, FixedCost(JitCost(30))) ) ) ) @@ -8333,7 +8321,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), 1.toByte ), - SCollection.getMethodByName("getOrElse").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + SCollectionMethods.getMethodByName("getOrElse").withConcreteTypes(Map(STypeVar("IV") -> SInt)), Vector( SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) @@ -8407,13 +8395,13 @@ class SigmaDslSpecification extends SigmaDslTesting if (lowerMethodCallsInTests) Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), i) + ast.SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), i) ) else Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - SeqCostItem(MethodDesc(SCollection.MapMethod), PerItemCost(JitCost(20), JitCost(1), 10), i) + ast.SeqCostItem(MethodDesc(SCollectionMethods.MapMethod), PerItemCost(JitCost(20), JitCost(1), 10), i) ) ) ++ repeatPlusChunk(i) @@ -8445,7 +8433,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SCollectionType(SInt))), MethodCall.typed[Value[SCollection[SInt.type]]]( ValUse(1, SCollectionType(SInt)), - SCollection.getMethodByName("map").withConcreteTypes( + SCollectionMethods.getMethodByName("map").withConcreteTypes( Map(STypeVar("IV") -> SInt, STypeVar("OV") -> SInt) ), Vector( @@ -8461,13 +8449,13 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails1 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 0) ) ) val costDetails2 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -8481,7 +8469,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails3 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 1), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -8495,7 +8483,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails4 = TracedCost( traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 2), + ast.SeqCostItem(CompanionDesc(MapCollection), PerItemCost(JitCost(20), JitCost(1), 10), 2), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -8563,7 +8551,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), i) + ast.SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), i) ) ++ gtChunk ) @@ -8617,7 +8605,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), i) + ast.SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), i) ) ++ repeatLeftBranch(i) ) @@ -8626,7 +8614,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 3) + ast.SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 3) ) ++ repeatLeftBranch(2) ++ rightBranch @@ -8635,7 +8623,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 5) + ast.SeqCostItem(CompanionDesc(Filter), PerItemCost(JitCost(20), JitCost(1), 10), 5) ) ++ leftBranch ++ rightBranch ++ @@ -8679,7 +8667,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(SelectField), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), @@ -8689,7 +8677,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(CompanionDesc(Slice), PerItemCost(JitCost(10), JitCost(2), 100), i) + ast.SeqCostItem(CompanionDesc(Slice), PerItemCost(JitCost(10), JitCost(2), 100), i) ) ) val samples = genSamples(collWithRangeGen, DefaultMinSuccessful) @@ -8762,7 +8750,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SCollectionType(SInt), SPair(SInt, SInt))), 1.toByte ), - SCollection.getMethodByName("slice").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + SCollectionMethods.getMethodByName("slice").withConcreteTypes(Map(STypeVar("IV") -> SInt)), Vector( SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 1.toByte), SelectField.typed[Value[SInt.type]](ValUse(3, SPair(SInt, SInt)), 2.toByte) @@ -8781,7 +8769,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField), FixedCostItem(ValUse), FixedCostItem(SelectField), - SeqCostItem(CompanionDesc(Append), PerItemCost(JitCost(20), JitCost(2), 100), i) + ast.SeqCostItem(CompanionDesc(Append), PerItemCost(JitCost(20), JitCost(2), 100), i) ) ) if (lowerMethodCallsInTests) { @@ -8831,7 +8819,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), 1.toByte ), - SCollection.getMethodByName("append").withConcreteTypes(Map(STypeVar("IV") -> SInt)), + SCollectionMethods.getMethodByName("append").withConcreteTypes(Map(STypeVar("IV") -> SInt)), Vector( SelectField.typed[Value[SCollection[SInt.type]]]( ValUse(1, SPair(SCollectionType(SInt), SCollectionType(SInt))), @@ -8857,14 +8845,14 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.FilterMethod, FixedCost(JitCost(20))) + FixedCostItem(SOptionMethods.FilterMethod, FixedCost(JitCost(20))) ) ) val costDetails5 = TracedCost( traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.FilterMethod, FixedCost(JitCost(20))), + FixedCostItem(SOptionMethods.FilterMethod, FixedCost(JitCost(20))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -8875,14 +8863,14 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.MapMethod, FixedCost(JitCost(20))) + FixedCostItem(SOptionMethods.MapMethod, FixedCost(JitCost(20))) ) ) val costDetails7 = TracedCost( traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.MapMethod, FixedCost(JitCost(20))), + FixedCostItem(SOptionMethods.MapMethod, FixedCost(JitCost(20))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -8925,7 +8913,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SOption(SLong))), MethodCall.typed[Value[SOption[SLong.type]]]( ValUse(1, SOption(SLong)), - SOption.getMethodByName("filter").withConcreteTypes(Map(STypeVar("T") -> SLong)), + SOptionMethods.getMethodByName("filter").withConcreteTypes(Map(STypeVar("T") -> SLong)), Vector(FuncValue(Vector((3, SLong)), EQ(ValUse(3, SLong), LongConstant(1L)))), Map() ) @@ -8943,7 +8931,7 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SOption(SLong))), MethodCall.typed[Value[SOption[SLong.type]]]( ValUse(1, SOption(SLong)), - SOption.getMethodByName("map").withConcreteTypes( + SOptionMethods.getMethodByName("map").withConcreteTypes( Map(STypeVar("T") -> SLong, STypeVar("R") -> SLong) ), Vector( @@ -8962,14 +8950,14 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.FilterMethod, FixedCost(JitCost(20))) + FixedCostItem(SOptionMethods.FilterMethod, FixedCost(JitCost(20))) ) ) val costDetails2 = TracedCost( traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.FilterMethod, FixedCost(JitCost(20))), + FixedCostItem(SOptionMethods.FilterMethod, FixedCost(JitCost(20))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -8982,7 +8970,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.FilterMethod, FixedCost(JitCost(20))), + FixedCostItem(SOptionMethods.FilterMethod, FixedCost(JitCost(20))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -9008,7 +8996,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SOption(SLong))), MethodCall.typed[Value[SOption[SLong.type]]]( ValUse(1, SOption(SLong)), - SOption.getMethodByName("filter").withConcreteTypes(Map(STypeVar("T") -> SLong)), + SOptionMethods.getMethodByName("filter").withConcreteTypes(Map(STypeVar("T") -> SLong)), Vector( FuncValue( Array((3, SLong)), @@ -9027,14 +9015,14 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.MapMethod, FixedCost(JitCost(20))) + FixedCostItem(SOptionMethods.MapMethod, FixedCost(JitCost(20))) ) ) val costDetails5 = TracedCost( traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.MapMethod, FixedCost(JitCost(20))), + FixedCostItem(SOptionMethods.MapMethod, FixedCost(JitCost(20))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -9047,7 +9035,7 @@ class SigmaDslSpecification extends SigmaDslTesting traceBase ++ Array( FixedCostItem(MethodCall), FixedCostItem(FuncValue), - FixedCostItem(SOption.MapMethod, FixedCost(JitCost(20))), + FixedCostItem(SOptionMethods.MapMethod, FixedCost(JitCost(20))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(Constant), @@ -9073,7 +9061,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((1, SOption(SLong))), MethodCall.typed[Value[SOption[SLong.type]]]( ValUse(1, SOption(SLong)), - SOption.getMethodByName("map").withConcreteTypes( + SOptionMethods.getMethodByName("map").withConcreteTypes( Map(STypeVar("T") -> SLong, STypeVar("R") -> SLong) ), Vector( @@ -9185,7 +9173,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("blake2b256 benchmark: to estimate timeout") { val cases = (1 to 10).map { i => - val block = Colls.fromArray(Array.fill(ErgoTreeEvaluator.DataBlockSize * i)(0.toByte)) + val block = Colls.fromArray(Array.fill(CErgoTreeEvaluator.DataBlockSize * i)(0.toByte)) block } benchmarkCases(cases, @@ -9197,8 +9185,8 @@ class SigmaDslSpecification extends SigmaDslTesting } property("blake2b256, sha256 equivalence") { - def costDetailsBlake(i: Int) = TracedCost(traceBase :+ SeqCostItem(CompanionDesc(CalcBlake2b256), PerItemCost(JitCost(20), JitCost(7), 128), i)) - def costDetailsSha(i: Int) = TracedCost(traceBase :+ SeqCostItem(CompanionDesc(CalcSha256), PerItemCost(JitCost(80), JitCost(8), 64), i)) + def costDetailsBlake(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(CalcBlake2b256), PerItemCost(JitCost(20), JitCost(7), 128), i)) + def costDetailsSha(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(CalcSha256), PerItemCost(JitCost(80), JitCost(8), 64), i)) verifyCases( Seq( @@ -9269,7 +9257,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(Constant), TypeBasedCostItem(ArithOp.Minus, SInt), FixedCostItem(ValUse), - SeqCostItem(CompanionDesc(AtLeast), PerItemCost(JitCost(20), JitCost(3), 5), i) + ast.SeqCostItem(CompanionDesc(AtLeast), PerItemCost(JitCost(20), JitCost(3), 5), i) ) ) @@ -9333,11 +9321,11 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField) ) - val costDetails1 = TracedCost(testTraceBase :+ SeqCostItem(CompanionDesc(SigmaAnd), PerItemCost(JitCost(10), JitCost(2), 1), 2)) + val costDetails1 = TracedCost(testTraceBase :+ ast.SeqCostItem(CompanionDesc(SigmaAnd), PerItemCost(JitCost(10), JitCost(2), 1), 2)) val costDetails2 = TracedCost( testTraceBase ++ Array( FixedCostItem(BoolToSigmaProp), - SeqCostItem(CompanionDesc(SigmaAnd), PerItemCost(JitCost(10), JitCost(2), 1), 2) + ast.SeqCostItem(CompanionDesc(SigmaAnd), PerItemCost(JitCost(10), JitCost(2), 1), 2) ) ) @@ -9415,7 +9403,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(SelectField) ) - val costDetails1 = TracedCost(testTraceBase :+ SeqCostItem(CompanionDesc(SigmaOr), PerItemCost(JitCost(10), JitCost(2), 1), 2)) + val costDetails1 = TracedCost(testTraceBase :+ ast.SeqCostItem(CompanionDesc(SigmaOr), PerItemCost(JitCost(10), JitCost(2), 1), 2)) verifyCases( { def success[T](v: T, newCost: Int) = Expected(Success(v), newCost, costDetails1, newCost) @@ -9462,7 +9450,7 @@ class SigmaDslSpecification extends SigmaDslTesting val costDetails2 = TracedCost( testTraceBase ++ Array( FixedCostItem(BoolToSigmaProp), - SeqCostItem(CompanionDesc(SigmaOr), PerItemCost(JitCost(10), JitCost(2), 1), 2) + ast.SeqCostItem(CompanionDesc(SigmaOr), PerItemCost(JitCost(10), JitCost(2), 1), 2) ) ) verifyCases( @@ -9524,7 +9512,7 @@ class SigmaDslSpecification extends SigmaDslTesting "0008980204cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) ), cost = 1780, newDetails(18), expectedNewCost = 1780), - CSigmaProp(COR(Array(pk, dht, and, or, threshold))) -> Expected(Success( + CSigmaProp(data.COR(Array(pk, dht, and, or, threshold))) -> Expected(Success( Helpers.decodeBytes( "00089705cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441980204cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419702cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b04419602cd039d0b1e46c21540d033143440d2fb7dd5d650cf89981c99ee53c6e0374d2b1b6fce03c046fccb95549910767d0543f5e8ce41d66ae6a8720a46f4049cac3b3d26dafb023479c9c3b86a0d3c8be3db0a2d186788e9af1db76d55f3dad127d15185d83d0303d7898641cb6653585a8e1dabfa7f665e61e0498963e329e6e3744bd764db2d72037ae057d89ec0b46ff8e9ff4c37e85c12acddb611c3f636421bef1542c11b0441" ) @@ -9556,7 +9544,7 @@ class SigmaDslSpecification extends SigmaDslTesting } property("allOf equivalence") { - def costDetails(i: Int) = TracedCost(traceBase :+ SeqCostItem(CompanionDesc(AND), PerItemCost(JitCost(10), JitCost(5), 32), i)) + def costDetails(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(AND), PerItemCost(JitCost(10), JitCost(5), 32), i)) verifyCases( Seq( (Coll[Boolean]() -> Expected(Success(true), 1765, costDetails(0), 1765)), @@ -9577,7 +9565,7 @@ class SigmaDslSpecification extends SigmaDslTesting } property("anyOf equivalence") { - def costDetails(i: Int) = TracedCost(traceBase :+ SeqCostItem(CompanionDesc(OR), PerItemCost(JitCost(5), JitCost(5), 64), i)) + def costDetails(i: Int) = TracedCost(traceBase :+ ast.SeqCostItem(CompanionDesc(OR), PerItemCost(JitCost(5), JitCost(5), 64), i)) verifyCases( Seq( (Coll[Boolean]() -> Expected(Success(false), 1764, costDetails(0), 1764)), @@ -9654,14 +9642,14 @@ class SigmaDslSpecification extends SigmaDslTesting property("substConstants equivalence") { // tree without constant segregation - val t1 = ErgoTree(ErgoTree.DefaultHeader, Vector(), TrueSigmaProp) + val t1 = ErgoTree(ErgoTree.ZeroHeader, Vector(), TrueSigmaProp) // tree with constant segregation, but without constants - val t2 = ErgoTree(ErgoTree.ConstantSegregationHeader, Vector(), TrueSigmaProp) + val t2 = ErgoTree(ErgoTree.setConstantSegregation(ZeroHeader), Vector(), TrueSigmaProp) // tree with one segregated constant - val t3 = ErgoTree(ErgoTree.ConstantSegregationHeader, Vector(TrueSigmaProp), ConstantPlaceholder(0, SSigmaProp)) + val t3 = ErgoTree(ErgoTree.setConstantSegregation(ZeroHeader), Vector(TrueSigmaProp), ConstantPlaceholder(0, SSigmaProp)) // tree with one segregated constant of different type val t4 = ErgoTree( - ErgoTree.ConstantSegregationHeader, + ErgoTree.setConstantSegregation(ZeroHeader), Vector(IntConstant(10)), BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(20)))) def costDetails(i: Int) = TracedCost( @@ -9673,7 +9661,7 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(ConcreteCollection), FixedCostItem(Constant), FixedCostItem(BoolToSigmaProp), - SeqCostItem(CompanionDesc(SubstConstants), PerItemCost(JitCost(100), JitCost(100), 1), i) + ast.SeqCostItem(CompanionDesc(SubstConstants), PerItemCost(JitCost(100), JitCost(100), 1), i) ) ) verifyCases( @@ -9745,22 +9733,22 @@ class SigmaDslSpecification extends SigmaDslTesting FixedCostItem(GetVar), FixedCostItem(OptionGet), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), - SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), + ast.SeqCostItem(CompanionDesc(BlockValue), PerItemCost(JitCost(1), JitCost(1), 10), 1), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - FixedCostItem(SContext.headersMethod, FixedCost(JitCost(15))), + FixedCostItem(SContextMethods.headersMethod, FixedCost(JitCost(15))), FixedCostItem(FuncValue.AddToEnvironmentDesc, FixedCost(JitCost(5))), FixedCostItem(ValUse), FixedCostItem(PropertyCall), - SeqCostItem(MethodDesc(SCollection.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), 1), + ast.SeqCostItem(MethodDesc(SCollectionMethods.IndicesMethod), PerItemCost(JitCost(20), JitCost(2), 16), 1), FixedCostItem(Constant), FixedCostItem(ValUse), FixedCostItem(SizeOf), FixedCostItem(Constant), TypeBasedCostItem(ArithOp.Minus, SInt), - SeqCostItem(CompanionDesc(Slice), PerItemCost(JitCost(10), JitCost(2), 100), 0), + ast.SeqCostItem(CompanionDesc(Slice), PerItemCost(JitCost(10), JitCost(2), 100), 0), FixedCostItem(FuncValue), - SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 0) + ast.SeqCostItem(CompanionDesc(ForAll), PerItemCost(JitCost(3), JitCost(1), 10), 0) ) ) @@ -9811,7 +9799,7 @@ class SigmaDslSpecification extends SigmaDslTesting List(), MethodCall.typed[Value[SCollection[SHeader.type]]]( ValUse(1, SContext), - SContext.getMethodByName("headers"), + SContextMethods.getMethodByName("headers"), Vector(), Map() ) @@ -9821,7 +9809,7 @@ class SigmaDslSpecification extends SigmaDslTesting Slice( MethodCall.typed[Value[SCollection[SInt.type]]]( ValUse(3, SCollectionType(SHeader)), - SCollection.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SHeader)), + SCollectionMethods.getMethodByName("indices").withConcreteTypes(Map(STypeVar("IV") -> SHeader)), Vector(), Map() ), @@ -9842,7 +9830,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((6, SHeader)), MethodCall.typed[Value[SCollection[SByte.type]]]( ValUse(6, SHeader), - SHeader.getMethodByName("parentId"), + SHeaderMethods.getMethodByName("parentId"), Vector(), Map() ) @@ -9858,7 +9846,7 @@ class SigmaDslSpecification extends SigmaDslTesting Array((6, SHeader)), MethodCall.typed[Value[SCollection[SByte.type]]]( ValUse(6, SHeader), - SHeader.getMethodByName("id"), + SHeaderMethods.getMethodByName("id"), Vector(), Map() ) @@ -9921,7 +9909,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(3, SPair(SByteArray, SByteArray)), 1.toByte ), - SCollection.getMethodByName("zip").withConcreteTypes( + SCollectionMethods.getMethodByName("zip").withConcreteTypes( Map(STypeVar("IV") -> SByte, STypeVar("OV") -> SByte) ), Vector( @@ -9961,7 +9949,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(1, SPair(SByteArray2, SByteArray)), 1.toByte ), - SCollection.getMethodByName("fold").withConcreteTypes(Map(STypeVar("IV") -> SCollection(SByte), STypeVar("OV") -> SCollection(SByte))), + SCollectionMethods.getMethodByName("fold").withConcreteTypes(Map(STypeVar("IV") -> SCollection(SByte), STypeVar("OV") -> SCollection(SByte))), Vector( SelectField.typed[Value[SCollection[SByte.type]]]( ValUse(1, SPair(SByteArray2, SByteArray)), @@ -9975,7 +9963,7 @@ class SigmaDslSpecification extends SigmaDslTesting ValUse(3, SPair(SByteArray, SByteArray)), 1.toByte ), - SCollection.getMethodByName("zip").withConcreteTypes( + SCollectionMethods.getMethodByName("zip").withConcreteTypes( Map(STypeVar("IV") -> SByte, STypeVar("OV") -> SByte) ), Vector( @@ -9986,7 +9974,7 @@ class SigmaDslSpecification extends SigmaDslTesting ), Map() ), - SCollection.getMethodByName("map").withConcreteTypes( + SCollectionMethods.getMethodByName("map").withConcreteTypes( Map(STypeVar("IV") -> SPair(SByte, SByte), STypeVar("OV") -> SByte) ), Vector( @@ -10031,12 +10019,12 @@ class SigmaDslSpecification extends SigmaDslTesting // TODO v6.0: Add support of SFunc in TypeSerializer (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/847) assertExceptionThrown( f.verifyCase(Coll[Int](), Expected(Success(Coll[Int]()), 0)), - exceptionLike[MatchError]("(SInt$) => SInt$ (of class sigmastate.SFunc)") + exceptionLike[MatchError]("(SInt$) => SInt$ (of class sigma.ast.SFunc)") ) } override protected def afterAll(): Unit = { - printDebug(ErgoTreeEvaluator.DefaultProfiler.generateReport) + printDebug(CErgoTreeEvaluator.DefaultProfiler.generateReport) printDebug("==========================================================") printDebug(Interpreter.verifySignatureProfiler.generateReport) printDebug("==========================================================") diff --git a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala index ea29c47745..5ac9b80889 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslStaginTests.scala @@ -2,9 +2,9 @@ package sigma import org.scalatest.BeforeAndAfterAll import scalan.{BaseCtxTests, BaseLiftableTests} -import sigmastate.eval.Extensions._ +import sigma.data.TrivialProp +import sigma.eval.Extensions.toAnyValue import sigmastate.eval._ -import sigmastate.{TrivialProp, eval} import scala.language.reflectiveCalls @@ -23,7 +23,7 @@ class SigmaDslStaginTests extends BaseCtxTests with ErgoScriptTestkit with BaseL import SigmaDslBuilder._ import SigmaProp._ - val dsl: SSigmaDslBuilder = eval.SigmaDsl + val dsl: SSigmaDslBuilder = sigma.eval.SigmaDsl type RSigmaDslBuilder = cake.SigmaDslBuilder type RContext = cake.Context type RBox = cake.Box @@ -33,8 +33,8 @@ class SigmaDslStaginTests extends BaseCtxTests with ErgoScriptTestkit with BaseL val ctx: SContext = newContext(10, boxA1, VersionContext.MaxSupportedScriptVersion, VersionContext.MaxSupportedScriptVersion) .withInputs(boxA2) .withVariables(Map(1 -> toAnyValue(30), 2 -> toAnyValue(40))) - val p1: SSigmaProp = eval.SigmaDsl.SigmaProp(TrivialProp(true)) - val p2: SSigmaProp = eval.SigmaDsl.SigmaProp(TrivialProp(false)) + val p1: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(true)) + val p2: SSigmaProp = sigma.eval.SigmaDsl.SigmaProp(TrivialProp(false)) cake.check(dsl, { env: EnvRep[RSigmaDslBuilder] => for { dsl <- env; arg <- lifted(true) } yield dsl.sigmaProp(arg) }, dsl.sigmaProp(true)) diff --git a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala index 4aff49761e..46222d9fb1 100644 --- a/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala +++ b/sc/shared/src/test/scala/sigma/SigmaDslTesting.scala @@ -3,8 +3,7 @@ package sigma import debox.cfor import org.ergoplatform._ import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, TestContractSpec} -import org.ergoplatform.validation.ValidationRules.CheckSerializableTypeCode -import org.ergoplatform.validation.{SigmaValidationSettings, ValidationException, ValidationRules} +import org.ergoplatform.validation.ValidationRules import org.scalacheck.Arbitrary._ import org.scalacheck.Gen.frequency import org.scalacheck.{Arbitrary, Gen} @@ -13,27 +12,32 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scalan.Platform.threadSleepOrNoOp -import sigma.data.{CollType, OptionType, PairType, RType} +import sigma.Extensions.ArrayOps +import sigma.data.{CBox, CollType, OptionType, PairType, ProveDlog, RType, SigmaLeaf} import sigma.util.BenchmarkUtil import sigma.util.CollectionUtil._ import sigma.util.Extensions._ import sigma.util.StringUtil.StringUtilExtensions -import sigmastate.SType.AnyOps -import sigmastate.Values.{ByteArrayConstant, Constant, ConstantNode, ErgoTree, IntConstant, SValue} -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} +import sigma.ast.ErgoTree.ZeroHeader +import sigma.ast.SType.AnyOps +import sigma.ast.syntax.{SValue, ValueOps} +import sigma.ast._ +import sigma.eval.{CostDetails, EvalSettings, SigmaDsl} +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.crypto.SigmaProtocolPrivateInput -import sigmastate.eval.Extensions._ -import sigmastate.eval.{CompiletimeIRContext, CostingBox, CostingDataContext, Evaluation, IRContext, SigmaDsl} +import sigmastate.eval.{CContext, CompiletimeIRContext, IRContext} import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestInterpreter, SigmaPPrint} import sigmastate.interpreter.Interpreter.{ScriptEnv, VerificationResult} import sigmastate.interpreter._ -import sigmastate.lang.Terms.{Apply, ValueOps} -import sigmastate.serialization.ValueSerializer -import sigmastate.serialization.generators.ObjectGenerators +import sigma.ast.Apply +import sigma.eval.Extensions.SigmaBooleanOps +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.ValueSerializer +import sigma.serialization.generators.ObjectGenerators import sigmastate.utils.Helpers._ -import sigmastate.utxo.{DeserializeContext, DeserializeRegister, GetVar, OptionGet} -import sigmastate.{SOption, SSigmaProp, SType, SigmaLeaf, eval} +import sigma.validation.ValidationRules.CheckSerializableTypeCode +import sigma.validation.{SigmaValidationSettings, ValidationException} import scala.collection.mutable import scala.reflect.ClassTag @@ -254,11 +258,11 @@ class SigmaDslTesting extends AnyPropSpec } } - /** Creates a new ErgoLikeContext using given [[CostingDataContext]] as template. + /** Creates a new ErgoLikeContext using given [[CContext]] as template. * Copies most of the data from ctx and the missing data is taken from the args. * This is a helper method to be used in tests only. */ - def createErgoLikeContext(ctx: CostingDataContext, + def createErgoLikeContext(ctx: CContext, validationSettings: SigmaValidationSettings, costLimit: Long, initCost: Long @@ -313,43 +317,43 @@ class SigmaDslTesting extends AnyPropSpec """.stripMargin val IR = new CompiletimeIRContext - val pkAlice = prover.pubKeys.head.toSigmaProp + val pkAlice = prover.pubKeys.head.toSigmaPropValue val env = Map("pkAlice" -> pkAlice) // Compile script the same way it is performed by applications (i.e. via Ergo Appkit) val prop = compile(env, code)(IR).asSigmaProp // Add additional oparations which are not yet implemented in ErgoScript compiler - val multisig = sigmastate.AtLeast( + val multisig = AtLeast( IntConstant(2), Array( pkAlice, DeserializeRegister(ErgoBox.R5, SSigmaProp), // deserialize pkBob DeserializeContext(2, SSigmaProp))) // deserialize pkCarol - val header = ErgoTree.headerWithVersion(ergoTreeVersionInTests) - ErgoTree.withSegregation(header, sigmastate.SigmaOr(prop, multisig)) + val header = ErgoTree.headerWithVersion(ZeroHeader, ergoTreeVersionInTests) + ErgoTree.withSegregation(header, SigmaOr(prop, multisig)) } def ergoCtx(prover: FeatureProvingInterpreter, compiledTree: ErgoTree, expectedValue: B) = { - val pkBobBytes = ValueSerializer.serialize(prover.pubKeys(1).toSigmaProp) - val pkCarolBytes = ValueSerializer.serialize(prover.pubKeys(2).toSigmaProp) + val pkBobBytes = ValueSerializer.serialize(prover.pubKeys(1).toSigmaPropValue) + val pkCarolBytes = ValueSerializer.serialize(prover.pubKeys(2).toSigmaPropValue) val newRegisters = Map( ErgoBox.R4 -> Constant[SType](expectedValue.asInstanceOf[SType#WrappedType], tpeB), ErgoBox.R5 -> ByteArrayConstant(pkBobBytes) ) val ctx = input match { - case ctx: CostingDataContext => + case ctx: CContext => // the context is passed as function argument (see func in the script) // Since Context is singleton, we should use this instance as the basis // for execution of verify instead of a new dummy context. - val self = ctx.selfBox.asInstanceOf[CostingBox] + val self = ctx.selfBox.asInstanceOf[CBox] val newSelf = self.copy( ebox = updatedRegisters(self.ebox, newRegisters) ) // We add ctx as it's own variable with id = 1 - val ctxVar = eval.Extensions.toAnyValue[sigma.Context](ctx)(sigma.ContextRType) - val carolVar = eval.Extensions.toAnyValue[Coll[Byte]](pkCarolBytes.toColl)(RType[Coll[Byte]]) + val ctxVar = sigma.eval.Extensions.toAnyValue[sigma.Context](ctx)(sigma.ContextRType) + val carolVar = sigma.eval.Extensions.toAnyValue[Coll[Byte]](pkCarolBytes.toColl)(RType[Coll[Byte]]) val newCtx = ctx .withUpdatedVars(1 -> ctxVar, 2 -> carolVar) .copy( @@ -458,7 +462,7 @@ class SigmaDslTesting extends AnyPropSpec } /** A number of times the newF function in each test feature is repeated. - * In combination with [[sigmastate.eval.Profiler]] it allows to collect more accurate + * In combination with [[sigmastate.eval.CProfiler]] it allows to collect more accurate * timings for all operations. * @see SigmaDslSpecification */ def nBenchmarkIters: Int = 0 diff --git a/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala b/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala index cd7e15b841..28f907c199 100644 --- a/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala +++ b/sc/shared/src/test/scala/sigmastate/CompilerTestsBase.scala @@ -1,13 +1,14 @@ package sigmastate import scala.util.DynamicVariable -import sigmastate.lang.{TransformingSigmaBuilder, CompilerResult, CompilerSettings, SigmaCompiler} +import sigmastate.lang.{CompilerResult, CompilerSettings, SigmaCompiler} import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.Values.{SigmaPropValue, SValue, Value, ErgoTree} +import sigma.ast.{ErgoTree, SType, TransformingSigmaBuilder, Value} import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix -import sigmastate.serialization.ValueSerializer +import sigma.ast.syntax.{SValue, SigmaPropValue} +import sigma.serialization.ValueSerializer import sigmastate.eval.IRContext -import sigmastate.lang.Terms.ValueOps +import sigma.ast.syntax.ValueOps trait CompilerTestsBase extends TestsBase { protected val _lowerMethodCalls = new DynamicVariable[Boolean](true) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 23406d9cb4..b0b6273fad 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -1,26 +1,28 @@ -package sigmastate +package sigma import org.ergoplatform.settings.ErgoAlgos -import org.ergoplatform.validation.{ValidationException, ValidationRules} -import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoLikeContext, Self} -import sigma.data.RType.asType -import sigma.data.{Nullable, RType} -import sigma.VersionContext -import sigmastate.SCollection.{SByteArray, checkValidFlatmap} -import sigmastate.Values._ +import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoLikeContext} import sigma.VersionContext._ -import sigmastate.eval.{CostingBox, Evaluation, Profiler} -import sigmastate.exceptions.{CostLimitException, InterpreterException} +import sigma.ast.SCollection.SByteArray +import sigma.ast._ +import sigma.ast.syntax.{SValue, SigmaPropValue, TrueSigmaProp} +import sigma.data.RType.asType +import sigma.data.{CBox, Nullable, RType, TrivialProp} +import sigma.validation.ValidationException +import sigma.validation.ValidationRules.CheckTypeCode +import ErgoTree.HeaderType +import SCollectionMethods.checkValidFlatmap +import sigmastate.eval.CProfiler import sigmastate.helpers.{ErgoLikeContextTesting, SigmaPPrint} -import sigmastate.interpreter.{ErgoTreeEvaluator, EvalSettings} import sigmastate.interpreter.Interpreter.ReductionResult -import sigmastate.lang.{CompilerSettings, SourceContext} -import sigmastate.lang.Terms._ -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer +import sigmastate.interpreter.CErgoTreeEvaluator +import sigma.ast.syntax._ +import sigma.eval.EvalSettings +import sigma.exceptions.{CostLimitException, InterpreterException} +import sigmastate.lang.CompilerSettings +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer +import sigmastate.Plus import sigmastate.utils.Helpers.TryOps -import sigmastate.utxo._ -import sigma._ -import sigma.{ContractsTestkit, SigmaDslTesting} /** Regression tests with ErgoTree related test vectors. @@ -46,7 +48,6 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { Block(Seq(), null), ValNode("x", SInt, null), ApplyTypes(null, Seq()), - TaggedVariableNode(1, SByte), ValDef(1, null), ValUse(1, SInt), BlockValue(IndexedSeq(), null) @@ -59,15 +60,15 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { property("ErgoTree.toProposition") { val t1 = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) ) val t = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), - Left(UnparsedErgoTree(t1.bytes, ValidationException("", ValidationRules.CheckTypeCode, Seq()))) + Left(UnparsedErgoTree(t1.bytes, ValidationException("", CheckTypeCode, Seq()))) ) assertExceptionThrown( t.toProposition(true), @@ -78,7 +79,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { property("ErgoTree.template") { { val t = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) ) @@ -93,7 +94,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { property("ErgoTree.bytes") { val t = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) ) @@ -111,7 +112,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { Value.hasDeserialize(EQ(const, dc)) shouldBe true Value.hasDeserialize(Plus(Plus(const, dc), dr)) shouldBe true val t = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) ) @@ -121,7 +122,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { property("ErgoTree.hasDeserialize") { { val t = new ErgoTree( - 0.toByte, + HeaderType @@ 0.toByte, Array[Constant[SType]](), Right(TrueSigmaProp)) t._hasDeserialize shouldBe None @@ -130,7 +131,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { { val t = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), DeserializeContext(1.toByte, SInt)))) ) @@ -139,15 +140,113 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { } } + property("ErgoTree.isUsingBlockchainContext") { + { + val t = new ErgoTree( + HeaderType @@ 0.toByte, + Array[Constant[SType]](), + Right(TrueSigmaProp)) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe false + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Array(IntConstant(1)), + Right(BoolToSigmaProp(GT(Height, ConstantPlaceholder(0, SInt)))) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Vector(), + Right(OptionIsDefined(IR.builder.mkMethodCall( + LastBlockUtxoRootHash, SAvlTreeMethods.getMethod, + IndexedSeq(ExtractId(GetVarBox(22: Byte).get), GetVarByteArray(23: Byte).get)).asOption[SByteArray])) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Vector(), + Right(BoolToSigmaProp(EQ(MinerPubkey, ErgoLikeContextTesting.dummyPubkey))) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Vector(), + Right(OptionIsDefined(IR.builder.mkMethodCall( + Context, SContextMethods.headersMethod, Vector()).asOption[SByteArray])) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Vector(), + Right(OptionIsDefined(IR.builder.mkMethodCall( + Context, SContextMethods.preHeaderMethod, Vector()).asOption[SByteArray])) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Vector(), + Right(OptionIsDefined(IR.builder.mkMethodCall( + Context, SContextMethods.heightMethod, Vector()).asOption[SByteArray])) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Vector(), + Right(OptionIsDefined(IR.builder.mkMethodCall( + Context, SContextMethods.lastBlockUtxoRootHashMethod, Vector()).asOption[SByteArray])) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + + { + val t = new ErgoTree( + HeaderType @@ 16.toByte, + Vector(), + Right(OptionIsDefined(IR.builder.mkMethodCall( + Context, SContextMethods.minerPubKeyMethod, Vector()).asOption[SByteArray])) + ) + t._isUsingBlockchainContext shouldBe None + t.isUsingBlockchainContext shouldBe true + } + } + property("ErgoTree equality") { val t1 = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), IntConstant(1)))) ) - val t2 = new ErgoTree(16.toByte, Array(IntConstant(1)), Right(TrueSigmaProp)) - val t3 = new ErgoTree(16.toByte, Array(IntConstant(1)), Right(TrueSigmaProp)) - val t4 = new ErgoTree(16.toByte, Vector(), Right(TrueSigmaProp)) + val t2 = new ErgoTree(HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(TrueSigmaProp)) + val t3 = new ErgoTree(HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(TrueSigmaProp)) + val t4 = new ErgoTree(HeaderType @@ 16.toByte, Vector(), Right(TrueSigmaProp)) val t5 = new ErgoTree(ErgoTree.DefaultHeader, Vector(), Right(TrueSigmaProp)) assert(t1 != t2) assert(t2 == t3) @@ -228,14 +327,14 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { { // SNumericType.typeId is erroneously shadowed by SGlobal.typeId // this should be preserved in v3.x and fixed in v4.0 (SNumericType.typeId, Seq( - MInfo(methodId = 1, SGlobal.groupGeneratorMethod), - MInfo(2, SGlobal.xorMethod) + MInfo(methodId = 1, SGlobalMethods.groupGeneratorMethod), + MInfo(2, SGlobalMethods.xorMethod) ), true) }, { // SBigInt inherit methods from SNumericType.methods // however they are not resolvable via SBigInt.typeId - import SNumericType._ + import SNumericTypeMethods._ (SBigInt.typeId, Seq( MInfo(methodId = 1, ToByteMethod, isResolvableFromIds = false), MInfo(2, ToShortMethod, isResolvableFromIds = false), @@ -246,7 +345,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(7, ToBitsMethod, isResolvableFromIds = false) ), true) }, - { import SGroupElement._ + { import SGroupElementMethods._ (SGroupElement.typeId, Seq( MInfo(2, GetEncodedMethod), MInfo(3, ExponentiateMethod), @@ -254,13 +353,13 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(5, NegateMethod) ), true) }, - { import SSigmaProp._ + { import SSigmaPropMethods._ (SSigmaProp.typeId, Seq( MInfo(1, PropBytesMethod), MInfo(2, IsProvenMethod) // TODO v5.x (3h): this method must be removed (see https://github.com/ScorexFoundation/sigmastate-interpreter/pull/800) ), true) }, - { import SBox._ + { import SBoxMethods._ (SBox.typeId, Seq( MInfo(1, ValueMethod), MInfo(2, PropositionBytesMethod), @@ -274,7 +373,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { .zipWithIndex .map { case (m,i) => MInfo((8 + i + 1).toByte, m) }, true) }, - { import SAvlTree._ + { import SAvlTreeMethods._ (SAvlTree.typeId, Seq( MInfo(1, digestMethod), MInfo(2, enabledOperationsMethod), @@ -293,7 +392,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(15, updateDigestMethod) ), true) }, - { import SHeader._ + { import SHeaderMethods._ (SHeader.typeId, Seq( MInfo(1, idMethod), MInfo(2, versionMethod), MInfo(3, parentIdMethod), MInfo(4, ADProofsRootMethod), MInfo(5, stateRootMethod), MInfo(6, transactionsRootMethod), @@ -302,14 +401,14 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(13, powNonceMethod), MInfo(14, powDistanceMethod), MInfo(15, votesMethod) ), true) }, - { import SPreHeader._ + { import SPreHeaderMethods._ (SPreHeader.typeId, Seq( MInfo(1, versionMethod), MInfo(2, parentIdMethod), MInfo(3, timestampMethod), MInfo(4, nBitsMethod), MInfo(5, heightMethod), MInfo(6, minerPkMethod), MInfo(7, votesMethod) ), true) }, - { import SContext._ + { import SContextMethods._ (SContext.typeId, Seq( MInfo(1, dataInputsMethod), MInfo(2, headersMethod), MInfo(3, preHeaderMethod), MInfo(4, inputsMethod), MInfo(5, outputsMethod), MInfo(6, heightMethod), @@ -317,12 +416,12 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { MInfo(10, minerPubKeyMethod), MInfo(11, getVarMethod) ), true) }, - { import SGlobal._ + { import SGlobalMethods._ (SGlobal.typeId, Seq( MInfo(1, groupGeneratorMethod), MInfo(2, xorMethod) ), true) }, - { import SCollection._ + { import SCollectionMethods._ (SCollection.typeId, Seq( MInfo(1, SizeMethod), MInfo(2, GetOrElseMethod), @@ -364,7 +463,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { */ ), true) }, - { import SOption._ + { import SOptionMethods._ (SOption.typeId, Seq( MInfo(2, IsDefinedMethod), MInfo(3, GetMethod), @@ -384,7 +483,8 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { case Some(tyDesc) => assert(canHaveMethods, s"Type $tyDesc should NOT have methods") - tyDesc.methods.length shouldBe methods.length + val mc = MethodsContainer(tyDesc.typeId) + mc.methods.length shouldBe methods.length for (expectedMethod <- methods) { if (expectedMethod.isResolvableFromIds) { @@ -608,11 +708,11 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { } property("checkValidFlatmap") { - implicit val E = ErgoTreeEvaluator.forProfiling(new Profiler, evalSettings) + implicit val E = CErgoTreeEvaluator.forProfiling(new CProfiler, evalSettings) def mkLambda(t: SType, mkBody: SValue => SValue) = { MethodCall( ValUse(1, SCollectionType(t)), - SCollection.getMethodByName("flatMap").withConcreteTypes( + SCollectionMethods.getMethodByName("flatMap").withConcreteTypes( Map(STypeVar("IV") -> t, STypeVar("OV") -> SByte) ), Vector(FuncValue(Vector((3, t)), mkBody(ValUse(3, t)))), @@ -625,7 +725,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { (SBox, x => ExtractBytes(x.asBox)), (SBox, x => ExtractBytesWithNoRef(x.asBox)), (SSigmaProp, x => SigmaPropBytes(x.asSigmaProp)), - (SBox, x => MethodCall(x, SBox.getMethodByName("id"), Vector(), Map())) + (SBox, x => MethodCall(x, SBoxMethods.getMethodByName("id"), Vector(), Map())) ).map { case (t, f) => mkLambda(t, f) } validLambdas.foreach { l => @@ -643,7 +743,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { // invalid MC like `boxes.flatMap(b => b.id, 10)` MethodCall( ValUse(1, SBox), - SCollection.getMethodByName("flatMap").withConcreteTypes( + SCollectionMethods.getMethodByName("flatMap").withConcreteTypes( Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SByte) ), Vector( @@ -655,7 +755,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { // invalid MC like `boxes.flatMap((b,_) => b.id)` MethodCall( ValUse(1, SBox), - SCollection.getMethodByName("flatMap").withConcreteTypes( + SCollectionMethods.getMethodByName("flatMap").withConcreteTypes( Map(STypeVar("IV") -> SBox, STypeVar("OV") -> SByte) ), Vector( @@ -680,25 +780,25 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { val addr = ErgoAddressEncoder.Mainnet.fromString("Fo6oijFP2JM87ac7w").getOrThrow val tree = addr.script tree shouldBe new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Vector(TrueLeaf), Right(BoolToSigmaProp(BoolToSigmaProp(ConstantPlaceholder(0, SBoolean)).asBoolValue)) ) def createCtx: ErgoLikeContext = ErgoLikeContextTesting - .dummy(CostingBox(fakeSelf), VersionContext.current.activatedVersion) + .dummy(fakeSelf, VersionContext.current.activatedVersion) .withErgoTreeVersion(tree.version) VersionContext.withVersions(activatedVersion = 1, tree.version) { // v4.x behavior - val res = ErgoTreeEvaluator.evalToCrypto(createCtx, tree, evalSettings) + val res = CErgoTreeEvaluator.evalToCrypto(createCtx, tree, evalSettings) res shouldBe ReductionResult(TrivialProp(true), 3) } VersionContext.withVersions(activatedVersion = 2, tree.version) { // v5.0 behavior assertExceptionThrown( - ErgoTreeEvaluator.evalToCrypto(createCtx, tree, evalSettings), + CErgoTreeEvaluator.evalToCrypto(createCtx, tree, evalSettings), exceptionLike[ClassCastException]() ) } @@ -716,7 +816,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { val addr = ErgoAddressEncoder.Mainnet.fromString("Fo6oijFP2JM87ac7w").getOrThrow val tree = addr.script tree shouldBe new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Vector(TrueLeaf), Right(BoolToSigmaProp(BoolToSigmaProp(ConstantPlaceholder(0, SBoolean)).asBoolValue)) ) @@ -748,7 +848,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { // 4503b5d77cb74b4354771b835cd61e9d5257022a8efff0fddfac249e0c25b492 val addr = ErgoAddressEncoder.Mainnet.fromString("28JURWHTHwTnXJt5F38").getOrThrow val tree = addr.script - tree shouldBe new ErgoTree(16.toByte, Vector(), + tree shouldBe new ErgoTree(HeaderType @@ 16.toByte, Vector(), Right(BoolToSigmaProp( CreateProveDlog(OptionGet(ExtractRegisterAs(Self, ErgoBox.R4, SOption(SGroupElement)))).asBoolValue) )) diff --git a/sc/shared/src/test/scala/sigmastate/FailingToProveSpec.scala b/sc/shared/src/test/scala/sigmastate/FailingToProveSpec.scala index 775785b709..e047a8d158 100644 --- a/sc/shared/src/test/scala/sigmastate/FailingToProveSpec.scala +++ b/sc/shared/src/test/scala/sigmastate/FailingToProveSpec.scala @@ -2,8 +2,9 @@ package sigmastate import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ import org.scalatest.TryValues._ +import sigma.data.AvlTreeData import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import scala.util.Success diff --git a/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala b/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala index ad5af68649..b0206fa464 100644 --- a/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ScriptVersionSwitchSpecification.scala @@ -3,19 +3,22 @@ package sigmastate import org.ergoplatform.ErgoBox.AdditionalRegisters import org.ergoplatform._ import scorex.util.ModifierId -import sigmastate.Values.ErgoTree.{DefaultHeader, updateVersionBits} -import sigmastate.Values._ import sigma.VersionContext.MaxSupportedScriptVersion +import sigma.ast.ErgoTree.{HeaderType, ZeroHeader, setConstantSegregation, setVersionBits} +import sigma.ast._ +import sigma.{Box, SigmaDslTesting} import sigmastate.eval._ -import sigmastate.exceptions.InterpreterException -import sigmastate.helpers.{ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers.createBox -import sigmastate.interpreter.ErgoTreeEvaluator.DefaultEvalSettings -import sigmastate.interpreter.EvalSettings.EvaluationMode -import sigmastate.interpreter.{CostedProverResult, ErgoTreeEvaluator, EvalSettings, Interpreter, ProverResult} -import sigmastate.lang.Terms.ValueOps +import sigmastate.helpers.{ErgoLikeContextTesting, ErgoLikeTestInterpreter} +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings +import sigma.eval.EvalSettings.EvaluationMode +import sigmastate.interpreter._ +import sigma.ast.syntax.ValueOps +import sigma.data.CBox +import sigma.eval.EvalSettings +import sigma.exceptions.InterpreterException +import sigma.interpreter.{CostedProverResult, ProverResult} import sigmastate.utils.Helpers._ -import sigma.{Box, SigmaDslTesting} /** Specification to verify that the interpreter behaves according to docs/aot-jit-switch.md. * @@ -25,17 +28,17 @@ import sigma.{Box, SigmaDslTesting} class ScriptVersionSwitchSpecification extends SigmaDslTesting { override implicit val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration(minSuccessful = 30) override implicit val evalSettings: EvalSettings = - ErgoTreeEvaluator.DefaultEvalSettings.copy( + CErgoTreeEvaluator.DefaultEvalSettings.copy( costTracingEnabled = true // should always be enabled in tests (and false by default) ) implicit def IR: IRContext = createIR() - lazy val b1 = CostingBox( + lazy val b1 = CBox( new ErgoBox( 1L, new ErgoTree( - 0.toByte, + HeaderType @@ 0.toByte, Vector(), Right(BoolToSigmaProp(OR(ConcreteCollection(Array(FalseLeaf, AND(ConcreteCollection(Array(FalseLeaf, FalseLeaf), SBoolean))), SBoolean)))) ), @@ -48,7 +51,7 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { ) /** Creates ErgoTree with segregated constants and also the given header flags. */ - def createErgoTree(headerFlags: Byte)(implicit IR: IRContext): ErgoTree = { + def createErgoTree(header: HeaderType)(implicit IR: IRContext): ErgoTree = { val code = s"""{ | val func = { (x: Coll[Box]) => x.filter({(b: Box) => b.value > 1 }) } @@ -65,7 +68,7 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { checkCompilerResult(res) res.buildTree.asSigmaProp } - ErgoTree.withSegregation(headerFlags, compiledTree) + ErgoTree.withSegregation(header, compiledTree) } /** Proves the given ergoTree in a dummy context with the given activatedScriptVersion. @@ -136,7 +139,10 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { property("new versions of scripts will require size bit in the header") { (1 to 7).foreach { version => assertExceptionThrown( - createErgoTree(headerFlags = updateVersionBits(DefaultHeader, version.toByte)), + { + val tree = createErgoTree(header = setVersionBits(ZeroHeader, version.toByte)) + new ErgoTree(setVersionBits(setConstantSegregation(ZeroHeader), version.toByte), tree.constants, tree.root) + }, exceptionLike[IllegalArgumentException]("For newer version the size bit is required") ) } @@ -160,7 +166,7 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { forEachErgoTreeVersion(treeVers) { // SF inactive: check cost vectors of v4.x interpreter - val headerFlags = ErgoTree.headerWithVersion(ergoTreeVersionInTests) + val headerFlags = ErgoTree.defaultHeaderWithVersion(ergoTreeVersionInTests) val ergoTree = createErgoTree(headerFlags) // both prove and verify are accepting with full evaluation @@ -187,8 +193,8 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { forEachActivatedScriptVersion(Array[Byte](0, 1)) { // Block Versions 1, 2 forEachErgoTreeVersion(ergoTreeVers = Array[Byte](2)) { // only Script v2 - val headerFlags = ErgoTree.headerWithVersion(ergoTreeVersionInTests /* Script v2 */) - val ergoTree = createErgoTree(headerFlags = headerFlags) + val header = ErgoTree.headerWithVersion(ZeroHeader, ergoTreeVersionInTests /* Script v2 */) + val ergoTree = createErgoTree(header) // prover is rejecting ErgoTree versions higher than activated assertExceptionThrown( @@ -246,7 +252,7 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { forEachActivatedScriptVersion(activatedVers = Array[Byte](2)) // version for Block v3 { forEachErgoTreeVersion(ergoTreeVers = Array[Byte](3, 4)) { // scripts >= v3 - val headerFlags = ErgoTree.headerWithVersion(ergoTreeVersionInTests) + val headerFlags = ErgoTree.defaultHeaderWithVersion(ergoTreeVersionInTests) val ergoTree = createErgoTree(headerFlags) // prover is rejecting ErgoTree versions higher than activated @@ -273,7 +279,7 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { forEachActivatedScriptVersion(activatedVers = Array[Byte](3)) // version for Block v4 { forEachErgoTreeVersion(ergoTreeVers = Array[Byte](3, 4)) { // scripts >= v3 - val headerFlags = ErgoTree.headerWithVersion(ergoTreeVersionInTests) + val headerFlags = ErgoTree.defaultHeaderWithVersion(ergoTreeVersionInTests) val ergoTree = createErgoTree(headerFlags) // prover is rejecting, because such context parameters doesn't make sense @@ -305,7 +311,7 @@ class ScriptVersionSwitchSpecification extends SigmaDslTesting { { forEachErgoTreeVersion(Array[Byte](0, 1, 2)) { // tree versions supported by v5.x // SF inactive: check cost vectors of v4.x interpreter - val headerFlags = ErgoTree.headerWithVersion(ergoTreeVersionInTests) + val headerFlags = ErgoTree.defaultHeaderWithVersion(ergoTreeVersionInTests) val ergoTree = createErgoTree(headerFlags) // both prove and verify are accepting with full evaluation diff --git a/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala b/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala index c46b5a3119..bca2d0e638 100644 --- a/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala @@ -2,24 +2,28 @@ package sigmastate import org.ergoplatform._ import org.ergoplatform.validation.ValidationRules._ -import org.ergoplatform.validation._ import org.scalatest.BeforeAndAfterAll -import sigmastate.SPrimType.MaxPrimTypeCode -import sigmastate.Values.ErgoTree.EmptyConstants -import sigmastate.Values.{ByteArrayConstant, ErgoTree, IntConstant, NotReadyValueInt, UnparsedErgoTree, ValueCompanion} -import sigmastate.exceptions.{InterpreterException, SerializerException} +import sigma.{Colls, SigmaTestingData} +import sigma.ast._ +import sigma.ast.SPrimType.MaxPrimTypeCode +import sigma.ast.TypeCodes.LastConstantCode +import sigma.data.AvlTreeData +import sigma.serialization.SerializerException +import sigma.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckSerializableTypeCode, CheckTypeCode, CheckTypeWithMethods, trySoftForkable} +import sigma.validation.{ChangedRule, ReplacedRule, SigmaValidationSettings, ValidationException, ValidationRule} +import ErgoTree.{EmptyConstants, HeaderType, ZeroHeader, setSizeBit} import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter} -import sigmastate.interpreter.ErgoTreeEvaluator.DataEnv import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} -import sigmastate.interpreter.{ContextExtension, ErgoTreeEvaluator, ProverResult} -import sigmastate.lang.Terms._ -import sigmastate.serialization.OpCodes.{LastConstantCode, OpCode} -import sigmastate.serialization.SigmaSerializer.startReader -import sigmastate.serialization._ +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.ast.syntax._ +import sigma.eval.ErgoTreeEvaluator +import sigma.eval.ErgoTreeEvaluator.DataEnv +import sigma.exceptions.InterpreterException +import sigma.serialization.SigmaSerializer.startReader +import sigma.serialization._ +import sigma.serialization.ValueCodes.OpCode import sigmastate.utils.Helpers._ -import sigmastate.utxo.DeserializeContext -import sigma.{Colls, SigmaTestingData} class SoftForkabilitySpecification extends SigmaTestingData with CompilerTestingCommons @@ -39,7 +43,7 @@ class SoftForkabilitySpecification extends SigmaTestingData // cast Boolean typed prop to SigmaProp (which is invalid) // ErgoTree v0 lazy val invalidPropV1: ErgoTree = ErgoTree.fromProposition( - ErgoTree.headerWithVersion(0), + ErgoTree.headerWithVersion(ZeroHeader, 0), booleanPropV1.asSigmaProp) lazy val invalidTxV1 = createTransaction(createBox(boxAmt, invalidPropV1, 1)) @@ -48,7 +52,7 @@ class SoftForkabilitySpecification extends SigmaTestingData lazy val propV1 = booleanPropV1.toSigmaProp lazy val txV1 = createTransaction( createBox(boxAmt, - ErgoTree.fromProposition(ErgoTree.headerWithVersion(0), propV1), // ErgoTree v0 + ErgoTree.fromProposition(ErgoTree.headerWithVersion(ZeroHeader, 0), propV1), // ErgoTree v0 1)) lazy val txV1bytes = txV1.messageToSign @@ -131,7 +135,7 @@ class SoftForkabilitySpecification extends SigmaTestingData lazy val booleanPropV2 = GT(Height2, IntConstant(deadline)) lazy val invalidPropV2: ErgoTree = ErgoTree.fromProposition( - headerFlags = ErgoTree.headerWithVersion(0), // ErgoTree v0 + header = ErgoTree.headerWithVersion(ZeroHeader, 0), // ErgoTree v0 prop = booleanPropV2.asSigmaProp) lazy val invalidTxV2 = createTransaction(createBox(boxAmt, invalidPropV2, 1)) @@ -139,7 +143,7 @@ class SoftForkabilitySpecification extends SigmaTestingData lazy val propV2 = booleanPropV2.toSigmaProp // prepare bytes using special serialization WITH `size flag` in the header - lazy val propV2tree = ErgoTree.withSegregation(ErgoTree.SizeFlag, propV2) + lazy val propV2tree = ErgoTree.withSegregation(setSizeBit(ZeroHeader), propV2) lazy val propV2treeBytes = runOnV2Node { propV2tree.bytes } @@ -154,7 +158,7 @@ class SoftForkabilitySpecification extends SigmaTestingData property("node v1, soft-fork up to v2, script v2 without size bit") { // try prepare v2 script without `size bit` in the header assertExceptionThrown({ - ErgoTree(1.toByte, EmptyConstants, propV2) + new ErgoTree(HeaderType @@ 1.toByte, EmptyConstants, Right(propV2)) }, { case _: IllegalArgumentException => true case _ => false @@ -163,7 +167,7 @@ class SoftForkabilitySpecification extends SigmaTestingData // prepare bytes using default serialization and then replacing version in the header val v2tree_withoutSize_bytes = runOnV2Node { val tree = ErgoTree.fromProposition( - ErgoTree.headerWithVersion(0), propV2) // ErgoTree v0 + ErgoTree.headerWithVersion(ZeroHeader, 0), propV2) // ErgoTree v0 val bytes = tree.bytes // set version to v2 while not setting the size bit, // we cannot do this using ErgoTree constructor (due to require() check) @@ -268,7 +272,7 @@ class SoftForkabilitySpecification extends SigmaTestingData // v1 main script which deserializes v2 script from context val mainProp = BinAnd(GT(Height, IntConstant(deadline)), DeserializeContext(1, SBoolean)).toSigmaProp val mainTree = ErgoTree.fromProposition( - headerFlags = ErgoTree.headerWithVersion(0), // ErgoTree v0 + header = ErgoTree.headerWithVersion(ZeroHeader, 0), // ErgoTree v0 prop = mainProp) val tx = createTransaction(createBox(boxAmt, mainTree, 1)) diff --git a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index 2398a48535..fe5a678679 100644 --- a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -1,19 +1,21 @@ package sigmastate -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} +import sigmastate.crypto.DLogProtocol.DLogProverInput import scorex.crypto.hash.Blake2b256 -import sigmastate.Values._ +import sigma.ast._ +import sigma.ast.syntax._ import sigmastate.interpreter._ import Interpreter._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ import org.ergoplatform._ import org.scalatest.BeforeAndAfterAll import scorex.util.encode.Base58 +import sigma.crypto.CryptoConstants +import sigma.data.{AvlTreeData, CAND, ProveDlog, SigmaBoolean, TrivialProp} import sigma.util.Extensions.IntOps -import sigmastate.crypto.CryptoConstants import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.serialization.ValueSerializer +import sigma.serialization.ValueSerializer import sigmastate.utils.Helpers._ import scala.util.Random diff --git a/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala b/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala index 3d5edf22f2..9a4c5ce1b8 100644 --- a/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/TypesSpecification.scala @@ -1,9 +1,10 @@ package sigmastate import sigma.Environment -import sigmastate.SType.isValueOfType -import sigmastate.eval.{CSigmaProp, CostingSigmaDslBuilder} +import sigma.ast.SType.isValueOfType import sigma.SigmaTestingData +import sigma.ast._ +import sigma.data.{CSigmaDslBuilder, CSigmaProp} class TypesSpecification extends SigmaTestingData { @@ -96,8 +97,8 @@ class TypesSpecification extends SigmaTestingData { assertValidType(t1, SAvlTree) assertInvalidType(t1, SShort) - assertValidType(CostingSigmaDslBuilder, SGlobal) - assertInvalidType(CostingSigmaDslBuilder, SShort) + assertValidType(CSigmaDslBuilder, SGlobal) + assertInvalidType(CSigmaDslBuilder, SShort) assertValidType(h1, SHeader) assertInvalidType(h1, SShort) diff --git a/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala b/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala index 5d949d65fc..abbed09992 100644 --- a/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala +++ b/sc/shared/src/test/scala/sigmastate/eval/ErgoScriptTestkit.scala @@ -2,18 +2,22 @@ package sigmastate.eval import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform.validation.ValidationSpecification -import org.ergoplatform.{Context => _, _} +import org.ergoplatform._ import scalan.BaseCtxTests import sigma.VersionContext -import sigmastate.Values.{BigIntArrayConstant, EvaluatedValue, SValue, SigmaPropConstant, Value} +import sigma.ast.{BigIntArrayConstant, ErgoTree, EvaluatedValue, SigmaPropConstant, Value} +import sigma.ast.SType +import sigma.ast.syntax.SValue +import sigma.data.AvlTreeData import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting} import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.interpreter.{ContextExtension, ErgoTreeEvaluator} -import sigmastate.lang.Terms.ValueOps +import sigmastate.interpreter.CErgoTreeEvaluator +import sigma.ast.syntax.ValueOps +import sigma.interpreter.ContextExtension import sigmastate.lang.{CompilerResult, CompilerSettings, LangTests, SigmaCompiler} -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.{AvlTreeData, CompilerTestsBase, SType} +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer +import sigmastate.CompilerTestsBase import sigma.{ContractsTestkit, Context => DContext} import scala.annotation.unused @@ -30,7 +34,7 @@ trait ErgoScriptTestkit extends ContractsTestkit with LangTests import Context._ import Liftables._ - override lazy val compiler = new SigmaCompiler(CompilerSettings( + override lazy val compiler = SigmaCompiler(CompilerSettings( TestnetNetworkPrefix, IR.builder, lowerMethodCalls = true @@ -67,8 +71,8 @@ trait ErgoScriptTestkit extends ContractsTestkit with LangTests lazy val boxToSpend = testBox(10, TrueTree, 0, additionalRegisters = Map(ErgoBox.R4 -> BigIntArrayConstant(bigIntegerArr1))) - lazy val tx1Output1 = testBox(minToRaise, projectPubKey, 0) - lazy val tx1Output2 = testBox(1, projectPubKey, 0) + lazy val tx1Output1 = testBox(minToRaise, ErgoTree.fromProposition(projectPubKey), 0) + lazy val tx1Output2 = testBox(1, ErgoTree.fromProposition(projectPubKey), 0) lazy val tx1 = new ErgoLikeTransaction(IndexedSeq(), IndexedSeq(), IndexedSeq(tx1Output1, tx1Output2)) lazy val ergoCtx = ErgoLikeContextTesting( currentHeight = timeout - 1, @@ -166,11 +170,11 @@ trait ErgoScriptTestkit extends ContractsTestkit with LangTests } // check calc - val (res, _) = ErgoTreeEvaluator.eval( + val (res, _) = CErgoTreeEvaluator.eval( context = ectx, constants = ergoTree.constants, exp = ergoTree.toProposition(replaceConstants = false), - evalSettings = ErgoTreeEvaluator.DefaultEvalSettings + evalSettings = CErgoTreeEvaluator.DefaultEvalSettings ) checkExpected(res, expectedResult.calc, "Calc evaluation:\n value = %s,\n expectedResult.calc: %s\n") diff --git a/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala b/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala index ed9733b57c..526012f212 100644 --- a/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala +++ b/sc/shared/src/test/scala/sigmastate/eval/ErgoTreeBuildingTest.scala @@ -1,14 +1,13 @@ package sigmastate.eval -import org.ergoplatform.{Height, Outputs, Self, Inputs} import sigmastate._ -import sigmastate.Values.{LongConstant, FuncValue, BlockValue, SigmaPropConstant, IntConstant, ValDef, ValUse} +import sigma.ast._ import sigmastate.helpers.ContextEnrichingTestProvingInterpreter import sigmastate.interpreter.Interpreter._ import scalan.BaseCtxTests +import sigma.ast.syntax.ValueOps import sigmastate.lang.LangTests -import sigmastate.lang.Terms.{ValueOps, Apply} -import sigmastate.utxo._ +import sigma.ast.Apply class ErgoTreeBuildingTest extends BaseCtxTests with LangTests with ExampleContracts with ErgoScriptTestkit { diff --git a/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala b/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala index 9aefb272b6..5fcbd0f220 100644 --- a/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala +++ b/sc/shared/src/test/scala/sigmastate/eval/EvaluationTest.scala @@ -1,16 +1,17 @@ package sigmastate.eval import org.ergoplatform.ErgoBox -import sigmastate.Values.{ConcreteCollection, IntArrayConstant, IntConstant, SigmaPropConstant, SigmaPropValue} +import sigma.ast.{AND, ConcreteCollection, CreateProveDlog, DecodePoint, EQ, ErgoTree, IntArrayConstant, IntConstant, SSigmaProp, SigmaPropConstant, SigmaPropIsProven, SubstConstants} import sigmastate.helpers.ContextEnrichingTestProvingInterpreter import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ import scalan.BaseCtxTests +import sigma.ast.syntax.SigmaPropValue +import sigma.data.ProveDlog import sigmastate.lang.LangTests import sigma.util.BenchmarkUtil._ -import sigmastate._ -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer +import sigmastate.crypto.DLogProtocol.DLogProverInput +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer class EvaluationTest extends BaseCtxTests with LangTests with ExampleContracts with ErgoScriptTestkit { @@ -38,7 +39,7 @@ class EvaluationTest extends BaseCtxTests test("lazy logical ops") { val prover = new ContextEnrichingTestProvingInterpreter val pk = prover.dlogSecrets.head.publicImage - val self = testBox(1, pk, 0, additionalRegisters = Map(ErgoBox.R4 -> IntConstant(10))) + val self = testBox(1, ErgoTree.fromSigmaBoolean(pk), 0, additionalRegisters = Map(ErgoBox.R4 -> IntConstant(10))) val ctx = newErgoContext(height = 1, self) // guarded register access: existing reg reduce(emptyEnv, "lazy1", "SELF.R4[Int].isDefined && SELF.R4[Int].get == 10", ctx, true) @@ -101,7 +102,7 @@ class EvaluationTest extends BaseCtxTests test("SubstConst") { def script(pk: ProveDlog): SigmaPropValue = - AND(EQ(IntConstant(1), IntConstant(1)), SigmaPropConstant(pk).isProven).toSigmaProp + AND(EQ(IntConstant(1), IntConstant(1)), SigmaPropIsProven(SigmaPropConstant(pk))).toSigmaProp val pk1 = DLogProverInput.random().publicImage val pk2 = DLogProverInput.random().publicImage diff --git a/sc/shared/src/test/scala/sigmastate/eval/ExampleContracts.scala b/sc/shared/src/test/scala/sigmastate/eval/ExampleContracts.scala index 913a82167f..ac9e6aac07 100644 --- a/sc/shared/src/test/scala/sigmastate/eval/ExampleContracts.scala +++ b/sc/shared/src/test/scala/sigmastate/eval/ExampleContracts.scala @@ -1,6 +1,7 @@ package sigmastate.eval import scalan.BaseCtxTests +import sigma.data.CAnyValue trait ExampleContracts extends ErgoScriptTestkit { self: BaseCtxTests => diff --git a/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala b/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala index ed86bb86ed..332ee902a2 100644 --- a/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala +++ b/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala @@ -1,28 +1,28 @@ package sigmastate.helpers import org.ergoplatform._ -import org.ergoplatform.validation.ValidationRules.CheckSerializableTypeCode -import org.ergoplatform.validation.{ValidationException, ValidationSpecification} +import org.ergoplatform.validation.ValidationSpecification import org.scalacheck.Arbitrary.arbByte import org.scalacheck.Gen -import org.scalatest.Assertion import sigma.util.BenchmarkUtil import scalan.TestContexts -import sigma.{Colls, TestUtils} -import sigma.data.RType -import sigmastate.Values.{Constant, ErgoTree, SValue, SigmaBoolean, SigmaPropValue} -import sigmastate.eval._ +import sigma.ast.{Constant, CostItem, ErgoTree, JitCost, SOption, SType} +import sigma.{Colls, Evaluation, TestUtils} +import sigma.data.{RType, SigmaBoolean} +import sigma.validation.ValidationException +import sigma.validation.ValidationRules.CheckSerializableTypeCode +import sigma.ast.syntax.{SValue, SigmaPropValue} +import sigma.eval.{CostDetails, EvalSettings, Extensions, GivenCost, TracedCost} import sigmastate.helpers.TestingHelpers._ -import sigmastate.interpreter.ContextExtension.VarBinding -import sigmastate.interpreter.ErgoTreeEvaluator.DefaultProfiler +import sigma.interpreter.ContextExtension.VarBinding +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultProfiler import sigmastate.interpreter.Interpreter.ScriptEnv import sigmastate.interpreter._ -import sigmastate.lang.{CompilerSettings, SigmaCompiler, Terms} -import sigmastate.serialization.SigmaSerializer -import sigmastate.{CompilerTestsBase, JitCost, SOption, SType} +import sigmastate.lang.{CompilerSettings, SigmaCompiler} +import sigma.serialization.SigmaSerializer +import sigmastate.CompilerTestsBase +import sigmastate.eval.{CContext, IRContext} -import scala.language.implicitConversions -import scala.reflect.ClassTag import scala.util.DynamicVariable trait CompilerTestingCommons extends TestingCommons @@ -48,7 +48,7 @@ trait CompilerTestingCommons extends TestingCommons def createContexts[A](in: A, bindings: Seq[VarBinding])(implicit tA: RType[A]) = { val tpeA = Evaluation.rtypeToSType(tA) in match { - case ctx: CostingDataContext => + case ctx: CContext => // the context is passed as function argument (this is for testing only) // This is to overcome non-functional semantics of context operations // (such as Inputs, Height, etc which don't have arguments and refer to the @@ -83,7 +83,7 @@ trait CompilerTestingCommons extends TestingCommons .withErgoTreeVersion(ergoTreeVersionInTests) .withBindings(1.toByte -> Constant[SType](in.asInstanceOf[SType#WrappedType], tpeA)) .withBindings(bindings: _*) - val calcCtx = ergoCtx.toSigmaContext().asInstanceOf[CostingDataContext] + val calcCtx = ergoCtx.toSigmaContext().asInstanceOf[CContext] calcCtx } } @@ -116,7 +116,7 @@ trait CompilerTestingCommons extends TestingCommons compiledTree } - def evalSettings = ErgoTreeEvaluator.DefaultEvalSettings + def evalSettings = CErgoTreeEvaluator.DefaultEvalSettings def printCostDetails(script: String, details: CostDetails) = { val traceLines = SigmaPPrint(details, height = 550, width = 150) @@ -132,20 +132,18 @@ trait CompilerTestingCommons extends TestingCommons (implicit IR: IRContext, evalSettings: EvalSettings, compilerSettings: CompilerSettings): CompiledFunc[A, B] = { - val tA = RType[A] val f = (in: A) => { - implicit val cA: ClassTag[A] = tA.classTag val sigmaCtx = createContexts(in, bindings) val accumulator = new CostAccumulator( initialCost = JitCost(0), costLimit = Some(JitCost.fromBlockCost(evalSettings.scriptCostLimitInEvaluator))) - val evaluator = new ErgoTreeEvaluator( + val evaluator = new CErgoTreeEvaluator( context = sigmaCtx, constants = ErgoTree.EmptyConstants, coster = accumulator, evalSettings.profilerOpt.getOrElse(DefaultProfiler), evalSettings) val (res, actualTime) = BenchmarkUtil.measureTimeNano( - evaluator.evalWithCost[B](ErgoTreeEvaluator.EmptyDataEnv, expr)) + evaluator.evalWithCost[B](CErgoTreeEvaluator.EmptyDataEnv, expr)) val costDetails = if (evalSettings.costTracingEnabled) { val trace: Seq[CostItem] = evaluator.getCostTrace() val costDetails = TracedCost(trace, Some(actualTime)) @@ -163,7 +161,7 @@ trait CompilerTestingCommons extends TestingCommons } (res.value, costDetails) } - val Terms.Apply(funcVal, _) = expr.asInstanceOf[SValue] + val sigma.ast.Apply(funcVal, _) = expr.asInstanceOf[SValue] CompiledFunc(funcScript, bindings, funcVal, expr, f) } diff --git a/sc/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala b/sc/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala index 690630d327..1b0f7b112d 100644 --- a/sc/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala +++ b/sc/shared/src/test/scala/sigmastate/helpers/SigmaPPrint.scala @@ -4,26 +4,23 @@ import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId import org.ergoplatform.settings.ErgoAlgos import pprint.{PPrinter, Tree} -import sigma.data.{CollType, PrimitiveType} +import sigma.ast.SCollection.{SBooleanArray, SByteArray, SByteArray2} +import sigma.ast._ +import sigma.crypto.EcPointType +import sigma.data.{AvlTreeData, AvlTreeFlags, CollType, PrimitiveType, TrivialProp} +import sigma.serialization.GroupElementSerializer import sigma.{Coll, GroupElement} -import sigmastate.SCollection._ -import sigmastate.Values.{ConstantNode, ErgoTree, FuncValue, ValueCompanion} +import sigma.ast.{ConstantNode, FuncValue, ValueCompanion} import sigmastate._ -import sigmastate.crypto.CryptoConstants.EcPointType import sigmastate.crypto.GF2_192_Poly -import sigmastate.interpreter.{CompanionDesc, FixedCostItem, MethodDesc} -import sigmastate.lang.Terms -import sigmastate.lang.Terms.MethodCall -import sigmastate.serialization.GroupElementSerializer -import sigmastate.utxo.SelectField - +import sigma.ast.MethodCall import java.math.BigInteger import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable import scala.collection.mutable.ArrayBuffer import scala.reflect.ClassTag -/** Pretty-printer customized to print [[sigmastate.Values.Value]] instances +/** Pretty-printer customized to print [[sigma.ast.Value]] instances * into a valid Scala code (can be cut-and-pasted).*/ object SigmaPPrint extends PPrinter { override def showFieldNames = false @@ -211,11 +208,11 @@ object SigmaPPrint extends PPrinter { Tree.Literal(s"FuncValue.AddToEnvironmentDesc") case MethodDesc(method) => Tree.Apply("MethodDesc", Seq(methodLiteral(method)).iterator) - case sigmastate.SGlobal => + case SGlobal => Tree.Literal(s"SGlobal") - case sigmastate.SCollection => + case SCollection => Tree.Literal(s"SCollection") - case sigmastate.SOption => + case SOption => Tree.Literal(s"SOption") case t: STypeCompanion if t.isInstanceOf[SType] => Tree.Literal(s"S${t.typeName}") @@ -247,7 +244,7 @@ object SigmaPPrint extends PPrinter { case mc @ MethodCall(obj, method, args, typeSubst) => val objType = apply(method.objType).plainText val methodTemplate = method.objType.getMethodByName(method.name) - val methodT = Terms.unifyTypeLists(methodTemplate.stype.tDom, obj.tpe +: args.map(_.tpe)) match { + val methodT = unifyTypeLists(methodTemplate.stype.tDom, obj.tpe +: args.map(_.tpe)) match { case Some(subst) if subst.nonEmpty => val getMethod = s"""$objType.getMethodByName("${method.name}").withConcreteTypes""" Tree.Apply(getMethod, treeifySeq(Seq(subst))) diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index 397c5c5e5d..1f758370a0 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -1,17 +1,18 @@ package sigmastate.lang -import org.ergoplatform.{Height, Inputs, Outputs, Self} import org.ergoplatform.ErgoAddressEncoder._ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigmastate.Values._ +import sigma.ast.{NoType, SBoolean, SBox, SCollection, SFunc, SInt, SLong, STuple} +import sigma.ast._ +import sigma.ast.syntax.SValue import sigmastate._ import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.lang.SigmaPredef.PredefinedFuncRegistry -import sigmastate.lang.Terms._ -import sigmastate.exceptions.BinderException -import sigmastate.eval._ +import SigmaPredef.PredefinedFuncRegistry +import sigma.ast.syntax._ +import sigma.eval.SigmaDsl +import sigma.exceptions.BinderException class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests { import StdSigmaBuilder._ @@ -167,7 +168,7 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat property("predefined primitives") { bind(env, "{ (box: Box) => box.value }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), "value")) - bind(env, "{ (box: Box) => box.propositionBytes }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), SBox.PropositionBytes)) + bind(env, "{ (box: Box) => box.propositionBytes }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), SBoxMethods.PropositionBytes)) bind(env, "{ (box: Box) => box.bytes }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), "bytes")) bind(env, "{ (box: Box) => box.id }") shouldBe Lambda(IndexedSeq("box" -> SBox), NoType, Select(Ident("box"), "id")) } diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 129f096d85..88c75a90b6 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -3,16 +3,15 @@ package sigmastate.lang import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform._ import scorex.util.encode.Base58 -import sigmastate.Values._ +import sigma.ast.{ByIndex, ExtractAmount, GetVar, _} +import sigma.ast.syntax._ import sigmastate._ -import sigmastate.exceptions.{GraphBuildingException, InvalidArguments, TyperException} import sigmastate.helpers.CompilerTestingCommons import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.lang.Terms.{Apply, MethodCall, ZKProofBlock} -import sigmastate.serialization.ValueSerializer -import sigmastate.serialization.generators.ObjectGenerators -import sigmastate.utxo.{ByIndex, ExtractAmount, GetVar} - +import sigma.ast.{Apply, MethodCall, ZKProofBlock} +import sigma.exceptions.{GraphBuildingException, InvalidArguments, TyperException} +import sigma.serialization.ValueSerializer +import sigma.serialization.generators.ObjectGenerators import scala.annotation.unused class SigmaCompilerTest extends CompilerTestingCommons with LangTests with ObjectGenerators { @@ -80,8 +79,8 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec } property("global methods") { - comp(env, "{ groupGenerator }") shouldBe MethodCall(Global, SGlobal.groupGeneratorMethod, IndexedSeq(), Terms.EmptySubst) - comp(env, "{ Global.groupGenerator }") shouldBe MethodCall(Global, SGlobal.groupGeneratorMethod, IndexedSeq(), Terms.EmptySubst) + comp(env, "{ groupGenerator }") shouldBe MethodCall(Global, SGlobalMethods.groupGeneratorMethod, IndexedSeq(), EmptySubst) + comp(env, "{ Global.groupGenerator }") shouldBe MethodCall(Global, SGlobalMethods.groupGeneratorMethod, IndexedSeq(), EmptySubst) comp(env, "{ Global.xor(arr1, arr2) }") shouldBe Xor(ByteArrayConstant(arr1), ByteArrayConstant(arr2)) comp(env, "{ xor(arr1, arr2) }") shouldBe Xor(ByteArrayConstant(arr1), ByteArrayConstant(arr2)) } @@ -221,24 +220,24 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec comp("Coll(true, false).indices") shouldBe mkMethodCall( ConcreteCollection.fromItems(TrueLeaf, FalseLeaf), - SCollection.IndicesMethod.withConcreteTypes(Map(SCollection.tIV -> SBoolean)), + SCollectionMethods.IndicesMethod.withConcreteTypes(Map(SCollection.tIV -> SBoolean)), Vector() ) } property("SBox.tokens") { comp("SELF.tokens") shouldBe - mkMethodCall(Self, SBox.tokensMethod, IndexedSeq()) + mkMethodCall(Self, SBoxMethods.tokensMethod, IndexedSeq()) } property("SContext.dataInputs") { comp("CONTEXT.dataInputs") shouldBe - mkMethodCall(Context, SContext.dataInputsMethod, IndexedSeq()) + mkMethodCall(Context, SContextMethods.dataInputsMethod, IndexedSeq()) } property("SAvlTree.digest") { comp("getVar[AvlTree](1).get.digest") shouldBe - mkMethodCall(GetVar(1.toByte, SAvlTree).get, SAvlTree.digestMethod, IndexedSeq()) + mkMethodCall(GetVar(1.toByte, SAvlTree).get, SAvlTreeMethods.digestMethod, IndexedSeq()) } property("SGroupElement.exp") { @@ -250,7 +249,7 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec property("SOption.map") { comp("getVar[Int](1).map({(i: Int) => i + 1})") shouldBe mkMethodCall(GetVarInt(1), - SOption.MapMethod.withConcreteTypes(Map(SType.tT -> SInt, SType.tR -> SInt)), + SOptionMethods.MapMethod.withConcreteTypes(Map(SType.tT -> SInt, SType.tR -> SInt)), IndexedSeq(FuncValue( Vector((1, SInt)), Plus(ValUse(1, SInt), IntConstant(1)))), Map() @@ -260,7 +259,7 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec property("SOption.filter") { comp("getVar[Int](1).filter({(i: Int) => i > 0})") shouldBe mkMethodCall(GetVarInt(1), - SOption.FilterMethod.withConcreteTypes(Map(SType.tT -> SInt)), + SOptionMethods.FilterMethod.withConcreteTypes(Map(SType.tT -> SInt)), IndexedSeq(FuncValue( Vector((1, SInt)), GT(ValUse(1, SInt), IntConstant(0)))), Map() @@ -271,7 +270,7 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec comp("Coll(1, 2).patch(1, Coll(3), 1)") shouldBe mkMethodCall( ConcreteCollection.fromItems(IntConstant(1), IntConstant(2)), - SCollection.PatchMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), + SCollectionMethods.PatchMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), Vector(IntConstant(1), ConcreteCollection.fromItems(IntConstant(3)), IntConstant(1)), Map()) } @@ -280,7 +279,7 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec comp("Coll(1, 2).updated(1, 1)") shouldBe mkMethodCall( ConcreteCollection.fromItems(IntConstant(1), IntConstant(2)), - SCollection.UpdatedMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), + SCollectionMethods.UpdatedMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), Vector(IntConstant(1), IntConstant(1)), Map()) } @@ -289,7 +288,7 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec comp("Coll(1, 2).updateMany(Coll(1), Coll(3))") shouldBe mkMethodCall( ConcreteCollection.fromItems(IntConstant(1), IntConstant(2)), - SCollection.UpdateManyMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), + SCollectionMethods.UpdateManyMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), Vector(ConcreteCollection.fromItems(IntConstant(1)), ConcreteCollection.fromItems(IntConstant(3))), Map()) } @@ -298,7 +297,7 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec comp("Coll(1, 2).indexOf(1, 0)") shouldBe mkMethodCall( ConcreteCollection.fromItems(IntConstant(1), IntConstant(2)), - SCollection.IndexOfMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), + SCollectionMethods.IndexOfMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), Vector(IntConstant(1), IntConstant(0)), Map()) } @@ -307,7 +306,7 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec comp("Coll(1, 2).zip(Coll(1, 1))") shouldBe mkMethodCall( ConcreteCollection.fromItems(IntConstant(1), IntConstant(2)), - SCollection.ZipMethod.withConcreteTypes(Map(SCollection.tIV -> SInt, SCollection.tOV -> SInt)), + SCollectionMethods.ZipMethod.withConcreteTypes(Map(SCollection.tIV -> SInt, SCollection.tOV -> SInt)), Vector(ConcreteCollection.fromItems(IntConstant(1), IntConstant(1))) ) } diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTemplateCompilerTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTemplateCompilerTest.scala new file mode 100644 index 0000000000..df8f644172 --- /dev/null +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTemplateCompilerTest.scala @@ -0,0 +1,88 @@ +package sigmastate.lang + +import org.ergoplatform.ErgoAddressEncoder +import org.ergoplatform.sdk.Parameter +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigma.ast.{IntConstant, SInt, SLong, SString, StringConstant} +import sigmastate.eval.CompiletimeIRContext + +class SigmaTemplateCompilerTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers { + property("compiles full contract template") { + val source = + """/** This is my contracts description. + |* Here is another line describing what it does in more detail. + |* + |* @param p1 describe p1 + |* @param p2 description of the 2nd parameter + |* which is pretty complex and on many + |* lines to describe functions + |* @param param3 the final parameter + |* @return + |*/ + |@contract def contractName(p1: Int = 5, p2: String = "default string", param3: Long) = { + | sigmaProp(true) + |}""".stripMargin + val compiler = SigmaTemplateCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) + val template = compiler.compile(Map.empty, source) + + template.name shouldBe "contractName" + template.description shouldBe "This is my contracts description. Here is another line describing what it does in more detail." + template.parameters should contain theSameElementsInOrderAs IndexedSeq( + Parameter("p1", "describe p1", 0), + Parameter("p2", "description of the 2nd parameter which is pretty complex and on many lines to describe functions", 1), + Parameter("param3", "the final parameter", 2) + ) + template.constTypes should contain theSameElementsInOrderAs Seq(SInt, SString, SLong) + template.constValues.get should contain theSameElementsInOrderAs IndexedSeq( + Some(IntConstant(5).asWrappedType), + Some(StringConstant("default string").asWrappedType), + None + ) + + val sigmaCompiler = new SigmaCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) + implicit val ir = new CompiletimeIRContext + val result = sigmaCompiler.compile(Map.empty, "{ sigmaProp(true) }") + + template.expressionTree shouldBe result.buildTree + } + + property("compiles contract template without braces") { + val source = + """/** This is my contracts description. + |* Here is another line describing what it does in more detail. + |* + |* @param p1 describe p1 + |* @param p2 description of the 2nd parameter + |* which is pretty complex and on many + | + |* lines to describe functions + |* @return + |*/ + |@contract def contractName(p1: Int = 5, p2: String = "default string") = sigmaProp(true) + |""".stripMargin + val compiler = SigmaTemplateCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) + val template = compiler.compile(Map.empty, source) + + template.name shouldBe "contractName" + template.description shouldBe "This is my contracts description. Here is another line describing what it does in more detail." + template.parameters should contain theSameElementsInOrderAs IndexedSeq( + Parameter("p1", "describe p1", 0), + Parameter("p2", "description of the 2nd parameter which is pretty complex and on many lines to describe functions", 1) + ) + template.constTypes should contain theSameElementsInOrderAs Seq(SInt, SString) + template.constValues.get should contain theSameElementsInOrderAs IndexedSeq( + Some(IntConstant(5).asWrappedType), + Some(StringConstant("default string").asWrappedType) + ) + + val sigmaCompiler = new SigmaCompiler(ErgoAddressEncoder.MainnetNetworkPrefix) + implicit val ir = new CompiletimeIRContext + val result = sigmaCompiler.compile(Map.empty, "sigmaProp(true)") + + template.expressionTree shouldBe result.buildTree + } +} + + diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala index b12938db03..99ad2ae908 100644 --- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala +++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala @@ -6,19 +6,20 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import sigma.Colls -import sigmastate.SCollection._ -import sigmastate.Values._ +import sigma.ast.SCollection._ +import sigma.ast._ +import sigma.ast.syntax.{SValue, SigmaPropValue, SigmaPropValueOps} +import sigma.crypto.CryptoConstants +import sigma.data.ProveDlog import sigmastate._ -import sigmastate.crypto.CryptoConstants -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.exceptions.TyperException +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.lang.SigmaPredef._ -import sigmastate.lang.Terms._ -import sigmastate.lang.syntax.ParserException -import sigmastate.serialization.ErgoTreeSerializer -import sigmastate.serialization.generators.ObjectGenerators -import sigmastate.utxo.{Append, ExtractCreationInfo} +import SigmaPredef._ +import sigmastate.lang.parsers.ParserException +import sigma.serialization.ErgoTreeSerializer +import sigma.serialization.generators.ObjectGenerators +import sigma.ast.Select +import sigma.exceptions.TyperException class SigmaTyperTest extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with LangTests with ObjectGenerators { @@ -638,7 +639,7 @@ class SigmaTyperTest extends AnyPropSpec property("substConst") { def script(pk: ProveDlog): SigmaPropValue = - AND(EQ(IntConstant(1), IntConstant(1)), SigmaPropConstant(pk).isProven).toSigmaProp + AND(EQ(IntConstant(1), IntConstant(1)), SigmaPropIsProven(SigmaPropConstant(pk))).toSigmaProp val pk1 = DLogProverInput.random().publicImage val pk2 = DLogProverInput.random().publicImage diff --git a/sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala b/sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala index 52ef46c642..f4344fbf8d 100644 --- a/sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala +++ b/sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala @@ -1,28 +1,29 @@ -package sigmastate.serialization +package sigma.serialization -import org.ergoplatform.validation.ValidationException -import org.ergoplatform.validation.ValidationRules.CheckPositionLimit -import org.ergoplatform.{ErgoBoxCandidate, Outputs} +import org.ergoplatform.ErgoBoxCandidate import org.scalacheck.Gen -import sigma.util.BenchmarkUtil import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert} import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.serialization.{Reader, VLQByteBufferReader} +import sigma.ast.{SBoolean, SInt, SizeOf} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAND, SigmaBoolean} +import sigma.util.{BenchmarkUtil, safeNewArray} +import sigma.validation.ValidationException +import sigma.validation.ValidationRules.CheckPositionLimit import sigma.{Colls, Environment} -import sigmastate.Values.{BlockValue, GetVarInt, IntConstant, SValue, SigmaBoolean, SigmaPropValue, Tuple, ValDef, ValUse} +import sigma.ast._ +import sigma.ast.syntax._ import sigmastate._ -import sigmastate.crypto.CryptoConstants -import sigmastate.eval.Extensions._ +import sigma.Extensions.ArrayOps +import sigma.eval.Extensions.SigmaBooleanOps +import sigma.eval.SigmaDsl +import sigma.interpreter.{ContextExtension, CostedProverResult} +import sigma.eval.Extensions.EvalIterableOps import sigmastate.eval._ -import sigmastate.exceptions.{DeserializeCallDepthExceeded, InvalidTypePrefix, ReaderPositionLimitExceeded, SerializerException} import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestInterpreter} -import sigmastate.interpreter.{ContextExtension, CostedProverResult} -import sigmastate.serialization.OpCodes._ -import sigma.util.safeNewArray +import sigma.serialization.OpCodes._ import sigmastate.utils.Helpers._ -import sigmastate.utils.SigmaByteReader -import sigmastate.utxo.SizeOf import java.nio.ByteBuffer import scala.collection.immutable.Seq @@ -120,8 +121,8 @@ class DeserializationResilience extends DeserializationResilienceTesting { property("exceeding ergo box propositionBytes max size check") { val oversizedTree = mkTestErgoTree(SigmaAnd( - Gen.listOfN(SigmaSerializer.MaxPropositionSize / CryptoConstants.groupSize, - proveDlogGen.map(_.toSigmaProp)).sample.get)) + Gen.listOfN(SigmaSerializer.MaxPropositionSize / sigma.crypto.groupSize, + proveDlogGen.map(_.toSigmaPropValue)).sample.get)) val b = new ErgoBoxCandidate(1L, oversizedTree, 1) val w = SigmaSerializer.startWriter() ErgoBoxCandidate.serializer.serialize(b, w) @@ -137,7 +138,7 @@ class DeserializationResilience extends DeserializationResilienceTesting { { case SerializerException(_, Some(ValidationException(_,CheckPositionLimit,_, - Some(_: ReaderPositionLimitExceeded)))) => true + Some(_: ReaderPositionLimitExceeded))), _) => true case _ => false }) case _ => @@ -159,8 +160,8 @@ class DeserializationResilience extends DeserializationResilienceTesting { property("ergo box propositionBytes max size check") { val bigTree = mkTestErgoTree(SigmaAnd( - Gen.listOfN((SigmaSerializer.MaxPropositionSize / 2) / CryptoConstants.groupSize, - proveDlogGen.map(_.toSigmaProp)).sample.get)) + Gen.listOfN((SigmaSerializer.MaxPropositionSize / 2) / sigma.crypto.groupSize, + proveDlogGen.map(_.toSigmaPropValue)).sample.get)) val b = new ErgoBoxCandidate(1L, bigTree, 1) val w = SigmaSerializer.startWriter() ErgoBoxCandidate.serializer.serialize(b, w) @@ -232,7 +233,9 @@ class DeserializationResilience extends DeserializationResilienceTesting { property("reader.level is updated in DataSerializer.deserialize") { val expr = IntConstant(1) val (callDepths, levels) = traceReaderCallDepth(expr) - callDepths shouldEqual levels + if (Environment.current.isJVM) { + callDepths shouldEqual levels // on JS stacktrace differs from JVM + } callDepths shouldEqual IndexedSeq(1, 2, 2, 1) } @@ -245,7 +248,9 @@ class DeserializationResilience extends DeserializationResilienceTesting { property("reader.level is updated in SigmaBoolean.serializer.parse") { val expr = CAND(Seq(proveDlogGen.sample.get, proveDHTGen.sample.get)) val (callDepths, levels) = traceReaderCallDepth(expr) - callDepths shouldEqual levels + if (Environment.current.isJVM) { + callDepths shouldEqual levels // on JS stacktrace differs from JVM + } callDepths shouldEqual IndexedSeq(1, 2, 3, 4, 4, 4, 4, 3, 2, 1) } @@ -258,7 +263,9 @@ class DeserializationResilience extends DeserializationResilienceTesting { property("reader.level is updated in TypeSerializer") { val expr = Tuple(Tuple(IntConstant(1), IntConstant(1)), IntConstant(1)) val (callDepths, levels) = traceReaderCallDepth(expr) - callDepths shouldEqual levels + if (Environment.current.isJVM) { + callDepths shouldEqual levels // on JS stacktrace differs from JVM + } callDepths shouldEqual IndexedSeq(1, 2, 3, 4, 4, 3, 3, 4, 4, 3, 2, 2, 3, 3, 2, 1) } @@ -270,8 +277,8 @@ class DeserializationResilience extends DeserializationResilienceTesting { property("exceed ergo box max size check") { val bigTree = mkTestErgoTree(SigmaAnd( - Gen.listOfN((SigmaSerializer.MaxPropositionSize / 2) / CryptoConstants.groupSize, - proveDlogGen.map(_.toSigmaProp)).sample.get)) + Gen.listOfN((SigmaSerializer.MaxPropositionSize / 2) / sigma.crypto.groupSize, + proveDlogGen.map(_.toSigmaPropValue)).sample.get)) val tokens = additionalTokensGen(127).sample.get.map(_.sample.get).toColl val b = new ErgoBoxCandidate(1L, bigTree, 1, tokens) val w = SigmaSerializer.startWriter() @@ -413,7 +420,7 @@ class DeserializationResilience extends DeserializationResilienceTesting { val v = k avlProver.performOneOperation(Insert(ADKey @@@ k, ADValue @@@ v)) val proof = avlProver.generateProof() - val verifier = AvlTreeVerifier(tree, Colls.fromArray(proof)) + val verifier = CAvlTreeVerifier(tree, Colls.fromArray(proof)) verifier.performOneOperation(Insert(ADKey @@@ k, ADValue @@@ v)).isFailure shouldBe true // NOTE, even though performOneOperation fails, some AvlTree$ methods used in Interpreter // (remove_eval, update_eval, contains_eval) won't throw, while others will. diff --git a/sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala b/sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala index c4d8ebf13d..e20d8a6d1e 100644 --- a/sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala @@ -1,16 +1,22 @@ -package sigmastate.serialization +package sigma.serialization -import java.math.BigInteger import org.ergoplatform.ErgoBox -import org.ergoplatform.validation.ValidationException import org.ergoplatform.validation.ValidationRules.CheckDeserializedScriptIsSigmaProp -import sigmastate.Values.{BigIntConstant, ByteConstant, ConstantPlaceholder, ErgoTree, IntConstant, ShortConstant, SigmaPropValue, UnparsedErgoTree} +import sigma.SigmaProp +import sigma.ast._ +import sigma.ast.syntax.SigmaPropValue +import sigma.data.CBigInt +import sigma.util.Extensions.SigmaPropOps +import sigma.validation.ValidationException +import ErgoTree.EmptyConstants +import ErgoTree.HeaderType +import sigma.eval.Extensions.SigmaBooleanOps import sigmastate._ -import sigmastate.eval.{CBigInt, IRContext} -import sigmastate.exceptions.{SerializerException, ReaderPositionLimitExceeded} +import sigmastate.eval.IRContext import sigmastate.helpers.CompilerTestingCommons -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.utxo.{DeserializeContext, DeserializeRegister} +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer + +import java.math.BigInteger class ErgoTreeSerializerSpecification extends SerializationSpecification with CompilerTestingCommons with CompilerCrossVersionProps { @@ -32,7 +38,7 @@ class ErgoTreeSerializerSpecification extends SerializationSpecification Seq(ErgoTree(ergoTreeHeaderInTests, constants, outExpr)) } else { Seq( - ErgoTree((ConstantSegregationHeader | ergoTreeHeaderInTests).toByte, constants, outExpr), + ErgoTree(setConstantSegregation(ergoTreeHeaderInTests), constants, outExpr), ErgoTree(ergoTreeHeaderInTests, EmptyConstants, prop) ) } @@ -171,7 +177,7 @@ class ErgoTreeSerializerSpecification extends SerializationSpecification forAll(samples) { (exp, hasDeserialize) => val t = new ErgoTree( - 16.toByte, + HeaderType @@ 16.toByte, Array(IntConstant(1)), Right(BoolToSigmaProp(EQ(ConstantPlaceholder(0, SInt), exp))) ) @@ -207,4 +213,13 @@ class ErgoTreeSerializerSpecification extends SerializationSpecification test(positions = Array(1, 2), expected = Array(-1, 0, 1, -1, -1)) test(positions = Array(1, 2, 4), expected = Array(-1, 0, 1, -1, 2)) } + + property("SigmaProp.propBytes vs ErgoTree.serializer equivalence") { + forAll(MinSuccessful(100)) { sp: SigmaProp => + val propBytes = sp.propBytes + val ergoTree = new ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, Right(sp.toSigmaBoolean.toSigmaPropValue), null, None, None) + val treeBytes = DefaultSerializer.serializeErgoTree(ergoTree) + treeBytes shouldBe propBytes.toArray + } + } } diff --git a/sc/shared/src/test/scala/sigmastate/utils/GenInfoObjects.scala b/sc/shared/src/test/scala/sigmastate/utils/GenInfoObjects.scala index 9ebdce9f31..78962f20e3 100644 --- a/sc/shared/src/test/scala/sigmastate/utils/GenInfoObjects.scala +++ b/sc/shared/src/test/scala/sigmastate/utils/GenInfoObjects.scala @@ -1,6 +1,6 @@ package sigmastate.utils -import sigmastate.SMethod +import sigma.ast.SMethod import sigma.util.PrintExtensions._ import scala.util.Try import Helpers._ // required for Scala 2.11 diff --git a/sc/shared/src/test/scala/sigmastate/utils/SpecGen.scala b/sc/shared/src/test/scala/sigmastate/utils/SpecGen.scala index 7eccb1dd00..4c56580c9b 100644 --- a/sc/shared/src/test/scala/sigmastate/utils/SpecGen.scala +++ b/sc/shared/src/test/scala/sigmastate/utils/SpecGen.scala @@ -1,18 +1,16 @@ package sigmastate.utils -import sigma.util.PrintExtensions.IterableExtensions -import sigmastate._ -import sigmastate.eval.Evaluation._ -import sigma.util.Extensions.ByteOps +import sigma.Evaluation._ +import sigma.ast.TypeCodes.LastConstantCode +import sigma.ast._ +import sigma.serialization.CoreByteWriter.ArgInfo import sigma.util.CollectionUtil +import sigma.util.Extensions.ByteOps import sigma.util.PrintExtensions._ -import sigmastate.Values._ -import sigmastate.lang.SigmaPredef.{PredefinedFunc, PredefinedFuncRegistry} -import sigmastate.lang.StdSigmaBuilder -import sigmastate.lang.Terms.{MethodCall, PropertyCall} -import sigmastate.serialization.OpCodes.OpCode -import sigmastate.serialization.{OpCodes, ValueSerializer} -import sigmastate.utxo.{SelectField, SigmaPropIsProven} +import SigmaPredef.{PredefinedFunc, PredefinedFuncRegistry} +import sigma.ast.{MethodCall, PropertyCall} +import sigma.serialization.ValueCodes.OpCode +import sigma.serialization.ValueSerializer object SpecGenUtils { val types = SType.allPredefTypes.diff(Seq(SString)) @@ -29,8 +27,8 @@ trait SpecGen { description: String, args: Seq[ArgInfo], op: Either[PredefinedFunc, SMethod]) - def collectSerializers(): Seq[ValueSerializer[_ <: Values.Value[SType]]] = { - ((OpCodes.LastConstantCode + 1) to 255).collect { + def collectSerializers(): Seq[ValueSerializer[_ <: Value[SType]]] = { + ((LastConstantCode + 1) to 255).collect { case i if ValueSerializer.serializers(i.toByte) != null => val ser = ValueSerializer.serializers(i.toByte) assert(i == ser.opDesc.opCode.toUByte) @@ -39,7 +37,7 @@ trait SpecGen { } def collectFreeCodes(): Seq[Int] = { - ((OpCodes.LastConstantCode + 1) to 255).collect { + ((LastConstantCode + 1) to 255).collect { case i if ValueSerializer.serializers(i.toByte) == null => i } } @@ -47,7 +45,8 @@ trait SpecGen { def collectMethods(): Seq[SMethod] = { for { tc <- typesWithMethods.sortBy(_.typeId) - m <- tc.methods.sortBy(_.methodId) + mc = MethodsContainer(tc.typeId) + m <- mc.methods.sortBy(_.methodId) } yield m } @@ -215,7 +214,8 @@ trait SpecGen { } def printMethods(tc: STypeCompanion) = { - val methodSubsections = for { m <- tc.methods.sortBy(_.methodId) } yield { + val mc = MethodsContainer(tc.typeId) + val methodSubsections = for { m <- mc.methods.sortBy(_.methodId) } yield { methodSubsection(tc.typeName, m) } val res = methodSubsections.mkString("\n\n") @@ -252,7 +252,7 @@ object GenPrimOpsApp extends SpecGen { val methods = collectMethods() val ops = collectSerializableOperations() val noOps = Set( - TaggedVariable, ValUse, ConstantPlaceholder, TrueLeaf, FalseLeaf, + ValUse, ConstantPlaceholder, TrueLeaf, FalseLeaf, ConcreteCollection, ConcreteCollectionBooleanConstant, Tuple, SelectField, SigmaPropIsProven, ValDef, FunDef, BlockValue ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala index 5133692685..566bf5bd58 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/AVLTreeScriptsSpecification.scala @@ -5,20 +5,25 @@ import org.ergoplatform._ import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, TestContractSpec} import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.{ADKey, ADValue, SerializedAdProof} -import scorex.crypto.hash.{Digest32, Blake2b256} -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ +import scorex.crypto.hash.{Blake2b256, Digest32} +import sigma.ast.SCollection.SByteArray +import sigma.ast._ import sigmastate._ -import sigmastate.eval.{CSigmaProp, IRContext} +import sigmastate.eval.IRContext import sigmastate.eval._ -import sigmastate.eval.Extensions._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} +import sigma.Extensions.ArrayOps +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp -import sigmastate.interpreter.ProverResult -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ import sigma.Coll +import sigma.ast.SAvlTree +import sigma.ast.syntax.{GetVarByteArray, OptionValueOps} +import sigma.data.{AvlTreeData, AvlTreeFlags, CSigmaProp, TrivialProp} +import sigma.eval.SigmaDsl +import sigma.interpreter.ProverResult import sigma.{AvlTree, Context} +import sigmastate.eval.Extensions.AvlTreeOps class AVLTreeScriptsSpecification extends CompilerTestingCommons @@ -204,12 +209,12 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons val propExp = IR.builder.mkMethodCall( ExtractRegisterAs[SAvlTree.type](Self, reg1).get, - SAvlTree.containsMethod, + SAvlTreeMethods.containsMethod, IndexedSeq(ByteArrayConstant(key), ByteArrayConstant(proof)) ).asBoolValue.toSigmaProp prop shouldBe propExp - val newBox1 = testBox(10, pubkey, 0) + val newBox1 = testBox(10, ErgoTree.fromSigmaBoolean(pubkey), 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) @@ -257,7 +262,7 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(selfBox), - createTransaction(testBox(1, recipientProposition, 0)), + createTransaction(testBox(1, ErgoTree.fromSigmaBoolean(recipientProposition), 0)), self = selfBox, activatedVersionInTests) avlProver.performOneOperation(Lookup(treeElements.head._1)) @@ -320,12 +325,12 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons val propExp = IR.builder.mkMethodCall( ExtractRegisterAs[SAvlTree.type](Self, reg1).get, - SAvlTree.containsMethod, + SAvlTreeMethods.containsMethod, IndexedSeq(ExtractRegisterAs[SByteArray](Self, reg2).get, GetVarByteArray(proofId).get) ).asBoolValue.toSigmaProp prop shouldBe propExp - val newBox1 = testBox(10, pubkey, 0) + val newBox1 = testBox(10, ErgoTree.fromSigmaBoolean(pubkey), 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) @@ -379,7 +384,7 @@ class AVLTreeScriptsSpecification extends CompilerTestingCommons val propTree = ErgoTree.fromProposition(ergoTreeHeaderInTests, prop) - val newBox1 = testBox(10, pubkey, 0) + val newBox1 = testBox(10, ErgoTree.fromSigmaBoolean(pubkey), 0) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = ErgoLikeTransaction(IndexedSeq(), newBoxes) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index eddd607178..346ad69e1a 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -1,25 +1,27 @@ package sigmastate.utxo -import java.math.BigInteger import org.ergoplatform.ErgoBox.{AdditionalRegisters, R6, R8} import org.ergoplatform._ -import sigma.data.RType -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ +import sigma.Extensions.ArrayOps +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType.AnyOps +import sigma.data.{AvlTreeData, CAnyValue} +import sigma.util.StringUtil._ +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.crypto.CryptoConstants import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} +import sigma.interpreter.ContextExtension.VarBinding +import sigmastate.interpreter.CErgoTreeEvaluator.DefaultEvalSettings import sigmastate.interpreter.Interpreter._ -import sigmastate.lang.Terms._ -import SType.AnyOps -import sigmastate.crypto.CryptoConstants -import sigmastate.eval.{CAnyValue, InvalidType} -import sigmastate.interpreter.ContextExtension.VarBinding -import sigmastate.interpreter.ErgoTreeEvaluator.DefaultEvalSettings -import sigmastate.interpreter.EvalSettings +import sigma.ast.Apply +import sigma.eval.EvalSettings +import sigma.exceptions.InvalidType import sigmastate.utils.Helpers._ -import sigma.util.StringUtil._ + +import java.math.BigInteger class BasicOpsSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala index 54f0d1dc04..0de1a13f08 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala @@ -1,14 +1,19 @@ package sigmastate.utxo -import sigmastate.Values._ +import sigma.ast._ import sigmastate._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ import org.ergoplatform._ -import sigmastate.SCollection._ +import sigma.ast.SCollection._ +import sigma.data.AvlTreeData +import SCollectionMethods.{FlatMapMethod, IndexOfMethod, IndicesMethod, PatchMethod, UpdateManyMethod, UpdatedMethod} +import sigma.ast import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} -import sigmastate.serialization.OpCodes._ +import sigma.serialization.OpCodes._ +import sigma.ast.MethodCall +import sigma.eval.Extensions.SigmaBooleanOps import sigmastate.utils.Helpers._ class CollectionOperationsSpecification extends CompilerTestingCommons @@ -87,7 +92,7 @@ class CollectionOperationsSpecification extends CompilerTestingCommons val prover = new ContextEnrichingTestProvingInterpreter val verifier = new ErgoLikeTestInterpreter - val pubkey = prover.dlogSecrets.head.publicImage.toSigmaProp + val pubkey = prover.dlogSecrets.head.publicImage.toSigmaPropValue val pubkeyTree = mkTestErgoTree(pubkey) val prop = compile(Map(), "OUTPUTS.exists({ (box: Box) => box.value + 5 > 10 })").asBoolValue.toSigmaProp @@ -202,7 +207,7 @@ class CollectionOperationsSpecification extends CompilerTestingCommons val prover = new ContextEnrichingTestProvingInterpreter val verifier = new ErgoLikeTestInterpreter - val pubkey = prover.dlogSecrets.head.publicImage.toSigmaProp + val pubkey = prover.dlogSecrets.head.publicImage.toSigmaPropValue val pubkeyTree = mkTestErgoTree(pubkey) val prop = compile(Map(), @@ -215,8 +220,8 @@ class CollectionOperationsSpecification extends CompilerTestingCommons FuncValue( Vector((1, SBox)), EQ( - utxo.ExtractRegisterAs[SLong.type](ValUse(1, SBox), reg1).get, - Plus(utxo.ExtractRegisterAs[SLong.type](Self, reg1).get, LongConstant(1))) + ast.ExtractRegisterAs[SLong.type](ValUse(1, SBox), reg1).get, + Plus(ast.ExtractRegisterAs[SLong.type](Self, reg1).get, LongConstant(1))) ) ).toSigmaProp prop shouldBe propExpected @@ -258,8 +263,8 @@ class CollectionOperationsSpecification extends CompilerTestingCommons FuncValue( Vector((1, SBox)), EQ( - utxo.ExtractRegisterAs[SLong.type](ValUse(1, SBox), reg1).getOrElse(LongConstant(0)), - Plus(utxo.ExtractRegisterAs[SLong.type](Self, reg1).get, LongConstant(1)) + ast.ExtractRegisterAs[SLong.type](ValUse(1, SBox), reg1).getOrElse(LongConstant(0)), + Plus(ast.ExtractRegisterAs[SLong.type](Self, reg1).get, LongConstant(1)) ) ) ).toSigmaProp @@ -531,7 +536,7 @@ class CollectionOperationsSpecification extends CompilerTestingCommons assertProof("OUTPUTS.zip(INPUTS).size == 2", EQ( SizeOf(MethodCall(Outputs, - SCollection.ZipMethod.withConcreteTypes(Map(SCollection.tIV -> SBox, SCollection.tOV -> SBox)), + SCollectionMethods.ZipMethod.withConcreteTypes(Map(SCollection.tIV -> SBox, SCollection.tOV -> SBox)), Vector(Inputs), Map()).asCollection[STuple]), IntConstant(2)), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/ComplexSigSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/ComplexSigSpecification.scala index 89e734a6e8..961bc08020 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/ComplexSigSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/ComplexSigSpecification.scala @@ -1,11 +1,11 @@ package sigmastate.utxo -import org.ergoplatform.Height import org.scalacheck.Gen -import sigmastate.Values.IntConstant +import sigma.data.{AvlTreeData, CAND, COR, CTHRESHOLD} +import sigma.ast.{GT, Height, IntConstant, SigmaAnd, SigmaOr} import sigmastate._ import sigmastate.helpers._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ import scala.util.Random diff --git a/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala index 6c84a5f54f..fe3dac68b3 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/ContextEnrichingSpecification.scala @@ -2,9 +2,9 @@ package sigmastate.utxo import scorex.util.encode.Base16 import scorex.crypto.hash.Blake2b256 -import sigmastate.Values._ +import sigma.ast._ +import sigma.ast.syntax._ import sigmastate._ -import sigmastate.lang.Terms._ import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} import sigma.Coll diff --git a/sc/shared/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala index 9276413e1e..74c28c2ec9 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/DistributedSigSpecification.scala @@ -1,9 +1,11 @@ package sigmastate.utxo +import sigma.ast.ErgoTree +import sigma.data.CAND import sigmastate._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeTestProvingInterpreter, CompilerTestingCommons} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeTestProvingInterpreter} import sigmastate.interpreter._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ /** * Distributed signatures examples. @@ -518,7 +520,10 @@ class DistributedSigSpecification extends CompilerTestingCommons val sigAlice = proverA.signMessage(sigmaTree, msg, bagA).get - val bagB = proverB.bagForMultisig(ctx, sigmaTree, sigAlice, Seq(pubkeyAlice)) + val bagB = proverB.bagForMultisig(ctx, + ErgoTree.fromSigmaBoolean(ergoTreeHeaderInTests, sigmaTree), + proof = sigAlice, + realSecretsToExtract = Seq(pubkeyAlice)) .addHint(hintsFromBob.ownCommitments.head) val sigBob = proverB.signMessage(sigmaTree, msg, bagB).get diff --git a/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala index 58bbb522c6..615a826649 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/ErgoLikeInterpreterSpecification.scala @@ -3,23 +3,23 @@ package sigmastate.utxo import scorex.utils.Bytes import org.ergoplatform.ErgoBox.R4 import org.ergoplatform._ -import org.ergoplatform.validation.ValidationException import org.scalatest.TryValues._ import scorex.crypto.hash.Blake2b256 -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ +import sigma.ast.SCollection.SByteArray +import sigma.ast._ import sigmastate._ +import sigma.ast.syntax._ +import sigma.data.{AvlTreeData, ProveDHTuple, ProveDlog, TrivialProp} +import sigma.util.Extensions.EcpOps +import sigma.validation.ValidationException import sigmastate.eval._ -import sigmastate.eval.Extensions._ import sigmastate.interpreter.Interpreter._ -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.ProveDHTuple import sigmastate.helpers._ import sigmastate.helpers.TestingHelpers._ -import sigmastate.interpreter.ContextExtension.VarBinding -import sigmastate.interpreter.{ContextExtension, CostedProverResult} -import sigmastate.lang.Terms._ -import sigmastate.serialization.{ValueSerializer, SerializationSpecification} +import sigma.interpreter.ContextExtension.VarBinding +import sigma.eval.Extensions.SigmaBooleanOps +import sigma.interpreter.{ContextExtension, CostedProverResult} +import sigma.serialization.{SerializationSpecification, ValueSerializer} import sigmastate.utils.Helpers._ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons @@ -29,6 +29,15 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons implicit lazy val IR: TestingIRContext = new TestingIRContext private val reg1 = ErgoBox.nonMandatoryRegisters.head + def sum[T <: SNumericType](input: Value[SCollection[T]], varId: Int)(implicit tT: T) = + Fold(input, + Constant(tT.upcast(0.toByte), tT), + FuncValue(Array((varId, STuple(tT, tT))), + Plus( + SelectField(ValUse(varId, STuple(tT, tT)), 1).asNumValue, + SelectField(ValUse(varId, STuple(tT, tT)), 2).asNumValue)) + ) + property("scripts EQ/NEQ") { val prover1 = new ContextEnrichingTestProvingInterpreter val prover2 = new ContextEnrichingTestProvingInterpreter @@ -42,8 +51,8 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons .withErgoTreeVersion(ergoTreeVersionInTests) val e = compile(Map( - "h1" -> h1.treeWithSegregation(ergoTreeHeaderInTests).bytes, - "h2" -> h2.treeWithSegregation(ergoTreeHeaderInTests).bytes), + "h1" -> ErgoTree.withSegregation(ergoTreeHeaderInTests, h1).bytes, + "h2" -> ErgoTree.withSegregation(ergoTreeHeaderInTests, h2).bytes), "h1 == h1") val exp = TrueLeaf e shouldBe exp @@ -52,8 +61,8 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons res shouldBe TrivialProp.TrueProp val ergoTree = mkTestErgoTree( EQ( - ByteArrayConstant(h1.treeWithSegregation(ergoTreeHeaderInTests).bytes), - ByteArrayConstant(h2.treeWithSegregation(ergoTreeHeaderInTests).bytes) + ByteArrayConstant(ErgoTree.withSegregation(ergoTreeHeaderInTests, h1).bytes), + ByteArrayConstant(ErgoTree.withSegregation(ergoTreeHeaderInTests, h2).bytes) ).toSigmaProp ) val res2 = verifier.fullReduction(ergoTree, ctx).value @@ -247,7 +256,7 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons SigmaPropConstant(pubkey), BoolToSigmaProp( GT( - Fold.sum[SLong.type](MapCollection(Outputs, + sum[SLong.type](MapCollection(Outputs, FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox)))), 1), LongConstant(20)) ) @@ -411,7 +420,7 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons val prover0 = new ContextEnrichingTestProvingInterpreter() - val customScript = prover0.dlogSecrets.head.publicImage.toSigmaProp + val customScript = prover0.dlogSecrets.head.publicImage.toSigmaPropValue val scriptBytes = ValueSerializer.serialize(customScript) val scriptHash = Blake2b256(scriptBytes).take(bytesCount) @@ -742,7 +751,7 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons } property("non-const ProveDHT") { - import sigmastate.crypto.CryptoConstants.dlogGroup + import sigma.crypto.CryptoConstants.dlogGroup compile(Map("gA" -> dlogGroup.generator.toGroupElement), "proveDHTuple(gA, OUTPUTS(0).R4[GroupElement].get, gA, gA)" ).asInstanceOf[BlockValue].result shouldBe a [CreateProveDHTuple] diff --git a/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala b/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala index 8899b90584..0fdf6db97b 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala @@ -3,8 +3,8 @@ package sigmastate.utxo import org.ergoplatform._ import sigma.util.BenchmarkUtil import sigmastate.helpers.CompilerTestingCommons -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.serialization.generators.ObjectGenerators +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.generators.ObjectGenerators import debox.{Buffer => DBuffer} import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec diff --git a/sc/shared/src/test/scala/sigmastate/utxo/SigmaCompilerSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/SigmaCompilerSpecification.scala index ffbb02dd98..e84dc6211b 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/SigmaCompilerSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/SigmaCompilerSpecification.scala @@ -1,11 +1,12 @@ package sigmastate.utxo -import sigmastate.Values._ -import sigmastate.eval.CAnyValue +import sigma.ast.{GE, ModQ, SType} +import sigma.data.CAnyValue +import sigma.ast._ +import sigma.ast.syntax.{GetVarInt, OptionValueOps} import sigmastate.helpers.CompilerTestingCommons import sigmastate.interpreter.Interpreter.ScriptEnv -import sigmastate.lang.Terms._ -import sigmastate.{GE, ModQ, SType} +import sigma.ast.syntax._ /** * Specification for compile function diff --git a/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala index 31536a76fd..2328b51989 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala @@ -1,10 +1,13 @@ package sigmastate.utxo -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.Values.{ConcreteCollection, FalseLeaf, IntConstant, SigmaPropConstant, SigmaPropValue, TrueLeaf} +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.data.{AvlTreeData, CAND, COR, ProveDlog, TrivialProp} +import sigma.eval.Extensions.SigmaBooleanOps +import sigma.exceptions.GraphBuildingException import sigmastate._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTransactionTesting, CompilerTestingCommons} -import sigmastate.exceptions.GraphBuildingException +import sigmastate.crypto.DLogProtocol.DLogProverInput +import sigmastate.helpers._ class ThresholdSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -88,7 +91,7 @@ class ThresholdSpecification extends CompilerTestingCommons proverD.prove(compiledTree3, ctx, fakeMessage).isFailure shouldBe true { - val prop3Or = COR(Seq(pubkeyA, pubkeyB, pubkeyC)).toSigmaProp + val prop3Or = COR(Seq(pubkeyA, pubkeyB, pubkeyC)).toSigmaPropValue val res1 = testReduce(proverA)(ctx, compiledProp3) val res2 = testReduce(proverA)(ctx, prop3Or) res1 shouldBe res2 @@ -113,7 +116,6 @@ class ThresholdSpecification extends CompilerTestingCommons } property("threshold reduce to crypto") { - import TrivialProp._ val prover = new ContextEnrichingTestProvingInterpreter val ctx = ErgoLikeContextTesting( currentHeight = 1, diff --git a/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala index e83d9a8569..e3d43ab868 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/UsingContextPropertiesSpecification.scala @@ -1,13 +1,14 @@ package sigmastate.utxo -import sigmastate.{TrivialProp, CompilerCrossVersionProps} -import sigmastate.eval.{IRContext, CSigmaProp} -import sigmastate.eval.Extensions._ +import sigmastate.CompilerCrossVersionProps +import sigmastate.eval.IRContext +import sigma.Extensions.ArrayOps import sigma.Context import sigmastate.helpers.CompilerTestingCommons -import org.ergoplatform.dsl.{SigmaContractSyntax, ContractSpec, TestContractSpec} +import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, TestContractSpec} import org.ergoplatform.ErgoBox import scorex.crypto.hash.Blake2b256 +import sigma.data.{CSigmaProp, TrivialProp} class UsingContextPropertiesSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { suite => diff --git a/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationSpecification.scala index 13ec9f2518..f063cf5aa0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationSpecification.scala @@ -1,15 +1,12 @@ package sigmastate.utxo.blockchain -import java.io.{FileWriter, File} - +import sigma.ast.TrueLeaf +import sigma.ast.syntax.{GetVarBoolean, OptionValueOps} import sigmastate.CompilerCrossVersionProps -import sigmastate.Values.{TrueLeaf, GetVarBoolean} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeTestProvingInterpreter} -import sigmastate.interpreter.ContextExtension +import sigmastate.helpers.ErgoLikeTestProvingInterpreter +import sigma.interpreter.ContextExtension import sigmastate.utxo.blockchain.BlockchainSimulationTestingCommons._ -import scala.collection.concurrent.TrieMap - class BlockchainSimulationSpecification extends BlockchainSimulationTestingCommons with CompilerCrossVersionProps { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala b/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala index e9cf82616b..0dca8a4471 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/blockchain/BlockchainSimulationTestingCommons.scala @@ -5,8 +5,7 @@ import org.ergoplatform._ import scorex.crypto.authds.{ADDigest, ADKey, ADValue} import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, Remove} import scorex.crypto.hash.{Blake2b256, Digest32} -import sigmastate.{AvlTreeData, AvlTreeFlags, Values} -import sigmastate.Values.{ErgoTree, LongConstant} +import sigma.ast.{ErgoTree, LongConstant, TrueLeaf} import sigmastate.eval._ import sigmastate.helpers.{BlockchainState, CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestProvingInterpreter, ErgoTransactionValidator} import sigmastate.helpers.TestingHelpers._ @@ -16,7 +15,10 @@ import scala.collection.mutable import scala.util.{Random, Try} import scorex.util._ import sigma.Colls -import sigmastate.interpreter.ContextExtension +import sigma.data.{AvlTreeData, AvlTreeFlags} +import ErgoTree.ZeroHeader +import sigma.eval.Extensions.SigmaBooleanOps +import sigma.interpreter.ContextExtension import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import sigmastate.utxo.blockchain.BlockchainSimulationTestingCommons.{FullBlock, ValidationState} @@ -45,7 +47,7 @@ trait BlockchainSimulationTestingCommons extends CompilerTestingCommons { propOpt: Option[ErgoTree] = None, extension: ContextExtension = ContextExtension.empty): FullBlock = { val prop: ErgoTree = propOpt.getOrElse( - mkTestErgoTree(prover.dlogSecrets.head.publicImage.toSigmaProp)) + mkTestErgoTree(prover.dlogSecrets.head.publicImage.toSigmaPropValue)) val minerPubkey = prover.dlogSecrets.head.publicImage.pkBytes val boxesToSpend = state.boxesReader.randomBoxes(30 + height) @@ -155,8 +157,8 @@ object BlockchainSimulationTestingCommons extends CompilerTestingCommons { val boxes = (1 to 50).map(_ => testBox(10, ErgoTree.fromProposition( - ErgoTree.headerWithVersion(scriptVersion), - Values.TrueLeaf.toSigmaProp), + ErgoTree.headerWithVersion(ZeroHeader, scriptVersion), + TrueLeaf.toSigmaProp), i, Seq(), Map(), txId)) createTransaction(boxes) }, diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeErgoTests.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeErgoTests.scala index 5eb852265d..7d9258bc5c 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeErgoTests.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeErgoTests.scala @@ -5,7 +5,7 @@ import org.ergoplatform.dsl.ContractSyntax.Token import org.ergoplatform.dsl.ErgoContractSpec import sigma.Coll import scorex.crypto.hash.Blake2b256 -import sigmastate.eval.Digest32Coll +import sigma.data.Digest32Coll class AssetsAtomicExchangeErgoTests extends CompilerTestingCommons { suite => lazy val spec = new ErgoContractSpec()(new TestingIRContext) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeTests.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeTests.scala index b8fe0a8410..1bda3992c7 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeTests.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/AssetsAtomicExchangeTests.scala @@ -1,18 +1,18 @@ package sigmastate.utxo.examples -import org.ergoplatform.{Height, Outputs, ErgoBox, Self} +import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.R4 import sigmastate.helpers.CompilerTestingCommons import org.ergoplatform.dsl.ContractSyntax.Token import org.ergoplatform.dsl.TestContractSpec import scorex.crypto.hash.Blake2b256 -import sigmastate.SCollection.SByteArray -import sigmastate._ -import sigmastate.Values.{BlockValue, ByteArrayConstant, LongConstant, ValDef, ValUse, Value} -import sigmastate.eval.{CSigmaProp, Digest32Coll} +import sigma.ast.SCollection.SByteArray +import sigma.ast._ +import sigma.ast.syntax._ +import sigma.data.{CSigmaProp, Digest32Coll, TrivialProp} +import sigma.ast.{BlockValue, ByteArrayConstant, LongConstant, ValDef, ValUse, Value} import sigmastate.eval.Extensions._ -import sigmastate.lang.Terms.ValueOps -import sigmastate.utxo._ +import sigma.ast.syntax.ValueOps /** An example of an atomic ergo <=> asset exchange. * Let's assume that Alice is willing to buy 60 assets of type "token1" for 100 ergo coins, and Bob @@ -140,7 +140,7 @@ class AssetsAtomicExchangeTests extends CompilerTestingCommons { suite => // ASSERT val input0 = spendingTx.inputs(0) - val buyerExt = Map(Byte.MaxValue -> toAnyValue(0.toShort)) + val buyerExt = Map(Byte.MaxValue -> ShortConstant(0.toShort)) val res = input0.runDsl(buyerExt) res shouldBe CSigmaProp(TrivialProp.TrueProp) @@ -148,7 +148,7 @@ class AssetsAtomicExchangeTests extends CompilerTestingCommons { suite => contract.verifier.verify(input0, buyerProof) shouldBe true val input1 = spendingTx.inputs(1) - val sellerExt = Map(Byte.MaxValue -> toAnyValue(1.toShort)) + val sellerExt = Map(Byte.MaxValue -> ShortConstant(1.toShort)) val res1 = input1.runDsl(sellerExt) res1 shouldBe CSigmaProp(TrivialProp.TrueProp) val sellerProof = contract.tokenSeller.prove(input1, sellerExt).get diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala index ef004f602a..131aba22d0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/AtomicSwapExampleSpecification.scala @@ -1,14 +1,14 @@ package sigmastate.utxo.examples -import org.ergoplatform.Height import scorex.crypto.hash.Blake2b256 import scorex.utils.Random -import sigmastate.Values._ +import sigma.data.AvlTreeData +import sigma.ast._ +import sigma.ast.syntax.{CollectionConstant, GetVarByteArray, OptionValueOps} import sigmastate._ import sigmastate.interpreter.Interpreter._ import sigmastate.helpers._ -import sigmastate.lang.Terms._ -import sigmastate.utxo.SizeOf +import sigma.ast.syntax._ class AtomicSwapExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { private implicit lazy val IR: TestingIRContext = new TestingIRContext diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala index a6c612a037..12e6a236c8 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/CoinEmissionSpecification.scala @@ -3,15 +3,14 @@ package sigmastate.utxo.examples import org.ergoplatform._ import org.ergoplatform.settings.ErgoAlgos import sigma.Colls -import sigmastate.Values.{BlockValue, ErgoTree, IntConstant, LongConstant, ValDef, ValUse} +import sigma.ast.syntax.OptionValueOps +import sigma.ast._ import sigmastate._ -import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting} -import sigmastate.interpreter.ContextExtension +import sigma.interpreter.ContextExtension import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} -import sigmastate.lang.Terms._ -import sigmastate.utxo._ +import sigma.ast.syntax._ import sigmastate.utxo.blockchain.BlockchainSimulationTestingCommons._ /** @@ -167,7 +166,7 @@ block 1600 in 1622 ms, 30000000000 coins remain, defs: 61661 height: Int): ErgoLikeTransaction = { assert(state.state.currentHeight == height - 1) val ut = if (emissionBox.value > s.oneEpochReduction) { - val minerBox = new ErgoBoxCandidate(emissionAtHeight(height), minerProp, height, Colls.emptyColl, Map()) + val minerBox = new ErgoBoxCandidate(emissionAtHeight(height), ErgoTree.fromSigmaBoolean(minerProp), height, Colls.emptyColl, Map()) val newEmissionBox: ErgoBoxCandidate = new ErgoBoxCandidate(emissionBox.value - minerBox.value, tree, height, Colls.emptyColl, Map(register -> IntConstant(height))) @@ -176,8 +175,8 @@ block 1600 in 1622 ms, 30000000000 coins remain, defs: 61661 IndexedSeq(newEmissionBox, minerBox) ) } else { - val minerBox1 = new ErgoBoxCandidate(emissionBox.value - 1, minerProp, height, Colls.emptyColl, Map(register -> IntConstant(height))) - val minerBox2 = new ErgoBoxCandidate(1, minerProp, height, Colls.emptyColl, Map(register -> IntConstant(height))) + val minerBox1 = new ErgoBoxCandidate(emissionBox.value - 1, ErgoTree.fromSigmaBoolean(minerProp), height, Colls.emptyColl, Map(register -> IntConstant(height))) + val minerBox2 = new ErgoBoxCandidate(1, ErgoTree.fromSigmaBoolean(minerProp), height, Colls.emptyColl, Map(register -> IntConstant(height))) UnsignedErgoLikeTransaction( IndexedSeq(new UnsignedInput(emissionBox.id)), IndexedSeq(minerBox1, minerBox2) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala index 1230177437..9bcfd81ecf 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletAdvContractExampleSpecification.scala @@ -1,13 +1,14 @@ package sigmastate.utxo.examples -import org.ergoplatform.ErgoBox.{R6, R4, R5} +import org.ergoplatform.ErgoBox.{R4, R5, R6} import org.ergoplatform._ -import sigmastate.{AvlTreeData, CompilerCrossVersionProps} -import sigmastate.Values.{LongConstant, IntConstant} -import sigmastate.helpers.{ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter, CompilerTestingCommons, ContextEnrichingTestProvingInterpreter} +import sigma.data.AvlTreeData +import sigmastate.CompilerCrossVersionProps +import sigma.ast.{ErgoTree, IntConstant, LongConstant} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ class ColdWalletAdvContractExampleSpecification extends CompilerTestingCommons @@ -126,7 +127,7 @@ class ColdWalletAdvContractExampleSpecification extends CompilerTestingCommons R6 -> LongConstant(avbl2Key) // new avbl2Key (= old avbl2Key) ) ) - val firstWithdrawOutput1Key = testBox(firstWithdrawAmount1Key, carolPubKey, firstWithdrawHeight) + val firstWithdrawOutput1Key = testBox(firstWithdrawAmount1Key, ErgoTree.fromSigmaBoolean(carolPubKey), firstWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val firstWithdrawTx1Key = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(firstChangeOutput1Key, firstWithdrawOutput1Key)) @@ -163,7 +164,7 @@ class ColdWalletAdvContractExampleSpecification extends CompilerTestingCommons R6 -> LongConstant(avbl2Key - firstWithdrawAmount2Key) // new avbl2Key (= old avbl2Key) ) ) - val firstWithdrawOutput2Key = testBox(firstWithdrawAmount2Key, carolPubKey, firstWithdrawHeight) + val firstWithdrawOutput2Key = testBox(firstWithdrawAmount2Key, ErgoTree.fromSigmaBoolean(carolPubKey), firstWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val firstWithdrawTx2Key = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(firstChangeOutput2Key, firstWithdrawOutput2Key)) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala index ffd4b17018..1948bc16d9 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ColdWalletContractExampleSpecification.scala @@ -2,12 +2,13 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5} import org.ergoplatform._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, CompilerTestingCommons, ErgoLikeTestInterpreter} +import sigma.data.AvlTreeData +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.{AvlTreeData, CompilerCrossVersionProps} -import sigmastate.Values.{LongConstant, IntConstant} +import sigmastate.CompilerCrossVersionProps +import sigma.ast.{ErgoTree, IntConstant, LongConstant} import sigmastate.interpreter.Interpreter.ScriptNameProp -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ class ColdWalletContractExampleSpecification extends CompilerTestingCommons @@ -92,7 +93,7 @@ class ColdWalletContractExampleSpecification extends CompilerTestingCommons // Both Alice ane Bob withdraw val withdrawAmountFull = depositAmount // full amount is withdrawn - val withdrawOutputAliceAndBob = testBox(withdrawAmountFull, carolPubKey, firstWithdrawHeight) + val withdrawOutputAliceAndBob = testBox(withdrawAmountFull, ErgoTree.fromSigmaBoolean(carolPubKey), firstWithdrawHeight) val withdrawTxAliceAndBob = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(withdrawOutputAliceAndBob)) @@ -122,7 +123,7 @@ class ColdWalletContractExampleSpecification extends CompilerTestingCommons R5 -> LongConstant(min) // newMin (= old min) = 99000 ) ) - val firstWithdrawOutput = testBox(firstWithdrawAmount, carolPubKey, firstWithdrawHeight) + val firstWithdrawOutput = testBox(firstWithdrawAmount, ErgoTree.fromSigmaBoolean(carolPubKey), firstWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val firstWithdrawTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(firstChangeOutput, firstWithdrawOutput)) @@ -152,7 +153,7 @@ class ColdWalletContractExampleSpecification extends CompilerTestingCommons R5 -> LongConstant(min) // newMin (= old min) ) ) - val withdrawOutputInvalid = testBox(withdrawAmountInvalid, carolPubKey, firstWithdrawHeight) + val withdrawOutputInvalid = testBox(withdrawAmountInvalid, ErgoTree.fromSigmaBoolean(carolPubKey), firstWithdrawHeight) // normally this transaction would be invalid, but we're not checking it in this test val withdrawTxInvalid = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(changeOutputInvalid, withdrawOutputInvalid)) @@ -187,7 +188,7 @@ class ColdWalletContractExampleSpecification extends CompilerTestingCommons R5 -> LongConstant(secondMin) // newMin ) ) - val secondWithdrawOutput = testBox(secondWithdrawAmount, carolPubKey, secondWithdrawHeight) + val secondWithdrawOutput = testBox(secondWithdrawAmount, ErgoTree.fromSigmaBoolean(carolPubKey), secondWithdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val secondWithdrawTx = ErgoLikeTransaction(IndexedSeq(), IndexedSeq(secondChangeOutput, secondWithdrawOutput)) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala index 44b69db572..d197845e80 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/CoopExampleSpecification.scala @@ -1,15 +1,17 @@ package sigmastate.utxo.examples -import org.ergoplatform.{ErgoLikeContext, ErgoLikeTransaction, ErgoBox} +import org.ergoplatform.{ErgoBox, ErgoLikeContext, ErgoLikeTransaction} import org.scalatest.Assertion import org.scalatest.TryValues._ -import sigmastate.crypto.DLogProtocol.{ProveDlog, DLogProverInput} import scorex.crypto.hash.Blake2b256 -import sigmastate.Values.{ByteArrayConstant, ErgoTree, BooleanConstant} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, CompilerTestingCommons, ErgoLikeTestInterpreter} +import sigma.data.{AvlTreeData, ProveDlog} +import sigma.ast.ErgoTree.ZeroHeader +import sigma.ast.{BooleanConstant, ByteArrayConstant, ErgoTree} +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.helpers.TestingHelpers._ -import sigmastate.lang.Terms._ -import sigmastate.{AvlTreeData, CompilerCrossVersionProps} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} +import sigma.ast.syntax._ +import sigmastate.CompilerCrossVersionProps class CoopExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -65,7 +67,7 @@ class CoopExampleSpecification extends CompilerTestingCommons def pkWithTree(in: DLogProverInput): (ProveDlog, ErgoTree) = { val pk = in.publicImage - val tree = ErgoTree.fromSigmaBoolean(ErgoTree.headerWithVersion(0), pk) + val tree = ErgoTree.fromSigmaBoolean(ErgoTree.headerWithVersion(ZeroHeader, 0), pk) (pk, tree) } diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala index 92f6e6c4cb..9246cc1c8e 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/DHTupleExampleSpecification.scala @@ -2,17 +2,17 @@ package sigmastate.utxo.examples import java.math.BigInteger - import org.ergoplatform.ErgoBox.{R4, R5} -import sigmastate.{AvlTreeData, CompilerCrossVersionProps} -import sigmastate.Values.GroupElementConstant -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.{DiffieHellmanTupleProverInput, ProveDHTuple, CryptoConstants} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, CompilerTestingCommons, ErgoLikeTestInterpreter} +import sigma.data.{AvlTreeData, ProveDHTuple, ProveDlog} +import sigma.util.Extensions.EcpOps +import sigmastate.CompilerCrossVersionProps +import sigma.ast.GroupElementConstant +import sigma.crypto.CryptoConstants +import sigmastate.crypto.DiffieHellmanTupleProverInput +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ -import sigmastate.lang.Terms._ -import sigmastate.eval.Extensions._ +import sigma.ast.syntax._ class DHTupleExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -31,7 +31,7 @@ class DHTupleExampleSpecification extends CompilerTestingCommons val g = dlogGroup.generator val alice = new ContextEnrichingTestProvingInterpreter - val alicePubKey:ProveDlog = alice.dlogSecrets.head.publicImage + val alicePubKey: ProveDlog = alice.dlogSecrets.head.publicImage val x:BigInteger = alice.dlogSecrets.head.w // x is Alice's private key diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala index 8eb5b3b04c..4ec6922c38 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/DemurrageExampleSpecification.scala @@ -2,12 +2,13 @@ package sigmastate.utxo.examples import sigmastate.interpreter.Interpreter._ import org.ergoplatform._ -import sigmastate.Values.ShortConstant +import sigma.data.AvlTreeData +import sigma.ast.ShortConstant import sigmastate._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.interpreter.ContextExtension -import sigmastate.lang.Terms._ +import sigma.interpreter.ContextExtension +import sigma.ast.syntax._ class DemurrageExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/DummyExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/DummyExamplesSpecification.scala index 754e3baa73..f10a54463a 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/DummyExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/DummyExamplesSpecification.scala @@ -3,9 +3,9 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} import scorex.crypto.hash +import sigma.ast.{ByteArrayConstant, CollectionConstant, SByte, SCollection} import sigmastate.helpers.CompilerTestingCommons -import sigma.{Box, Context, Coll} -import sigmastate.eval.Extensions +import sigma.{Box, Coll, Context} class DummyExamplesSpecification extends CompilerTestingCommons { suite => implicit lazy val IR = new TestingIRContext @@ -92,9 +92,9 @@ class DummyExamplesSpecification extends CompilerTestingCommons { suite => val res = in.runDsl( Map( - 0.toByte -> Extensions.toAnyValue(dummyPath), - 1.toByte -> Extensions.toAnyValue(dummyBytes), - 2.toByte -> Extensions.toAnyValue(dummyBytes) + 0.toByte -> CollectionConstant[SCollection[SByte.type]](dummyPath, SCollection(SByte)), + 1.toByte -> ByteArrayConstant(dummyBytes), + 2.toByte -> ByteArrayConstant(dummyBytes) ) ) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala index 5ea4ef7a05..b75b404aed 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ExecuteFromExamplesSpecification.scala @@ -4,7 +4,7 @@ import org.ergoplatform._ import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} import sigmastate.helpers.CompilerTestingCommons import sigma.Context -import sigmastate.eval.Extensions._ +import sigma.ast.ByteArrayConstant class ExecuteFromExamplesSpecification extends CompilerTestingCommons { suite => implicit lazy val IR = new TestingIRContext @@ -55,7 +55,7 @@ class ExecuteFromExamplesSpecification extends CompilerTestingCommons { suite => tx.outBox(20, contract.aliceSignature) val in = tx.inputs(0) - val vars = Map(1.toByte -> toAnyValue(alice.pubKey.propBytes)) + val vars = Map(1.toByte -> ByteArrayConstant(alice.pubKey.propBytes)) val res = in.runDsl(vars) res shouldBe alice.pubKey diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala index 77918e453a..8f4f3414a3 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/FsmExampleSpecification.scala @@ -6,16 +6,17 @@ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash import scorex.crypto.hash.{Blake2b256, Digest32} import sigma.Colls -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ +import sigma.ast.SCollection.SByteArray +import sigma.ast._ import sigmastate._ -import sigmastate.eval._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ +import sigma.data.{AvlTreeData, AvlTreeFlags} +import sigma.eval.Extensions.SigmaBooleanOps +import sigma.eval.SigmaDsl import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} -import sigmastate.serialization.ValueSerializer -import sigmastate.utxo._ +import sigma.serialization.ValueSerializer class FsmExampleSpecification extends CompilerTestingCommons @@ -48,10 +49,10 @@ class FsmExampleSpecification extends CompilerTestingCommons val prover = new ContextEnrichingTestProvingInterpreter - val script1 = prover.dlogSecrets.head.publicImage.toSigmaProp - val script2 = prover.dhSecrets.head.publicImage.toSigmaProp + val script1 = prover.dlogSecrets.head.publicImage.toSigmaPropValue + val script2 = prover.dhSecrets.head.publicImage.toSigmaPropValue val script3 = SigmaAnd(script1, script2) - val script4 = prover.dlogSecrets.tail.head.publicImage.toSigmaProp //a script to leave FSM + val script4 = prover.dlogSecrets.tail.head.publicImage.toSigmaPropValue //a script to leave FSM val script1Hash = hash.Blake2b256(ValueSerializer.serialize(script1)) val script2Hash = hash.Blake2b256(ValueSerializer.serialize(script2)) @@ -89,7 +90,7 @@ class FsmExampleSpecification extends CompilerTestingCommons val isMember = OptionIsDefined( IR.builder.mkMethodCall( OptionGet(ExtractRegisterAs[SAvlTree.type](Self, fsmDescRegister)), - SAvlTree.getMethod, + SAvlTreeMethods.getMethod, IndexedSeq(Append( ConcreteCollection.fromItems[SByte.type]( OptionGet(ExtractRegisterAs[SByte.type](Self, currentStateRegister)), diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala index 95899f0aa9..c930135bc2 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/IcoExample.scala @@ -9,19 +9,22 @@ import scorex.crypto.authds.avltree.batch._ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} import sigma.Colls -import sigmastate.Values._ +import sigma.ast.SByte +import sigma.data.{AvlTreeData, AvlTreeFlags, Digest32Coll} +import sigma.ast._ import sigmastate._ -import sigmastate.crypto.CryptoConstants -import sigmastate.eval.Extensions.ArrayOps +import sigma.Extensions.ArrayOps +import sigma.ast.syntax.{CollectionConstant, SigmaPropValue} +import sigma.crypto.CryptoConstants import sigmastate.eval._ import sigmastate.helpers.TestingHelpers._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestProvingInterpreter} import sigmastate.interpreter.Interpreter.ScriptNameProp import sigmastate.interpreter.Interpreter -import sigmastate.lang.Terms._ -import sigmastate.serialization.ErgoTreeSerializer -import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer -import sigmastate.utxo.ComplexityTableStat +import sigma.ast.syntax._ +import sigma.eval.SigmaDsl +import sigma.serialization.ErgoTreeSerializer +import sigma.serialization.ErgoTreeSerializer.DefaultSerializer import scala.util.Random @@ -550,10 +553,6 @@ class IcoExample extends CompilerTestingCommons printDebug("lookup proof size: " + lookupProof.length) } - property("ComplexityTableStat") { - printDebug(ComplexityTableStat.complexityTableString) - } - /** This is the last executed test suite, so this method is executed after all tests. * We output statistics of how PrecompiledScriptProcessor cache was used. */ override protected def afterAll(): Unit = { diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala index 22f7f1c7c9..ce4ed2114e 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/LetsSpecification.scala @@ -6,15 +6,17 @@ import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, Lookup} import scorex.crypto.hash.{Blake2b256, Digest32} import sigma.Colls -import sigmastate.{AvlTreeData, AvlTreeFlags, CompilerCrossVersionProps, TrivialProp} -import sigmastate.Values.{AvlTreeConstant, ByteArrayConstant, LongConstant, SigmaPropConstant} -import sigmastate.eval.Extensions.ArrayOps -import sigmastate.eval.{Digest32Coll, IRContext, SigmaDsl} +import sigma.data.{AvlTreeData, AvlTreeFlags, Digest32Coll, TrivialProp} +import sigmastate.CompilerCrossVersionProps +import sigma.ast.{AvlTreeConstant, ByteArrayConstant, LongConstant, SigmaPropConstant} +import sigma.Extensions.ArrayOps +import sigmastate.eval.IRContext import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestProvingInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp -import sigmastate.serialization.ErgoTreeSerializer -import sigmastate.lang.Terms._ +import sigma.serialization.ErgoTreeSerializer +import sigma.ast.syntax._ +import sigma.eval.SigmaDsl import scala.util.Random diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala index 54d2ec0241..368fe3d8ea 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/MASTExampleSpecification.scala @@ -4,17 +4,19 @@ import org.ergoplatform._ import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, Lookup} import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ +import sigma.ast.{SAvlTree, SBoolean, SLong} +import sigma.ast.SCollection.SByteArray +import sigma.data.{AvlTreeData, AvlTreeFlags} +import sigma.ast._ import sigmastate._ -import sigmastate.eval.Extensions.ArrayOps +import sigma.Extensions.ArrayOps +import sigma.ast.syntax.{GetVarByteArray, OptionValueOps} import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ +import sigma.eval.SigmaDsl import sigmastate.interpreter.Interpreter._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utxo._ -import sigmastate.eval._ +import sigma.serialization.ValueSerializer import scala.util.Random @@ -96,7 +98,7 @@ class MASTExampleSpecification extends CompilerTestingCommons val merklePathToScript = OptionIsDefined( IR.builder.mkMethodCall( ExtractRegisterAs[SAvlTree.type](Self, reg1).get, - SAvlTree.getMethod, + SAvlTreeMethods.getMethod, IndexedSeq( CalcBlake2b256(GetVarByteArray(scriptId).get), GetVarByteArray(proofId).get)).asOption[SByteArray] @@ -113,7 +115,7 @@ class MASTExampleSpecification extends CompilerTestingCommons lastBlockUtxoRoot = AvlTreeData.dummy, minerPubkey = ErgoLikeContextTesting.dummyPubkey, boxesToSpend = IndexedSeq(selfBox), - createTransaction(testBox(1, recipientProposition, 0)), + createTransaction(testBox(1, ErgoTree.fromSigmaBoolean(recipientProposition), 0)), self = selfBox, activatedVersionInTests) avlProver.performOneOperation(Lookup(knownSecretTreeKey)) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala index 6bbf410dc1..fc19a485ee 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/MixExampleSpecification.scala @@ -1,19 +1,20 @@ package sigmastate.utxo.examples import java.math.BigInteger - import org.ergoplatform.ErgoBox.{R4, R5} import scorex.crypto.hash.Blake2b256 -import sigmastate.{AvlTreeData, CompilerCrossVersionProps} -import sigmastate.Values.GroupElementConstant -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.{DiffieHellmanTupleProverInput, ProveDHTuple, CryptoConstants} -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, CompilerTestingCommons, ErgoLikeTestInterpreter} +import sigma.data.{AvlTreeData, ProveDHTuple, ProveDlog} +import sigma.util.Extensions.EcpOps +import sigmastate.CompilerCrossVersionProps +import sigma.ast.{ErgoTree, GroupElementConstant} +import sigma.ast.syntax.GroupElementConstant +import sigma.crypto.CryptoConstants +import sigmastate.crypto.DiffieHellmanTupleProverInput +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ -import sigmastate.lang.Terms._ -import sigmastate.eval._ -import sigmastate.eval.Extensions._ +import sigma.ast.syntax._ +import sigma.eval.SigmaDsl class MixExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -185,7 +186,7 @@ class MixExampleSpecification extends CompilerTestingCommons val carolPubKey: ProveDlog = carol.dlogSecrets.head.publicImage val spendHeight = 90 - val carolOutput = testBox(mixAmount, carolPubKey, spendHeight) + val carolOutput = testBox(mixAmount, ErgoTree.fromSigmaBoolean(carolPubKey), spendHeight) // normally this transaction would be invalid, but we're not checking it in this test val spendingTx = createTransaction(carolOutput) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleDataInputsExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleDataInputsExamplesSpecification.scala index 564385fd96..3207245fbb 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleDataInputsExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleDataInputsExamplesSpecification.scala @@ -4,7 +4,7 @@ import org.ergoplatform._ import org.ergoplatform.dsl.ContractSyntax.Token import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} import scorex.crypto.hash.Blake2b256 -import sigmastate.eval.Digest32Coll +import sigma.data.Digest32Coll import sigmastate.helpers.CompilerTestingCommons import sigma.Coll import sigma.Context diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala index 8566ce437e..9d0ffc880b 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleExamplesSpecification.scala @@ -5,21 +5,21 @@ import org.ergoplatform.ErgoBox.RegisterId import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert, Lookup} import scorex.crypto.authds.{ADKey, ADValue} import scorex.crypto.hash.{Blake2b256, Digest32} -import sigmastate.SCollection.SByteArray -import sigmastate.Values._ +import sigma.ast.SCollection.SByteArray +import sigma.ast._ import sigmastate._ import sigmastate.eval._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import org.ergoplatform._ import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} -import sigmastate.crypto.CryptoConstants -import sigmastate.crypto.{BigIntegers, CryptoFacade} -import sigmastate.eval.Extensions.ArrayOps +import sigma.Extensions.ArrayOps import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} -import sigmastate.utxo._ import sigma.Context +import sigma.ast.syntax._ +import sigma.crypto.{BigIntegers, CryptoConstants, CryptoFacade} +import sigma.data.{AvlTreeData, AvlTreeFlags} import sigmastate.utils.Helpers._ class OracleExamplesSpecification extends CompilerTestingCommons @@ -103,7 +103,7 @@ class OracleExamplesSpecification extends CompilerTestingCommons val oracleBox = testBox( value = 1L, - ergoTree = oraclePubKey, + ergoTree = ErgoTree.fromSigmaBoolean(oraclePubKey), creationHeight = 0, additionalRegisters = Map( reg1 -> LongConstant(temperature), @@ -135,7 +135,7 @@ class OracleExamplesSpecification extends CompilerTestingCommons val oracleProp = SigmaAnd( OptionIsDefined(IR.builder.mkMethodCall( - LastBlockUtxoRootHash, SAvlTree.getMethod, + LastBlockUtxoRootHash, SAvlTreeMethods.getMethod, IndexedSeq(ExtractId(GetVarBox(22: Byte).get), GetVarByteArray(23: Byte).get)).asOption[SByteArray]), EQ(extract[SByteArray](ErgoBox.ScriptRegId), ByteArrayConstant(ErgoTree.fromSigmaBoolean(oraclePubKey).bytes)), EQ(Exponentiate(GroupGenerator, extract[SBigInt.type](reg3)), @@ -156,7 +156,7 @@ class OracleExamplesSpecification extends CompilerTestingCommons avlProver.performOneOperation(Lookup(ADKey @@@ oracleBox.id)) val proof = avlProver.generateProof() - val newBox1 = testBox(20, alicePubKey, 0, boxIndex = 2) + val newBox1 = testBox(20, ErgoTree.fromSigmaBoolean(alicePubKey), 0, boxIndex = 2) val newBoxes = IndexedSeq(newBox1) val spendingTransaction = createTransaction(newBoxes) @@ -250,8 +250,8 @@ class OracleExamplesSpecification extends CompilerTestingCommons ) val sOracle = oracleBox - val sAlice = testBox(10, prop, 0, Seq(), Map()) - val sBob = testBox(10, prop, 0, Seq(), Map()) + val sAlice = testBox(10, ErgoTree.fromProposition(prop), 0, Seq(), Map()) + val sBob = testBox(10, ErgoTree.fromProposition(prop), 0, Seq(), Map()) val newBox1 = testBox(20, mkTestErgoTree(alicePubKey), 0) val newBoxes = IndexedSeq(newBox1) @@ -265,8 +265,10 @@ class OracleExamplesSpecification extends CompilerTestingCommons spendingTransaction, self = sOracle, activatedVersionInTests) - val prA = alice.prove(emptyEnv + (ScriptNameProp -> "alice_prove"), prop, ctx, fakeMessage).get - verifier.verify(emptyEnv + (ScriptNameProp -> "verify"), prop, ctx, prA, fakeMessage).get._1 shouldBe true + val prA = alice.prove(emptyEnv + (ScriptNameProp -> "alice_prove"), + mkTestErgoTree(prop), ctx, fakeMessage).get + verifier.verify(emptyEnv + (ScriptNameProp -> "verify"), + mkTestErgoTree(prop), ctx, prA, fakeMessage).get._1 shouldBe true } case class OracleContract[Spec <: ContractSpec] diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleTokenExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleTokenExamplesSpecification.scala index 5a30cd6689..e14f47f1c7 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleTokenExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/OracleTokenExamplesSpecification.scala @@ -4,7 +4,7 @@ import org.ergoplatform._ import org.ergoplatform.dsl.ContractSyntax.Token import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} import scorex.crypto.hash.Blake2b256 -import sigmastate.eval.Digest32Coll +import sigma.data.Digest32Coll import sigmastate.helpers.CompilerTestingCommons import sigma.Coll import sigma.Context diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala index 1f00dbfc3c..c695b8e07d 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/RPSGameExampleSpecification.scala @@ -4,13 +4,13 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5, R6, R7} import scorex.crypto.hash.Blake2b256 import scorex.utils.Random -import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, SigmaPropConstant} +import sigma.data.{AvlTreeData, ProveDlog} +import sigma.ast.{ByteArrayConstant, ByteConstant, ErgoTree, IntConstant, SigmaPropConstant} import sigmastate._ -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} import sigmastate.helpers.TestingHelpers._ +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.interpreter.Interpreter._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ class RPSGameExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -184,7 +184,7 @@ class RPSGameExampleSpecification extends CompilerTestingCommons val carolPubKey:ProveDlog = carol.dlogSecrets.head.publicImage // note that playAmount below is not checked. It could be anything. - val gameOverOutput = testBox(playAmount, carolPubKey, gameOverHeight) + val gameOverOutput = testBox(playAmount, ErgoTree.fromSigmaBoolean(carolPubKey), gameOverHeight) // normally this transaction would be invalid, but we're not checking it in this test val gameOverTx = createTransaction(gameOverOutput) @@ -273,7 +273,7 @@ class RPSGameExampleSpecification extends CompilerTestingCommons // assume Bob is paying to Carol // note that playAmount*2 below is not checked. It could be anything. - val defaultWinOutput = testBox(playAmount*2, carolPubKey, defaultWinHeight) + val defaultWinOutput = testBox(playAmount*2, ErgoTree.fromSigmaBoolean(carolPubKey), defaultWinHeight) //normally this transaction would invalid (why?), but we're not checking it in this test val defaultWinTx = createTransaction(defaultWinOutput) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/RevenueSharingExamplesSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/RevenueSharingExamplesSpecification.scala index 41d3b59039..39326bb01c 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/RevenueSharingExamplesSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/RevenueSharingExamplesSpecification.scala @@ -1,9 +1,9 @@ package sigmastate.utxo.examples -import org.ergoplatform.dsl.{SigmaContractSyntax, ContractSpec, TestContractSpec, StdContracts} -import sigmastate.eval.Extensions +import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, StdContracts, TestContractSpec} import sigmastate.helpers.CompilerTestingCommons import sigma.Context +import sigma.ast.IntConstant class RevenueSharingExamplesSpecification extends CompilerTestingCommons { suite => implicit lazy val IR = new TestingIRContext @@ -121,7 +121,7 @@ class RevenueSharingExamplesSpecification extends CompilerTestingCommons { suite val in = tx.inputs(0) - val res = in.runDsl(Map(1.toByte -> Extensions.toAnyValue(1))) + val res = in.runDsl(Map(1.toByte -> IntConstant(1))) val pr = alice.prove(in).get contract.verifier.verify(in, pr) shouldBe true diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala index f68c97079b..4294dc5b4b 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/ReversibleTxExampleSpecification.scala @@ -3,12 +3,13 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5} import org.ergoplatform._ import scorex.crypto.hash.Blake2b256 -import sigmastate.Values.{IntConstant, SigmaPropConstant} +import sigma.data.AvlTreeData +import sigma.ast.{ErgoTree, IntConstant, SigmaPropConstant} import sigmastate._ -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ class ReversibleTxExampleSpecification extends CompilerTestingCommons @@ -174,7 +175,7 @@ class ReversibleTxExampleSpecification extends CompilerTestingCommons val bobSpendAmount = 10 val bobSpendHeight = bobDeadline+1 - val bobSpendOutput = testBox(bobSpendAmount, davePubKey, bobSpendHeight) + val bobSpendOutput = testBox(bobSpendAmount, ErgoTree.fromSigmaBoolean(davePubKey), bobSpendHeight) //normally this transaction would be invalid (why?), but we're not checking it in this test val bobSpendTx = createTransaction(bobSpendOutput) @@ -201,7 +202,7 @@ class ReversibleTxExampleSpecification extends CompilerTestingCommons val carolSpendHeight = bobDeadline - 1 // Carol sends to Dave - val carolSpendOutput = testBox(carolSpendAmount, davePubKey, carolSpendHeight) + val carolSpendOutput = testBox(carolSpendAmount, ErgoTree.fromSigmaBoolean(davePubKey), carolSpendHeight) //normally this transaction would be invalid (why?), but we're not checking it in this test val carolSpendTx = createTransaction(carolSpendOutput) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala index 00b25ba8e1..a8c58bcc40 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/Rule110Specification.scala @@ -4,15 +4,15 @@ import org.ergoplatform._ import scorex.crypto.hash.Blake2b256 import scorex.util._ import sigma.Colls -import sigmastate.Values._ +import sigma.ast.syntax.{BooleanConstant, GetVarByteArray, OptionValueOps} +import sigma.ast._ +import sigma.data.AvlTreeData import sigmastate._ -import sigmastate.eval._ -import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ -import sigmastate.interpreter.ContextExtension -import sigmastate.lang.Terms._ -import sigmastate.serialization.ValueSerializer -import sigmastate.utxo._ +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} +import sigma.ast.syntax._ +import sigma.interpreter.ContextExtension +import sigma.serialization.ValueSerializer import sigmastate.utxo.blockchain.BlockchainSimulationTestingCommons._ /** diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala index 04390272e8..2657a49eb0 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/TimedPaymentExampleSpecification.scala @@ -1,13 +1,14 @@ package sigmastate.utxo.examples import org.ergoplatform._ -import sigmastate.Values.IntConstant +import sigma.ast.{ErgoTree, IntConstant} +import sigma.data.AvlTreeData import sigmastate._ -import sigmastate.exceptions.InterpreterException -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, CompilerTestingCommons, ErgoLikeTestInterpreter} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter.ScriptNameProp -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ +import sigma.exceptions.InterpreterException class TimedPaymentExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { @@ -53,7 +54,7 @@ class TimedPaymentExampleSpecification extends CompilerTestingCommons val withdrawHeight = 100 val confDeadline = 110 - val timedWithdrawOutput = testBox(withdrawAmount, bobPubKey, withdrawHeight) + val timedWithdrawOutput = testBox(withdrawAmount, ErgoTree.fromSigmaBoolean(bobPubKey), withdrawHeight) //normally this transaction would be invalid, but we're not checking it in this test val withdrawTx = createTransaction(IndexedSeq(timedWithdrawOutput)) diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala index ecaa4c0a14..50348ce7ab 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/TrustlessLETS.scala @@ -5,7 +5,7 @@ import scorex.crypto.hash.Blake2b256 import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, CompilerTestingCommons} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ class TrustlessLETS1 extends CompilerTestingCommons { private implicit lazy val IR: TestingIRContext = new TestingIRContext diff --git a/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala index 436115e9c2..b3d6d4eff8 100644 --- a/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/utxo/examples/XorGameExampleSpecification.scala @@ -4,13 +4,13 @@ package sigmastate.utxo.examples import org.ergoplatform.ErgoBox.{R4, R5, R6} import scorex.crypto.hash.Blake2b256 import scorex.utils.Random -import sigmastate.Values.{ByteArrayConstant, ByteConstant, IntConstant, SigmaPropConstant} +import sigma.data.{AvlTreeData, ProveDlog} +import sigma.ast.{ByteArrayConstant, ByteConstant, IntConstant, SigmaPropConstant} import sigmastate._ -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, CompilerTestingCommons} +import sigmastate.helpers.{CompilerTestingCommons, ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter} import sigmastate.helpers.TestingHelpers._ import sigmastate.interpreter.Interpreter._ -import sigmastate.lang.Terms._ +import sigma.ast.syntax._ class XorGameExampleSpecification extends CompilerTestingCommons with CompilerCrossVersionProps { diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/AvlTree.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/AvlTree.scala deleted file mode 100644 index f1cf64377c..0000000000 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/AvlTree.scala +++ /dev/null @@ -1,16 +0,0 @@ -package org.ergoplatform.sdk.js - -import scala.scalajs.js -import scala.scalajs.js.UndefOr -import scala.scalajs.js.annotation.JSExportTopLevel - -/** Equivalent of [[sigma.AvlTree]] available from JS. */ -@JSExportTopLevel("AvlTree") -class AvlTree( - val digest: String, - val insertAllowed: Boolean, - val updateAllowed: Boolean, - val removeAllowed: Boolean, - val keyLength: Int, - val valueLengthOpt: UndefOr[Int] -) extends js.Object diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala new file mode 100644 index 0000000000..7b3d1dd19a --- /dev/null +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala @@ -0,0 +1,155 @@ +package org.ergoplatform.sdk.js + +import org.ergoplatform.sdk +import org.ergoplatform.sdk.js.ContractTemplate.isoToSdk +import org.scalablytyped.runtime.StringDictionary +import sigma.ast.Constant +import sigma.ast.js.{ErgoTree, Expr} +import sigma.ast.syntax.SigmaPropValue +import sigma.data.Iso +import sigma.js.Type +import sigma.js.Isos._ + +import scala.scalajs.js +import scala.scalajs.js.UndefOr +import scala.scalajs.js.annotation.JSExportTopLevel + +/** + * Represents a ContractTemplate parameter. + */ +@JSExportTopLevel("Parameter") +class Parameter( + /** User readable parameter name (string bytes in UTF-8 encoding) */ + val name: String, + /** User readable parameter description (string bytes in UTF-8 encoding) */ + val description: String, + /** Index in the ErgoTree.constants array */ + val constantIndex: Int +) extends js.Object + +@JSExportTopLevel("Parameter$") +object Parameter extends js.Object { + implicit val isoToSdk: sigma.data.Iso[Parameter, sdk.Parameter] = + new sigma.data.Iso[Parameter, sdk.Parameter] { + override def to(p: Parameter): sdk.Parameter = + sdk.Parameter(p.name, p.description, p.constantIndex) + + override def from(p: sdk.Parameter): Parameter = + new Parameter(p.name, p.description, p.constantIndex) + } +} + + +/** Represents a reusable ContractTemplate with support to generate ErgoTree based on provided parameters. + * + * @param treeVersion the optional version of ErgoTree which should be used. If this value is not provided here then + * it must be provided while generating the `ErgoTree` by calling `applyTemplate`. + * @param name user readable name (non-empty string bytes in UTF-8 encoding) + * @param description user readable contract description (string bytes in UTF-8 encoding) + * @param constTypes list denoting the type of ConstantPlaceholders in the expressionTree + * @param constValues optional list of optional default values for the ConstantPlaceholders in the expressionTree. + * If an entry in the sequence is None, it must have a corresponding entry in parameters and its + * value must be provided while generating the `ErgoTree` by calling `applyTemplate`. If all the + * entries are None, the whole `constValues` field can be set to None. + * @param parameters typed template parameters of the contract template. It must have an entry for each + * `ConstantPlaceholder` which has a `None` in the `constValues` field. Other fields which do have + * a value defined in `constValues` can also be allowed to be optionally overridden by accepting + * it in `parameters`. + * @param expressionTree root of the contract which is a valid expression of `SigmaProp` type. Must have constants + * segregated into `constTypes` and optionally `constValues` + */ +@JSExportTopLevel("ContractTemplate") +class ContractTemplate( + val treeVersion: UndefOr[Byte], + val name: String, + val description: String, + val constTypes: js.Array[sigma.js.Type], + val constValues: UndefOr[js.Array[UndefOr[sigma.js.Value]]], + val parameters: js.Array[Parameter], + val expressionTree: Expr +) extends js.Object { + /** + * Generate the ErgoTree from the template by providing the values for parameters. + * + * @param version the version of the `ErgoTree` to use. Must be provided if the `treeVersion` was not provided in the + * template. + * @param paramValues the name-value map for the parameters accepted by the `ContractTemplate`. Must contain an entry + * for each parameter for which no default value was provided in the template. Optionally, can also + * provide values to override for parameters which do have a default value defined in the template. + * The type of the provided value must match with the corresponding entry in the `constTypes` + * provided in the template. + * @return `ErgoTree` generated by replacing the template parameters with the value provided in `paramValues`. + */ + def applyTemplate( + version: UndefOr[Byte], + paramValues: StringDictionary[sigma.js.Value]): sigma.ast.js.ErgoTree = { + val params = StringDictionary + .wrapStringDictionary(paramValues) + .view.mapValues(v => sigma.ast.js.isoValueToConstant.to(v)) + .toMap + val tree = isoToSdk.to(this).applyTemplate( + version = isoUndefOr(Iso.identityIso[Byte]).to(version), + paramValues = params + ) + new ErgoTree(tree) + } + + /** @return JSON representation of this contract template pretty-printed to a string + * indentation of two spaces. + */ + def toJsonString(): String = { + val template = ContractTemplate.isoToSdk.to(this) + template.toJsonString + } +} + +@JSExportTopLevel("ContractTemplate$") +object ContractTemplate extends js.Object { + + /** Create a new contract template from a JSON string. + * + * @param json JSON string representing a contract template. + * @return a new contract template. + */ + def fromJsonString(json: String): ContractTemplate = { + io.circe.parser.parse(json) match { + case Left(err) => throw err + case Right(json) => + val ct = sdk.ContractTemplate.jsonEncoder.decoder(json.hcursor).toOption.get + ContractTemplate.isoToSdk.from(ct) + } + } + + private val constsIso = isoUndefOr(isoArrayToIndexed(isoUndefOr(sigma.ast.js.isoValueToConstant))) + + implicit val isoToSdk: sigma.data.Iso[ContractTemplate, sdk.ContractTemplate] = + new sigma.data.Iso[ContractTemplate, sdk.ContractTemplate] { + override def to(ct: ContractTemplate): sdk.ContractTemplate = { + new sdk.ContractTemplate( + isoUndefOr(Iso.identityIso[Byte]).to(ct.treeVersion), + ct.name, + ct.description, + isoArrayToIndexed(Type.isoToSType).to(ct.constTypes), + constsIso.to(ct.constValues).map(_.map(costOpt => costOpt.map(_.value))), + isoArrayToIndexed(Parameter.isoToSdk).to(ct.parameters), + ct.expressionTree.wrappedValue.asInstanceOf[SigmaPropValue] + ) + } + + override def from(ct: sdk.ContractTemplate): ContractTemplate = { + // optionally, match each value with its type + val constants = ct.constValues.map(values => values.zip(ct.constTypes).map { + case (value, tpe) => value.map(v => Constant(v, tpe)) + }) + new ContractTemplate( + isoUndefOr(Iso.identityIso[Byte]).from(ct.treeVersion), + ct.name, + ct.description, + isoArrayToIndexed(Type.isoToSType).from(ct.constTypes), + constsIso.from(constants), + isoArrayToIndexed(Parameter.isoToSdk).from(ct.parameters), + new Expr(ct.expressionTree) + ) + } + } +} \ No newline at end of file diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala index 4d10c9f3dd..ef53e13dbd 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala @@ -1,5 +1,7 @@ package org.ergoplatform.sdk.js +import sigma.js.{AvlTree, GroupElement} + import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala index b2ffd664e8..f6393f62bb 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala @@ -1,60 +1,37 @@ package org.ergoplatform.sdk.js import org.ergoplatform.ErgoBox._ +import org.ergoplatform._ +import org.ergoplatform.sdk.ExtendedInputBox import org.ergoplatform.sdk.JavaHelpers.UniversalConverter -import org.ergoplatform.sdk.{Iso, ExtendedInputBox} import org.ergoplatform.sdk.wallet.protocol.context -import org.ergoplatform._ -import sigma.data.RType import scorex.crypto.authds.ADKey import scorex.util.ModifierId import scorex.util.encode.Base16 -import sigmastate.Values.{Constant, GroupElementConstant} -import sigmastate.eval.Extensions.ArrayOps -import sigmastate.eval.{CBigInt, Digest32Coll, Evaluation, CAvlTree, CGroupElement, CPreHeader, CHeader} +import sigma.Extensions.CollBytesOps +import sigma.ast.syntax.GroupElementConstant +import sigma.ast.{Constant, GroupElementConstant, SType} +import sigma.data.Iso.{isoStringToArray, isoStringToColl} +import sigma.data.{CBigInt, CGroupElement, Digest32Coll, Digest32CollRType, Iso} +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.js.{AvlTree, GroupElement} +import sigma.serialization.{ErgoTreeSerializer, ValueSerializer} +import sigma.{Coll, Colls} +import sigmastate.eval.{CHeader, CPreHeader} import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.HexString import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters import sigmastate.fleetSdkCommon.distEsmTypesTokenMod.TokenAmount -import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.{UnsignedTransaction, SignedTransaction} -import sigmastate.fleetSdkCommon.{distEsmTypesProverResultMod => proverResultMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod} -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.serialization.{ErgoTreeSerializer, ValueSerializer} -import sigmastate.{AvlTreeData, AvlTreeFlags, SType} -import sigma.{Coll, Colls} -import sigma.Extensions.CollBytesOps +import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.{SignedTransaction, UnsignedTransaction} +import sigmastate.fleetSdkCommon.{distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesProverResultMod => proverResultMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod} import java.math.BigInteger import scala.collection.immutable.ListMap -import scala.reflect.ClassTag import scala.scalajs.js -import scala.scalajs.js.JSConverters.JSRichOption import scala.scalajs.js.Object /** Definitions of isomorphisms. */ object Isos { - /** Conversion between `Value` and `Constant[SType]`. */ - implicit val isoValueToConstant: Iso[Value, Constant[SType]] = new Iso[Value, Constant[SType]] { - override def to(x: Value): Constant[SType] = - Constant(x.runtimeData.asInstanceOf[SType#WrappedType], Evaluation.rtypeToSType(x.tpe.rtype)) - - override def from(x: Constant[SType]): Value = { - val rtype = Evaluation.stypeToRType(x.tpe) - val jsvalue = Value.fromRuntimeData(x.value, rtype) - new Value(jsvalue, new Type(rtype)) - } - } - - val isoStringToArray: Iso[String, Array[Byte]] = new Iso[String, Array[Byte]] { - override def to(x: String): Array[Byte] = Base16.decode(x).get - override def from(x: Array[Byte]): String = Base16.encode(x) - } - - val isoStringToColl: Iso[String, Coll[Byte]] = new Iso[String, Coll[Byte]] { - override def to(x: String): Coll[Byte] = Colls.fromArray(Base16.decode(x).get) - override def from(x: Coll[Byte]): String = x.toHex - } - val isoStringToGroupElement: Iso[String, sigma.GroupElement] = new Iso[String, sigma.GroupElement] { override def to(x: String): sigma.GroupElement = { val bytes = Base16.decode(x).get @@ -93,31 +70,6 @@ object Isos { } } - implicit val isoAvlTree: Iso[AvlTree, sigma.AvlTree] = new Iso[AvlTree, sigma.AvlTree] { - override def to(x: AvlTree): sigma.AvlTree = { - CAvlTree( - AvlTreeData( - digest = isoStringToArray.to(x.digest).toColl, - treeFlags = AvlTreeFlags(x.insertAllowed, x.updateAllowed, x.removeAllowed), - x.keyLength, - valueLengthOpt = isoUndefOr(Iso.identityIso[Int]).to(x.valueLengthOpt), - ), - ) - } - override def from(x: sigma.AvlTree): AvlTree = { - val tree = x.asInstanceOf[CAvlTree] - val data = tree.treeData - new AvlTree( - digest = isoStringToColl.from(tree.digest), - insertAllowed = data.treeFlags.insertAllowed, - updateAllowed = data.treeFlags.updateAllowed, - removeAllowed = data.treeFlags.removeAllowed, - keyLength = data.keyLength, - valueLengthOpt = isoUndefOr(Iso.identityIso[Int]).from(data.valueLengthOpt), - ) - } - } - implicit val isoHeader: Iso[Header, sigma.Header] = new Iso[Header, sigma.Header] { override def to(a: Header): sigma.Header = { CHeader( @@ -125,16 +77,16 @@ object Isos { version = a.version, parentId = isoStringToColl.to(a.parentId), ADProofsRoot = isoStringToColl.to(a.ADProofsRoot), - stateRoot = isoAvlTree.to(a.stateRoot), + stateRoot = AvlTree.isoAvlTree.to(a.stateRoot), transactionsRoot = isoStringToColl.to(a.transactionsRoot), - timestamp = isoBigIntToLong.to(a.timestamp), - nBits = isoBigIntToLong.to(a.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.to(a.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.to(a.nBits), height = a.height, extensionRoot = isoStringToColl.to(a.extensionRoot), minerPk = isoGroupElement.to(a.minerPk), powOnetimePk = isoGroupElement.to(a.powOnetimePk), powNonce = isoStringToColl.to(a.powNonce), - powDistance = isoBigInt.to(a.powDistance), + powDistance = sigma.js.Isos.isoBigInt.to(a.powDistance), votes = isoStringToColl.to(a.votes) ) } @@ -145,16 +97,16 @@ object Isos { version = header.version, parentId = isoStringToColl.from(header.parentId), ADProofsRoot = isoStringToColl.from(header.ADProofsRoot), - stateRoot = isoAvlTree.from(header.stateRoot), + stateRoot = AvlTree.isoAvlTree.from(header.stateRoot), transactionsRoot = isoStringToColl.from(header.transactionsRoot), - timestamp = isoBigIntToLong.from(header.timestamp), - nBits = isoBigIntToLong.from(header.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.from(header.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.from(header.nBits), height = header.height, extensionRoot = isoStringToColl.from(header.extensionRoot), minerPk = isoGroupElement.from(header.minerPk), powOnetimePk = isoGroupElement.from(header.powOnetimePk), powNonce = isoStringToColl.from(header.powNonce), - powDistance = isoBigInt.from(header.powDistance), + powDistance = sigma.js.Isos.isoBigInt.from(header.powDistance), votes = isoStringToColl.from(header.votes) ) } @@ -165,8 +117,8 @@ object Isos { CPreHeader( version = a.version, parentId = isoStringToColl.to(a.parentId), - timestamp = isoBigIntToLong.to(a.timestamp), - nBits = isoBigIntToLong.to(a.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.to(a.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.to(a.nBits), height = a.height, minerPk = isoGroupElement.to(a.minerPk), votes = isoStringToColl.to(a.votes) @@ -177,8 +129,8 @@ object Isos { new PreHeader( version = header.version, parentId = isoStringToColl.from(header.parentId), - timestamp = isoBigIntToLong.from(header.timestamp), - nBits = isoBigIntToLong.from(header.nBits), + timestamp = sigma.js.Isos.isoBigIntToLong.from(header.timestamp), + nBits = sigma.js.Isos.isoBigIntToLong.from(header.nBits), height = header.height, minerPk = isoGroupElement.from(header.minerPk), votes = isoStringToColl.from(header.votes) @@ -197,8 +149,8 @@ object Isos { dataInputCost = a.dataInputCost, outputCost = a.outputCost, maxBlockCost = a.maxBlockCost, - softForkStartingHeight = Isos.isoUndefOr[Int, Int](Iso.identityIso).to(a.softForkStartingHeight), - softForkVotesCollected = Isos.isoUndefOr[Int, Int](Iso.identityIso).to(a.softForkVotesCollected), + softForkStartingHeight = sigma.js.Isos.isoUndefOr[Int, Int](Iso.identityIso).to(a.softForkStartingHeight), + softForkVotesCollected = sigma.js.Isos.isoUndefOr[Int, Int](Iso.identityIso).to(a.softForkVotesCollected), blockVersion = a.blockVersion ) } @@ -212,8 +164,8 @@ object Isos { dataInputCost = b.dataInputCost, outputCost = b.outputCost, maxBlockCost = b.maxBlockCost, - softForkStartingHeight = Isos.isoUndefOr[Int, Int](Iso.identityIso).from(b.softForkStartingHeight), - softForkVotesCollected = Isos.isoUndefOr[Int, Int](Iso.identityIso).from(b.softForkVotesCollected), + softForkStartingHeight = sigma.js.Isos.isoUndefOr[Int, Int](Iso.identityIso).from(b.softForkStartingHeight), + softForkVotesCollected = sigma.js.Isos.isoUndefOr[Int, Int](Iso.identityIso).from(b.softForkVotesCollected), blockVersion = b.blockVersion ) } @@ -222,7 +174,7 @@ object Isos { implicit val isoBlockchainStateContext: Iso[BlockchainStateContext, context.BlockchainStateContext] = new Iso[BlockchainStateContext, context.BlockchainStateContext] { override def to(a: BlockchainStateContext): context.BlockchainStateContext = { context.CBlockchainStateContext( - sigmaLastHeaders = isoArrayToColl(isoHeader).to(a.sigmaLastHeaders), + sigmaLastHeaders = sigma.js.Isos.isoArrayToColl(isoHeader).to(a.sigmaLastHeaders), previousStateDigest = isoStringToColl.to(a.previousStateDigest), sigmaPreHeader = isoPreHeader.to(a.sigmaPreHeader) ) @@ -230,7 +182,7 @@ object Isos { override def from(b: context.BlockchainStateContext): BlockchainStateContext = { new BlockchainStateContext( - sigmaLastHeaders = isoArrayToColl(isoHeader).from(b.sigmaLastHeaders), + sigmaLastHeaders = sigma.js.Isos.isoArrayToColl(isoHeader).from(b.sigmaLastHeaders), previousStateDigest = isoStringToColl.from(b.previousStateDigest), sigmaPreHeader = isoPreHeader.from(b.sigmaPreHeader) ) @@ -295,22 +247,6 @@ object Isos { override def from(x: DataInput): inputsMod.DataInput = inputsMod.DataInput(x.boxId.convertTo[boxesMod.BoxId]) } - implicit val isoBigInt: Iso[js.BigInt, sigma.BigInt] = new Iso[js.BigInt, sigma.BigInt] { - override def to(x: js.BigInt): sigma.BigInt = { - CBigInt(new BigInteger(x.toString(10))) - } - override def from(x: sigma.BigInt): js.BigInt = { - val bi = x.asInstanceOf[CBigInt].wrappedValue - val s = bi.toString(10) - js.BigInt(s) - } - } - - implicit val isoBigIntToLong: Iso[js.BigInt, Long] = new Iso[js.BigInt, Long] { - override def to(x: js.BigInt): Long = java.lang.Long.parseLong(x.toString(10)) - override def from(x: Long): js.BigInt = js.BigInt(x.toString) - } - implicit val isoAmount: Iso[commonMod.Amount, Long] = new Iso[commonMod.Amount, Long] { override def to(x: commonMod.Amount): Long = x.asInstanceOf[Any] match { case s: String => BigInt(s).toLong @@ -328,28 +264,13 @@ object Isos { tokenMod.TokenAmount[commonMod.Amount](isoAmount.from(x._2), x._1.toHex) } - implicit def isoUndefOr[A, B](implicit iso: Iso[A, B]): Iso[js.UndefOr[A], Option[B]] = new Iso[js.UndefOr[A], Option[B]] { - override def to(x: js.UndefOr[A]): Option[B] = x.toOption.map(iso.to) - override def from(x: Option[B]): js.UndefOr[A] = x.map(iso.from).orUndefined - } - - implicit def isoArrayToColl[A, B](iso: Iso[A, B])(implicit ctA: ClassTag[A], tB: RType[B]): Iso[js.Array[A], Coll[B]] = new Iso[js.Array[A], Coll[B]] { - override def to(x: js.Array[A]): Coll[B] = Colls.fromArray(x.map(iso.to).toArray(tB.classTag)) - override def from(x: Coll[B]): js.Array[A] = js.Array(x.toArray.map(iso.from):_*) - } - - implicit def isoArrayToIndexed[A, B](iso: Iso[A, B])(implicit cB: ClassTag[B]): Iso[js.Array[A], IndexedSeq[B]] = new Iso[js.Array[A], IndexedSeq[B]] { - override def to(x: js.Array[A]): IndexedSeq[B] = x.map(iso.to).toArray(cB).toIndexedSeq - override def from(x: IndexedSeq[B]): js.Array[A] = js.Array(x.map(iso.from):_*) - } - val isoTokenArray: Iso[js.Array[tokenMod.TokenAmount[commonMod.Amount]], Coll[Token]] = new Iso[js.Array[tokenMod.TokenAmount[commonMod.Amount]], Coll[Token]] { override def to(x: js.Array[tokenMod.TokenAmount[commonMod.Amount]]): Coll[Token] = { - isoArrayToColl(isoToken).to(x) + sigma.js.Isos.isoArrayToColl(isoToken).to(x) } override def from(x: Coll[Token]): js.Array[tokenMod.TokenAmount[commonMod.Amount]] = { - isoArrayToColl(isoToken).from(x) + sigma.js.Isos.isoArrayToColl(isoToken).from(x) } } @@ -484,17 +405,17 @@ object Isos { new Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] { override def to(a: UnsignedTransaction): UnsignedErgoLikeTransaction = { new UnsignedErgoLikeTransaction( - inputs = isoArrayToIndexed(isoUnsignedInput).to(a.inputs), - dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs), - outputCandidates = isoArrayToIndexed(isoBoxCandidate).to(a.outputs), + inputs = sigma.js.Isos.isoArrayToIndexed(isoUnsignedInput).to(a.inputs), + dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).to(a.dataInputs), + outputCandidates = sigma.js.Isos.isoArrayToIndexed(isoBoxCandidate).to(a.outputs), ) } override def from(b: UnsignedErgoLikeTransaction): UnsignedTransaction = { UnsignedTransaction( - inputs = isoArrayToIndexed(isoUnsignedInput).from(b.inputs), - dataInputs = isoArrayToIndexed(isoDataInput).from(b.dataInputs), - outputs = isoArrayToIndexed(isoBoxCandidate).from(b.outputCandidates) + inputs = sigma.js.Isos.isoArrayToIndexed(isoUnsignedInput).from(b.inputs), + dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).from(b.dataInputs), + outputs = sigma.js.Isos.isoArrayToIndexed(isoBoxCandidate).from(b.outputCandidates) ) } } @@ -503,16 +424,16 @@ object Isos { new Iso[SignedTransaction, ErgoLikeTransaction] { override def to(a: SignedTransaction): ErgoLikeTransaction = { new ErgoLikeTransaction( - inputs = isoArrayToIndexed(isoSignedInput).to(a.inputs), - dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs), - outputCandidates = isoArrayToIndexed(isoBox).to(a.outputs), + inputs = sigma.js.Isos.isoArrayToIndexed(isoSignedInput).to(a.inputs), + dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).to(a.dataInputs), + outputCandidates = sigma.js.Isos.isoArrayToIndexed(isoBox).to(a.outputs), ) } override def from(tx: ErgoLikeTransaction): SignedTransaction = { - val inputs = isoArrayToIndexed(isoSignedInput).from(tx.inputs) - val dataInputs = isoArrayToIndexed(isoDataInput).from(tx.dataInputs) - val outputs = isoArrayToIndexed(isoBox).from(tx.outputs) + val inputs = sigma.js.Isos.isoArrayToIndexed(isoSignedInput).from(tx.inputs) + val dataInputs = sigma.js.Isos.isoArrayToIndexed(isoDataInput).from(tx.dataInputs) + val outputs = sigma.js.Isos.isoArrayToIndexed(isoBox).from(tx.outputs) SignedTransaction(dataInputs, tx.id, inputs, outputs) } } diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala index 08a8ace0d9..c2c0c993bf 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala @@ -1,5 +1,7 @@ package org.ergoplatform.sdk.js +import sigma.js.GroupElement + import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala index 2c27a177d4..12ecf1d03f 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala @@ -6,7 +6,7 @@ import org.ergoplatform.sdk.SecretString import scala.scalajs.js import scala.scalajs.js.annotation.JSExportTopLevel import Isos._ -import sigmastate.eval.SigmaDsl +import sigma.eval.SigmaDsl /** Equivalent of [[sdk.ProverBuilder]] available from JS. * @@ -70,7 +70,7 @@ class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js. isoStringToGroupElement.to(h), isoStringToGroupElement.to(u), isoStringToGroupElement.to(v), - SigmaDsl.toBigInteger(isoBigInt.to(x)) + SigmaDsl.toBigInteger(sigma.js.Isos.isoBigInt.to(x)) ) this } @@ -84,7 +84,7 @@ class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js. * as proveDlog(a) && proveDlog(b), where a and b are two group elements. */ def withDLogSecret(x: js.BigInt): ProverBuilder = { - _builder.withDLogSecret(SigmaDsl.toBigInteger(isoBigInt.to(x))) + _builder.withDLogSecret(SigmaDsl.toBigInteger(sigma.js.Isos.isoBigInt.to(x))) this } @@ -95,7 +95,7 @@ class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js. } } -@JSExportTopLevel("ProverBuilderObj") +@JSExportTopLevel("ProverBuilder$") object ProverBuilder extends js.Object { /** Creates a new [[ProverBuilder]] instance with the given blockchain parameters * and network prefix. diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala index a27c61d261..c01a61adb8 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala @@ -12,7 +12,7 @@ class ReducedTransaction(val _tx: sdk.ReducedTransaction) extends js.Object { def toHex(): String = _tx.toHex } -@JSExportTopLevel("ReducedTransactionObj") +@JSExportTopLevel("ReducedTransaction$") object ReducedTransaction extends js.Object { /** Creates a [[ReducedTransaction]] from serialized bytes in hex format. */ def fromHex(hex: String): ReducedTransaction = { diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProp.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProp.scala deleted file mode 100644 index 2dc665d918..0000000000 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProp.scala +++ /dev/null @@ -1,24 +0,0 @@ -package org.ergoplatform.sdk.js - -import sigmastate.Values.SigmaBoolean -import sigmastate.crypto.DLogProtocol.ProveDlog - -import scala.scalajs.js -import scala.scalajs.js.annotation.JSExportTopLevel - -/** Equivalent of [[sigma.SigmaProp]] available from JS. */ -@JSExportTopLevel("SigmaProp") -class SigmaProp(val sigmaBoolean: SigmaBoolean) extends js.Object { -} - -@JSExportTopLevel("SigmaPropObj") -object SigmaProp extends js.Object { - /** Creates a new [[SigmaProp]] from the given hex string of public key. - * @param pointHex hex representation of elliptic curve point (ASN.1 encoded) - * @see CryptoFacade.getASN1Encoding, GroupElement.fromPointHex, Point - */ - def fromPointHex(pointHex: String): SigmaProp = { - val point = GroupElement.fromPointHex(pointHex).point - new SigmaProp(ProveDlog(point)) - } -} diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala index 6aedecf2e9..0a6aca4830 100644 --- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala +++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala @@ -24,7 +24,7 @@ class SigmaProver(_prover: sdk.SigmaProver) extends js.Object { /** Returns the prover's secret key. */ def getSecretKey(): js.BigInt = - isoBigInt.from(_prover.getSecretKey) + sigma.js.Isos.isoBigInt.from(_prover.getSecretKey) /** Returns an array of EIP-3 addresses associated with the prover's secret keys. */ def getEip3Addresses(): js.Array[String] = { @@ -50,9 +50,9 @@ class SigmaProver(_prover: sdk.SigmaProver) extends js.Object { baseCost: Int): ReducedTransaction = { val unreducedTx = sdk.UnreducedTransaction( unsignedTx = isoUnsignedTransaction.to(unsignedTx), - boxesToSpend = isoArrayToIndexed(isoEIP12UnsignedInput).to(boxesToSpend), - dataInputs = isoArrayToIndexed(isoBox).to(dataInputs), - tokensToBurn = isoArrayToIndexed(isoToken.andThen(sdk.Iso.isoErgoTokenToPair.inverse)).to(tokensToBurn) + boxesToSpend = sigma.js.Isos.isoArrayToIndexed(isoEIP12UnsignedInput).to(boxesToSpend), + dataInputs = sigma.js.Isos.isoArrayToIndexed(isoBox).to(dataInputs), + tokensToBurn = sigma.js.Isos.isoArrayToIndexed(isoToken.andThen(sdk.SdkIsos.isoErgoTokenToPair.inverse)).to(tokensToBurn) ) val ctx = isoBlockchainStateContext.to(stateCtx) val reducedTx = _prover.reduce(ctx, unreducedTx, baseCost) diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala index 2164b9b339..a366e33d3c 100644 --- a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala +++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala @@ -2,52 +2,30 @@ package org.ergoplatform.sdk.js import org.ergoplatform.ErgoBox.{AdditionalRegisters, BoxId, TokenId} import org.ergoplatform._ -import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, CBlockchainStateContext} -import org.ergoplatform.sdk.{ExtendedInputBox, Iso} -import org.scalacheck.{Arbitrary, Gen} -import org.scalatest.matchers.should.Matchers -import org.scalatest.propspec.AnyPropSpec -import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigmastate.SType -import sigmastate.Values.Constant -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.serialization.generators.ObjectGenerators -import sigma.{Coll, Colls, GroupElement} +import org.ergoplatform.sdk.ExtendedInputBox +import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext +import org.scalacheck.Arbitrary +import sigma.ast.{Constant, SType} +import sigma.data.Iso +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.js.AvlTree +import sigma.{Coll, GroupElement} import scala.scalajs.js -class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with ScalaCheckPropertyChecks{ - - lazy val extendedInputBoxGen: Gen[ExtendedInputBox] = for { - box <- ergoBoxGen - extension <- contextExtensionGen - } yield ExtendedInputBox(box, extension) - - lazy val blockchainStateContextGen: Gen[BlockchainStateContext] = for { - stateRoot <- avlTreeGen - headers <- headersGen(stateRoot) - preHeader <- preHeaderGen(headers.headOption.map(_.id).getOrElse(modifierIdBytesGen.sample.get)) - } yield CBlockchainStateContext( - sigmaLastHeaders = Colls.fromItems(headers:_*), - previousStateDigest = stateRoot.digest, - sigmaPreHeader = preHeader - ) - - def roundtrip[A,B](iso: Iso[A,B])(b: B): Unit = { - iso.to(iso.from(b)) shouldBe b - } +class IsosSpec extends IsosSpecBase with sdk.generators.ObjectGenerators { override implicit val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration(minSuccessful = 30) property("Iso.isoStringToArray") { forAll() { (bytes: Array[Byte]) => - roundtrip(Isos.isoStringToArray)(bytes) + roundtrip(Iso.isoStringToArray)(bytes) } } property("Iso.isoStringToColl") { forAll() { (bytes: Coll[Byte]) => - roundtrip(Isos.isoStringToColl)(bytes) + roundtrip(Iso.isoStringToColl)(bytes) } } @@ -71,7 +49,7 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca property("Iso.avlTree") { forAll { (c: sigma.AvlTree) => - roundtrip(Isos.isoAvlTree)(c) + roundtrip(AvlTree.isoAvlTree)(c) } } @@ -125,13 +103,13 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca property("Iso.isoBigInt") { forAll { (c: sigma.BigInt) => - roundtrip(Isos.isoBigInt)(c) + roundtrip(sigma.js.Isos.isoBigInt)(c) } } property("Iso.isoBigIntToLong") { forAll { (c: Long) => - roundtrip(Isos.isoBigIntToLong)(c) + roundtrip(sigma.js.Isos.isoBigIntToLong)(c) } } @@ -156,7 +134,7 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca property("Iso.isoUndefOr") { forAll { opt: Option[Long] => - roundtrip(Isos.isoUndefOr(Iso.identityIso[Long]))(opt) + roundtrip(sigma.js.Isos.isoUndefOr(Iso.identityIso[Long]))(opt) } } @@ -195,4 +173,11 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca roundtrip(Isos.isoSignedTransaction)(tx) } } + + property("Iso.isoContractTemplate") { + forAll(contractTemplateGen) { (tx: sdk.ContractTemplate) => + roundtrip(ContractTemplate.isoToSdk)(tx) + } + } + } diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpecBase.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpecBase.scala new file mode 100644 index 0000000000..ca15ed6853 --- /dev/null +++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpecBase.scala @@ -0,0 +1,32 @@ +package org.ergoplatform.sdk.js + +import org.ergoplatform.sdk.ExtendedInputBox +import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, CBlockchainStateContext} +import org.scalacheck.Gen +import org.scalatest.matchers.should.Matchers +import org.scalatest.propspec.AnyPropSpec +import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks +import sigma.Colls +import sigma.data.Iso +import sigma.serialization.generators.ObjectGenerators + +class IsosSpecBase extends AnyPropSpec with Matchers with ObjectGenerators with ScalaCheckPropertyChecks { + lazy val extendedInputBoxGen: Gen[ExtendedInputBox] = for { + box <- ergoBoxGen + extension <- contextExtensionGen + } yield ExtendedInputBox(box, extension) + + lazy val blockchainStateContextGen: Gen[BlockchainStateContext] = for { + stateRoot <- avlTreeGen + headers <- headersGen(stateRoot) + preHeader <- preHeaderGen(headers.headOption.map(_.id).getOrElse(modifierIdBytesGen.sample.get)) + } yield CBlockchainStateContext( + sigmaLastHeaders = Colls.fromItems(headers: _*), + previousStateDigest = stateRoot.digest, + sigmaPreHeader = preHeader + ) + + def roundtrip[A, B](iso: Iso[A, B])(b: B): Unit = { + iso.to(iso.from(b)) shouldBe b + } +} diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/ValueSpec.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/ValueSpec.scala index be0997fcc6..4599980006 100644 --- a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/ValueSpec.scala +++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/ValueSpec.scala @@ -4,27 +4,27 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import scorex.util.encode.Base16 -import sigmastate.SType -import sigmastate.Values.{AvlTreeConstant, BigIntConstant, BooleanConstant, BoxConstant, ByteConstant, Constant, GroupElementConstant, IntConstant, LongConstant, ShortConstant, SigmaPropConstant, UnitConstant} -import sigmastate.crypto.CryptoConstants.dlogGroup -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.CryptoFacade -import sigmastate.eval.CSigmaProp -import sigmastate.lang.DeserializationSigmaBuilder -import sigmastate.serialization.ConstantSerializer +import sigma.ast.{DeserializationSigmaBuilder, SType} +import sigma.ast._ +import sigma.crypto.CryptoConstants.dlogGroup +import sigma.crypto.CryptoFacade +import sigma.serialization.ConstantSerializer import sigmastate.utils.Helpers import sigma.SigmaTestingData +import sigma.data.{CSigmaProp, ProveDlog} +import sigma.ast.js.isoValueToConstant +import sigma.js.Value import java.math.BigInteger class ValueSpec extends AnyPropSpec with Matchers with SigmaTestingData with ScalaCheckPropertyChecks { def test[T <: SType](c: Constant[T], expectedHex: String) = { - val v = Isos.isoValueToConstant.from(c) + val v = isoValueToConstant.from(c) val S = ConstantSerializer(DeserializationSigmaBuilder) Base16.encode(S.toBytes(c)) shouldBe expectedHex v.toHex() shouldBe expectedHex - Isos.isoValueToConstant.to(Value.fromHex(expectedHex)) shouldBe c + isoValueToConstant.to(Value.fromHex(expectedHex)) shouldBe c } property("Boolean toHex()/fromHex()") { @@ -61,7 +61,8 @@ class ValueSpec extends AnyPropSpec with Matchers with SigmaTestingData with Sca test(AvlTreeConstant(TestData.t3), "643100d2e101ff01fc047c7f6f00ff80129df69a5090012f01ffca99f5bfff0c803601800100") } - property("Box toHex()/fromHex()") { + // TODO turn on when Value.fromHex is implemented for Box + ignore("Box toHex()/fromHex()") { test(BoxConstant(TestData.b2), "63b96000d1968302010100ff83020193040204020100c0843d000401010e32297000800b80f1d56c809a8c6affbed864b87f007f6f007f00ac00018c01c4fdff011088807f0100657f00f9ab0101ff6d6505a4a7b5a2e7a4a4dd3a05feffffffffffffffff01003bd5c630803cfff6c1ff7f7fb980ff136afc011f8080b8b04ad4dbda2d7f4e01") } diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala index b3b470c604..c0dc075a9b 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala @@ -8,16 +8,15 @@ import org.ergoplatform.sdk.utils.ArithUtils import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, TransactionContext} import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey import org.ergoplatform.validation.ValidationRules -import sigma.util.Extensions.LongOps import sigma.VersionContext -import sigmastate.Values.SigmaBoolean -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} +import sigma.data.{AvlTreeData, ProveDlog, SigmaBoolean} +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.util.Extensions.LongOps +import sigmastate.crypto.DLogProtocol.DLogProverInput import sigmastate.crypto.{DiffieHellmanTupleProverInput, SigmaProtocolPrivateInput} import sigmastate.interpreter.Interpreter.{ReductionResult, estimateCryptoVerifyCost} import sigmastate.interpreter._ -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate.AvlTreeData +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} import java.util import java.util.{Objects, List => JList} @@ -40,7 +39,7 @@ class AppkitProvingInterpreter( extends ReducingInterpreter(params) with ProverInterpreter { override type CTX = ErgoLikeContext - import org.ergoplatform.sdk.Iso._ + import org.ergoplatform.sdk.SdkIsos._ /** All secrets available to this interpreter including [[ExtendedSecretKey]], dlog and * dht secrets. diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala index 47d2a06d3e..5e491630bd 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ContractTemplate.scala @@ -1,24 +1,21 @@ package org.ergoplatform.sdk -import cats.syntax.either._ // required for Scala 2.11 +import cats.syntax.either._ // required for Scala 2.11 import debox.cfor import io.circe._ import io.circe.syntax.EncoderOps import org.ergoplatform.sdk.utils.SerializationUtils.{parseString, serializeString} import org.ergoplatform.sdk.utils.Zero +import sigma.Evaluation +import sigma.ast.{DeserializationSigmaBuilder, SType, StdSigmaBuilder} +import sigma.serialization.{SerializerException, SigmaByteReader, SigmaByteWriter} import sigma.util.safeNewArray -import sigmastate.Values.ErgoTree.headerWithVersion -import sigmastate.Values.{ErgoTree, _} -import sigmastate._ -import sigmastate.eval._ -import sigmastate.exceptions.SerializerException -import sigmastate.lang.{DeserializationSigmaBuilder, StdSigmaBuilder} -import sigmastate.serialization._ -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} - +import sigma.ast.ErgoTree.{ZeroHeader, headerWithVersion, setConstantSegregation} +import sigma.ast._ +import sigma.ast.syntax.SigmaPropValue +import sigma.serialization._ import java.util.Objects import scala.collection.mutable -import scala.language.implicitConversions /** * Represents a ContractTemplate parameter. @@ -145,7 +142,7 @@ case class ContractTemplate( * provided in the template. * @return `ErgoTree` generated by replacing the template parameters with the value provided in `paramValues`. */ - def applyTemplate(version: Option[Byte], paramValues: Map[String, Values.Constant[SType]]): Values.ErgoTree = { + def applyTemplate(version: Option[Byte], paramValues: Map[String, Constant[SType]]): ErgoTree = { require(treeVersion.isDefined || version.isDefined, "ErgoTreeVersion must be provided to generate the ErgoTree.") val nConsts = constTypes.size val requiredParameterNames = @@ -173,14 +170,22 @@ case class ContractTemplate( } } - val usedErgoTreeVersion = headerWithVersion(if (version.isDefined) version.get else treeVersion.get) + val usedErgoTreeHeader = headerWithVersion(ZeroHeader, if (version.isDefined) version.get else treeVersion.get) ErgoTree( - (ErgoTree.ConstantSegregationHeader | usedErgoTreeVersion).toByte, + setConstantSegregation(usedErgoTreeHeader), constants, this.expressionTree ) } + /** @return Json representation of this contract template */ + def toJson: Json = ContractTemplate.jsonEncoder.encoder(this) + + /** @return JSON representation of this contract template pretty-printed to a string + * indentation of two spaces. + */ + def toJsonString: String = toJson.spaces2 + override def hashCode(): Int = Objects.hash(treeVersion, name, description, constTypes, constValues, parameters, expressionTree) override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala index a92eef9947..fc95b77e61 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/DataJsonEncoder.scala @@ -5,22 +5,21 @@ import io.circe._ import io.circe.syntax._ import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, Token} -import org.ergoplatform.settings.ErgoAlgos -import sigma.data.RType +import sigma.data.{CAnyValue, RType} import scorex.util._ -import sigmastate.Values.{Constant, EvaluatedValue} -import sigmastate._ import sigmastate.lang.SigmaParser import sigmastate.eval._ import sigma._ import debox.cfor -import sigmastate.exceptions.SerializerException +import scorex.util.encode.Base16 + import scala.collection.compat.immutable.ArraySeq import scala.collection.mutable -import fastparse.{Parsed, parse} -import sigmastate.serialization.SigmaSerializer -import sigmastate.serialization.DataSerializer -import sigmastate.serialization.ErgoTreeSerializer +import sigma.ast._ +import sigma.eval.SigmaDsl +import sigma.serialization.SerializerException +import sigma.serialization.{DataSerializer, SigmaSerializer} +import sigma.serialization.ErgoTreeSerializer object DataJsonEncoder { def encode[T <: SType](v: T#WrappedType, tpe: T): Json = { @@ -33,7 +32,7 @@ object DataJsonEncoder { } private def encodeBytes: Encoder[Array[Byte]] = Encoder.instance((bytes: Array[Byte]) => { - ErgoAlgos.encode(bytes).asJson + Base16.encode(bytes).asJson }) def encodeAnyValue(v: AnyValue): Json = { @@ -153,7 +152,7 @@ object DataJsonEncoder { private def decodeBytes(json: Json): Array[Byte] = { val jsonStr = json.as[String] jsonStr match { - case Right(jsonStr) => ErgoAlgos.decode(jsonStr).get + case Right(jsonStr) => Base16.decode(jsonStr).get case Left(error) => throw new SerializerException(error.getMessage) } } diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ErgoId.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ErgoId.scala index 7d56b05295..330400a824 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ErgoId.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ErgoId.scala @@ -2,8 +2,6 @@ package org.ergoplatform.sdk import scorex.utils.Ints -import java.util - object ErgoId { /** Creates a new ErgoId decoding it from the given hex string. */ def create(base16Str: String) = new ErgoId(JavaHelpers.decodeStringToBytes(base16Str)) @@ -17,7 +15,7 @@ class ErgoId(val _idBytes: Array[Byte]) { /** Extracts underlying byte array with id bytes. */ def getBytes = _idBytes - override def hashCode = + override def hashCode: Int = if (_idBytes != null && _idBytes.length >= 4) Ints.fromByteArray(_idBytes) else java.util.Arrays.hashCode(_idBytes) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala index 05cd83daea..81b1ba0085 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala @@ -2,7 +2,7 @@ package org.ergoplatform.sdk import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, UnsignedInput} import scorex.util.ModifierId -import sigmastate.interpreter.ContextExtension +import sigma.interpreter.ContextExtension /** Input ErgoBox paired with context variables (aka ContextExtensions). * diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala index c8fb2e57f8..320aae5919 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala @@ -13,29 +13,6 @@ import scala.reflect.ClassTag object Extensions { - implicit class GenIterableOps[A, Source[X] <: GenIterable[X]](val xs: Source[A]) extends AnyVal { - - /** Apply m for each element of this collection, group by key and reduce each group - * using r. - * Note, the ordering of the resulting keys is deterministic and the keys appear in - * the order they first produced by `map`. - * - * @returns one item for each group in a new collection of (K,V) pairs. */ - def mapReduce[K, V](map: A => (K, V))(reduce: (V, V) => V) - (implicit cbf: BuildFrom[Source[A], (K, V), Source[(K, V)]]): Source[(K, V)] = { - val result = scala.collection.mutable.LinkedHashMap.empty[K, V] - xs.foreach { x => - val (key, value) = map(x) - val reduced = if (result.contains(key)) reduce(result(key), value) else value - result.update(key, reduced) - } - - val b = cbf.newBuilder(xs) - for ( kv <- result ) b += kv - b.result() - } - } - implicit class CollOps[A](val coll: Coll[A]) extends AnyVal { /** Partitions this $coll in two $colls according to a predicate. @@ -205,7 +182,4 @@ object Extensions { } } - implicit class DoubleOps(val i: Double) extends AnyVal { - def erg: Long = (i * 1000000000L).toLong - } } diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/InputBoxesValidator.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/InputBoxesValidator.scala index 7e3e24a8b7..98e4f45ac7 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/InputBoxesValidator.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/InputBoxesValidator.scala @@ -1,6 +1,6 @@ package org.ergoplatform.sdk -import org.ergoplatform.SigmaConstants.MaxBoxSize +import sigma.data.SigmaConstants.MaxBoxSize import org.ergoplatform.sdk.wallet.Constants.MaxAssetsPerBox import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} import org.ergoplatform.{ErgoBoxAssets, ErgoBoxAssetsHolder} diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala index 75825defa3..14cdf7f6bb 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala @@ -9,20 +9,20 @@ import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey} import org.ergoplatform.sdk.wallet.{Constants, TokensMap} import org.ergoplatform.settings.ErgoAlgos import scorex.crypto.authds.ADKey -import sigmastate.utils.Helpers._ // don't remove, required for Scala 2.11 import scorex.util.encode.Base16 import scorex.util.{ModifierId, bytesToId, idToBytes} +import sigma.ast.{ErgoTree, SType} +import sigma.ast.syntax.SValue +import sigma.crypto.{CryptoFacade, EcPointType} import sigma.data.ExactIntegral.LongIsExactIntegral -import sigma.data.RType +import sigma.data.{CSigmaDslBuilder, Digest32Coll, Iso, ProveDHTuple, ProveDlog, RType, SigmaBoolean, SigmaConstants} +import sigma.serialization.GroupElementSerializer import sigma.util.StringUtil.StringUtilExtensions -import sigma.{AnyValue, AvlTree, Coll, Colls, GroupElement, Header} -import sigmastate.SType -import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, SValue, SigmaBoolean, SigmaPropConstant} -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.crypto.{CryptoFacade, DiffieHellmanTupleProverInput, ProveDHTuple} -import sigmastate.eval.{CostingSigmaDslBuilder, Digest32Coll, Evaluation} -import sigmastate.serialization.{ErgoTreeSerializer, GroupElementSerializer, SigmaSerializer, ValueSerializer} +import sigma.{AnyValue, AvlTree, Coll, Colls, Evaluation, GroupElement, Header} +import sigma.ast.{Constant, EvaluatedValue, SigmaPropConstant} +import sigmastate.crypto.DiffieHellmanTupleProverInput +import sigma.serialization.{ErgoTreeSerializer, SigmaSerializer, ValueSerializer} +import sigmastate.utils.Helpers._ // required for Scala 2.11 import java.lang.{Boolean => JBoolean, Byte => JByte, Integer => JInt, Long => JLong, Short => JShort, String => JString} import java.math.BigInteger @@ -30,45 +30,10 @@ import java.util import java.util.{List => JList, Map => JMap} import scala.collection.{JavaConverters, mutable} -/** Type-class of isomorphisms between types. - * Isomorphism between two types `A` and `B` essentially say that both types - * represents the same information (entity) but in a different way. - *

- * The information is not lost so that both are true: - * 1) a == from(to(a)) - * 2) b == to(from(b)) - *

- * It is used to define type-full conversions: - * - different conversions between Java and Scala data types. - * - conversion between Ergo representations and generated API representations - */ -abstract class Iso[A, B] { - def to(a: A): B - def from(b: B): A - def andThen[C](iso: Iso[B,C]): Iso[A,C] = ComposeIso(iso, this) - def inverse: Iso[B, A] = InverseIso(this) -} -final case class InverseIso[A,B](iso: Iso[A,B]) extends Iso[B,A] { - override def to(a: B): A = iso.from(a) - override def from(b: A): B = iso.to(b) -} -final case class ComposeIso[A, B, C](iso2: Iso[B, C], iso1: Iso[A, B]) extends Iso[A, C] { - def from(c: C): A = iso1.from(iso2.from(c)) - def to(a: A): C = iso2.to(iso1.to(a)) -} - trait LowPriorityIsos { } -object Iso extends LowPriorityIsos { - implicit def identityIso[A]: Iso[A, A] = new Iso[A, A] { - override def to(a: A): A = a - - override def from(b: A): A = b - } - - implicit def inverseIso[A,B](implicit iso: Iso[A,B]): Iso[B,A] = InverseIso[A,B](iso) - +object SdkIsos extends LowPriorityIsos { implicit val jbyteToByte: Iso[JByte, Byte] = new Iso[JByte, Byte] { override def to(b: JByte): Byte = b override def from(a: Byte): JByte = a @@ -295,14 +260,6 @@ object JavaHelpers { ErgoAlgos.encode(ErgoAlgos.hash(s)) } - - def toSigmaBoolean(ergoTree: ErgoTree): SigmaBoolean = { - val prop = ergoTree.toProposition(ergoTree.isConstantSegregation) - prop match { - case SigmaPropConstant(p) => SigmaDsl.toSigmaBoolean(p) - } - } - def toErgoTree(sigmaBoolean: SigmaBoolean): ErgoTree = ErgoTree.fromSigmaBoolean(sigmaBoolean) def getStateDigest(tree: AvlTree): Array[Byte] = { @@ -368,7 +325,7 @@ object JavaHelpers { def BoxRType: RType[sigma.Box] = sigma.BoxRType - def SigmaDsl: CostingSigmaDslBuilder = sigmastate.eval.SigmaDsl + def SigmaDsl: CSigmaDslBuilder = sigma.eval.SigmaDsl def collFrom(arr: Array[Byte]): Coll[Byte] = { Colls.fromArray(arr) diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala index 878c936c80..ae14fd831a 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala @@ -1,38 +1,30 @@ package org.ergoplatform.sdk -import java.math.BigInteger import cats.syntax.either._ import io.circe._ import io.circe.syntax._ -import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, Token, TokenId} +import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, Token, TokenId} +import org.ergoplatform._ import org.ergoplatform.settings.ErgoAlgos -import org.ergoplatform.validation.{SigmaValidationSettings, SigmaValidationSettingsSerializer} +import org.ergoplatform.validation.SigmaValidationSettingsSerializer import scorex.crypto.authds.{ADDigest, ADKey} import scorex.crypto.hash.Digest32 import scorex.util.ModifierId -import sigmastate.Values.{EvaluatedValue, ErgoTree} -import sigmastate.eval.Extensions._ -import sigmastate.eval.{WrapperOf, CPreHeader, _} -import sigmastate.exceptions.SigmaException -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.{AvlTreeData, SType, AvlTreeFlags} -import sigma.{Header, AnyValue, Colls, Coll, PreHeader} - -import scala.util.Try -import sigmastate.utils.Helpers._ // required for Scala 2.11 -import org.ergoplatform.ErgoBox -import sigmastate.serialization.ValueSerializer -import org.ergoplatform.DataInput -import org.ergoplatform.Input -import org.ergoplatform.UnsignedInput -import sigmastate.serialization.ErgoTreeSerializer -import org.ergoplatform.ErgoLikeTransaction -import org.ergoplatform.UnsignedErgoLikeTransaction -import org.ergoplatform.ErgoLikeTransactionTemplate -import org.ergoplatform.ErgoBoxCandidate -import org.ergoplatform.ErgoLikeContext +import sigma.Extensions.ArrayOps +import sigma.ast.{ErgoTree, EvaluatedValue, SType} +import sigma.data.{AvlTreeData, AvlTreeFlags, CBigInt, Digest32Coll, WrapperOf} +import sigma.eval.Extensions.EvalIterableOps +import sigma.eval.SigmaDsl +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.{ErgoTreeSerializer, ValueSerializer} +import sigma.validation.SigmaValidationSettings +import sigma.{AnyValue, Coll, Colls, Header, PreHeader, SigmaException} +import sigmastate.eval.{CPreHeader, _} +import sigmastate.utils.Helpers._ // required for Scala 2.11 +import java.math.BigInteger import scala.collection.mutable +import scala.util.Try trait JsonCodecs { diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/OutBoxBuilder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/OutBoxBuilder.scala index 61a71fcfa2..1046767fbf 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/OutBoxBuilder.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/OutBoxBuilder.scala @@ -2,11 +2,11 @@ package org.ergoplatform.sdk import org.ergoplatform.ErgoBox.TokenId import org.ergoplatform.sdk.JavaHelpers.collRType -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, SigmaConstants} +import org.ergoplatform.{ErgoBox, ErgoBoxCandidate} import sigma.Colls -import sigma.data.RType -import sigmastate.SType -import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue} +import sigma.ast.{ErgoTree, SType} +import sigma.data.{RType, SigmaConstants} +import sigma.ast.{Constant, EvaluatedValue} import scala.collection.mutable.ArrayBuffer @@ -69,7 +69,7 @@ object OutBoxBuilder { require(nRegs <= nonMandatoryRegisters.length, s"Too many additional registers $nRegs. Max allowed ${nonMandatoryRegisters.length}") implicit val TokenIdRType: RType[TokenId] = collRType(sigma.ByteType).asInstanceOf[RType[TokenId]] - val ts = Colls.fromItems(tokens.map(Iso.isoErgoTokenToPair.to(_)): _*) + val ts = Colls.fromItems(tokens.map(SdkIsos.isoErgoTokenToPair.to(_)): _*) val rs = registers.zipWithIndex.map { case (c, i) => val id = ErgoBox.nonMandatoryRegisters(i) id -> c.asInstanceOf[EvaluatedValue[_ <: SType]] diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala index 1e73c49640..229853a172 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala @@ -6,11 +6,11 @@ import org.ergoplatform.sdk.utils.ArithUtils import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, TransactionContext} import org.ergoplatform.validation.ValidationRules import org.ergoplatform.{ErgoLikeContext, ErgoLikeInterpreter} +import sigma.ast.ErgoTree +import sigma.data.AvlTreeData +import sigma.exceptions.CostLimitException import sigma.util.Extensions.LongOps -import sigmastate.AvlTreeData -import sigmastate.Values.ErgoTree -import sigmastate.eval.Evaluation.addCostChecked -import sigmastate.exceptions.CostLimitException +import sigmastate.eval.addCostChecked import sigmastate.interpreter.Interpreter import sigmastate.interpreter.Interpreter.ScriptEnv @@ -21,7 +21,7 @@ import scala.collection.mutable /** Interpreter that can reduce transactions with given chain parameters. */ class ReducingInterpreter(params: BlockchainParameters) extends ErgoLikeInterpreter { override type CTX = ErgoLikeContext - import org.ergoplatform.sdk.Iso._ + import org.ergoplatform.sdk.SdkIsos._ /** Reduces the given ErgoTree in the given context to the sigma proposition. * @@ -31,7 +31,7 @@ class ReducingInterpreter(params: BlockchainParameters) extends ErgoLikeInterpre * @return data object containing enough data to sign a transaction without Context. */ def reduce(env: ScriptEnv, ergoTree: ErgoTree, context: CTX): ReducedInputData = { - val initCost = ergoTree.complexity + context.initCost + val initCost = context.initCost val remainingLimit = context.costLimit - initCost if (remainingLimit <= 0) throw new CostLimitException(initCost, diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SecretString.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SecretString.scala index 83ff2e0a10..99b576d870 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SecretString.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SecretString.scala @@ -2,7 +2,6 @@ package org.ergoplatform.sdk import debox.cfor -import java.util /** * Encapsulates secret array of characters (char[]) with proper equality. diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala index aa281f5b10..6dc08fbfee 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala @@ -3,7 +3,8 @@ package org.ergoplatform.sdk import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix import org.ergoplatform._ import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext -import sigmastate.eval.{CostingSigmaDslBuilder, SigmaDsl} +import sigma.data.CSigmaDslBuilder +import sigma.eval.SigmaDsl import sigmastate.interpreter.HintsBag import sigmastate.utils.Helpers.TryOps import sigma.{BigInt, SigmaProp} @@ -27,7 +28,7 @@ class SigmaProver(_prover: AppkitProvingInterpreter, networkPrefix: NetworkPrefi /** Returns the prover's secret key. */ def getSecretKey: BigInt = - CostingSigmaDslBuilder.BigInt(_prover.secretKeys(0).privateInput.w) + CSigmaDslBuilder.BigInt(_prover.secretKeys(0).privateInput.w) /** Returns a sequence of EIP-3 addresses associated with the prover's secret keys. */ def getEip3Addresses: Seq[P2PKAddress] = { diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala index 8848e73c42..a3a4510653 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala @@ -3,9 +3,7 @@ package org.ergoplatform.sdk import org.ergoplatform.sdk.JavaHelpers.StringExtensions import org.ergoplatform.{ErgoBox, ErgoLikeTransaction, UnsignedErgoLikeTransaction} import sigmastate.eval.Extensions.ArrayByteOps -import sigmastate.serialization.SigmaSerializer - -import java.util +import sigma.serialization.SigmaSerializer /** Represents a transaction data chat can be reduced to [[ReducedTransaction]]. * diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/UnsignedTransactionBuilder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/UnsignedTransactionBuilder.scala index dab4a73efd..0f18b5e74b 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/UnsignedTransactionBuilder.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/UnsignedTransactionBuilder.scala @@ -7,11 +7,12 @@ import org.ergoplatform.sdk.BoxSelection.InputBoxesValidator import org.ergoplatform.sdk.Extensions.HeaderOps import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap} import scorex.util.{ModifierId, bytesToId} -import sigmastate.eval.Extensions.ArrayOps +import sigma.Extensions.ArrayOps import sigmastate.utils.Extensions.ModifierIdOps import sigma.Coll import sigma.Extensions.CollBytesOps import sigma.PreHeader +import sigma.data.Digest32CollRType import scala.collection.mutable.ArrayBuffer import scala.util.Try @@ -87,7 +88,7 @@ class UnsignedTransactionBuilder(val ctx: BlockchainContext) { val changeAddress = getDefined(_changeAddress, "Change address is not defined") val inputBoxesSeq = boxesToSpend.map(eb => eb.box) val requestedToBurn = _tokensToBurn.fold(IndexedSeq.empty[ErgoToken])(_.toIndexedSeq) - val burnTokens = Iso.isoErgoTokenSeqToLinkedMap.to(requestedToBurn).toMap + val burnTokens = SdkIsos.isoErgoTokenSeqToLinkedMap.to(requestedToBurn).toMap val rewardDelay = ctx.networkType match { case NetworkType.Mainnet => BlockchainParameters.MinerRewardDelay_Mainnet case NetworkType.Testnet => BlockchainParameters.MinerRewardDelay_Testnet diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala index 1530e1876a..4291a750f4 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/SerializationUtils.scala @@ -1,7 +1,6 @@ package org.ergoplatform.sdk.utils -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} - +import sigma.serialization.{SigmaByteReader, SigmaByteWriter} import java.nio.charset.StandardCharsets object SerializationUtils { diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala index 6cc7e6718c..78dfe2dc94 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/utils/Zero.scala @@ -1,17 +1,16 @@ package org.ergoplatform.sdk.utils import org.ergoplatform.ErgoBox -import sigma.data.{CollType, FuncType, OptionType, PairType, RType, TupleType} +import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, CBigInt, CGroupElement, CSigmaProp, CollType, FuncType, OptionType, PairType, RType, TrivialProp, TupleType} import sigma.data.RType._ import scorex.crypto.authds.avltree.batch.BatchAVLProver import scorex.crypto.hash.{Blake2b256, Digest32} import scorex.util.ModifierId -import sigmastate.Values.ErgoTree -import sigmastate.crypto.CryptoConstants import sigmastate.eval._ -import sigmastate.{AvlTreeData, AvlTreeFlags, TrivialProp} import sigma._ - +import sigma.ast.ErgoTree +import ErgoTree.HeaderType +import sigma.crypto.CryptoConstants import java.math.BigInteger import scala.language.implicitConversions @@ -62,7 +61,7 @@ object Zero extends ZeroLowPriority { new ErgoBox( LongIsZero.zero, new ErgoTree( - ByteIsZero.zero, + HeaderType @@ ByteIsZero.zero, IndexedSeq.empty, Right(sigmaPropIsZero.zero) ), diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/DerivationPath.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/DerivationPath.scala index 601a45e537..ac2143694e 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/DerivationPath.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/DerivationPath.scala @@ -1,10 +1,7 @@ package org.ergoplatform.sdk.wallet.secrets import org.ergoplatform.sdk.wallet.Constants -import scorex.util.serialization.{Reader, Writer} -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} - +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} import scala.util.{Failure, Success, Try} /** diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedPublicKey.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedPublicKey.scala index 2ad287972e..43488974b9 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedPublicKey.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedPublicKey.scala @@ -1,12 +1,9 @@ package org.ergoplatform.sdk.wallet.secrets -import sigmastate.crypto.DLogProtocol.{DLogProverInput, ProveDlog} -import sigmastate.crypto.CryptoConstants -import sigmastate.crypto.CryptoFacade -import sigmastate.crypto.BigIntegers -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} - +import sigma.crypto.{BigIntegers, CryptoConstants, CryptoFacade} +import sigma.data.ProveDlog +import sigmastate.crypto.DLogProtocol.DLogProverInput +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} import scala.annotation.tailrec /** diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedSecretKey.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedSecretKey.scala index 4b87e31e13..5493929ac5 100644 --- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedSecretKey.scala +++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/ExtendedSecretKey.scala @@ -1,13 +1,10 @@ package org.ergoplatform.sdk.wallet.secrets +import sigma.crypto.{BigIntegers, CryptoConstants, CryptoFacade} +import sigma.data.ProveDlog import java.math.BigInteger -import sigmastate.crypto.BigIntegers -import sigmastate.crypto.DLogProtocol import sigmastate.crypto.DLogProtocol.DLogProverInput -import sigmastate.crypto.CryptoConstants -import sigmastate.crypto.CryptoFacade -import sigmastate.serialization.SigmaSerializer -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer} /** * Secret, its chain code and path in key tree. @@ -23,7 +20,7 @@ final class ExtendedSecretKey(/*private[secrets]*/ val keyBytes: Array[Byte], override def privateInput: DLogProverInput = DLogProverInput(BigIntegers.fromUnsignedByteArray(keyBytes)) - def publicImage: DLogProtocol.ProveDlog = privateInput.publicImage + def publicImage: ProveDlog = privateInput.publicImage def child(idx: Int): ExtendedSecretKey = ExtendedSecretKey.deriveChildSecretKey(this, idx) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala index 268012ac3c..dace012c9a 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ContractTemplateSpecification.scala @@ -3,12 +3,15 @@ package org.ergoplatform.sdk import org.ergoplatform.sdk.generators.ObjectGenerators import org.scalatest.compatible.Assertion import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks -import sigmastate.Values._ +import sigma.ast._ import sigmastate._ -import sigmastate.eval.CBigInt import sigmastate.helpers.NegativeTesting -import sigmastate.serialization.{SerializationSpecification, SigmaSerializer} -import sigma.ContractsTestkit +import sigma.serialization.{SerializationSpecification, SigmaSerializer} +import sigma.{ContractsTestkit, VersionContext} +import sigma.ast.syntax.SigmaPropValue +import sigma.ast.{SByte, SInt, SType} +import sigma.data.CBigInt +import ErgoTree.setConstantSegregation import java.math.BigInteger @@ -66,15 +69,15 @@ class ContractTemplateSpecification extends SerializationSpecification property("unequal length of constTypes and constValues") { assertExceptionThrown( createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SByte, SByte, SByte).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(Some(10.toByte), Some(20.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), IndexedSeq( createParameter("p1", 0), createParameter("p2", 1), createParameter("p3", 2)), - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + EQ(Plus(ConstantPlaceholder(0, SByte), + ConstantPlaceholder(1, SByte)), + ConstantPlaceholder(2, SByte)).toSigmaProp ), exceptionLike[IllegalArgumentException]("constValues must be empty or of same length as constTypes. Got 2, expected 3") ) @@ -83,16 +86,16 @@ class ContractTemplateSpecification extends SerializationSpecification property("more parameters than constants") { assertExceptionThrown( createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SByte, SByte, SByte).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), IndexedSeq( createParameter("p1", 0), createParameter("p2", 1), createParameter("p3", 2), createParameter("p4", 3)), - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + EQ(Plus(ConstantPlaceholder(0, SByte), + ConstantPlaceholder(1, SByte)), + ConstantPlaceholder(2, SByte)).toSigmaProp ), exceptionLike[IllegalArgumentException]("number of parameters must be <= number of constants") ) @@ -101,15 +104,15 @@ class ContractTemplateSpecification extends SerializationSpecification property("invalid parameter constantIndex") { assertExceptionThrown( createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SByte, SByte, SByte).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), IndexedSeq( createParameter("p1", 0), createParameter("p2", 1), createParameter("p3", 100)), - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + EQ(Plus(ConstantPlaceholder(0, SByte), + ConstantPlaceholder(1, SByte)), + ConstantPlaceholder(2, SByte)).toSigmaProp ), exceptionLike[IllegalArgumentException]("parameter constantIndex must be in range [0, 3)") ) @@ -118,15 +121,15 @@ class ContractTemplateSpecification extends SerializationSpecification property("duplicate parameter constantIndex") { assertExceptionThrown( createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SByte, SByte, SByte).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), IndexedSeq( createParameter("p1", 0), createParameter("p2", 1), createParameter("p3", 1)), - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + EQ(Plus(ConstantPlaceholder(0, SByte), + ConstantPlaceholder(1, SByte)), + ConstantPlaceholder(2, SByte)).toSigmaProp ), exceptionLike[IllegalArgumentException]("multiple parameters point to the same constantIndex") ) @@ -135,15 +138,15 @@ class ContractTemplateSpecification extends SerializationSpecification property("duplicate parameter names") { assertExceptionThrown( createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SByte, SByte, SByte).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), IndexedSeq( createParameter("duplicate_name", 0), createParameter("p2", 1), createParameter("duplicate_name", 2)), - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + EQ(Plus(ConstantPlaceholder(0, SByte), + ConstantPlaceholder(1, SByte)), + ConstantPlaceholder(2, SByte)).toSigmaProp ), exceptionLike[IllegalArgumentException]("parameter names must be unique. Found duplicate parameters with name duplicate_name") ) @@ -152,14 +155,14 @@ class ContractTemplateSpecification extends SerializationSpecification property("constantIndex without default value and parameter") { assertExceptionThrown( createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SByte, SByte, SByte).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(None, Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), IndexedSeq( createParameter("p2", 1), createParameter("p3", 2)), - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp + EQ(Plus(ConstantPlaceholder(0, SByte), + ConstantPlaceholder(1, SByte)), + ConstantPlaceholder(2, SByte)).toSigmaProp ), exceptionLike[IllegalArgumentException]("constantIndex 0 does not have a default value and absent from parameter as well") ) @@ -171,23 +174,23 @@ class ContractTemplateSpecification extends SerializationSpecification createParameter("p2", 1), createParameter("p3", 2)) val expressionTrees = IndexedSeq( - EQ(Plus(ConstantPlaceholder(0, SType.typeByte), - ConstantPlaceholder(1, SType.typeByte)), - ConstantPlaceholder(2, SType.typeByte)).toSigmaProp, - EQ(Plus(ConstantPlaceholder(0, SType.typeInt), - ConstantPlaceholder(1, SType.typeInt)), - ConstantPlaceholder(2, SType.typeInt)).toSigmaProp, + EQ(Plus(ConstantPlaceholder(0, SByte), + ConstantPlaceholder(1, SByte)), + ConstantPlaceholder(2, SByte)).toSigmaProp, + EQ(Plus(ConstantPlaceholder(0, SInt), + ConstantPlaceholder(1, SInt)), + ConstantPlaceholder(2, SInt)).toSigmaProp, EQ(Plus(CBigInt(BigInteger.valueOf(10L)), BigIntConstant(20L)), BigIntConstant(30L)).toSigmaProp ) val templates = Seq( createContractTemplate( - IndexedSeq(SType.typeByte, SType.typeByte, SType.typeByte).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SByte, SByte, SByte).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(Some(10.toByte), Some(20.toByte), Some(30.toByte)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), parameters, expressionTrees(0) ), createContractTemplate( - IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SInt, SInt, SInt).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(Some(10), None, Some(30)).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), parameters, expressionTrees(1) @@ -204,13 +207,10 @@ class ContractTemplateSpecification extends SerializationSpecification Map("p1" -> IntConstant(10), "p2" -> IntConstant(20)), Map.empty[String, Constant[SType]] ) - var expectedErgoTreeVersion = (ErgoTree.ConstantSegregationHeader | ergoTreeVersionInTests).toByte - if (ergoTreeVersionInTests > 0) { - expectedErgoTreeVersion = (expectedErgoTreeVersion | ErgoTree.SizeFlag).toByte - } + val expectedErgoTreeHeader = setConstantSegregation(ergoTreeHeaderInTests) val expectedErgoTree = Seq( ErgoTree( - expectedErgoTreeVersion, + expectedErgoTreeHeader, IndexedSeq( ByteConstant(10.toByte), ByteConstant(40.toByte), @@ -219,7 +219,7 @@ class ContractTemplateSpecification extends SerializationSpecification expressionTrees(0) ), ErgoTree( - expectedErgoTreeVersion, + expectedErgoTreeHeader, IndexedSeq( IntConstant(10), IntConstant(20), @@ -228,15 +228,17 @@ class ContractTemplateSpecification extends SerializationSpecification expressionTrees(1) ), ErgoTree( - expectedErgoTreeVersion, + expectedErgoTreeHeader, Constant.EmptySeq, expressionTrees(2) ) ) - templates.indices.foreach(i => - templates(i).applyTemplate(Some(ergoTreeVersionInTests), templateValues(i)) shouldEqual expectedErgoTree(i) - ) + templates.indices.foreach { i => + val template = templates(i) + val applied = template.applyTemplate(Some(ergoTreeVersionInTests), templateValues(i)) + applied shouldEqual expectedErgoTree(i) + } } property("applyTemplate num(parameters) < num(constants)") { @@ -244,23 +246,20 @@ class ContractTemplateSpecification extends SerializationSpecification createParameter("p1", 0), createParameter("p2", 2)) val expressionTree = - EQ(Plus(ConstantPlaceholder(0, SType.typeInt), - ConstantPlaceholder(1, SType.typeInt)), - ConstantPlaceholder(2, SType.typeInt)).toSigmaProp + EQ(Plus(ConstantPlaceholder(0, SInt), + ConstantPlaceholder(1, SInt)), + ConstantPlaceholder(2, SInt)).toSigmaProp val template = createContractTemplate( - IndexedSeq(SType.typeInt, SType.typeInt, SType.typeInt).asInstanceOf[IndexedSeq[SType]], + IndexedSeq(SInt, SInt, SInt).asInstanceOf[IndexedSeq[SType]], Some(IndexedSeq(None, Some(20), None).asInstanceOf[IndexedSeq[Option[SType#WrappedType]]]), parameters, expressionTree ) val templateValues = Map("p1" -> IntConstant(10), "p2" -> IntConstant(30)) - var expectedErgoTreeVersion = (ErgoTree.ConstantSegregationHeader | ergoTreeVersionInTests).toByte - if (ergoTreeVersionInTests > 0) { - expectedErgoTreeVersion = (expectedErgoTreeVersion | ErgoTree.SizeFlag).toByte - } + val expectedErgoTreeHeader = setConstantSegregation(ergoTreeHeaderInTests) val expectedErgoTree = ErgoTree( - expectedErgoTreeVersion, + expectedErgoTreeHeader, IndexedSeq( IntConstant(10), IntConstant(20), diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala index 89330bc759..c3f7b43af4 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/DataJsonEncoderSpecification.scala @@ -4,17 +4,17 @@ package org.ergoplatform.sdk import java.math.BigInteger import org.scalacheck.Arbitrary._ import org.scalacheck.Gen -import sigma.data.RType -import sigmastate.SCollection.SByteArray -import sigmastate.SType.AnyOps -import sigmastate.Values.SigmaBoolean -import sigmastate._ -import sigmastate.eval.Extensions._ -import sigmastate.eval.{Evaluation, _} -import sigmastate.crypto.CryptoConstants.EcPointType -import sigmastate.exceptions.SerializerException -import sigma.{AvlTree, Box, Colls} -import sigmastate.serialization.SerializationSpecification +import sigma.data.{CAnyValue, RType, SigmaBoolean, TupleColl} +import sigma.ast._ +import sigma.ast.SCollection.SByteArray +import sigma.ast.SType.AnyOps +import sigma.crypto.EcPointType +import sigma.serialization.SerializerException +import sigma.util.Extensions.{BigIntegerOps, EcpOps, SigmaBooleanOps} +import sigma.Extensions.ArrayOps +import sigma.eval.SigmaDsl +import sigma.{AvlTree, Box, Colls, Evaluation} +import sigma.serialization.SerializationSpecification import scala.annotation.nowarn import scala.reflect.ClassTag @@ -51,8 +51,9 @@ class DataJsonEncoderSpecification extends SerializationSpecification { def testTuples[T <: SType](tpe: T) = { implicit val wWrapped: Gen[T#WrappedType] = wrappedTypeGen(tpe) - implicit val tag : ClassTag[T#WrappedType] = tpe.classTag[T#WrappedType] - implicit val tAny : RType[Any] = sigma.AnyType + val tT = Evaluation.stypeToRType(tpe) + @nowarn implicit val tag : ClassTag[T#WrappedType] = tT.classTag + @nowarn implicit val tAny : RType[Any] = sigma.AnyType forAll { in: (T#WrappedType, T#WrappedType) => val (x,y) = (in._1, in._2) roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe)) @@ -63,8 +64,8 @@ class DataJsonEncoderSpecification extends SerializationSpecification { @nowarn def testAnyValue[T <: SType](tpe: T) = { implicit val wWrapped = wrappedTypeGen(tpe) - implicit val tag = tpe.classTag[T#WrappedType] implicit val tT = Evaluation.stypeToRType(tpe) + implicit val tag = tT.classTag implicit val tAny = sigma.AnyType forAll { in: T#WrappedType => val x = CAnyValue(in) @@ -91,9 +92,9 @@ class DataJsonEncoderSpecification extends SerializationSpecification { forAll { x: Boolean => roundtrip[SBoolean.type](x, SBoolean) } forAll { x: Long => roundtrip[SLong.type](x, SLong) } forAll { x: String => roundtrip[SString.type](x, SString) } - forAll { x: BigInteger => roundtrip[SBigInt.type](x, SBigInt) } - forAll { x: EcPointType => roundtrip[SGroupElement.type](x, SGroupElement) } - forAll { x: SigmaBoolean => roundtrip[SSigmaProp.type](x, SSigmaProp) } + forAll { x: BigInteger => roundtrip[SBigInt.type](x.toBigInt, SBigInt) } + forAll { x: EcPointType => roundtrip[SGroupElement.type](x.toGroupElement, SGroupElement) } + forAll { x: SigmaBoolean => roundtrip[SSigmaProp.type](x.toSigmaProp, SSigmaProp) } forAll { x: AvlTree => roundtrip[SAvlTree.type](x, SAvlTree) } forAll { x: Array[Byte] => roundtrip[SByteArray](x.toColl, SByteArray) } forAll { x: Box => roundtrip[SBox.type](x, SBox) } @@ -183,8 +184,9 @@ class DataJsonEncoderSpecification extends SerializationSpecification { def testEncodeError[T <: SType](tpe: T) = { implicit val wWrapped = wrappedTypeGen(tpe) - implicit val tag = tpe.classTag[T#WrappedType] - implicit val tAny = sigma.AnyType + val tT = Evaluation.stypeToRType(tpe) + @nowarn implicit val tag = tT.classTag + @nowarn implicit val tAny = sigma.AnyType forAll { x: T#WrappedType => an[SerializerException] should be thrownBy { DataJsonEncoder.encode(TupleColl(x, x, x).asWrappedType, STuple(tpe, tpe, tpe)) diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala index f250b9fce5..1f3c8d9064 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala @@ -4,20 +4,16 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.propspec.AnyPropSpec import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks import sigma.{Coll, CollGens} -import org.ergoplatform.sdk.Extensions.{CollBuilderOps, CollOps, GenIterableOps, PairCollOps} -import sigma.data.RType -import sigmastate.eval.CostingSigmaDslBuilder +import org.ergoplatform.sdk.Extensions.{CollBuilderOps, CollOps, PairCollOps} +import sigma.Extensions.ArrayOps +import sigma.data.{CSigmaDslBuilder, RType} class ExtensionsSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with CollGens { def Coll[T](items: T*)(implicit cT: RType[T]) = - CostingSigmaDslBuilder.Colls.fromItems(items: _*) + CSigmaDslBuilder.Colls.fromItems(items: _*) val items: Iterable[(Int, String)] = Array((1, "a"), (2, "b"), (1, "c")) - property("Traversable.mapReduce") { - val res = items.mapReduce(p => (p._1, p._2))((v1, v2) => v1 + v2) - assertResult(List((1, "ac"), (2, "b")))(res) - } property("Coll.partition") { forAll(collGen) { col: Coll[Int] => @@ -29,14 +25,14 @@ class ExtensionsSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matc } property("Coll.mapReduce") { - def m(x: Int) = (math.abs(x) % 10, x) + def m(x: Int): (Int, Int) = (math.abs(x) % 10, x) forAll(collGen) { col => val res = col.mapReduce(m, plusF) val (ks, vs) = builder.unzip(res) vs.toArray.sum shouldBe col.toArray.sum ks.length <= 10 shouldBe true - res.toArray shouldBe col.toArray.toIterable.mapReduce(m)(plus).toArray + res.toArray shouldBe col.toArray.toColl.mapReduce[Int, Int](m, plusF).toArray } } diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala index 34c966df0a..0753b1ffc7 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala @@ -5,23 +5,18 @@ import io.circe._ import io.circe.syntax._ import org.ergoplatform.ErgoBox._ import org.ergoplatform.validation.ValidationRules +import org.ergoplatform._ import org.scalacheck.Arbitrary.arbitrary import scorex.crypto.authds.{ADDigest, ADKey} import scorex.util.ModifierId import scorex.util.encode.Base16 -import sigmastate.{AvlTreeData, SType} -import sigmastate.Values.{ByteArrayConstant, ByteConstant, ErgoTree, EvaluatedValue, IntConstant, LongArrayConstant, SigmaPropConstant} -import sigmastate.crypto.CryptoConstants -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.eval.Digest32Coll -import sigmastate.interpreter.{ContextExtension, ProverResult} -import sigmastate.serialization.SerializationSpecification -import sigmastate.utils.Helpers.DecoderResultOps // required for Scala 2.11 (extension method toTry) -import sigma.Coll -import sigma.{Header, PreHeader} -import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, ErgoLikeContext, ErgoLikeTransaction, ErgoLikeTransactionTemplate, Input, UnsignedErgoLikeTransaction, UnsignedInput} - -import scala.collection.mutable +import sigma.data.{AvlTreeData, Digest32Coll, ProveDlog} +import sigma.{Coll, Header, PreHeader} +import sigma.ast._ +import sigma.crypto.CryptoConstants +import sigma.interpreter.{ContextExtension, ProverResult} +import sigma.serialization.SerializationSpecification +import sigmastate.utils.Helpers.DecoderResultOps // required for Scala 2.11 class JsonSerializationSpec extends SerializationSpecification with JsonCodecs { @@ -138,7 +133,7 @@ class JsonSerializationSpec extends SerializationSpecification with JsonCodecs { val minerPkHex = "0326df75ea615c18acc6bb4b517ac82795872f388d5d180aac90eaa84de750b942" val minerPk = Base16.decode(minerPkHex).map { point => ProveDlog( - CryptoConstants.dlogGroup.ctx.decodePoint(point).asInstanceOf[CryptoConstants.EcPointType] + CryptoConstants.dlogGroup.ctx.decodePoint(point) ) }.get val regs = scala.collection.Map( diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala index 3908760e59..1c6d2efc5d 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/generators/ObjectGenerators.scala @@ -3,9 +3,9 @@ package org.ergoplatform.sdk.generators import org.ergoplatform.sdk.{ContractTemplate, Parameter} import org.ergoplatform.validation.ValidationSpecification import org.scalacheck.Gen -import sigmastate.Values.{ErgoTree, SigmaPropValue} -import sigmastate.serialization.generators.{ConcreteCollectionGenerators, TypeGenerators, ObjectGenerators => InterpreterObjectGenerators} -import sigmastate.{SType, TestsBase} +import sigma.ast.{ErgoTree, SType} +import sigmastate.TestsBase +import sigma.serialization.generators.{ConcreteCollectionGenerators, TypeGenerators, ObjectGenerators => InterpreterObjectGenerators} import scala.util.Random diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala index 80164f5f23..56b2ea02d1 100644 --- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala +++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala @@ -2,22 +2,20 @@ package org.ergoplatform.sdk.wallet.utils import org.ergoplatform.ErgoBox.{BoxId, NonMandatoryRegisterId, Token} import org.ergoplatform.sdk.wallet.Constants -import org.ergoplatform.sdk.wallet.secrets.ExtendedPublicKey -import org.ergoplatform.sdk.wallet.secrets.{DerivationPath, ExtendedSecretKey, Index, SecretKey} +import org.ergoplatform.sdk.wallet.secrets._ import org.ergoplatform.sdk.wallet.settings.EncryptionSettings +import org.ergoplatform._ import org.scalacheck.Arbitrary.arbByte import org.scalacheck.{Arbitrary, Gen} import scorex.crypto.authds.ADKey -import sigmastate.Values.{ByteArrayConstant, CollectionConstant, ErgoTree, EvaluatedValue, FalseLeaf, TrueLeaf} -import sigmastate.crypto.DLogProtocol.ProveDlog -import sigmastate.{SByte, SType} import scorex.util._ -import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, ErgoTreePredef, UnsignedErgoLikeTransaction, UnsignedInput} -import sigmastate.eval.Extensions._ -import scorex.util.{ModifierId, bytesToId} -import sigmastate.eval._ +import sigma.Extensions.ArrayOps +import sigma.ast._ +import sigma.ast.syntax.CollectionConstant +import sigma.crypto.CryptoFacade +import sigma.data.{Digest32Coll, ProveDlog} +import sigma.eval.Extensions.EvalIterableOps import sigmastate.helpers.TestingHelpers._ -import sigmastate.crypto.CryptoFacade trait Generators { @@ -100,7 +98,7 @@ trait Generators { Gen.choose(minValue, CoinsTotalTest / 1000) } - def ergoBoxGen(propGen: Gen[ErgoTree] = Gen.const(TrueLeaf.toSigmaProp), + def ergoBoxGen(propGen: Gen[ErgoTree] = Gen.const(ErgoTree.fromProposition(TrueLeaf.toSigmaProp)), tokensGen: Gen[Seq[Token]] = additionalTokensGen, valueGenOpt: Option[Gen[Long]] = None, heightGen: Gen[Int] = heightGen): Gen[ErgoBox] = for { @@ -144,10 +142,8 @@ trait Generators { } } - - def unsignedTxGen(secret: SecretKey): Gen[(IndexedSeq[ErgoBox], UnsignedErgoLikeTransaction)] = { - val dlog: Gen[ErgoTree] = Gen.const(secret.privateInput.publicImage.asInstanceOf[ProveDlog].toSigmaProp) + val dlog: Gen[ErgoTree] = Gen.const(ErgoTree.fromSigmaBoolean(secret.privateInput.publicImage.asInstanceOf[ProveDlog])) for { ins <- Gen.listOfN(2, ergoBoxGen(dlog)) diff --git a/sigma-js/README.md b/sigma-js/README.md index 1531088cbd..ff867e1cb6 100644 --- a/sigma-js/README.md +++ b/sigma-js/README.md @@ -1,5 +1,4 @@ [![CI](https://github.com/ScorexFoundation/sigmastate-interpreter/actions/workflows/ci.yml/badge.svg)](https://github.com/ScorexFoundation/sigmastate-interpreter/actions/workflows/ci.yml) -[![codecov](https://codecov.io/gh/ScorexFoundation/sigmastate-interpreter/branch/develop/graph/badge.svg?token=HNu2ZEOoV6)](https://codecov.io/gh/ScorexFoundation/sigmastate-interpreter) # ErgoScript compiler and ErgoTree interpreter @@ -21,29 +20,91 @@ Run following command to add Sigma.JS as a project dependency: npm install sigmastate-js ``` -# Examples +## Package organization + +All classes of this package are separated into several modules (which can also be thought +as layers). Each module contains a subset of all the class exported to JavaScript. You can +decide which modules to import in your application depending on which classes from this +package you want to use. +Each subsequent module contains all the classes from the previous modules and some new +classes thus forming a layering of modules. + +See TypeScript [definitions](sigmastate-js.d.ts) for the list of all exported classes, +methods and documentation. + +NOTE, you only need to import only one of the modules, the one which contains all the +classes you need. This will allow optimizing the size of the final bundle. + +The modules are compiled from Scala classes, which are exported to JavaScript (here is an +[example](../core/js/src/main/scala/sigma/js/Type.scala)). +The Scala declarations correspond to the TypeScript definitions. +Each exported type have two parts: the first part is a Scala class, the second part is a +companion object. In Scala the companion object is used to declare static methods and has +the same name as the corresponding class. In TypeScript the companion object is exported +with `$` suffix, thus if X is the JS class, then X$ is the JS object, which corresponds to X. + +## The list of modules and their exported classes +- [sigma-core module](../core/js) - contains core classes of Sigma.js library + - [Type](../core/js/src/main/scala/sigma/js/Type.scala) + - [Value](../core/js/src/main/scala/sigma/js/Value.scala) + - [GroupElement](../core/js/src/main/scala/sigma/js/GroupElement.scala) + - [SigmaProp](../core/js/src/main/scala/sigma/js/SigmaProp.scala) + - [AvlTree](../core/js/src/main/scala/sigma/js/AvlTree.scala) + +- [sigma-data module](../data/js) - contains classes for working with ErgoTree, addresses and all related serializers + - all classes from sigma-core module + - [ErgoTree](../data/js/src/main/scala/sigma/ast/js/ErgoTree.scala) + - [Address](../data/js/src/main/scala/org/ergoplatform/js/Address.scala) + - [Expr](../data/js/src/main/scala/sigma/ast/js/Expr.scala) + +- [sigma-interpreter module](../interpreter/js) - contains classes for proving sigma proposition and their verification + - all classes from sigma-data module + - [ProverHints](../interpreter/js/src/main/scala/sigma/interpreter/js/ProverHints.scala) + - [ProverSecret](../interpreter/js/src/main/scala/sigma/interpreter/js/ProverSecret.scala) + - [SigmaPropProver](../interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropProver.scala) + - [SigmaPropVerifier](../interpreter/js/src/main/scala/sigma/interpreter/js/SigmaPropVerifier.scala) + +- [sigma-sdk module](../sdk/js) - contains classes for reducing and signing transactions + - all classes from sigma-interpreter module + - [BlockchainParameters](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala) + - [BlockchainStateContext](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala) + - [ContractTemplate](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/ContractTemplate.scala) + - [Header](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala) + - [PreHeader](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala) + - [ProverBuilder](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala) + - [ReducedTransaction](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala) + - [SigmaProver](../sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala) + +- [sigma-compiler module](../sc/js) - contains classes for working with ErgoScript compiler + - [SigmaCompiler](../sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala) + +## Examples ### How to create Sigma type descriptors -Import `TypeObj` module, then use: +Import `Type$` module, then use its fields to access pre-defined descriptors of simple +types (e.g. `Type$.Int`). -- fields to create simple types (e.g. `TypeObj.Int`) -- method `TypeObj.pairType` (e.g. `TypeObj.pairType(TypeObj.Int, TypeObj.Long)`) -- method `TypeObj.collType` (e.g. `TypeObj.collType(TypeObj.Int)`) +Use factory methods like `Type$.pairType` to create more complex type descriptors. For +example,`Type$.pairType(Type$.Int, Type$.Long)` will create a descriptor of a pair of Int +and Long types `(Int, Long)`. -See examples in tests [Type.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/Type.spec.js) +See also examples in tests [Type.spec.js](tests/js/Type.spec.js) ### How to create Sigma values -Import `ValueObj` module, then use its methods. -See examples in tests [Value.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/Value.spec.js) +Import `Value$` module, then use its factory methods. +See examples in tests [Value.spec.js](tests/js/Value.spec.js) ### How to work with ErgoTree -Import `ErgoTreeObj` module, and `ErgoTree` class then use its methods. -See examples in tests [ErgoTree.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/79df4ca171a77233947d835042ce5c82ee520469/sigma-js/tests/js/ErgoTree.spec.js) +Import `ErgoTree$` module, and `ErgoTree` class then use its methods. +See examples in tests [ErgoTree.spec.js](tests/js/ErgoTree.spec.js) ### Compile ErgoScript to ErgoTree -Import `SigmaCompilerObj` module and `SigmaCompiler` class, then use its methods. -See compiler tests in [SigmaCompiler.spec.js](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/933acd7a3753725c8b41994c2126a20279b6809b/sigma-js/tests/js/SigmaCompiler.spec.js) +Import `SigmaCompiler$` module and `SigmaCompiler` class, then use its methods. +See compiler tests in [SigmaCompiler.spec.js](tests/js/SigmaCompiler.spec.js) + +### Other examples +See tests in [tests/js](tests/js) folder. diff --git a/sigma-js/package.json b/sigma-js/package.json index 79f15e803d..77ff920585 100644 --- a/sigma-js/package.json +++ b/sigma-js/package.json @@ -1,6 +1,6 @@ { "name": "sigmastate-js", - "version": "0.3.0", + "version": "0.4.0", "description": "Sigma.js library", "files": [ "dist/", diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts index 775f585570..b70ca39258 100644 --- a/sigma-js/sigmastate-js.d.ts +++ b/sigma-js/sigmastate-js.d.ts @@ -1,304 +1,664 @@ declare module "sigmastate-js/main" { - import { - Amount, - Box, - EIP12UnsignedInput, - NonMandatoryRegisters, SignedTransaction, TokenAmount, - UnsignedTransaction - } from "@fleet-sdk/common"; - - type SigmaCompilerNamedConstantsMap = { [key: string]: Value }; - type HexString = string; - type ByteArray = { u: Int8Array }; - - export declare class ErgoTree { - toHex(): HexString; - bytes(): ByteArray; - header(): number; - version(): number; - isConstantSegregation(): boolean; - hasSize(): boolean; - constants(): Value[]; - template(): ByteArray; - templateHex(): HexString; - toString(): string; - } - - export declare class ErgoTreeObj { - static fromHex(value: HexString): ErgoTree; - } - - export declare class GroupElement { - toPointHex(): HexString; - } - - export declare class GroupElementObj { - static fromPointHex(value: HexString): GroupElement; - } - - export declare class SigmaProp { - } - - export declare class SigmaPropObj { - static fromPointHex(value: HexString): SigmaProp; - } - - export declare class AvlTree { - digest: HexString; - insertAllowed: Boolean; - updateAllowed: Boolean; - removeAllowed: Boolean; - keyLength: number; - valueLengthOpt: number | undefined; - } - - export declare class PreHeader { - /** Block version, to be increased on every soft and hardfork. */ - version: number; - /** Hex of id of parent block */ - parentId: HexString; - /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ - timestamp: bigint; - /** Current difficulty in a compressed view. - * NOTE: actually it is unsigned integer */ - nBits: bigint; - /** Block height */ - height: number; - /** Miner public key (hex of EC Point). Should be used to collect block rewards. */ - minerPk: GroupElement; - /** Hex of miner votes bytes for changing system parameters. */ - votes: HexString; - } - - export declare class Header { - /** Hex representation of ModifierId of this Header */ - id: HexString; - /** Block version, to be increased on every soft and hardfork. */ - version: number; - /** Hex representation of ModifierId of the parent block */ - parentId: HexString; - /** Hex hash of ADProofs for transactions in a block */ - ADProofsRoot: HexString; - /** AvlTree of a state after block application */ - stateRoot: AvlTree; - /** Hex of root hash (for a Merkle tree) of transactions in a block. */ - transactionsRoot: HexString; - /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ - timestamp: bigint; - /** Current difficulty in a compressed view. - * NOTE: actually it is unsigned Int */ - nBits: bigint; - /** Block height */ - height: number; - /** Hex of root hash of extension section */ - extensionRoot: HexString; - - /** Miner public key (hex of EC Point). Should be used to collect block rewards. - * Part of Autolykos solution. - */ - minerPk: GroupElement; - - /** One-time public key (hex of EC Point). Prevents revealing of miners secret. */ - powOnetimePk: GroupElement; - - /** Hex of nonce bytes */ - powNonce: HexString; - - /** Distance between pseudo-random number, corresponding to nonce `powNonce` and a secret, - * corresponding to `minerPk`. The lower `powDistance` is, the harder it was to find this solution. */ - powDistance: bigint; - - /** Miner votes for changing system parameters. */ - votes: HexString; - } - - export declare class BlockchainParameters { - storageFeeFactor: number; - minValuePerByte: number; - maxBlockSize: number; - tokenAccessCost: number; - inputCost: number; - dataInputCost: number; - outputCost: number; - maxBlockCost: number; - softForkStartingHeight: number | undefined; - softForkVotesCollected: number | undefined; - blockVersion: number; - } - - export declare class BlockchainStateContext { - sigmaLastHeaders: Header[]; - previousStateDigest: HexString; - sigmaPreHeader: PreHeader; - } - - export declare class Type { - name: string; - toString(): string; - } - - export declare class TypeObj { - static Byte: Type; - static Short: Type; - static Int: Type; - static Long: Type; - static BigInt: Type; - static GroupElement: Type; - static SigmaProp: Type; - static Box: Type; - static AvlTree: Type; - static Context: Type; - static Header: Type; - static PreHeader: Type; - static SigmaDslBuilder: Type; - static pairType(left: Type, right: Type): Type; - static collType(elemType: Type): Type; - } - - export declare class Value { - data: T; - tpe: Type; - toHex(): HexString; - } - - export declare class ValueObj { - static ofByte(value: number): Value; - static ofShort(value: number): Value; - static ofInt(value: number): Value; - static ofLong(value: bigint): Value; - static ofBigInt(value: bigint): Value; - static ofGroupElement(pointHex: string): Value; - static ofSigmaProp(pointHex: string): Value; - static pairOf(left: Value, right: Value): Value<[R, L]>; - static collOf(items: T[], elemType: Type): Value; - static fromHex(hex: HexString): Value; - } - - export declare class SigmaCompiler { - compile( - namedConstants: SigmaCompilerNamedConstantsMap, - segregateConstants: boolean, - additionalHeaderFlags: number, - ergoScript: string - ): ErgoTree; - } - - export declare class SigmaCompilerObj { - static forMainnet(): SigmaCompiler; - static forTestnet(): SigmaCompiler; - } - - /** Represents results for transaction reduction by {@link SigmaProver}. */ - export declare class ReducedTransaction { - /** Serialized bytes of this transaction in hex format. */ - toHex(): HexString; - } - - export declare class ReducedTransactionObj { - /** Creates a {@link ReducedTransaction} from serialized bytes in hex format. */ - fromHex(hex: HexString): ReducedTransaction; - } - - /** Represents a prover for signing Ergo transactions and messages. - * - * Equivalent of [[org.ergoplatform.sdk.SigmaProver]] available from JS. - */ - export declare class SigmaProver { - /** Returns the Pay-to-Public-Key (P2PK) address associated with the prover's public key. - * The returned address corresponds to the master secret derived from the mnemonic - * phrase configured in the [[ProverBuilder]]. + import { + Amount, + Box, + EIP12UnsignedInput, + NonMandatoryRegisters, SignedTransaction, TokenAmount, + UnsignedTransaction + } from "@fleet-sdk/common"; + + type SigmaCompilerNamedConstantsMap = { [key: string]: Value }; + type HexString = string; + type ByteArray = { u: Int8Array }; + + export declare class Address { + /** Serialize this address to bytes. */ + addressBytes(): ByteArray; + + /** Address type code used to differentiate between pay-to-public-key, pay-to-script, + * pay-to-script-hash addresses. + * + * @see [[P2PKAddress]], [[P2SAddress]], [[P2SHAddress]] + */ + addressTypePrefix(): number; + + /** @return true if this address from Ergo mainnet. */ + isMainnet(): boolean; + + /** @return true if this address has Pay-To-Public-Key type. */ + isP2PK(): boolean; + + /** @return underlying {@link P2PKAddress}. + * @throws IllegalArgumentException if this instance is not P2PK address + */ + asP2PK(): P2PKAddress; + + /** @return true if this address has Pay-To-Script type. */ + isP2S(): boolean; + + /** @return underlying {@link P2PKAddress}. + * @throws IllegalArgumentException if this instance is not P2PK address + */ + asP2S(): P2SAddress; + + /** @return true if this address has Pay-To-Script-Hash type. */ + isP2SH(): boolean; + + /** @return underlying {@link P2SHAddress}. + * @throws IllegalArgumentException if this instance is not P2SH address + */ + asP2SH(): P2SHAddress; + + /** Extracts a {@link SigmaProp} from this address of the underlying ErgoTree if of + * specific form. + * @see ErgoTree.toSigmaBooleanOpt() + */ + toSigmaPropOpt(): SigmaProp | undefined; + + /** ErgoTree which corresponds to the address (depending on the address type). + * + * @see P2PKAddress, P2SAddress, P2SHAddress + */ + toErgoTree(): ErgoTree; + + /** @return this addresses ErgoTree's proposition bytes. Use this to store this address + * on Box registers. + */ + toPropositionBytes(): ByteArray; + + /** Converts the given {@link Address} to Base58 string. */ + toString(): string; + } + + export declare class Address$ { + /** Creates JS wrapper over given [[ErgoAddress]]. */ + static fromErgoAddress(ergoAddress: any /*org.ergoplatform.ErgoAddress*/): Address; + + /** Deserializes an ErgoTree instance from an address string. + * + * @param base58String a Base58 string representing the serialized ErgoTree + */ + static fromString(base58String: string): Address; + + /** Creates an `Address` instance from an `ErgoTree` and a network prefix. + * + * @param ergoTree The `ErgoTree` instance to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `ErgoTree` and network prefix. + */ + static fromErgoTree(ergoTree: ErgoTree, networkPrefix: number): Address; + + /** + * Creates an `Address` from a `SigmaProp` and a network prefix. + * + * @param sigmaProp The `SigmaProp` to be converted into an `Address`. + * @param networkPrefix The network prefix indicating the network for which the address is valid. + * @return An `Address` instance corresponding to the given `SigmaProp` and network prefix. + */ + static fromSigmaProp(sigmaProp: SigmaProp, networkPrefix: number): Address; + + /** Creates address from given ergovalue containing an ErgoTree proposition bytes. + * Use this to convert a box register containing an ErgoTree into its address. + * + * @param networkPrefix mainnet or testnet network + * @param propositionBytes ErgoTree proposition bytes + */ + static fromPropositionBytes(networkPrefix: number, propositionBytes: ByteArray): Address + } + + /** Implementation of pay-to-public-key {@link Address}. */ + export declare class P2PKAddress extends Address { + /** Converts this address to the underlying ProveDlog sigma proposition wrapped in {@link SigmaProp}. */ + toSigmaProp(): SigmaProp; + + /** Extract the underlying {@link GroupElement} of this address. */ + getPublicKeyGE(): GroupElement; + } + + /** Implementation of pay-to-script {@link Address}. */ + export declare class P2SAddress extends Address { + } + + /** Implementation of pay-to-script-hash {@link Address}. */ + export declare class P2SHAddress extends Address { + } + + export declare class ErgoTree { + toHex(): HexString; + + bytes(): ByteArray; + + header(): number; + + version(): number; + + isConstantSegregation(): boolean; + + hasSize(): boolean; + + constants(): Value[]; + + template(): ByteArray; + + templateHex(): HexString; + + toString(): string; + } + + export declare class ErgoTree$ { + static fromHex(value: HexString): ErgoTree; + } + + export declare class GroupElement { + toPointHex(): HexString; + } + + export declare class GroupElement$ { + static fromPointHex(value: HexString): GroupElement; + } + + export declare class SigmaProp { + } + + export declare class SigmaProp$ { + static fromPointHex(value: HexString): SigmaProp; + } + + export declare class AvlTree { + digest: HexString; + insertAllowed: Boolean; + updateAllowed: Boolean; + removeAllowed: Boolean; + keyLength: number; + valueLengthOpt: number | undefined; + } + + export declare class PreHeader { + /** Block version, to be increased on every soft and hardfork. */ + version: number; + /** Hex of id of parent block */ + parentId: HexString; + /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ + timestamp: bigint; + /** Current difficulty in a compressed view. + * NOTE: actually it is unsigned integer */ + nBits: bigint; + /** Block height */ + height: number; + /** Miner public key (hex of EC Point). Should be used to collect block rewards. */ + minerPk: GroupElement; + /** Hex of miner votes bytes for changing system parameters. */ + votes: HexString; + } + + export declare class Header { + /** Hex representation of ModifierId of this Header */ + id: HexString; + /** Block version, to be increased on every soft and hardfork. */ + version: number; + /** Hex representation of ModifierId of the parent block */ + parentId: HexString; + /** Hex hash of ADProofs for transactions in a block */ + ADProofsRoot: HexString; + /** AvlTree of a state after block application */ + stateRoot: AvlTree; + /** Hex of root hash (for a Merkle tree) of transactions in a block. */ + transactionsRoot: HexString; + /** Block timestamp (in milliseconds since beginning of Unix Epoch) */ + timestamp: bigint; + /** Current difficulty in a compressed view. + * NOTE: actually it is unsigned Int */ + nBits: bigint; + /** Block height */ + height: number; + /** Hex of root hash of extension section */ + extensionRoot: HexString; + + /** Miner public key (hex of EC Point). Should be used to collect block rewards. + * Part of Autolykos solution. + */ + minerPk: GroupElement; + + /** One-time public key (hex of EC Point). Prevents revealing of miners secret. */ + powOnetimePk: GroupElement; + + /** Hex of nonce bytes */ + powNonce: HexString; + + /** Distance between pseudo-random number, corresponding to nonce `powNonce` and a secret, + * corresponding to `minerPk`. The lower `powDistance` is, the harder it was to find this solution. */ + powDistance: bigint; + + /** Miner votes for changing system parameters. */ + votes: HexString; + } + + export declare class BlockchainParameters { + storageFeeFactor: number; + minValuePerByte: number; + maxBlockSize: number; + tokenAccessCost: number; + inputCost: number; + dataInputCost: number; + outputCost: number; + maxBlockCost: number; + softForkStartingHeight: number | undefined; + softForkVotesCollected: number | undefined; + blockVersion: number; + } + + export declare class BlockchainStateContext { + sigmaLastHeaders: Header[]; + previousStateDigest: HexString; + sigmaPreHeader: PreHeader; + } + + export declare class Type { + name: string; + + toString(): string; + } + + export declare class Type$ { + static Byte: Type; + static Short: Type; + static Int: Type; + static Long: Type; + static BigInt: Type; + static GroupElement: Type; + static SigmaProp: Type; + static Box: Type; + static AvlTree: Type; + static Context: Type; + static Header: Type; + static PreHeader: Type; + static SigmaDslBuilder: Type; + + static pairType(left: Type, right: Type): Type; + + static collType(elemType: Type): Type; + } + + export declare class Value { + data: T; + tpe: Type; + + toHex(): HexString; + } + + export declare class Value$ { + static ofByte(value: number): Value; + + static ofShort(value: number): Value; + + static ofInt(value: number): Value; + + static ofLong(value: bigint): Value; + + static ofBigInt(value: bigint): Value; + + static ofGroupElement(pointHex: string): Value; + + static ofSigmaProp(pointHex: string): Value; + + static pairOf(left: Value, right: Value): Value<[R, L]>; + + static collOf(items: T[], elemType: Type): Value; + + static fromHex(hex: HexString): Value; + } + + export declare class SigmaCompiler { + compile( + namedConstants: SigmaCompilerNamedConstantsMap, + segregateConstants: boolean, + additionalHeaderFlags: number, + ergoScript: string + ): ErgoTree; + } + + export declare class SigmaCompiler$ { + static forMainnet(): SigmaCompiler; + + static forTestnet(): SigmaCompiler; + } + + /** Represents results for transaction reduction by {@link SigmaProver}. */ + export declare class ReducedTransaction { + /** Serialized bytes of this transaction in hex format. */ + toHex(): HexString; + } + + export declare class ReducedTransaction$ { + /** Creates a {@link ReducedTransaction} from serialized bytes in hex format. */ + fromHex(hex: HexString): ReducedTransaction; + } + + /** Represents hints used by [[SigmaPropProver]] to perform operations as part of + * multi-signature scheme. See [EIP-11](https://github.com/ergoplatform/eips/pull/8). */ - getP2PKAddress(): HexString; - - /** Returns the prover's secret key. */ - getSecretKey(): bigint; - - /** Returns an array of EIP-3 addresses associated with the prover's secret keys. */ - getEip3Addresses(): HexString[]; - - /** Reduces the transaction to the reduced form, which is ready to be signed. - * @param stateCtx blockchain state context - * @param unsignedTx unsigned transaction to be reduced (created by Fleet builders) - * @param boxesToSpend boxes to be spent by the transaction - * @param dataInputs data inputs to be used by the transaction - * @param tokensToBurn tokens to be burned by the transaction - * @param baseCost base cost of the transaction - * @return reduced transaction - */ - reduce( - stateCtx: BlockchainStateContext, - unsignedTx: UnsignedTransaction, - boxesToSpend: EIP12UnsignedInput[], - dataInputs: Box[], - tokensToBurn: TokenAmount[], - baseCost: number): ReducedTransaction; - - /** Signs the reduced transaction. - * @param reducedTx reduced transaction to be signed - * @return signed transaction containting all the required proofs (signatures) - */ - signReduced(reducedTx: ReducedTransaction): SignedTransaction; - } - - /** Equivalent of [[sdk.ProverBuilder]] available from JS. - * - * @param parameters Blockchain parameters re-adjustable via miners voting and - * voting-related data. All of them are included into extension - * section of a first block of a voting epoch. - * @param network Network prefix to use for addresses. - */ - export declare class ProverBuilder { - /** Configure this builder to use the given seed when building a new prover. + export declare class ProverHints { + + } + + export declare class ProverHints$ { + /** Empty bag of hints. Immutable value can be reused where necessary. */ + empty(): ProverHints + } + + /** Represents one secret (aka SigmaProtocolPrivateInput) used by [[SigmaPropProver]]. */ + export declare class ProverSecret { + /** Public key generated from the secret. + * Represents proof of knowledge sigma proposition. + */ + publicKey(): SigmaProp + + /** Secret random number stored in this instance. */ + secret(): bigint + } + + export declare class ProverSecret$ { + /** Creates a new [[ProverSecret]] instance for the given secret of descrete logarithm + * sigma protocol. + * @param w secret exponent value + */ + dlog(w: bigint): ProverSecret + + /** Creates a new [[ProverSecret]] instance for the given secret of Diffie Hellman tuple + * sigma protocol. + * @param w secret exponent value used to compute `u = g^w` and `v = h^w`, where `g` and `h` are generators + * @param dhtProp a [[SigmaProp]] representing public key of Diffie Hellman tuple sigma protocol, should be created using `w` + */ + dht(w: bigint, dhtProp: SigmaProp): ProverSecret + } + + /** Prover which can sign messages (generate proofs) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. * - * @param mnemonicPhrase secret seed phrase to be used in prover for generating proofs. - * @param mnemonicPass password to protect secret seed phrase. - */ - withMnemonic(mnemonicPhrase: HexString, mnemonicPass: HexString): ProverBuilder; - /** Configure this builder to derive the new EIP-3 secret key with the given index. - * The derivation uses master key derived from the mnemonic configured using - * [[ErgoProverBuilder.withMnemonic]]. + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. * - * @param index last index in the EIP-3 derivation path. + * @see SigmaPropVerifier */ - withEip3Secret(index: number): ProverBuilder; - - /** Configures this builder to use group elements (g, h, u, v) and secret x for a - * ProveDHTuple statement when building a new prover. - * - * ProveDHTuple is a statement consisting of 4 group elements (g, h, u, v) and - * requires the prover to prove knowledge of secret integer x such that. + export declare class SigmaPropProver { + /** + * A method which is generating commitments for all the public keys provided. + * This is used as part of multi-signature scheme. + * + * Currently only keys in form of ProveDlog and ProveDiffieHellman are supported, not more complex subtrees. + * + * @param sigmaTree - crypto-tree which is being signed + * @param generateFor - public keys for which commitments should be generated + * @return generated commitments in a form of prover hints + * - private, containing secret randomness + * - public, containing only commitments + */ + generateCommitmentsFor( + sigmaTree: SigmaProp, + generateFor: SigmaProp[]): ProverHints + + /** + * A method which is extracting partial proofs of secret knowledge for particular secrets with their + * respective public images given. Useful for distributed signature applications. + * + * See DistributedSigSpecification for examples of usage. + * + * @param sigmaTree - public key (in form of a sigma-tree) + * @param proof - signature for the key + * @param realSecretsToExtract - public keys of secrets with real proofs + * @param simulatedSecretsToExtract - public keys of secrets with simulated proofs + * @return - bag of OtherSecretProven and OtherCommitment hints + */ + hintsForMultisig( + sigmaTree: SigmaProp, + proof: Int8Array, + realSecretsToExtract: SigmaProp[], + simulatedSecretsToExtract: SigmaProp[]): ProverHints + + /** + * Generate commitments for given crypto-tree (sigma-tree) for prover's secrets. + */ + generateCommitments(sigmaTree: SigmaProp): ProverHints + + /** Sign arbitrary message under a key representing a statement provable via a sigma-protocol. + * + * @param sigmaProp - public key + * @param message - message to sign + * @param hintsBag - additional hints for a signer (useful for distributed signing) + * @return - signature or error + */ + signMessage( + sigmaProp: SigmaProp, + message: Int8Array, + hintsBag: ProverHints): Int8Array + } + + export declare class SigmaPropProver$ { + /** Creates a new [[SigmaPropProver]] with the given secrets. */ + withSecrets(secrets: ProverSecret[]): SigmaPropProver + } + + /** Verifier which can verify signature (proof) for arbitrary sigma propositions + * represented by [[SigmaProp]] values. * - * u = g^x - * and - * y = h^x + * See [EIP-11](https://github.com/ergoplatform/eips/pull/8) for details of multi-signature scheme. * - * @param g [[GroupElement]] instance defining g - * @param h [[GroupElement]] instance defining h - * @param u [[GroupElement]] instance defining u - * @param v [[GroupElement]] instance defining v - * @param x [[BigInteger]] instance defining x - * @see - * example - * @see - * implementation + * @see SigmaPropProver */ - withDHTSecret(g: HexString, h: HexString, u: HexString, v: HexString, x: bigint): ProverBuilder; - - /** This allows adding additional secret for use in proveDlog, when the secret is not - * part of the wallet. + export declare class SigmaPropVerifier { + /** + * Verify a signature on given (arbitrary) message for a given sigma proposition (public key). + * + * @param sigmaProp public key (represented as a sigma proposition) + * @param message message + * @param signature signature for the message + * @return whether signature is valid or not (valid signature contains proofs for the sigma proposition) + */ + verifySignature( + sigmaProp: SigmaProp, + message: Int8Array, + signature: Int8Array): boolean + } + export declare class SigmaPropVerifier$ { + /** Create a new instance of [[SigmaPropVerifier]]. */ + create(): SigmaPropVerifier + } + + export declare class Utils { + /** Convert an Int8Array to a hex string. */ + int8ArrayToHex(arr: Int8Array): string + } + + /** Represents a prover for signing Ergo transactions and messages. * - * Multiple secrets can be added by calling this method multiple times. + * Equivalent of [[org.ergoplatform.sdk.SigmaProver]] available from JS. + */ + export declare class SigmaProver { + /** Returns the Pay-to-Public-Key (P2PK) address associated with the prover's public key. + * The returned address corresponds to the master secret derived from the mnemonic + * phrase configured in the [[ProverBuilder]]. + */ + getP2PKAddress(): HexString; + + /** Returns the prover's secret key. */ + getSecretKey(): bigint; + + /** Returns an array of EIP-3 addresses associated with the prover's secret keys. */ + getEip3Addresses(): HexString[]; + + /** Reduces the transaction to the reduced form, which is ready to be signed. + * @param stateCtx blockchain state context + * @param unsignedTx unsigned transaction to be reduced (created by Fleet builders) + * @param boxesToSpend boxes to be spent by the transaction + * @param dataInputs data inputs to be used by the transaction + * @param tokensToBurn tokens to be burned by the transaction + * @param baseCost base cost of the transaction + * @return reduced transaction + */ + reduce( + stateCtx: BlockchainStateContext, + unsignedTx: UnsignedTransaction, + boxesToSpend: EIP12UnsignedInput[], + dataInputs: Box[], + tokensToBurn: TokenAmount[], + baseCost: number): ReducedTransaction; + + /** Signs the reduced transaction. + * @param reducedTx reduced transaction to be signed + * @return signed transaction containting all the required proofs (signatures) + */ + signReduced(reducedTx: ReducedTransaction): SignedTransaction; + } + + /** Equivalent of [[sdk.ProverBuilder]] available from JS. * - * Multiple secrets are necessary for statements that need multiple proveDlogs, such - * as proveDlog(a) && proveDlog(b), where a and b are two group elements. + * @param parameters Blockchain parameters re-adjustable via miners voting and + * voting-related data. All of them are included into extension + * section of a first block of a voting epoch. + * @param network Network prefix to use for addresses. */ - withDLogSecret(x: bigint): ProverBuilder; - - /** Builds a new prover using provided configuration. */ - build(): SigmaProver; - } - - export declare class ProverBuilderObj { - static create(parameters: BlockchainParameters, network: number): ProverBuilder; - } + export declare class ProverBuilder { + /** Configure this builder to use the given seed when building a new prover. + * + * @param mnemonicPhrase secret seed phrase to be used in prover for generating proofs. + * @param mnemonicPass password to protect secret seed phrase. + */ + withMnemonic(mnemonicPhrase: HexString, mnemonicPass: HexString): ProverBuilder; + + /** Configure this builder to derive the new EIP-3 secret key with the given index. + * The derivation uses master key derived from the mnemonic configured using + * [[ErgoProverBuilder.withMnemonic]]. + * + * @param index last index in the EIP-3 derivation path. + */ + withEip3Secret(index: number): ProverBuilder; + + /** Configures this builder to use group elements (g, h, u, v) and secret x for a + * ProveDHTuple statement when building a new prover. + * + * ProveDHTuple is a statement consisting of 4 group elements (g, h, u, v) and + * requires the prover to prove knowledge of secret integer x such that. + * + * u = g^x + * and + * y = h^x + * + * @param g [[GroupElement]] instance defining g + * @param h [[GroupElement]] instance defining h + * @param u [[GroupElement]] instance defining u + * @param v [[GroupElement]] instance defining v + * @param x [[BigInteger]] instance defining x + * @see + * example + * @see + * implementation + */ + withDHTSecret(g: HexString, h: HexString, u: HexString, v: HexString, x: bigint): ProverBuilder; + + /** This allows adding additional secret for use in proveDlog, when the secret is not + * part of the wallet. + * + * Multiple secrets can be added by calling this method multiple times. + * + * Multiple secrets are necessary for statements that need multiple proveDlogs, such + * as proveDlog(a) && proveDlog(b), where a and b are two group elements. + */ + withDLogSecret(x: bigint): ProverBuilder; + + /** Builds a new prover using provided configuration. */ + build(): SigmaProver; + } + + export declare class ProverBuilder$ { + static create(parameters: BlockchainParameters, network: number): ProverBuilder; + } + + /** + * Represents a ContractTemplate parameter. + */ + export declare class Parameter { + /** User readable parameter name (string bytes in UTF-8 encoding) */ + name: String + /** User readable parameter description (string bytes in UTF-8 encoding) */ + description: String + /** Index in the ErgoTree.constants array */ + constantIndex: number + } + + /** JavaScript class wrapping the Scala [[sigma.ast.Value]]. */ + export declare class Expr { + } + + /** + * Represents a reusable ContractTemplate with support to generate ErgoTree based on + * provided parameters. + */ + export declare class ContractTemplate { + /** + * The optional version of ErgoTree which should be used. If this value is not + * provided here then it must be provided while generating the `ErgoTree` by + * calling `applyTemplate`. + */ + treeVersion: number | undefined; + + /** User readable name (non-empty string bytes in UTF-8 encoding). */ + name: string; + + /** User readable contract description (string bytes in UTF-8 encoding). */ + description: string; + + /** List denoting the type of ConstantPlaceholders in the expressionTree. */ + constTypes: Type[]; + + /** + * Optional list of optional default values for the ConstantPlaceholders in the + * expressionTree. If an entry in the sequence is None, it must have a + * corresponding entry in parameters and its value must be provided while + * generating the `ErgoTree` by calling `applyTemplate`. If all the entries are + * None, the whole `constValues` field can be set to None. + */ + constValues: (Value | undefined)[] | undefined; + + /** + * Typed template parameters of the contract template. It must have an entry for + * each `ConstantPlaceholder` which has a `None` in the `constValues` field. Other + * fields which do have a value defined in `constValues` can also be allowed to be + * optionally overridden by accepting it in `parameters`. + */ + parameters: Parameter[]; + + /** Root of the contract which is a valid expression of `SigmaProp` type. Must + * have constants segregated into `constTypes` and optionally `constValues` + */ + expressionTree: Expr + + /** @return JSON representation of this contract template pretty-printed to a string + * indentation of two spaces. + */ + toJsonString(): String + + /** + * Generate the ErgoTree from the template by providing the values for parameters. + * + * @param version the version of the `ErgoTree` to use. Must be provided if the `treeVersion` was not provided in the + * template. + * @param paramValues the name-value map for the parameters accepted by the `ContractTemplate`. Must contain an entry + * for each parameter for which no default value was provided in the template. Optionally, can also + * provide values to override for parameters which do have a default value defined in the template. + * The type of the provided value must match with the corresponding entry in the `constTypes` + * provided in the template. + * @return `ErgoTree` generated by replacing the template parameters with the value provided in `paramValues`. + */ + applyTemplate( + version: number | undefined, + paramValues: SigmaCompilerNamedConstantsMap): ErgoTree + } + + export declare class ContractTemplate$ { + /** Create a new contract template from a JSON string. + * + * @param json JSON string representing a contract template. + * @return a new contract template. + */ + fromJsonString(json: String): ContractTemplate + } } diff --git a/sigma-js/tests/js/Address.spec.js b/sigma-js/tests/js/Address.spec.js new file mode 100644 index 0000000000..0e78434cf0 --- /dev/null +++ b/sigma-js/tests/js/Address.spec.js @@ -0,0 +1,53 @@ +const { Address, Address$ } = require("sigmastate-js/main"); + +describe("Smoke tests for API exporting", () => { + it("Should export Address object", () => { + expect(Address).not.toBeUndefined(); + }); +}); + +describe("Address", () => { + let addrStr = "9iJd9drp1KR3R7HLi7YmQbB5sJ5HFKZoPb5MxGepamggJs5vDHm"; + let p2sStr = "JryiCXrZf5VDetH1PM7rKDX3q4sLF34AdErWJFqG87Hf5ikTDf636b35Nr7goWMdRUKA3ZPxdeqFNbQzBjhnDR9SUMYwDX1tdV8ZuGgXwQPaRVcB9" + + it("roundtrip for P2PK", () => { + let addr = Address$.fromString(addrStr); + expect(addr.isP2PK()).toEqual(true) + expect(addr.isP2S()).toEqual(false) + + expect(addr.toString()).not.toBeUndefined(); + expect(addr.toString()).toEqual(addrStr) + expect(addr.asP2PK()).not.toBeUndefined(); + }); + + it("roundtrip for P2S", () => { + let addr = Address$.fromString(p2sStr); + expect(addr.isP2S()).toEqual(true) + expect(addr.isP2PK()).toEqual(false) + + expect(addr.toString()).not.toBeUndefined(); + expect(addr.toString()).toEqual(p2sStr) + expect(addr.asP2S()).not.toBeUndefined(); + }); + + it("toSigmaPropOpt", () => { + let addr = Address$.fromString(addrStr); + + expect(addr.isMainnet()).toEqual(true) + expect(addr.toSigmaPropOpt()).not.toBeUndefined() + }); + + it("other properties", () => { + let addr = Address$.fromString(addrStr); + expect(addr.toErgoTree()).not.toBeUndefined() + expect(addr.asP2PK()).not.toBeUndefined() + expect(addr.asP2PK().isP2PK()).toEqual(true) + expect(addr.asP2PK().toErgoTree()).not.toBeUndefined() + expect(addr.asP2PK().toSigmaProp()).not.toBeUndefined() + expect(addr.asP2PK().toPropositionBytes()).not.toBeUndefined() + expect(addr.asP2PK().addressTypePrefix()).not.toBeUndefined() + expect(addr.asP2PK().getPublicKeyGE()).not.toBeUndefined() + }); + +}); + diff --git a/sigma-js/tests/js/ContractTemplate.spec.js b/sigma-js/tests/js/ContractTemplate.spec.js new file mode 100644 index 0000000000..fc45198549 --- /dev/null +++ b/sigma-js/tests/js/ContractTemplate.spec.js @@ -0,0 +1,64 @@ +const {ContractTemplate, ContractTemplate$, Value$} = require("sigmastate-js/main"); + +describe("Smoke tests for API exporting", () => { + it("Should export ContractTempate object", () => { + expect(ContractTemplate).not.toBeUndefined(); + }); +}); + +describe("ContractTemplate", () => { + let templateJsonStr = "{\n" + + " \"treeVersion\" : null,\n" + + " \"name\" : \"TestContractTemplate\",\n" + + " \"description\" : \"TestContractTemplateDescription\",\n" + + " \"constTypes\" : [\n" + + " \"02\",\n" + + " \"02\",\n" + + " \"02\"\n" + + " ],\n" + + " \"constValues\" : [\n" + + " 10,\n" + + " 20,\n" + + " 30\n" + + " ],\n" + + " \"parameters\" : [\n" + + " {\n" + + " \"name\" : \"p1\",\n" + + " \"description\" : \"p1_description\",\n" + + " \"constantIndex\" : 0\n" + + " },\n" + + " {\n" + + " \"name\" : \"p2\",\n" + + " \"description\" : \"p2_description\",\n" + + " \"constantIndex\" : 1\n" + + " },\n" + + " {\n" + + " \"name\" : \"p3\",\n" + + " \"description\" : \"p3_description\",\n" + + " \"constantIndex\" : 2\n" + + " }\n" + + " ],\n" + + " \"expressionTree\" : \"d1939a730073017302\"\n" + + "}"; + + let template = ContractTemplate$.fromJsonString(templateJsonStr); + + it("Json encoding roundtrip", () => { + expect(template).not.toBeUndefined(); + expect(template.toJsonString()).toEqual(templateJsonStr); + } + ); + + it("applyTemplate", () => { + let templateValues = { + "p1": Value$.ofByte(10), + "p2": Value$.ofByte(40), + "p3": Value$.ofByte(50) + }; + let tree = template.applyTemplate(2, templateValues); + expect(tree.toHex()).toEqual("1a1003020a02280232d1939a730073017302"); + } + ); + +}); + diff --git a/sigma-js/tests/js/ErgoTree.spec.js b/sigma-js/tests/js/ErgoTree.spec.js index 688ea9bfa3..48058bc788 100644 --- a/sigma-js/tests/js/ErgoTree.spec.js +++ b/sigma-js/tests/js/ErgoTree.spec.js @@ -1,4 +1,4 @@ -const { ErgoTree, ErgoTreeObj } = require("sigmastate-js/main"); +const { ErgoTree, ErgoTree$ } = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { it("Should export ErgoTree object", () => { @@ -10,13 +10,13 @@ describe("Smoke tests for ErgoTree", () => { let hex = "100604000e000400040005000500d803d601e30004d602e4c6a70408d603e4c6a7050595e67201d804d604b2a5e4720100d605b2db63087204730000d606db6308a7d60799c1a7c17204d1968302019683050193c27204c2a7938c720501730193e4c672040408720293e4c672040505720393e4c67204060ec5a796830201929c998c7205029591b1720673028cb272067303000273047203720792720773057202"; it("Should create fromHex", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); expect(tree.toString()).not.toBeUndefined(); expect(tree.toHex()).toEqual(hex) }); it("Has properties", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); expect(tree.header()).toEqual(0x10) expect(tree.version()).toEqual(0) @@ -25,13 +25,13 @@ describe("Smoke tests for ErgoTree", () => { }); it("Has constants", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); let constants = tree.constants().map(c => c.toHex()) expect(constants).toEqual(["0400", "0e00", "0400", "0400", "0500", "0500"]) }); it("Has template", () => { - let tree = ErgoTreeObj.fromHex(hex); + let tree = ErgoTree$.fromHex(hex); let templateHex = tree.templateHex(); expect(templateHex).toEqual("d803d601e30004d602e4c6a70408d603e4c6a7050595e67201d804d604b2a5e4720100d605b2db63087204730000d606db6308a7d60799c1a7c17204d1968302019683050193c27204c2a7938c720501730193e4c672040408720293e4c672040505720393e4c67204060ec5a796830201929c998c7205029591b1720673028cb272067303000273047203720792720773057202") }); diff --git a/sigma-js/tests/js/GroupElement.spec.js b/sigma-js/tests/js/GroupElement.spec.js index 6d860691be..f408728602 100644 --- a/sigma-js/tests/js/GroupElement.spec.js +++ b/sigma-js/tests/js/GroupElement.spec.js @@ -1,13 +1,13 @@ -const { GroupElementObj, ValueObj } = require("sigmastate-js/main"); +const { GroupElement$, Value$ } = require("sigmastate-js/main"); let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; describe("GroupElement", () => { it("should implement toPointHex/fromPointHex", () => { - let ge = GroupElementObj.fromPointHex(pointAsn1Hex) + let ge = GroupElement$.fromPointHex(pointAsn1Hex) expect(ge.toPointHex()).toEqual(pointAsn1Hex) - let v = ValueObj.ofGroupElement(pointAsn1Hex) + let v = Value$.ofGroupElement(pointAsn1Hex) expect(v.toHex()).toEqual("07"/* GroupElement type id */ + pointAsn1Hex) }); }); \ No newline at end of file diff --git a/sigma-js/tests/js/SigmaCompiler.spec.js b/sigma-js/tests/js/SigmaCompiler.spec.js index 6e531d63aa..312747d504 100644 --- a/sigma-js/tests/js/SigmaCompiler.spec.js +++ b/sigma-js/tests/js/SigmaCompiler.spec.js @@ -1,7 +1,7 @@ -const { ValueObj, SigmaCompilerObj } = require("sigmastate-js/main"); +const { Value$, SigmaCompiler$ } = require("sigmastate-js/main"); describe("Smoke tests for API exporting", () => { - let compiler = SigmaCompilerObj.forMainnet(); + let compiler = SigmaCompiler$.forMainnet(); it("Should create SigmaCompiler", () => { expect(compiler).not.toBeUndefined(); @@ -21,7 +21,7 @@ describe("Smoke tests for API exporting", () => { it("SigmaCompiler should compile with named constants", () => { let treeWithSegregation = compiler.compile( - {"deadline": ValueObj.ofInt(100)}, + {"deadline": Value$.ofInt(100)}, true, 0, "sigmaProp(HEIGHT > deadline)"); expect(treeWithSegregation).not.toBeUndefined(); expect(treeWithSegregation.toHex()).toEqual(segregatedTreeHex) diff --git a/sigma-js/tests/js/SigmaProp.spec.js b/sigma-js/tests/js/SigmaProp.spec.js index 89568120c7..8191b24ccd 100644 --- a/sigma-js/tests/js/SigmaProp.spec.js +++ b/sigma-js/tests/js/SigmaProp.spec.js @@ -1,13 +1,13 @@ -const { SigmaPropObj, ValueObj } = require("sigmastate-js/main"); +const { SigmaProp$, Value$ } = require("sigmastate-js/main"); let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5"; describe("SigmaProp", () => { it("should implement fromPointHex", () => { - let ge = SigmaPropObj.fromPointHex(pointAsn1Hex) + let ge = SigmaProp$.fromPointHex(pointAsn1Hex) expect(ge).not.toBeUndefined() - let v = ValueObj.ofSigmaProp(pointAsn1Hex) + let v = Value$.ofSigmaProp(pointAsn1Hex) expect(v.toHex()) .toEqual("08"/* SigmaProp type id */ + "cd"/* ProveDlog.opCode */ + pointAsn1Hex) }); diff --git a/sigma-js/tests/js/SigmaPropProver.spec.js b/sigma-js/tests/js/SigmaPropProver.spec.js new file mode 100644 index 0000000000..4703f75b57 --- /dev/null +++ b/sigma-js/tests/js/SigmaPropProver.spec.js @@ -0,0 +1,30 @@ +const { + ProverSecret$, SigmaPropProver$, + ProverHints$, SigmaPropVerifier$ +} = require("sigmastate-js/main"); + +describe("SigmaPropProver", () => { + let w = 0xadf47e32000fc75e2923dba482c843c7f6b684cbf2ceec5bfdf5fe6d13cabe5dn + let secret = ProverSecret$.dlog(w) + expect(secret.secret()).toEqual(w) + + let p = SigmaPropProver$.withSecrets([secret]) + expect(p).not.toBeUndefined() + + it("generateCommitments", () => { + let hints = p.generateCommitments(secret.publicKey()) + expect(hints).not.toBeUndefined() + }); + + it("signMessage", () => { + let message = Int8Array.of(1, 2, 3) + let signature = p.signMessage(secret.publicKey(), message, ProverHints$.empty()) + expect(signature).not.toBeUndefined() + expect(signature.length).toBeGreaterThan(0) + + let V = SigmaPropVerifier$.create() + let ok = V.verifySignature(secret.publicKey(), message, signature) + expect(ok).toEqual(true) + }); + +}); \ No newline at end of file diff --git a/sigma-js/tests/js/Type.spec.js b/sigma-js/tests/js/Type.spec.js index fe1b0c0383..83d60481b7 100644 --- a/sigma-js/tests/js/Type.spec.js +++ b/sigma-js/tests/js/Type.spec.js @@ -1,27 +1,27 @@ -const { TypeObj } = require("sigmastate-js/main"); +const { Type$ } = require("sigmastate-js/main"); describe("Smoke tests for Types", () => { it("Should create primitive types", () => { - expect(TypeObj.Byte.name).toEqual("Byte"); - expect(TypeObj.Short.name).toEqual("Short"); - expect(TypeObj.Int.name).toEqual("Int"); - expect(TypeObj.Long.name).toEqual("Long"); - expect(TypeObj.BigInt.name).toEqual("BigInt"); - expect(TypeObj.GroupElement.name).toEqual("GroupElement"); - expect(TypeObj.SigmaProp.name).toEqual("SigmaProp"); - expect(TypeObj.Box.name).toEqual("Box"); - expect(TypeObj.AvlTree.name).toEqual("AvlTree"); - expect(TypeObj.Context.name).toEqual("Context"); - expect(TypeObj.Header.name).toEqual("Header"); - expect(TypeObj.PreHeader.name).toEqual("PreHeader"); - expect(TypeObj.SigmaDslBuilder.name).toEqual("SigmaDslBuilder"); + expect(Type$.Byte.name).toEqual("Byte"); + expect(Type$.Short.name).toEqual("Short"); + expect(Type$.Int.name).toEqual("Int"); + expect(Type$.Long.name).toEqual("Long"); + expect(Type$.BigInt.name).toEqual("BigInt"); + expect(Type$.GroupElement.name).toEqual("GroupElement"); + expect(Type$.SigmaProp.name).toEqual("SigmaProp"); + expect(Type$.Box.name).toEqual("Box"); + expect(Type$.AvlTree.name).toEqual("AvlTree"); + expect(Type$.Context.name).toEqual("Context"); + expect(Type$.Header.name).toEqual("Header"); + expect(Type$.PreHeader.name).toEqual("PreHeader"); + expect(Type$.SigmaDslBuilder.name).toEqual("SigmaDslBuilder"); }); it("Should create complex types", () => { - expect(TypeObj.pairType(TypeObj.Int, TypeObj.Long).name).toEqual("(Int, Long)"); - expect(TypeObj.collType(TypeObj.Int).name).toEqual("Coll[Int]"); - expect(TypeObj.collType(TypeObj.pairType(TypeObj.Int, TypeObj.Long)).name) + expect(Type$.pairType(Type$.Int, Type$.Long).name).toEqual("(Int, Long)"); + expect(Type$.collType(Type$.Int).name).toEqual("Coll[Int]"); + expect(Type$.collType(Type$.pairType(Type$.Int, Type$.Long)).name) .toEqual("Coll[(Int, Long)]"); }); }); diff --git a/sigma-js/tests/js/Value.spec.js b/sigma-js/tests/js/Value.spec.js index e687f8aecb..694ba2064b 100644 --- a/sigma-js/tests/js/Value.spec.js +++ b/sigma-js/tests/js/Value.spec.js @@ -1,4 +1,4 @@ -const { TypeObj, ValueObj, SigmaPropObj, SigmaProp} = require("sigmastate-js/main"); +const { Type$, Value$, SigmaProp$, SigmaProp} = require("sigmastate-js/main"); function testRange(factory, min, max) { expect(factory(max).data).toEqual(max); @@ -10,20 +10,20 @@ function testRange(factory, min, max) { describe("Smoke tests for Values", () => { it("Should create values of primitive types", () => { - expect(ValueObj.ofByte(0).data).toEqual(0); - expect(ValueObj.ofByte(0).tpe).toEqual(TypeObj.Byte); - testRange(function(v) { return ValueObj.ofByte(v); }, -128, 127); - testRange(function(v) { return ValueObj.ofShort(v); }, -32768, 32767); - testRange(function(v) { return ValueObj.ofInt(v); }, -0x7FFFFFFF - 1, 0x7FFFFFFF); - testRange(function(v) { return ValueObj.ofLong(v); }, -0x8000000000000000n, 0x7fffffffffffffffn); + expect(Value$.ofByte(0).data).toEqual(0); + expect(Value$.ofByte(0).tpe).toEqual(Type$.Byte); + testRange(function(v) { return Value$.ofByte(v); }, -128, 127); + testRange(function(v) { return Value$.ofShort(v); }, -32768, 32767); + testRange(function(v) { return Value$.ofInt(v); }, -0x7FFFFFFF - 1, 0x7FFFFFFF); + testRange(function(v) { return Value$.ofLong(v); }, -0x8000000000000000n, 0x7fffffffffffffffn); }); it("Should create values of complex types", () => { - let pair = ValueObj.pairOf(ValueObj.ofByte(10), ValueObj.ofLong(20n)); + let pair = Value$.pairOf(Value$.ofByte(10), Value$.ofLong(20n)); expect(pair.data).toEqual([10, 20n]); expect(pair.tpe.name).toEqual("(Byte, Long)"); - let coll = ValueObj.collOf([-10, 0, 10], TypeObj.Byte) + let coll = Value$.collOf([-10, 0, 10], Type$.Byte) expect(coll.tpe.name).toEqual("Coll[Byte]"); }); @@ -42,99 +42,100 @@ describe("Smoke tests for Values", () => { let pairHex = "3e050a28" it("Unit Value.toHex", () => { - let v = ValueObj.fromHex(unitHex) + let v = Value$.fromHex(unitHex) expect(v.toHex()).toEqual(unitHex) }); it("Boolean Value.toHex", () => { - let v = ValueObj.fromHex(booleanHex) + let v = Value$.fromHex(booleanHex) expect(v.toHex()).toEqual(booleanHex) }); it("Byte Value.toHex", () => { - let v = ValueObj.fromHex(byteHex) + let v = Value$.fromHex(byteHex) expect(v.toHex()).toEqual(byteHex) }); it("Short Value.toHex", () => { - let v = ValueObj.fromHex(shortHex) + let v = Value$.fromHex(shortHex) expect(v.toHex()).toEqual(shortHex) }); it("Int Value.toHex", () => { - let v = ValueObj.fromHex(intHex) + let v = Value$.fromHex(intHex) expect(v.toHex()).toEqual(intHex) }); it("Long Value.toHex", () => { - let v = ValueObj.ofLong(1200n) + let v = Value$.ofLong(1200n) expect(v.toHex()).toEqual(longHex) }); it("BigInt Value.toHex", () => { - let v = ValueObj.ofBigInt(0xfffffffffffffffen) + let v = Value$.ofBigInt(0xfffffffffffffffen) expect(v.toHex()).toEqual(bigIntHex) }); it("GroupElement Value.toHex", () => { - let v = ValueObj.fromHex(groupElementHex) + let v = Value$.fromHex(groupElementHex) expect(v.toHex()).toEqual(groupElementHex) }); it("SigmaProp Value.toHex", () => { - let v = ValueObj.fromHex(sigmaPropHex) + let v = Value$.fromHex(sigmaPropHex) expect(v.toHex()).toEqual(sigmaPropHex) }); it("AvlTree Value.toHex", () => { - let v = ValueObj.fromHex(avlTreeHex) + let v = Value$.fromHex(avlTreeHex) expect(v.toHex()).toEqual(avlTreeHex) }); - it("Box Value.toHex", () => { - let v = ValueObj.fromHex(boxHex) - expect(v.toHex()).toEqual(boxHex) - }); + // TODO uncomment when Box is implemented + // ignore("Box Value.toHex", () => { + // let v = Value$.fromHex(boxHex) + // expect(v.toHex()).toEqual(boxHex) + // }); it("Coll Value.toHex", () => { let arr = [ [1, 2, 3], [10, 20] ] - let t = TypeObj.collType(TypeObj.Byte) - let collV = ValueObj.collOf(arr, t) + let t = Type$.collType(Type$.Byte) + let collV = Value$.collOf(arr, t) expect(collV.tpe.name).toEqual("Coll[Coll[Byte]]"); expect(collV.toHex()).toEqual(collHex) }); it("Value of type Coll[SigmaProp]", () => { - let sp1 = SigmaPropObj.fromPointHex(groupElementHex.substring(2)) - let sp2 = SigmaPropObj.fromPointHex(sigmaPropHex.substring(4)) - let collV = ValueObj.collOf([sp1, sp2], TypeObj.SigmaProp) + let sp1 = SigmaProp$.fromPointHex(groupElementHex.substring(2)) + let sp2 = SigmaProp$.fromPointHex(sigmaPropHex.substring(4)) + let collV = Value$.collOf([sp1, sp2], Type$.SigmaProp) expect(collV.tpe.name).toEqual("Coll[SigmaProp]"); }); it("Pair Value.toHex", () => { - let fst = ValueObj.ofByte(10) - let snd = ValueObj.ofLong(20) - let pair = ValueObj.pairOf(fst, snd) + let fst = Value$.ofByte(10) + let snd = Value$.ofLong(20) + let pair = Value$.pairOf(fst, snd) expect(pair.tpe.name).toEqual("(Byte, Long)"); expect(pair.toHex()).toEqual(pairHex) }); it("Long Value.fromHex", () => { - let v = ValueObj.fromHex(longHex) + let v = Value$.fromHex(longHex) expect(v.data).toEqual(1200n) expect(v.tpe.name).toEqual("Long") }); it("Coll Value.fromHex", () => { - let coll = ValueObj.fromHex(collHex) + let coll = Value$.fromHex(collHex) expect(coll.tpe.name).toEqual("Coll[Coll[Byte]]"); expect(coll.toHex()).toEqual(collHex) }); it("Pair Value.fromHex", () => { - let p = ValueObj.fromHex(pairHex) + let p = Value$.fromHex(pairHex) expect(p.tpe.name).toEqual("(Byte, Long)"); expect(p.toHex()).toEqual(pairHex) });