diff --git a/README.md b/README.md index 64f08f0277..6b8429a82f 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ 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" % "6.0.0" ``` ## Repository Organization diff --git a/core/shared/src/main/scala/sigma/VersionContext.scala b/core/shared/src/main/scala/sigma/VersionContext.scala index a7928cd513..14ed6e36b0 100644 --- a/core/shared/src/main/scala/sigma/VersionContext.scala +++ b/core/shared/src/main/scala/sigma/VersionContext.scala @@ -1,6 +1,6 @@ package sigma -import VersionContext.JitActivationVersion +import VersionContext.{V6SoftForkVersion, JitActivationVersion} import scala.util.DynamicVariable @@ -21,6 +21,10 @@ case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) { /** @return true, if the activated script version of Ergo protocol on the network is * greater than v1. */ def isJitActivated: Boolean = activatedVersion >= JitActivationVersion + + /** @return true, if the activated script version of Ergo protocol on the network is + * including Evolution update. */ + def isV6SoftForkActivated: Boolean = activatedVersion >= V6SoftForkVersion } object VersionContext { @@ -31,13 +35,19 @@ object VersionContext { * - version 3.x this value must be 0 * - in v4.0 must be 1 * - in v5.x must be 2 + * - in 6.x must be 3 * etc. */ - val MaxSupportedScriptVersion: Byte = 2 // supported versions 0, 1, 2 + val MaxSupportedScriptVersion: Byte = 3 // supported versions 0, 1, 2, 3 /** The first version of ErgoTree starting from which the JIT costing interpreter is used. */ val JitActivationVersion: Byte = 2 + /** + * The version of ErgoTree corresponding to "evolution" (6.0) soft-fork + */ + val V6SoftForkVersion: Byte = 3 + private val _defaultContext = VersionContext( activatedVersion = 1/* v4.x */, ergoTreeVersion = 1 diff --git a/core/shared/src/test/scala/sigma/VersionTesting.scala b/core/shared/src/test/scala/sigma/VersionTesting.scala index a17fc7a7f9..bd3e3b8872 100644 --- a/core/shared/src/test/scala/sigma/VersionTesting.scala +++ b/core/shared/src/test/scala/sigma/VersionTesting.scala @@ -6,9 +6,7 @@ import scala.util.DynamicVariable trait VersionTesting { - /** In v5.x we run test for only one activated version on the network (== 2). - * In the branch for v6.0 the new version 3 should be added so that the tests run for both. - */ + /** Tests run for all supported versions starting from 0. */ protected val activatedVersions: Seq[Byte] = (0 to VersionContext.MaxSupportedScriptVersion).map(_.toByte).toArray[Byte] @@ -41,9 +39,10 @@ trait VersionTesting { _ + 1) { j => val treeVersion = ergoTreeVers(j) // for each tree version up to currently activated, set it up and execute block - _currErgoTreeVersion.withValue(treeVersion)(block) + _currErgoTreeVersion.withValue(treeVersion) { + VersionContext.withVersions(activatedVersion, treeVersion)(block) + } } - } } } diff --git a/interpreter/shared/src/main/scala/sigmastate/types.scala b/interpreter/shared/src/main/scala/sigmastate/types.scala index 586a06432e..34408d2bf0 100644 --- a/interpreter/shared/src/main/scala/sigmastate/types.scala +++ b/interpreter/shared/src/main/scala/sigmastate/types.scala @@ -227,6 +227,7 @@ object SType { } implicit class STypeOps(val tpe: SType) extends AnyVal { + def isBoolean: Boolean = tpe.isInstanceOf[SBoolean.type] def isCollectionLike: Boolean = tpe.isInstanceOf[SCollection[_]] def isCollection: Boolean = tpe.isInstanceOf[SCollectionType[_]] def isOption: Boolean = tpe.isInstanceOf[SOption[_]] @@ -907,13 +908,18 @@ case object SBoolean extends SPrimType with SEmbeddable with SLogical with SProd 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"), - ) - */ + + lazy val ToByteMethod: SMethod = SMethod( + this, ToByte, SFunc(this, SByte), 1, FixedCost(JitCost(1))) + .withInfo(PropertyCall, "Convert true to 1 and false to 0") + + protected override def getMethods(): Seq[SMethod] = { + if (VersionContext.current.isV6SoftForkActivated) { + super.getMethods() ++ Seq(ToByteMethod) + } else { + super.getMethods() + } + } } /** Descriptor of ErgoTree type `Byte` - 8-bit signed integer. */ diff --git a/sc/shared/src/main/scala/scalan/primitives/LogicalOps.scala b/sc/shared/src/main/scala/scalan/primitives/LogicalOps.scala index e81b546139..ffc91ed14e 100644 --- a/sc/shared/src/main/scala/scalan/primitives/LogicalOps.scala +++ b/sc/shared/src/main/scala/scalan/primitives/LogicalOps.scala @@ -29,6 +29,11 @@ trait LogicalOps extends Base { self: Scalan => override def applySeq(x: Boolean): Int = if (x) 1 else 0 } + /** Boolean to Byte conversion unary operation. */ + val BooleanToByte = new UnOp[Boolean, Byte]("ToByte") { + override def applySeq(x: Boolean): Byte = if (x) 1.toByte else 0.toByte + } + /** Extension methods over `Ref[Boolean]`. */ implicit class RepBooleanOps(value: Ref[Boolean]) { def &&(y: Ref[Boolean]): Ref[Boolean] = And(value, y) @@ -40,6 +45,7 @@ trait LogicalOps extends Base { self: Scalan => def unary_!() : Ref[Boolean] = Not(value) def toInt: Ref[Int] = BooleanToInt(value) + def toByte: Ref[Byte] = BooleanToByte(value) } diff --git a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala index 99467289f2..770a07e0dc 100644 --- a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala +++ b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala @@ -2,6 +2,7 @@ package sigmastate.eval import org.ergoplatform._ import scalan.MutableLazy +import sigma.VersionContext import sigma.data.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral} import sigma.data.ExactOrdering.{ByteIsExactOrdering, IntIsExactOrdering, LongIsExactOrdering, ShortIsExactOrdering} import sigma.util.Extensions.ByteOps @@ -509,6 +510,10 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext => else error(s"The type of $obj is expected to be Collection to select 'size' property", obj.sourceContext.toOption) + case Select(obj, SBoolean.ToByte, _) if obj.tpe.isBoolean && VersionContext.current.isV6SoftForkActivated => + val bool = eval(obj.asBoolValue) + bool.toByte + // Rule: proof.isProven --> IsValid(proof) case Select(p, SSigmaProp.IsProven, _) if p.tpe == SSigmaProp => eval(SigmaPropIsProven(p.asSigmaProp)) diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 23406d9cb4..556f2bddd8 100644 --- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -219,7 +219,6 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { // The following table should be made dependent on HF activation val methods = Table( ("typeId", "methods", "CanHaveMethods"), - (SBoolean.typeId, Seq.empty[MInfo], true), (SByte.typeId, Seq.empty[MInfo], false), (SShort.typeId, Seq.empty[MInfo], false), (SInt.typeId, Seq.empty[MInfo], false), @@ -233,6 +232,14 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit { ), true) }, + { + import SBoolean._ + if (VersionContext.current.isV6SoftForkActivated) { + (SBoolean.typeId, Seq(MInfo(1, ToByteMethod)), true) + } else { + (SBoolean.typeId, Seq.empty[MInfo], true) + } + }, { // SBigInt inherit methods from SNumericType.methods // however they are not resolvable via SBigInt.typeId import SNumericType._ diff --git a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index 2398a48535..65c8b2fa4a 100644 --- a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -9,6 +9,7 @@ import sigmastate.lang.Terms._ import org.ergoplatform._ import org.scalatest.BeforeAndAfterAll import scorex.util.encode.Base58 +import sigma.VersionContext.V6SoftForkVersion import sigma.util.Extensions.IntOps import sigmastate.crypto.CryptoConstants import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTestProvingInterpreter} @@ -202,6 +203,21 @@ class TestingInterpreterSpecification extends CompilerTestingCommons |}""".stripMargin) } + property("Evaluate boolean casting ops") { + val source = + """ + |{ + | val bool: Boolean = true + | bool.toByte == 1.toByte && false.toByte == 0.toByte + |} + |""".stripMargin + if (activatedVersionInTests < V6SoftForkVersion) { + assertExceptionThrown(testEval(source), rootCauseLike[sigmastate.exceptions.MethodNotFound]("Cannot find method 'toByte'")) + } else { + testEval(source) + } + } + property("Evaluate numeric casting ops") { def testWithCasting(castSuffix: String): Unit = { testEval(s"OUTPUTS.size.toByte.$castSuffix == 0.$castSuffix")