Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions core/shared/src/main/scala/sigma/ast/SType.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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")
}
}
Expand All @@ -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")
}
}
Expand All @@ -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")
}
}
Expand All @@ -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")
}
}
Expand All @@ -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)
}
}

Expand Down
77 changes: 60 additions & 17 deletions sc/shared/src/test/scala/sigma/LanguageSpecificationV6.scala
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good enough, but FYI there is also method exceptionLike[ConcreteExectionType]("Cannot upcast value") which tests both exception type and the contents of the message.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check is corresponding to previously written code, so such refactoring is a subject of a dedicated issue & PR I suppose

)

// 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
Expand Down Expand Up @@ -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
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand Down