Skip to content

Commit 56a42bd

Browse files
authored
Merge pull request #1073 from ergoplatform/60upcast
Disabling auto-upcast and checking method call args emptiness in trees >= v3
2 parents 9d6d99f + 7a66924 commit 56a42bd

File tree

10 files changed

+77
-16
lines changed

10 files changed

+77
-16
lines changed

data/shared/src/main/scala/sigma/ast/ErgoTree.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ case class UnparsedErgoTree(bytes: mutable.WrappedArray[Byte], error: Validation
7575
* for optimized execution.
7676
* ErgoTreeSerializer parsing method computes the value of
7777
* this flag and provides it to the constructor.
78+
* @param givenIsUsingBlockchainContext optional flag which indicates that blockchain context related operations
79+
* are used in the tree
7880
*/
7981
case class ErgoTree private[sigma](
8082
header: HeaderType,

data/shared/src/main/scala/sigma/ast/SigmaBuilder.scala

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import sigma.data.Nullable
1010
import sigma.exceptions.ConstraintFailed
1111
import sigma.serialization.OpCodes
1212
import sigma.serialization.ValueCodes.OpCode
13-
import sigma.{AnyValue, Coll, Colls, Environment, Evaluation, Platform}
13+
import sigma.{AnyValue, Coll, Colls, Environment, Evaluation, Platform, VersionContext}
1414

1515
import scala.util.DynamicVariable
1616

@@ -664,7 +664,7 @@ class TransformingSigmaBuilder extends StdSigmaBuilder {
664664
* @param right operand of the operation (right sub-expression)
665665
* @return a pair (l,r) of the arguments appropriately upcasted.
666666
*/
667-
private def applyUpcast[T <: SType](left: Value[T], right: Value[T]): (Value[T], Value[T]) =
667+
protected def applyUpcast[T <: SType](left: Value[T], right: Value[T]): (Value[T], Value[T]) =
668668
(left.tpe, right.tpe) match {
669669
case (t1: SNumericType, t2: SNumericType) if t1 != t2 =>
670670
val tmax = t1 max t2
@@ -740,7 +740,22 @@ case object CheckingSigmaBuilder extends CheckingSigmaBuilder
740740
case object TransformingSigmaBuilder extends TransformingSigmaBuilder
741741

742742
/** Builder of ErgoTree nodes which is used in deserializers. */
743-
case object DeserializationSigmaBuilder extends TransformingSigmaBuilder
743+
case object DeserializationSigmaBuilder extends TransformingSigmaBuilder {
744+
// since v3 trees, Upcast nodes are not inserted automatically
745+
// when v3 nodes appeared on the Ergo blockchain, 90+% of
746+
// mining nodes are rejecting trees invalid auto-upcast,
747+
// so no such trees expected in the longest chain
748+
// the reason to remove auto-upcast is to make deserialization
749+
// always producing tree which was serialized
750+
override protected def applyUpcast[T <: SType](left: Value[T], right: Value[T]): (Value[T], Value[T]) = {
751+
if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
752+
(left, right)
753+
} else {
754+
super.applyUpcast(left, right)
755+
}
756+
}
757+
}
758+
744759

745760
object Constraints {
746761
/** Represents a constraint on arguments of binary operation. */

data/shared/src/main/scala/sigma/ast/methods.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1996,7 +1996,7 @@ case object SGlobalMethods extends MonoTypeMethods {
19961996
.withIRInfo(MethodCallIrBuilder,
19971997
javaMethodOf[SigmaDslBuilder, RType[_]]("none"),
19981998
{ mtype => Array(mtype.tRange) })
1999-
.withInfo(MethodCall, "Returns empty Option[T] of given type T.")
1999+
.withInfo(PropertyCall, "Returns empty Option[T] of given type T.")
20002000

20012001
protected override def getMethods() = super.getMethods() ++ {
20022002
if (VersionContext.current.isV3OrLaterErgoTreeVersion) {

data/shared/src/main/scala/sigma/serialization/MethodCallSerializer.scala

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import sigma.ast.{MethodCall, SContextMethods, SMethod, SType, STypeSubst, SType
55
import sigma.util.safeNewArray
66
import SigmaByteWriter._
77
import debox.cfor
8+
import sigma.VersionContext
89
import sigma.ast.SContextMethods.BlockchainContextMethodNames
910
import sigma.serialization.CoreByteWriter.{ArgInfo, DataInfo}
1011

@@ -48,6 +49,10 @@ case class MethodCallSerializer(cons: (Value[SType], SMethod, IndexedSeq[Value[S
4849
val methodId = r.getByte()
4950
val obj = r.getValue()
5051
val args = r.getValues()
52+
// introducing the same check we have in serializer since v3 trees
53+
if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
54+
assert(args.nonEmpty)
55+
}
5156
val method = SMethod.fromIds(typeId, methodId)
5257

5358
val explicitTypes = if (method.hasExplicitTypeArgs) {

data/shared/src/main/scala/sigma/serialization/transformers/ByIndexSerializer.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import sigma.ast.{ByIndex, Value}
44
import sigma.ast.syntax._
55
import sigma.serialization.{SigmaByteReader, SigmaByteWriter, ValueSerializer}
66
import ValueSerializer._
7+
import sigma.VersionContext
78
import sigma.ast.syntax.SValue
89
import sigma.ast.Operations.ByIndexInfo._
910
import sigma.serialization.SigmaByteWriter._
@@ -25,7 +26,11 @@ case class ByIndexSerializer(cons: (Value[SCollection[SType]], Value[SInt.type],
2526

2627
override def parse(r: SigmaByteReader): Value[SType] = {
2728
val input = r.getValue().asCollection[SType]
28-
val index = r.getValue().upcastTo(SInt)
29+
val index = if (VersionContext.current.isV3OrLaterErgoTreeVersion){
30+
r.getValue().asValue[SInt.type]
31+
} else {
32+
r.getValue().upcastTo(SInt)
33+
}
2934
val default = r.getOption(r.getValue())
3035
cons(input, index, default)
3136
}
Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,25 @@
11
package sigma.serialization
22

3-
import sigma.ast.{EQ, SInt, SubstConstants}
3+
import sigma.VersionContext
4+
import sigma.ast.{ConcreteCollection, EQ, IntArrayConstant, IntConstant, SInt, SNumericType, SubstConstants, Upcast, Value}
45
import sigma.ast.syntax.IntValue
5-
import sigma.ast.{ConcreteCollection, IntArrayConstant, IntConstant}
66
import sigma.serialization.ErgoTreeSerializer.DefaultSerializer
77
import sigmastate.CrossVersionProps
88

99
class SubstConstantsSerializerSpecification extends SerializationSpecification
1010
with CrossVersionProps {
1111

1212
property("SubstConstant deserialization round trip") {
13-
forAll(numExprTreeNodeGen) { prop =>
13+
forAll(numExprTreeNodeGen) { propRaw =>
14+
val prop = if(VersionContext.current.isV3OrLaterErgoTreeVersion) {
15+
val p = VersionContext.withVersions(2, 2) {
16+
// roundtrip to add Upcast nodes needed for v3 trees
17+
ValueSerializer.deserialize(ValueSerializer.serialize(propRaw))
18+
}
19+
Upcast(p.asInstanceOf[Value[SNumericType]], SInt)
20+
} else {
21+
propRaw
22+
}
1423
val tree = mkTestErgoTree(EQ(prop, IntConstant(1)).toSigmaProp)
1524
val bytes = DefaultSerializer.serializeErgoTree(tree)
1625
val newVals = ConcreteCollection(Array[IntValue](1), SInt)
@@ -19,4 +28,4 @@ class SubstConstantsSerializerSpecification extends SerializationSpecification
1928
}
2029
}
2130

22-
}
31+
}

interpreter/shared/src/test/scala/sigma/serialization/generators/ObjectGenerators.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ trait ObjectGenerators extends TypeGenerators
7878
implicit lazy val arbBoxConstant: Arbitrary[BoxConstant] = Arbitrary(boxConstantGen)
7979
implicit lazy val arbAvlTreeConstant: Arbitrary[AvlTreeConstant] = Arbitrary(avlTreeConstantGen)
8080
implicit lazy val arbBigIntConstant: Arbitrary[BigIntConstant] = Arbitrary(bigIntConstGen)
81+
implicit lazy val arbUnsignedBigIntConstant: Arbitrary[UnsignedBigIntConstant] = Arbitrary(unsignedBigIntConstGen)
8182
implicit lazy val arbGetVarBox: Arbitrary[BoxValue] = Arbitrary(getVar[SBox.type])
8283
implicit lazy val arbGetVarAvlTree : Arbitrary[AvlTreeValue] = Arbitrary(getVarAvlTreeGen)
8384
implicit lazy val arbProveDlog : Arbitrary[ProveDlog] = Arbitrary(proveDlogGen)
@@ -572,12 +573,13 @@ trait ObjectGenerators extends TypeGenerators
572573
)
573574
} yield node
574575

575-
def numExprTreeGen: Gen[Value[SNumericType]] =
576+
def numExprTreeGen: Gen[Value[SNumericType]] = {
576577
Gen.oneOf(arbByteConstants.arbitrary,
577578
arbIntConstants.arbitrary,
578579
arbLongConstants.arbitrary,
579580
arbBigIntConstant.arbitrary,
580581
Gen.delay(numExprTreeNodeGen))
582+
}
581583

582584
def comparisonExprTreeNodeGen: Gen[Value[SBoolean.type]] = for {
583585
left <- numExprTreeNodeGen

sc/shared/src/test/scala/sigmastate/serialization/DeserializationResilience.scala

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,12 @@ import sigmastate.utils.Helpers._
2727

2828
import java.math.BigInteger
2929
import java.nio.ByteBuffer
30-
import scala.collection.immutable.Seq
3130
import scala.collection.mutable
3231
import scala.util.{Failure, Success, Try}
3332

3433
trait DeserializationResilienceTesting extends SerializationSpecification
3534
with CompilerTestingCommons with CompilerCrossVersionProps {
35+
3636
protected def traceReaderCallDepth(expr: SValue): (IndexedSeq[Int], IndexedSeq[Int]) = {
3737
class LoggingSigmaByteReader(r: Reader) extends
3838
SigmaByteReader(r,
@@ -207,10 +207,12 @@ class DeserializationResilience extends DeserializationResilienceTesting {
207207
val (callDepths, levels) = traceReaderCallDepth(expr)
208208
callDepths shouldEqual levels
209209
}
210-
forAll(numExprTreeNodeGen) { numExpr =>
211-
val expr = EQ(numExpr, IntConstant(1))
212-
val (callDepths, levels) = traceReaderCallDepth(expr)
213-
callDepths shouldEqual levels
210+
if(!VersionContext.current.isV3OrLaterErgoTreeVersion) { // to avoid issues with upcast in trees >= v3
211+
forAll(numExprTreeNodeGen) { numExpr =>
212+
val expr = EQ(numExpr, IntConstant(1))
213+
val (callDepths, levels) = traceReaderCallDepth(expr)
214+
callDepths shouldEqual levels
215+
}
214216
}
215217
forAll(sigmaBooleanGen) { sigmaBool =>
216218
val (callDepths, levels) = traceReaderCallDepth(sigmaBool)

sc/shared/src/test/scala/sigmastate/serialization/ErgoTreeSerializerSpecification.scala

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ package sigma.serialization
22

33
import org.ergoplatform.ErgoBox
44
import org.ergoplatform.validation.ValidationRules.CheckDeserializedScriptIsSigmaProp
5-
import sigma.SigmaProp
5+
import sigma.{SigmaProp, VersionContext}
66
import sigma.ast._
77
import sigma.ast.syntax.SigmaPropValue
88
import sigma.data.CBigInt
99
import sigma.util.Extensions.SigmaPropOps
1010
import sigma.validation.ValidationException
1111
import ErgoTree.EmptyConstants
1212
import ErgoTree.HeaderType
13+
import scorex.util.encode.Base16
1314
import sigma.compiler.ir.IRContext
1415
import sigma.eval.Extensions.SigmaBooleanOps
1516
import sigmastate._
@@ -25,6 +26,8 @@ class ErgoTreeSerializerSpecification extends SerializationSpecification
2526
beginPass(noConstPropagationPass)
2627
}
2728

29+
30+
2831
private def extractConstants(prop: SigmaPropValue)(implicit IR: IRContext): Seq[ErgoTree] = {
2932
import ErgoTree._
3033
val env = Map[String, Any]()
@@ -222,4 +225,16 @@ class ErgoTreeSerializerSpecification extends SerializationSpecification
222225
treeBytes shouldBe propBytes.toArray
223226
}
224227
}
228+
229+
property("v3 tree with upcast") {
230+
val treeBytes = Base16.decode("0b15ea02e4dc650cfe020300020008b20e020102020100").get
231+
232+
VersionContext.withVersions(3, 3) {
233+
val tree = DefaultSerializer.deserializeErgoTree(treeBytes)
234+
val treeBytes2 = DefaultSerializer.serializeErgoTree(tree)
235+
236+
treeBytes.sameElements(treeBytes2) shouldBe true
237+
}
238+
}
239+
225240
}

sc/shared/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ class BasicOpsSpecification extends CompilerTestingCommons
127127
prop shouldBe propExp
128128

129129
val tree = ErgoTree.fromProposition(ergoTreeHeaderInTests, prop)
130+
131+
// check ErgoTree roundtrip
132+
val tBytes = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(tree)
133+
val tBytes2 = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(tBytes))
134+
tBytes.sameElements(tBytes2) shouldBe true
135+
130136
val p3 = prover.dlogSecrets(2).publicImage
131137
val boxToSpend = testBox(10, tree,
132138
additionalRegisters = additionalRegistersOpt.getOrElse(Map(

0 commit comments

Comments
 (0)