diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index 6656ede3c9..717439bcbb 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -7,7 +7,7 @@ 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 sigma.{AvlTree, BigInt, Box, Coll, Context, Evaluation, GroupElement, Header, PreHeader, SigmaDslBuilder, SigmaProp, VersionContext} import java.math.BigInteger @@ -375,6 +375,7 @@ case object SByte extends SPrimType with SEmbeddable with SNumericType with SMon case s: Short => s.toByteExact case i: Int => i.toByteExact case l: Long => l.toByteExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toByte // toByteExact from int is called under the hood case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -396,6 +397,7 @@ case object SShort extends SPrimType with SEmbeddable with SNumericType with SMo case s: Short => s case i: Int => i.toShortExact case l: Long => l.toShortExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toShort // toShortExact from int is called under the hood case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -419,6 +421,7 @@ case object SInt extends SPrimType with SEmbeddable with SNumericType with SMono case s: Short => s.toInt case i: Int => i case l: Long => l.toIntExact + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toInt case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -444,6 +447,7 @@ case object SLong extends SPrimType with SEmbeddable with SNumericType with SMon case s: Short => s.toLong case i: Int => i.toLong case l: Long => l + case bi: BigInt if VersionContext.current.isV6SoftForkActivated => bi.toLong case _ => sys.error(s"Cannot downcast value $v to the type $this") } } @@ -465,24 +469,24 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM 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) + v match { + case x: Byte => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Short => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Int => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Long => CBigInt(BigInteger.valueOf(x)) + case x: BigInt if VersionContext.current.isV6SoftForkActivated => 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) + v match { + case x: Byte => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Short => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Int => CBigInt(BigInteger.valueOf(x.toLong)) + case x: Long => CBigInt(BigInteger.valueOf(x)) + case x: BigInt if VersionContext.current.isV6SoftForkActivated => x case _ => sys.error(s"Cannot downcast value $v to the type $this") } - CBigInt(bi) } } diff --git a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala index e82bcd886b..3b3de407b5 100644 --- a/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala +++ b/sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala @@ -1,8 +1,8 @@ package sigma -import sigma.ast.{Apply, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, Global, JitCost, MethodCall, NamedDesc, OptionGet, SBigInt, SByte, SGlobalMethods, SInt, SLong, SShort, STypeVar, ValUse} -import sigma.data.{CBigInt, ExactNumeric, RType} -import sigma.eval.{SigmaDsl, TracedCost} +import sigma.ast.{Apply, Downcast, FixedCost, FixedCostItem, FuncValue, GetVar, JitCost, OptionGet, SBigInt, SByte, SInt, SLong, SShort, ValUse} +import sigma.data.{CBigInt, ExactNumeric} +import sigma.eval.SigmaDsl import sigma.util.Extensions.{BooleanOps, ByteOps, IntOps, LongOps} import sigmastate.exceptions.MethodNotFound @@ -172,21 +172,26 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => } property("BigInt methods equivalence (new features)") { - // TODO v6.0: the behavior of `upcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877) - // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. - // It makes sense to fix this inconsistency as part of upcoming forks - assertExceptionThrown( - SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), - _.getMessage.contains("Cannot upcast value") - ) + if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { + // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // Fixed in 6.0 + assertExceptionThrown( + SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot upcast value") + ) - // TODO v6.0: the behavior of `downcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877) - // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. - // It makes sense to fix this inconsistency as part of HF - assertExceptionThrown( - SBigInt.downcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), - _.getMessage.contains("Cannot downcast value") - ) + // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. + // Fixed in 6.0 + assertExceptionThrown( + SBigInt.downcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } else { + forAll { x: BigInteger => + SBigInt.upcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) + SBigInt.downcast(CBigInt(x).asInstanceOf[AnyVal]) shouldBe CBigInt(x) + } + } if (activatedVersionInTests < VersionContext.V6SoftForkVersion) { // NOTE, for such versions the new features are not supported @@ -225,6 +230,44 @@ class LanguageSpecificationV6 extends LanguageSpecificationBase { suite => forAll { x: (BigInt, BigInt) => Seq(compareTo, bitOr, bitAnd).foreach(_.checkEquality(x)) } + + forAll { x: Long => + assertExceptionThrown( + SLong.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Int => + assertExceptionThrown( + SInt.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Byte => + assertExceptionThrown( + SByte.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + forAll { x: Short => + assertExceptionThrown( + SShort.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]), + _.getMessage.contains("Cannot downcast value") + ) + } + } else { + forAll { x: Long => + SLong.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Int => + SInt.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Byte => + SByte.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } + forAll { x: Short => + SShort.downcast(CBigInt(new BigInteger(x.toString)).asInstanceOf[AnyVal]) shouldBe x + } } } diff --git a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index 446f1972a7..1012e9a189 100644 --- a/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -10,6 +10,7 @@ import sigma.ast.syntax._ import org.ergoplatform._ import org.scalatest.BeforeAndAfterAll import scorex.util.encode.Base58 +import sigma.VersionContext import sigma.crypto.CryptoConstants import sigma.data.{AvlTreeData, CAND, ProveDlog, SigmaBoolean, TrivialProp} import sigma.util.Extensions.IntOps @@ -215,6 +216,46 @@ class TestingInterpreterSpecification extends CompilerTestingCommons testWithCasting("toBigInt") } + property("BigInt downcasting to byte") { + def test() = testEval("{ sigmaProp(0L.toBigInt.toByte <= CONTEXT.preHeader.version) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("BigInt downcasting to short") { + def test() = testEval("{ sigmaProp(0L.toBigInt.toShort <= CONTEXT.preHeader.version.toShort) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("BigInt downcasting to int") { + def test() = testEval("{ sigmaProp(1L.toBigInt.toInt < CONTEXT.preHeader.timestamp.toInt) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("BigInt downcasting to long") { + def test() = testEval("{ sigmaProp(1L.toBigInt.toLong < CONTEXT.preHeader.timestamp) }") + if(VersionContext.current.isV6SoftForkActivated) { + test() + } else { + an[Exception] shouldBe thrownBy(test()) + } + } + + property("upcasting to bigint") { + testEval("{ sigmaProp(1L.toBigInt < bigInt(\"2\")) }") + } + property("Evaluate arithmetic ops") { def testWithCasting(castSuffix: String): Unit = { testEval(s"1.$castSuffix + 2.$castSuffix == 3.$castSuffix")