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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.ergoplatform

import debox.cfor
import org.ergoplatform.ErgoBox._
import org.ergoplatform.validation.ValidationRules.CheckV6Type
import scorex.util.encode.Base16
import scorex.util.{ModifierId, bytesToId}
import sigma.Extensions.{ArrayOps, CollOps}
Expand Down Expand Up @@ -228,6 +229,7 @@ object ErgoBoxCandidate {
cfor(0)(_ < nRegs, _ + 1) { iReg =>
val reg = ErgoBox.nonMandatoryRegisters(iReg)
val v = r.getValue().asInstanceOf[EvaluatedValue[SType]] // READ
CheckV6Type(v)
b += ((reg, v)) // don't use `->` since it incur additional wrapper overhead
}
r.positionLimit = previousPositionLimit
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.ergoplatform.validation

import sigma.{SigmaException, VersionContext}
import sigma.ast.{DeserializeContext, ErgoTree, MethodsContainer, SMethod}
import sigma.ast._
import sigma.ast.TypeCodes.LastConstantCode
import sigma.serialization.{InvalidOpCode, SerializerException}
import sigma.util.Extensions.toUByte
Expand All @@ -13,6 +13,8 @@ import sigma.exceptions.InterpreterException
import sigma.serialization.ValueCodes.OpCode
import sigma.serialization.ValueSerializer

import scala.annotation.tailrec

/** All validation rules which are used to check soft-forkable conditions. Each validation
* rule throws a [[org.ergoplatform.validation.ValidationException]]. Each
* ValidationException can be caught and handled with respect to
Expand Down Expand Up @@ -162,6 +164,48 @@ object ValidationRules {
override protected def settings: SigmaValidationSettings = currentSettings
}

object CheckV6Type extends ValidationRule(1019,
"Check the type has the declared method.") {
override protected lazy val settings: SigmaValidationSettings = currentSettings

final def apply[T](v: EvaluatedValue[_]): Unit = {
checkRule()

def v6TypeCheck(tpe: SType) = {
if (tpe.isOption || tpe.typeCode == SHeader.typeCode || tpe.typeCode == SUnsignedBigInt.typeCode) {
throwValidationException(
SerializerException(s"V6 type used in register or context var extension: $tpe"),
Array[Any](tpe))
}
}

def step(s: SType): Unit = {
Copy link
Contributor

Choose a reason for hiding this comment

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

Doesn't seem to be sufficient for Coll type that's nested in another type like Tuple or Coll

    val b7 = new ErgoBoxCandidate(1L, trueProp, 1,
      additionalRegisters = Map(R4 -> ConcreteCollection(Seq(ConcreteCollection(Seq(UnsignedBigIntConstant(new BigInteger("1"))), SUnsignedBigInt)), (SCollection(SUnsignedBigInt)))))
    VersionContext.withVersions(3, 3) {
      val bs = ErgoBoxCandidate.serializer.toBytes(b7)
      a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
    }

Copy link
Member Author

Choose a reason for hiding this comment

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

oh right, fixed in 845c3e0

s match {
case st: STuple => st.items.foreach(step)
case sc: SCollection[_] => step(sc.elemType) // this case should be after STuple as STuple deriving from SCollection
case s: SType => v6TypeCheck(s)
}
}

v match {
case c: Constant[_] => step(c.tpe)
case t: Tuple => t.items.foreach(i => step(i.tpe))
case c: EvaluatedCollection[_, _] => step(c.elementType)
case GroupGenerator =>
}
Copy link
Member

Choose a reason for hiding this comment

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

What is the motivation to add and check this rule?

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 problem is that deserialization of values in registers and context extension is not versioned, so newly supported serializable types (Header, Option[], UnsignedBigInt) can't be put there (but serialized as Coll[Byte] value can be put there to be used with Global.deserialize )

}

override def isSoftFork(vs: SigmaValidationSettings,
ruleId: Short,
status: RuleStatus,
args: Seq[Any]): Boolean = (status, args) match {
case (ChangedRule(newValue), Seq(objType: MethodsContainer, methodId: Byte)) =>
val key = Array(objType.ownerType.typeId, methodId)
newValue.grouped(2).exists(java.util.Arrays.equals(_, key))
case _ => false
}
}

private val ruleSpecsV5: Seq[ValidationRule] = Seq(
CheckDeserializedScriptType,
CheckDeserializedScriptIsSigmaProp,
Expand All @@ -178,7 +222,8 @@ object ValidationRules {
CheckHeaderSizeBit,
CheckCostFuncOperation,
CheckPositionLimit,
CheckLoopLevelInCostFunction
CheckLoopLevelInCostFunction,
CheckV6Type
)

// v6 validation rules below
Expand Down
2 changes: 1 addition & 1 deletion data/shared/src/main/scala/sigma/ast/values.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ trait PerItemCostValueCompanion extends ValueCompanion {
*
* @see Constant, ConcreteCollection, Tuple
*/
abstract class EvaluatedValue[+S <: SType] extends Value[S] {
sealed trait EvaluatedValue[+S <: SType] extends Value[S] {
/** The evaluated data value of the corresponding underlying data type. */
val value: S#WrappedType

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package sigma.interpreter

import org.ergoplatform.validation.ValidationRules.CheckV6Type
import sigma.ast.{EvaluatedValue, SType}
import sigma.interpreter.ContextExtension.VarBinding
import sigma.serialization.{SigmaByteReader, SigmaByteWriter, SigmaSerializer}
Expand Down Expand Up @@ -49,7 +50,12 @@ object ContextExtension {
if (extSize < 0)
error(s"Negative amount of context extension values: $extSize")
val values = (0 until extSize)
.map(_ => (r.getByte(), r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]]))
.map{_ =>
val k = r.getByte()
val v = r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]]
CheckV6Type(v)
(k, v)
}
ContextExtension(values.toMap)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package sigma.serialization
import java.nio.ByteBuffer
import scorex.util.ByteArrayBuilder
import scorex.util.serialization._
import sigma.ast.{Constant, EvaluatedCollection, EvaluatedValue, GroupGenerator, SHeader, SType, SUnsignedBigInt}
import sigma.data.SigmaConstants
import sigma.serialization.SigmaByteWriter.{FixedCostCallback, PerItemCostCallback}
import sigma.serialization.ValueCodes.OpCode
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,7 +432,7 @@ trait ObjectGenerators extends TypeGenerators

