Skip to content

Commit

Permalink
Merge pull request #1021 from ergoplatform/i905
Browse files Browse the repository at this point in the history
[6.0] Revise liftToConstant method
  • Loading branch information
kushti authored Dec 5, 2024
2 parents 1969223 + 2ad87f4 commit d13dd4d
Show file tree
Hide file tree
Showing 21 changed files with 180 additions and 116 deletions.
24 changes: 13 additions & 11 deletions data/js/src/main/scala/sigma/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,33 @@ object Platform {
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))
case arr: Array[Byte] => Nullable(mkCollectionConstant[SByte.type](arr, SByte))
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[BigInt](n => CBigInt(n)), SBigInt))
case arr: Array[String] => Nullable(mkCollectionConstant[SString.type](arr, SString))
case arr: Array[Boolean] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean))
case arr: Array[Byte] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SByte.type](arr, SByte))
case arr: Array[Short] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SShort.type](arr, SShort))
case arr: Array[Int] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SInt.type](arr, SInt))
case arr: Array[Long] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SLong.type](arr, SLong))
case arr: Array[BigInteger] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBigInt.type](arr.map[BigInt](n => CBigInt(n)), SBigInt))
case arr: Array[String] if !VersionContext.current.isV6SoftForkActivated => 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](CBigInt(v), SBigInt))
case v: BigInteger if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SBigInt.type](CBigInt(v), SBigInt))
case n: sigma.BigInt => Nullable(mkConstant[SBigInt.type](n, SBigInt))
case n: sigma.UnsignedBigInt => Nullable(mkConstant[SUnsignedBigInt.type](n, SUnsignedBigInt))
case ge: GroupElement => Nullable(mkConstant[SGroupElement.type](ge, SGroupElement))
case b: Boolean => Nullable(if (b) TrueLeaf else FalseLeaf)
case v: String => Nullable(mkConstant[SString.type](v, SString))
case v: String if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SString.type](v, SString))
case h: Header if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SHeader.type](h, SHeader))
case h: PreHeader if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SPreHeader.type](h, SPreHeader))

// The Box lifting was broken in v4.x. `SigmaDsl.Box(b)` was missing which means the
// isCorrectType requirement would fail in ConstantNode constructor.
// This method is used as part of consensus in SubstConstants operation, however
// 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 =>
case b: ErgoBox if !VersionContext.current.isV6SoftForkActivated =>
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
Expand All @@ -48,7 +50,7 @@ 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](CAvlTree(avl), SAvlTree))
case avl: AvlTreeData if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SAvlTree.type](CAvlTree(avl), SAvlTree))
case avl: AvlTree => Nullable(mkConstant[SAvlTree.type](avl, SAvlTree))
case sb: SigmaBoolean => Nullable(mkConstant[SSigmaProp.type](CSigmaProp(sb), SSigmaProp))
case p: SigmaProp => Nullable(mkConstant[SSigmaProp.type](p, SSigmaProp))
Expand Down
26 changes: 15 additions & 11 deletions data/jvm/src/main/scala/sigma/Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,33 @@ object Platform {
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))
case arr: Array[Byte] => Nullable(mkCollectionConstant[SByte.type](arr, SByte))
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[String] => Nullable(mkCollectionConstant[SString.type](arr, SString))
case arr: Array[Boolean] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBoolean.type](arr, SBoolean))
case arr: Array[Byte] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SByte.type](arr, SByte))
case arr: Array[Short] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SShort.type](arr, SShort))
case arr: Array[Int] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SInt.type](arr, SInt))
case arr: Array[Long] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SLong.type](arr, SLong))
case arr: Array[BigInteger] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt(_)), SBigInt))
case arr: Array[String] if !VersionContext.current.isV6SoftForkActivated => Nullable(mkCollectionConstant[SString.type](arr, SString))
case v: Byte => Nullable(mkConstant[SByte.type](v, SByte))
case v: Short => Nullable(mkConstant[SShort.type](v, SShort))
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 if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SBigInt.type](SigmaDsl.BigInt(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)
case v: String => Nullable(mkConstant[SString.type](v, SString))
case h: Header if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SHeader.type](h, SHeader))
case h: PreHeader if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SPreHeader.type](h, SPreHeader))
case n: sigma.UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SUnsignedBigInt.type](n, SUnsignedBigInt))

case v: String if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SString.type](v, SString))

