Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[6.0] Fix for lazyness in getOrElse could cause disagreement between 6.0 and < 6.0 nodes, activation details and revised checkSoftForkCondition #1043

Open
wants to merge 29 commits into
base: v6.0.0
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
ac11237
CheckAndGetMethod/CheckAndGetMethodV6, versioned methodById
kushti Oct 15, 2024
e9e59e0
Merge branch 'v6.0.0' of github.com:ScorexFoundation/sigmastate-inter…
kushti Oct 29, 2024
901c868
1007 & 1008 replacement & versioning
kushti Oct 29, 2024
b3a10a5
1007 rule update
kushti Nov 14, 2024
491f878
versioned rulesSpecs
kushti Nov 15, 2024
234c8fb
merging w. 6.0.0
kushti Dec 5, 2024
f482814
6.0 specific serialization removed
kushti Dec 6, 2024
c1dc254
ergoTreeVersion removed from VersionContext
kushti Dec 6, 2024
2effbba
removing checkType parameter which is always true
kushti Dec 6, 2024
8993b3a
ergo tree version removed from withVersions
kushti Dec 6, 2024
22ce03b
unused env removed from reductionWithDeserialize
kushti Dec 6, 2024
e2720be
tree version based checks for lazy default
kushti Dec 11, 2024
59225d3
tree version based versioning
kushti Dec 13, 2024
2f557e1
back to activated script version for serializers
kushti Dec 13, 2024
988f42b
fixing LSV5 tests
kushti Dec 16, 2024
a14a257
fixing TestingInterpreterSpecification
kushti Dec 17, 2024
11a4d3e
activationType flag for LSV* tests
kushti Dec 18, 2024
592a68c
fixing LSV6 tests
kushti Dec 18, 2024
e9f24c0
MethodCallSerializerSpecification fix
kushti Dec 19, 2024
7aad66f
merging w. 6.0-serialization branch
kushti Dec 19, 2024
d8839f9
activation type constants
kushti Dec 19, 2024
f59f704
fromBlockVersion
kushti Dec 20, 2024
905448b
fromBlockVersion fix
kushti Dec 20, 2024
91b3b98
versioning in serializeErgoTree
kushti Dec 23, 2024
bce9f91
Merge branch 'i904' of github.com:ScorexFoundation/sigmastate-interpr…
kushti Dec 25, 2024
ce205f4
post-merge fix
kushti Dec 26, 2024
f6b801f
replacedrule fix
kushti Dec 26, 2024
15a3096
full replacedrule id check
kushti Dec 26, 2024
8aa8002
fix checkmethod rule test
kushti Jan 10, 2025
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
9 changes: 8 additions & 1 deletion core/shared/src/main/scala/sigma/VersionContext.scala
Original file line number Diff line number Diff line change
@@ -24,7 +24,10 @@ case class VersionContext(activatedVersion: Byte, ergoTreeVersion: Byte) {

/** @return true, if the activated script version of Ergo protocol on the network is
* including v6.0 update. */
def isV6SoftForkActivated: Boolean = activatedVersion >= V6SoftForkVersion
def isV3OrLaterErgoTreeVersion: Boolean = ergoTreeVersion >= V6SoftForkVersion

def isV6Activated: Boolean = activatedVersion >= V6SoftForkVersion

}

object VersionContext {
@@ -104,4 +107,8 @@ object VersionContext {
}
}