import ValidationRules._

val numRules = currentSettings.size
def numRules = currentSettings.size

val replacedRuleIdGen = Gen.chooseNum((FirstRuleId + numRules).toShort, Short.MaxValue)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@ class SigmaValidationSettingsSerializerSpec extends SerializationSpecification w

property("SigmaValidationSettings round trip") {
forAll(ruleIdGen, statusGen, MinSuccessful(100)) { (ruleId, status) =>
val vs = currentSettings.updated(ruleId, status)
roundtrip(vs)
whenever(currentSettings.getStatus(ruleId).isDefined) {
val vs = currentSettings.updated(ruleId, status)
roundtrip(vs)
}
}
}

Expand Down
6 changes: 4 additions & 2 deletions sc/shared/src/test/scala/sigma/SigmaDslTesting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import debox.cfor
import org.ergoplatform._
import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, TestContractSpec}
import org.ergoplatform.validation.ValidationRules
import org.ergoplatform.validation.ValidationRules.CheckV6Type
import org.scalacheck.Arbitrary._
import org.scalacheck.Gen.frequency
import org.scalacheck.{Arbitrary, Gen}
Expand Down Expand Up @@ -399,8 +400,9 @@ class SigmaDslTesting extends AnyPropSpec
parsed.bytes shouldBe box.bytes
}
catch {
case ValidationException(_, r: CheckSerializableTypeCode.type, Seq(SOption.OptionTypeCode), _) =>
// ignore the problem with Option serialization, but test all the other cases
// ignore the problem with Option serialization, but test all the other cases
case ValidationException(_, _: CheckV6Type.type, Seq(SOption(_)), _) =>
case ValidationException(_, _: CheckSerializableTypeCode.type, Seq(SOption.OptionTypeCode), _) =>
}