// The Box lifting was broken in v4.x. `SigmaDsl.Box(b)` was missing which means the
// isCorrectType requirement would fail in ConstantNode constructor.
// This method is used as part of consensus in SubstConstants operation, however
// 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 =>
case b: ErgoBox if !VersionContext.current.isV6SoftForkActivated =>
Nullable(mkConstant[SBox.type](SigmaDsl.Box(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
Expand All @@ -48,7 +52,7 @@ 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 if !VersionContext.current.isV6SoftForkActivated => Nullable(mkConstant[SAvlTree.type](SigmaDsl.avlTree(avl), SAvlTree))
case avl: AvlTree => Nullable(mkConstant[SAvlTree.type](avl, SAvlTree))
case sb: SigmaBoolean => Nullable(mkConstant[SSigmaProp.type](SigmaDsl.SigmaProp(sb), SSigmaProp))
case p: SigmaProp => Nullable(mkConstant[SSigmaProp.type](p, SSigmaProp))
Expand Down
85 changes: 71 additions & 14 deletions interpreter/shared/src/test/scala/sigma/ast/SigmaBuilderTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -117,14 +117,18 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma

def testArray[T <: SType]
(v: T#WrappedType, c: Constant[T])(implicit t: RType[T#WrappedType]) = {
// for any Byte and Short value `v`, lifting of array should succeed
val arr = Array.fill[T#WrappedType](10)(v)(t.classTag)
testSuccess(arr, TransformingSigmaBuilder.mkCollectionConstant(arr, c.tpe))
}

def testArrayFailure[T <: SType]
(v: T#WrappedType, c: Constant[T])(implicit t: RType[T#WrappedType]) = {
val arr = Array.fill[T#WrappedType](10)(v)(t.classTag)
testFailure(arr)
}

def testColl[T <: SType]
(v: T#WrappedType, c: Constant[T])(implicit t: RType[T#WrappedType]) = {
// for any Byte and Short value `v`, lifting of Coll should succeed
val arr = Array.fill[T#WrappedType](10)(v)(t.classTag)
val coll = arr.toColl
testSuccess(coll, TransformingSigmaBuilder.mkCollectionConstant(coll, c.tpe))
Expand All @@ -134,7 +138,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val v = true
val c = BooleanConstant(v)
test[SBoolean.type](v, c)
testArray[SBoolean.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
if (!VersionContext.current.isV6SoftForkActivated) {
testArray[SBoolean.type](v, c)
} else {
testArrayFailure[SBoolean.type](v, c)
}
testColl[SBoolean.type](v, c)
}

Expand All @@ -143,7 +151,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val c = ByteConstant(v)
testNumeric[SByte.type](v, c)
testLiftingOfCAnyValue[SByte.type](v, c)
testArray[SByte.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
if (!VersionContext.current.isV6SoftForkActivated) {
testArray[SByte.type](v, c)
} else {
testArrayFailure[SByte.type](v, c)
}
testColl[SByte.type](v, c)
}

Expand All @@ -152,40 +164,65 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val c = ShortConstant(v)
testNumeric[SShort.type](v, c)
testLiftingOfCAnyValue[SShort.type](v, c)
testArray[SShort.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
if (!VersionContext.current.isV6SoftForkActivated) {
testArray[SShort.type](v, c)
} else {
testArrayFailure[SShort.type](v, c)
}
testColl[SShort.type](v, c)
}

property("liftToConstant Int") {
val v = 1
val c = IntConstant(v)
test[SInt.type](v, c)
testArray[SInt.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
if (!VersionContext.current.isV6SoftForkActivated) {
testArray[SInt.type](v, c)
} else {
testArrayFailure[SInt.type](v, c)
}
testColl[SInt.type](v, c)
}

property("liftToConstant Long") {
val v = 1L
val c = LongConstant(v)
test[SLong.type](v, c)
testArray[SLong.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
if (!VersionContext.current.isV6SoftForkActivated) {
testArray[SLong.type](v, c)
} else {
testArrayFailure[SLong.type](v, c)
}
testColl[SLong.type](v, c)
}

property("liftToConstant String") {
val v = "abc"
val c = StringConstant(v)
test[SString.type](v, c)
testArray[SString.type](v, c) // TODO v6.0: String should be liftable at all (not supported in ErgoTree) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testColl[SString.type](v, c)
if (!VersionContext.current.isV6SoftForkActivated) {
// v6.0: String should not be liftable at all (not supported in ErgoTree) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
test[SString.type](v, c)
testArray[SString.type](v, c)
testColl[SString.type](v, c)
} else {
testFailure(v)
}
}

property("liftToConstant BigInteger") {
val v = BigInteger.valueOf(1L)
val c = BigIntConstant(v)
testSuccess(v, c) // TODO v6.0: both BigInteger and arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
if (!VersionContext.current.isV6SoftForkActivated) {
testSuccess(v, c)
} else {
testFailure(v)
}
val arr = Array.fill(10)(v)
testSuccess(arr, TransformingSigmaBuilder.mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt), c.tpe))
if (!VersionContext.current.isV6SoftForkActivated) {
testSuccess(arr, TransformingSigmaBuilder.mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt), c.tpe))
} else {
testFailure(arr)
}
}

property("liftToConstant BigInt") {
Expand All @@ -204,10 +241,26 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
testColl[SGroupElement.type](v, c)
}

property("liftToConstant Header") {
val h = TestData.h1
val c = HeaderConstant(h)
if (VersionContext.current.isV6SoftForkActivated) {
testSuccess(h, c)
} else {
testFailure(h)
}
testFailure(Array.fill(10)(h))
testColl[SHeader.type](h, c)
}

property("liftToConstant ErgoBox") {
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)
if (!VersionContext.current.isV6SoftForkActivated) {
testSuccess(v, c)
} else {
testFailure(v)
}
testFailure(Array.fill(10)(v))
}

Expand All @@ -234,7 +287,11 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
property("liftToConstant AvlTreeData") {
val v = TestData.t1.asInstanceOf[CAvlTree].wrappedValue
val c = AvlTreeConstant(SigmaDsl.avlTree(v))
testSuccess(v, c) // TODO v6.0: AvlTreeData should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
if (!VersionContext.current.isV6SoftForkActivated) {
testSuccess(v, c)
} else {
testFailure(v)
}
testFailure(Array.fill(10)(v))
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ergoplatform

import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
import sigma.Colls
import sigma.ast.SType
import sigma.ast.syntax.SigmaPropValue
import sigma.ast.Value
Expand Down Expand Up @@ -32,7 +33,7 @@ object ErgoScriptPredef {
networkPrefix: NetworkPrefix)
(implicit IR: IRContext): SigmaPropValue = {
val env = emptyEnv +
("tokenId" -> tokenId, "thresholdAmount" -> thresholdAmount)
("tokenId" -> Colls.fromArray(tokenId), "thresholdAmount" -> thresholdAmount)
val res = compileWithCosting(env,
"""{
| val sumValues = { (xs: Coll[Long]) => xs.fold(0L, { (acc: Long, amt: Long) => acc + amt }) }
Expand Down
24 changes: 12 additions & 12 deletions sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio
boxesToSpend = inputBoxes,
spendingTransaction,
self = inputBox, activatedVersionInTests)
val pr = prover.prove(emptyEnv + (ScriptNameProp -> "boxCreationHeight_prove"), propTree, ctx, fakeMessage).get
verifier.verify(emptyEnv + (ScriptNameProp -> "boxCreationHeight_verify"), propTree, ctx, pr, fakeMessage).get._1 shouldBe true
val pr = prover.prove(emptyEnv, propTree, ctx, fakeMessage).get
verifier.verify(emptyEnv, propTree, ctx, pr, fakeMessage).get._1 shouldBe true
}

property("collect coins from the founders' box") {
Expand Down Expand Up @@ -118,8 +118,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio
boxesToSpend = inputBoxes,
spendingTransaction,
self = inputBoxes.head, activatedVersionInTests)
val pr = prover.prove(emptyEnv + (ScriptNameProp -> "checkSpending_prove"), prop, ctx, fakeMessage).get
verifier.verify(emptyEnv + (ScriptNameProp -> "checkSpending_verify"), prop, ctx, pr, fakeMessage).get._1 shouldBe true
val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).get
verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).get._1 shouldBe true
}
}

Expand Down Expand Up @@ -148,13 +148,13 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio
self = inputBoxes.head, activatedVersionInTests)

// should not be able to collect before minerRewardDelay
val prove = prover.prove(emptyEnv + (ScriptNameProp -> "rewardOutputScript_prove"), prop, ctx, fakeMessage).get
verifier.verify(emptyEnv + (ScriptNameProp -> "rewardOutputScript_verify"), prop, prevBlockCtx, prove, fakeMessage)
val prove = prover.prove(emptyEnv, prop, ctx, fakeMessage).get
verifier.verify(emptyEnv, prop, prevBlockCtx, prove, fakeMessage)
.getOrThrow should matchPattern { case (false,_) => }

// should be able to collect after minerRewardDelay
val pr = prover.prove(emptyEnv + (ScriptNameProp -> "prove"), prop, ctx, fakeMessage).getOrThrow
verifier.verify(emptyEnv + (ScriptNameProp -> "verify"), prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true
val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).getOrThrow
verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true
}

property("create transaction collecting the emission box") {
Expand Down Expand Up @@ -232,8 +232,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio
self = inputBoxes.head,
activatedVersionInTests).withCostLimit(scriptCostLimitInTests * 10)

val pr = prover.prove(emptyEnv + (ScriptNameProp -> "tokenThresholdScript_prove"), prop, ctx, fakeMessage).getOrThrow
verifier.verify(emptyEnv + (ScriptNameProp -> "tokenThresholdScript_verify"), prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true
val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).getOrThrow
verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true
}


Expand Down Expand Up @@ -308,8 +308,8 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio
boxesToSpend = inputBoxes,
spendingTransaction,
self = inputBoxes.head, activatedVersionInTests)
val pr = prover.prove(emptyEnv + (ScriptNameProp -> "checkRewardTx_prove"), prop, ctx, fakeMessage).getOrThrow
verifier.verify(emptyEnv + (ScriptNameProp -> "checkRewardTx_verify"), prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true
val pr = prover.prove(emptyEnv, prop, ctx, fakeMessage).getOrThrow
verifier.verify(emptyEnv, prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true
spendingTransaction
}

Expand Down
Loading

0 comments on commit d13dd4d

Please sign in to comment.