diff --git a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala index 1936bbcd9a..e56744efd6 100644 --- a/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/TypeSerializer.scala @@ -5,7 +5,7 @@ import sigma.VersionContext import sigma.ast.SCollectionType.{CollectionTypeCode, NestedCollectionTypeCode} import sigma.ast._ import sigma.util.safeNewArray -import sigma.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckTypeCode} +import sigma.validation.ValidationRules.{CheckPrimitiveTypeCode, CheckPrimitiveTypeCodeV6, CheckTypeCode, CheckTypeCodeV6} import java.nio.charset.StandardCharsets @@ -14,7 +14,12 @@ class TypeSerializer { import TypeSerializer._ def getEmbeddableType(code: Int): SType = { - CheckPrimitiveTypeCode(code.toByte) + // todo : add unsigned bit int to embeddable id to type + if (VersionContext.current.isV6SoftForkActivated) { + CheckPrimitiveTypeCodeV6(code.toByte) + } else { + CheckPrimitiveTypeCode(code.toByte) + } embeddableIdToType(code) } @@ -215,9 +220,13 @@ class TypeSerializer { } SFunc(tDom, tRange, tpeParams) case _ => - // todo: 6.0: replace 1008 check with identical behavior but other opcode, to activate - // ReplacedRule(1008 -> new opcode) during 6.0 activation - CheckTypeCode(c.toByte) + // the #1008 check replaced with one with identical behavior but different opcode (1018), to activate + // ReplacedRule(1008 -> 1018) during 6.0 activation + if (VersionContext.current.isV6SoftForkActivated) { + CheckTypeCodeV6(c.toByte) + } else { + CheckTypeCode(c.toByte) + } NoType } } diff --git a/core/shared/src/main/scala/sigma/validation/ValidationRules.scala b/core/shared/src/main/scala/sigma/validation/ValidationRules.scala index 249e2fce0a..6321ae6ba6 100644 --- a/core/shared/src/main/scala/sigma/validation/ValidationRules.scala +++ b/core/shared/src/main/scala/sigma/validation/ValidationRules.scala @@ -1,6 +1,6 @@ package sigma.validation -import sigma.SigmaException +import sigma.{SigmaException, VersionContext} import sigma.ast.{SGlobal, SOption, TypeCodes} import sigma.serialization.{ReaderPositionLimitExceeded, SerializerException} import sigma.util.Extensions.toUByte @@ -77,7 +77,7 @@ object ValidationRules { /** The id of the first validation rule. Can be used as the beginning of the rules id range. */ val FirstRuleId = 1000.toShort - object CheckPrimitiveTypeCode extends ValidationRule(1007, + class CheckPrimitiveTypeCodeTemplate(ruleId: Short) extends ValidationRule(ruleId, "Check the primitive type code is supported or is added via soft-fork") with SoftForkWhenCodeAdded { override protected lazy val settings: SigmaValidationSettings = coreSettings @@ -93,10 +93,14 @@ object ValidationRules { } } - object CheckTypeCode extends ValidationRule(1008, + object CheckPrimitiveTypeCode extends CheckPrimitiveTypeCodeTemplate(1007) + + object CheckPrimitiveTypeCodeV6 extends CheckPrimitiveTypeCodeTemplate(1017) + + class CheckTypeCodeTemplate(ruleId: Short) extends ValidationRule(ruleId, "Check the non-primitive type code is supported or is added via soft-fork") with SoftForkWhenCodeAdded { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings final def apply[T](typeCode: Byte): Unit = { checkRule() @@ -109,10 +113,14 @@ object ValidationRules { } } + object CheckTypeCode extends CheckTypeCodeTemplate(1008) + + object CheckTypeCodeV6 extends CheckTypeCodeTemplate(1018) + object CheckSerializableTypeCode extends ValidationRule(1009, "Check the data values of the type (given by type code) can be serialized") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings /** Creates an exception which is used as a cause when throwing a ValidationException. */ def throwValidationException(typeCode: Byte): Nothing = { @@ -141,7 +149,7 @@ object ValidationRules { object CheckTypeWithMethods extends ValidationRule(1010, "Check the type (given by type code) supports methods") with SoftForkWhenCodeAdded { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings final def apply[T](typeCode: Byte, cond: Boolean): Unit = { checkRule() @@ -160,7 +168,7 @@ object ValidationRules { */ object CheckPositionLimit extends ValidationRule(1014, "Check that the Reader has not exceeded the position limit.") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = coreSettings + override protected def settings: SigmaValidationSettings = coreSettings /** Wraps the given cause into [[ValidationException]] and throws it. */ def throwValidationException(cause: ReaderPositionLimitExceeded): Nothing = { @@ -183,7 +191,7 @@ object ValidationRules { } } - private val ruleSpecs: Seq[ValidationRule] = Seq( + private val ruleSpecsV5: Seq[ValidationRule] = Seq( CheckPrimitiveTypeCode, CheckTypeCode, CheckSerializableTypeCode, @@ -191,13 +199,30 @@ object ValidationRules { CheckPositionLimit ) + private val ruleSpecsV6: Seq[ValidationRule] = Seq( + CheckPrimitiveTypeCodeV6, + CheckTypeCodeV6, + CheckSerializableTypeCode, + CheckTypeWithMethods, + CheckPositionLimit + ) + + private def ruleSpecs: Seq[ValidationRule] = { + if(VersionContext.current.isV6SoftForkActivated) { + ruleSpecsV6 + } else { + ruleSpecsV5 + } + } + /** Validation settings that correspond to the current version of the ErgoScript implementation. * Different version of the code will have a different set of rules here. * This variable is globally available and can be use wherever checking of the rules is necessary. * This is immutable data structure, it can be augmented with RuleStates from block extension * sections of the blockchain, but that augmentation is only available in stateful context. */ - val coreSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ + // todo: versioned cache here for efficiency + def coreSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ val map = ruleSpecs.map(r => r.id -> (r, EnabledRule)).toMap assert(map.size == ruleSpecs.size, s"Duplicate ruleIds ${ruleSpecs.groupBy(_.id).filter(g => g._2.length > 1)}") map diff --git a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala index 9d4de47a99..4c75de6623 100644 --- a/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala +++ b/data/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala @@ -1,6 +1,6 @@ package org.ergoplatform.validation -import sigma.SigmaException +import sigma.{SigmaException, VersionContext} import sigma.ast.{DeserializeContext, ErgoTree, MethodsContainer, SMethod} import sigma.ast.TypeCodes.LastConstantCode import sigma.serialization.{InvalidOpCode, SerializerException} @@ -23,7 +23,7 @@ object ValidationRules { object CheckDeserializedScriptType extends ValidationRule(FirstRuleId, "Deserialized script should have expected type") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply[T](d: DeserializeContext[_], script: SValue): Unit = { checkRule() @@ -38,7 +38,7 @@ object ValidationRules { object CheckDeserializedScriptIsSigmaProp extends ValidationRule(1001, "Deserialized script should have SigmaProp type") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings /** @param root candidate node before it is added as a root of ErgoTree */ final def apply[T](root: SValue): Unit = { @@ -54,7 +54,7 @@ object ValidationRules { object CheckValidOpCode extends ValidationRule(1002, "Check the opcode is supported by registered serializer or is added via soft-fork") with SoftForkWhenCodeAdded { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply[T](ser: ValueSerializer[_], opCode: OpCode): Unit = { checkRule() @@ -69,18 +69,18 @@ object ValidationRules { /** Not used since v5.0.1. */ object CheckIsSupportedIndexExpression extends ValidationRule(1003, "Check the index expression for accessing collection element is supported.") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } /** Not used since v5.0.3 */ object CheckCostFunc extends ValidationRule(1004, "Cost function should contain only operations from specified list.") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } object CheckCalcFunc extends ValidationRule(1005, "If SigmaProp.isProven method calls exists in the given function,\n then it is the last operation") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } /** This rule is not use in v5.x, keep the commented code below as a precise @@ -88,7 +88,7 @@ object ValidationRules { */ object CheckTupleType extends ValidationRule(1006, "Supported tuple type.") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings // final def apply[Ctx <: IRContext, T](ctx: Ctx)(e: ctx.Elem[_]): Unit = { // checkRule() @@ -102,9 +102,9 @@ object ValidationRules { // } } - object CheckAndGetMethod extends ValidationRule(1011, + class CheckAndGetMethodTemplate(ruleId: Short) extends ValidationRule(ruleId, "Check the type has the declared method.") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply[T](objType: MethodsContainer, methodId: Byte): SMethod = { checkRule() @@ -128,9 +128,12 @@ object ValidationRules { } } + object CheckAndGetMethod extends CheckAndGetMethodTemplate(1011) + object CheckAndGetMethodV6 extends CheckAndGetMethodTemplate(1016) + object CheckHeaderSizeBit extends ValidationRule(1012, "For version greater then 0, size bit should be set.") with SoftForkWhenReplaced { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings final def apply(header: HeaderType): Unit = { checkRule() @@ -146,16 +149,16 @@ object ValidationRules { /** Not used since v5.0.3 */ object CheckCostFuncOperation extends ValidationRule(1013, "Check the opcode is allowed in cost function") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } /** Not used since v5.0.1 */ object CheckLoopLevelInCostFunction extends ValidationRule(1015, "Check that loop level is not exceeded.") { - override protected lazy val settings: SigmaValidationSettings = currentSettings + override protected def settings: SigmaValidationSettings = currentSettings } - val ruleSpecs: Seq[ValidationRule] = Seq( + private val ruleSpecsV5: Seq[ValidationRule] = Seq( CheckDeserializedScriptType, CheckDeserializedScriptIsSigmaProp, CheckValidOpCode, @@ -174,13 +177,26 @@ object ValidationRules { CheckLoopLevelInCostFunction ) + // v6 validation rules below + private val ruleSpecsV6: Seq[ValidationRule] = { + ruleSpecsV5.filter(_.id != CheckAndGetMethod.id) ++ Seq(CheckAndGetMethodV6) + } + + def ruleSpecs: Seq[ValidationRule] = { + if (VersionContext.current.isV6SoftForkActivated) { + ruleSpecsV6 + } else { + ruleSpecsV5 + } + } + /** Validation settings that correspond to the current version of the ErgoScript implementation. * Different version of the code will have a different set of rules here. * This variable is globally available and can be use wherever checking of the rules is necessary. * This is immutable data structure, it can be augmented with RuleStates from block extension * sections of the blockchain, but that augmentation is only available in stateful context. */ - val currentSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ + def currentSettings: SigmaValidationSettings = new MapSigmaValidationSettings({ val map = ruleSpecs.map(r => r.id -> (r, EnabledRule)).toMap assert(map.size == ruleSpecs.size, s"Duplicate ruleIds ${ruleSpecs.groupBy(_.id).filter(g => g._2.length > 1)}") map diff --git a/data/shared/src/main/scala/sigma/ast/methods.scala b/data/shared/src/main/scala/sigma/ast/methods.scala index 4cba72e28d..f3fa1785fb 100644 --- a/data/shared/src/main/scala/sigma/ast/methods.scala +++ b/data/shared/src/main/scala/sigma/ast/methods.scala @@ -77,13 +77,17 @@ sealed trait MethodsContainer { /** Lookup method in this type by method's id or throw ValidationException. * This method can be used in trySoftForkable section to either obtain valid method - * or catch ValidatioinException which can be checked for soft-fork condition. + * or catch ValidationException which can be checked for soft-fork condition. * It delegate to getMethodById to lookup method. * * @see getMethodById */ def methodById(methodId: Byte): SMethod = { - ValidationRules.CheckAndGetMethod(this, methodId) + if (VersionContext.current.isV6SoftForkActivated) { + ValidationRules.CheckAndGetMethodV6(this, methodId) + } else { + ValidationRules.CheckAndGetMethod(this, methodId) + } } /** Finds a method descriptor [[SMethod]] for the given name. */ diff --git a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala index 2ae4f73703..8090ad82f9 100644 --- a/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala +++ b/data/shared/src/main/scala/sigma/data/CSigmaDslBuilder.scala @@ -23,7 +23,7 @@ import java.math.BigInteger * @see [[SigmaDslBuilder]] for detailed descriptions */ class CSigmaDslBuilder extends SigmaDslBuilder { dsl => - implicit val validationSettings: SigmaValidationSettings = ValidationRules.currentSettings + def validationSettings: SigmaValidationSettings = ValidationRules.currentSettings override val Colls: CollBuilder = sigma.Colls diff --git a/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala b/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala index b223355c5b..47068a9102 100644 --- a/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala +++ b/interpreter/shared/src/test/scala/org/ergoplatform/validation/ValidationSpecification.scala @@ -3,5 +3,5 @@ package org.ergoplatform.validation import sigma.validation.SigmaValidationSettings trait ValidationSpecification { - implicit val vs: SigmaValidationSettings = ValidationRules.currentSettings + def vs: SigmaValidationSettings = ValidationRules.currentSettings } diff --git a/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala b/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala index c8b9f06399..bce875fd14 100644 --- a/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala +++ b/sc/shared/src/test/scala/sigmastate/SoftForkabilitySpecification.scala @@ -289,7 +289,7 @@ class SoftForkabilitySpecification extends SigmaTestingData trySoftForkable(false) { action true - } + }(vs) }, { case ve: ValidationException if ve.rule == rule => true case _ => false