ErgoLikeContextTesting.dummy(box, activatedVersionInTests)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package sigma.serialization

import org.ergoplatform.ErgoBoxCandidate
import org.ergoplatform.ErgoBox.R4
import org.ergoplatform.{ErgoBoxCandidate, ErgoTreePredef}
import org.scalacheck.Gen
import scorex.crypto.authds.avltree.batch.{BatchAVLProver, Insert}
import scorex.crypto.authds.{ADKey, ADValue}
import scorex.crypto.hash.{Blake2b256, Digest32}
import scorex.util.serialization.{Reader, VLQByteBufferReader}
import sigma.ast.{SBoolean, SInt, SizeOf}
import sigma.ast.{SBoolean, SInt, SizeOf, _}
import sigma.data.{AvlTreeData, AvlTreeFlags, CAND, SigmaBoolean}
import sigma.util.{BenchmarkUtil, safeNewArray}
import sigma.validation.ValidationException
import sigma.validation.ValidationRules.CheckPositionLimit
import sigma.{Colls, Environment}
import sigma.ast._
import sigma.{Colls, Environment, VersionContext}
import sigma.ast.syntax._
import sigmastate._
import sigma.Extensions.ArrayOps
Expand All @@ -25,6 +25,7 @@ import sigmastate.helpers.{CompilerTestingCommons, ErgoLikeContextTesting, ErgoL
import sigma.serialization.OpCodes._
import sigmastate.utils.Helpers._

import java.math.BigInteger
import java.nio.ByteBuffer
import scala.collection.immutable.Seq
import scala.collection.mutable
Expand Down Expand Up @@ -425,4 +426,69 @@ class DeserializationResilience extends DeserializationResilienceTesting {
// NOTE, even though performOneOperation fails, some AvlTree$ methods used in Interpreter
// (remove_eval, update_eval, contains_eval) won't throw, while others will.
}


property("impossible to use v6 types in box registers") {
val trueProp = ErgoTreePredef.TrueProp(ErgoTree.defaultHeaderWithVersion(3))

val b = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> UnsignedBigIntConstant(new BigInteger("2"))))
VersionContext.withVersions(3, 3) {
val bs = ErgoBoxCandidate.serializer.toBytes(b)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
}

val b2 = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> Constant[SOption[SInt.type]](Some(2), SOption(SInt))))
VersionContext.withVersions(3, 3) {
val bs2 = ErgoBoxCandidate.serializer.toBytes(b2)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs2)
}

val b3 = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> Tuple(UnsignedBigIntConstant(new BigInteger("1")), IntConstant(1))))
VersionContext.withVersions(3, 3) {
val bs = ErgoBoxCandidate.serializer.toBytes(b3)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
}

val b4 = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> ConcreteCollection(Seq(UnsignedBigIntConstant(new BigInteger("1"))), SUnsignedBigInt)))
VersionContext.withVersions(3, 3) {
val bs = ErgoBoxCandidate.serializer.toBytes(b4)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
}

val b5 = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> Tuple(IntConstant(1), UnsignedBigIntConstant(new BigInteger("1")))))
VersionContext.withVersions(3, 3) {
val bs = ErgoBoxCandidate.serializer.toBytes(b5)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
}

val reader = new SigmaByteReader(new VLQByteBufferReader(ByteBuffer.wrap(decodeBytes("5402030209050a050105").toArray)), new ConstantStore(), false)
val v = VersionContext.withVersions(3, 3) {ConstantSerializer(DeserializationSigmaBuilder).parse(reader).asInstanceOf[Constant[STuple]] }
val b6 = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> v))
VersionContext.withVersions(3, 3) {
val bs6 = ErgoBoxCandidate.serializer.toBytes(b6)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs6)
}