def fromBlockVersion(blockVersion: Byte): VersionContext = {
VersionContext((blockVersion - 1).toByte, (blockVersion - 1).toByte)
}

}
26 changes: 13 additions & 13 deletions core/shared/src/main/scala/sigma/ast/SType.scala
Original file line number Diff line number Diff line change
@@ -114,7 +114,7 @@ object SType {
/** All pre-defined types should be listed here. Note, NoType is not listed.
* Should be in sync with sigmastate.lang.Types.predefTypes. */
def allPredefTypes: Seq[SType] = {
if(VersionContext.current.isV6SoftForkActivated) {
if(VersionContext.current.isV3OrLaterErgoTreeVersion) {
v6PredefTypes
} else {
v5PredefTypes
@@ -164,7 +164,7 @@ object SType {

private val v6TypesMap = v6Types.map { t => (t.typeId, t) }.toMap

def types: Map[Byte, STypeCompanion] = if (VersionContext.current.isV6SoftForkActivated) {
def types: Map[Byte, STypeCompanion] = if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
v6TypesMap
} else {
v5TypesMap
@@ -191,7 +191,7 @@ object SType {
case SInt => x.isInstanceOf[Int]
case SLong => x.isInstanceOf[Long]
case SBigInt => x.isInstanceOf[BigInt]
case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated => x.isInstanceOf[UnsignedBigInt]
case SUnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => x.isInstanceOf[UnsignedBigInt]
case SGroupElement => x.isInstanceOf[GroupElement]
case SSigmaProp => x.isInstanceOf[SigmaProp]
case SBox => x.isInstanceOf[Box]
@@ -409,8 +409,8 @@ 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 ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toByte // toByteExact from int is called under the hood
case bi: BigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => bi.toByte // toByteExact from int is called under the hood
case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => ubi.toByte // toByteExact from int is called under the hood
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
@@ -432,8 +432,8 @@ 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 ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toShort // toShortExact from int is called under the hood
case bi: BigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => bi.toShort // toShortExact from int is called under the hood
case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => ubi.toShort // toShortExact from int is called under the hood
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
@@ -457,8 +457,8 @@ 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 ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toInt
case bi: BigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => bi.toInt
case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => ubi.toInt
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
@@ -484,8 +484,8 @@ 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 ubi: UnsignedBigInt if VersionContext.current.isV6SoftForkActivated => ubi.toLong
case bi: BigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => bi.toLong
case ubi: UnsignedBigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => ubi.toLong
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
@@ -512,7 +512,7 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM
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 x: BigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => x
case _ => sys.error(s"Cannot upcast value $v to the type $this")
}
}
@@ -524,7 +524,7 @@ case object SBigInt extends SPrimType with SEmbeddable with SNumericType with SM
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 x: BigInt if VersionContext.current.isV3OrLaterErgoTreeVersion => x
case _ => sys.error(s"Cannot downcast value $v to the type $this")
}
}
4 changes: 2 additions & 2 deletions core/shared/src/main/scala/sigma/data/CollsOverArrays.scala
Original file line number Diff line number Diff line change
@@ -150,7 +150,7 @@ class CollOverArray[@specialized A](val toArray: Array[A], val builder: CollBuil
case obj: CollOverArray[_] if obj.tItem == this.tItem =>
java.util.Objects.deepEquals(obj.toArray, this.toArray)
case obj: PairColl[Any, Any] if obj.tItem == this.tItem =>
if (VersionContext.current.isV6SoftForkActivated) {
if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
equalsPairCollWithCollOverArray(obj, this.asInstanceOf[CollOverArray[Any]])
} else {
false
@@ -282,7 +282,7 @@ class PairOfCols[@specialized L, @specialized R](val ls: Coll[L], val rs: Coll[R
case that: PairColl[_, _] if that.tItem == this.tItem =>
ls == that.ls && rs == that.rs
case that: CollOverArray[Any] if that.tItem == this.tItem =>
if (VersionContext.current.isV6SoftForkActivated) {
if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
equalsPairCollWithCollOverArray(this.asInstanceOf[PairColl[Any, Any]], that)
} else {
false
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@ class CoreDataSerializer {
val data = v.asInstanceOf[BigInt].toBigInteger.toByteArray
w.putUShort(data.length)
w.putBytes(data)
case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated =>
case SUnsignedBigInt if VersionContext.current.isV6Activated =>
val data = BigIntegers.asUnsignedByteArray(v.asInstanceOf[CUnsignedBigInt].wrappedValue)
w.putUShort(data.length)
w.putBytes(data)
@@ -73,7 +73,7 @@ class CoreDataSerializer {
i += 1
}

case SOption(elemType) if VersionContext.current.isV6SoftForkActivated =>
case SOption(elemType) if VersionContext.current.isV6Activated =>
val o = v.asInstanceOf[Option[elemType.WrappedType]]
w.putOption(o){case (w, v) =>
serialize(v, elemType, w)
@@ -113,7 +113,7 @@ class CoreDataSerializer {
}
val valueBytes = r.getBytes(size)
CBigInt(new BigInteger(valueBytes))
case SUnsignedBigInt if VersionContext.current.isV6SoftForkActivated =>
case SUnsignedBigInt if VersionContext.current.isV6Activated =>
val size: Short = r.getUShort().toShort
if (size > SBigInt.MaxSizeInBytes) {
throw SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size")
@@ -135,7 +135,7 @@ class CoreDataSerializer {
}.toArray[Any]
val coll = Colls.fromArray(arr)(sigma.AnyType)
Evaluation.toDslTuple(coll, tuple)
case tOption: SOption[_] if VersionContext.current.isV6SoftForkActivated =>
case tOption: SOption[_] if VersionContext.current.isV6Activated =>
r.getOption[tOption.ElemWrappedType] {
deserialize(tOption.elemType, r).asInstanceOf[tOption.ElemWrappedType]
}
Original file line number Diff line number Diff line change
@@ -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.isV6Activated) {
CheckPrimitiveTypeCodeV6(code.toByte)
} else {
CheckPrimitiveTypeCode(code.toByte)
}
embeddableIdToType(code)
}

@@ -200,7 +205,7 @@ class TypeSerializer {
case SHeader.typeCode => SHeader
case SPreHeader.typeCode => SPreHeader
case SGlobal.typeCode => SGlobal
case SFunc.FuncTypeCode if VersionContext.current.isV6SoftForkActivated =>
case SFunc.FuncTypeCode if VersionContext.current.isV3OrLaterErgoTreeVersion =>
val tdLength = r.getUByte()

val tDom = (1 to tdLength).map { _ =>
@@ -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.isV6Activated) {
CheckTypeCodeV6(c.toByte)
} else {
CheckTypeCode(c.toByte)
}
NoType
}
}
@@ -243,7 +252,7 @@ object TypeSerializer extends TypeSerializer {
/** The list of embeddable types, i.e. types that can be combined with type constructor for optimized encoding.
* For each embeddable type `T`, and type constructor `C`, the type `C[T]` can be represented by single byte. */
def embeddableIdToType = {
if (VersionContext.current.isV6SoftForkActivated) {
if (VersionContext.current.isV3OrLaterErgoTreeVersion) {
embeddableV6
} else {
embeddableV5
Original file line number Diff line number Diff line change
@@ -32,7 +32,7 @@ case object DisabledRule extends RuleStatus {
* @see `ValidationSettings.isSoftFork`
* @param newRuleId id of a new rule which replaces the rule marked with this status
*/
case class ReplacedRule(newRuleId: Short) extends RuleStatus {
case class ReplacedRule(newRuleId: Short) extends RuleStatus {
val statusCode: Byte = RuleStatus.ReplacedRuleCode
}

Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package sigma.validation

import sigma.VersionContext

/**
* Configuration of validation. Each `ValidationRule` instance should be
* implemented as an `object` to facilitate type-safe usage. It then should be
@@ -48,7 +50,11 @@ abstract class SigmaValidationSettings extends Iterable[(Short, (ValidationRule,
def isSoftFork(ruleId: Short, ve: ValidationException): Boolean = {
val infoOpt = get(ruleId)
infoOpt match {
case Some((_, ReplacedRule(_))) => true
case Some((vr, ReplacedRule(_))) => if ((vr.id == 1011 || vr.id == 1007 || vr.id == 1008) && VersionContext.current.isV6Activated) {
false
} else {
true
}
case Some((rule, status)) => rule.isSoftFork(this, rule.id, status, ve.args)
case None => false
}
43 changes: 34 additions & 9 deletions core/shared/src/main/scala/sigma/validation/ValidationRules.scala
Original file line number Diff line number Diff line change
@@ -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,21 +191,38 @@ object ValidationRules {
}
}

private val ruleSpecs: Seq[ValidationRule] = Seq(
private val ruleSpecsV5: Seq[ValidationRule] = Seq(
CheckPrimitiveTypeCode,
CheckTypeCode,
CheckSerializableTypeCode,
CheckTypeWithMethods,
CheckPositionLimit
)

private val ruleSpecsV6: Seq[ValidationRule] = Seq(
CheckPrimitiveTypeCodeV6,
CheckTypeCodeV6,
CheckSerializableTypeCode,
CheckTypeWithMethods,
CheckPositionLimit
)

private def ruleSpecs: Seq[ValidationRule] = {
if(VersionContext.current.isV6Activated) {
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
Loading
Loading