val b7 = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> ConcreteCollection(Seq(Tuple(IntConstant(1), UnsignedBigIntConstant(new BigInteger("1")))), STuple(SInt, SUnsignedBigInt))))
VersionContext.withVersions(3, 3) {
val bs = ErgoBoxCandidate.serializer.toBytes(b7)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
}

val b8 = new ErgoBoxCandidate(1L, trueProp, 1,
additionalRegisters = Map(R4 -> ConcreteCollection(Seq(ConcreteCollection(Seq(UnsignedBigIntConstant(new BigInteger("1"))), SUnsignedBigInt)), (SCollection(SUnsignedBigInt)))))
VersionContext.withVersions(3, 3) {
val bs = ErgoBoxCandidate.serializer.toBytes(b8)
a[sigma.validation.ValidationException] should be thrownBy ErgoBoxCandidate.serializer.fromBytes(bs)
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ import sigmastate._
import sigma.ast.syntax._
import sigma.data.{AvlTreeData, CBox, ProveDHTuple, ProveDlog, TrivialProp}
import sigma.util.Extensions.EcpOps
import sigma.validation.ValidationException
import sigma.validation.{ReplacedRule, ValidationException, ValidationRules}
import sigmastate.eval._
import sigmastate.interpreter.Interpreter._
import sigmastate.helpers._
import sigmastate.helpers.TestingHelpers._
import sigma.interpreter.ContextExtension.VarBinding
import sigma.eval.Extensions.SigmaBooleanOps
import sigma.interpreter.{ContextExtension, CostedProverResult}
import sigma.interpreter.{ContextExtension, CostedProverResult, ProverResult}
import sigma.serialization.{SerializationSpecification, ValueSerializer}
import sigmastate.utils.Helpers._

import java.math.BigInteger

class ErgoLikeInterpreterSpecification extends CompilerTestingCommons
with SerializationSpecification
with CompilerCrossVersionProps {
Expand Down Expand Up @@ -761,6 +763,31 @@ class ErgoLikeInterpreterSpecification extends CompilerTestingCommons
prove(tree, script = 1.toByte -> ByteArrayConstant(scriptBytes))
}

property("DeserializeContext can return expression of UnsignedBigInt type in 6.0") {
def prove(ergoTree: ErgoTree, script: VarBinding) = {
val boxToSpend = testBox(10, ergoTree, creationHeight = 5)
val updVs = if (VersionContext.current.isV6Activated) {
ValidationRules.coreSettings
} else {
ValidationRules.coreSettings.updated(1007.toShort, ReplacedRule(1017.toShort))
}
val ctx = ErgoLikeContextTesting.dummy(boxToSpend, 3)
.withExtension(
ContextExtension(Seq(script).toMap)) // provide script bytes in context variable
.withValidationSettings(updVs)

val prover = new ErgoLikeTestProvingInterpreter()
prover.prove(ergoTree, ctx, fakeMessage).getOrThrow
}

val script = """{unsignedBigInt("0")}"""
val scriptProp = VersionContext.withVersions(3,3){compile(Map.empty, script)} // of Int type
val scriptBytes = VersionContext.withVersions(3,3){ValueSerializer.serialize(scriptProp)}
val tree = VersionContext.withVersions(3,3){ErgoTree.fromProposition(ErgoTree.defaultHeaderWithVersion(3),
EQ(DeserializeContext(1, SUnsignedBigInt), UnsignedBigIntConstant(new BigInteger("0"))).toSigmaProp)}
prove(tree, script = 1.toByte -> ByteArrayConstant(scriptBytes))
}

property("non-const ProveDHT") {
import sigma.crypto.CryptoConstants.dlogGroup
compile(Map("gA" -> dlogGroup.generator.toGroupElement),
Expand Down