diff --git a/core/shared/src/main/scala/sigma/ast/SType.scala b/core/shared/src/main/scala/sigma/ast/SType.scala index f75cbc9e8b..6656ede3c9 100644 --- a/core/shared/src/main/scala/sigma/ast/SType.scala +++ b/core/shared/src/main/scala/sigma/ast/SType.scala @@ -602,7 +602,8 @@ object STypeApply { /** Type description of optional values. Instances of `Option` * are either constructed by `Some` or by `None` constructors. */ case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct with SGenericType { - override type WrappedType = Option[ElemType#WrappedType] + type ElemWrappedType = ElemType#WrappedType + override type WrappedType = Option[ElemWrappedType] override val typeCode: TypeCode = SOption.OptionTypeCode override def toString = s"Option[$elemType]" override def toTermString: String = s"Option[${elemType.toTermString}]" diff --git a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala index 479b199da5..233494392a 100644 --- a/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala +++ b/core/shared/src/main/scala/sigma/serialization/CoreDataSerializer.scala @@ -68,7 +68,12 @@ class CoreDataSerializer { i += 1 } - // TODO v6.0 (3h): support Option[T] (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659) + case SOption(elemType) if VersionContext.current.isV6SoftForkActivated => + val o = v.asInstanceOf[Option[elemType.WrappedType]] + w.putOption(o){case (w, v) => + serialize(v, elemType, w) + } + case _ => CheckSerializableTypeCode(tpe.typeCode) throw new SerializerException(s"Don't know how to serialize ($v, $tpe)") @@ -118,6 +123,10 @@ class CoreDataSerializer { }.toArray[Any] val coll = Colls.fromArray(arr)(sigma.AnyType) Evaluation.toDslTuple(coll, tuple) + case tOption: SOption[_] if VersionContext.current.isV6SoftForkActivated => + r.getOption[tOption.ElemWrappedType] { + deserialize(tOption.elemType, r).asInstanceOf[tOption.ElemWrappedType] + } case t => CheckSerializableTypeCode(t.typeCode) throw new SerializerException(s"Not defined DataSerializer for type $t") diff --git a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala index 7cd9967e54..2d9da3a87e 100644 --- a/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala +++ b/interpreter/shared/src/test/scala/sigma/serialization/DataSerializerSpecification.scala @@ -3,10 +3,10 @@ package sigma.serialization import java.math.BigInteger import org.ergoplatform.ErgoBox import org.scalacheck.Arbitrary._ -import sigma.data.{DataValueComparer, RType, SigmaBoolean, TupleColl} +import sigma.data.{DataValueComparer, OptionType, RType, SigmaBoolean, TupleColl} import sigma.ast.SCollection.SByteArray import sigmastate.eval._ -import sigma.{AvlTree, Colls, Evaluation} +import sigma.{AvlTree, Colls, Evaluation, VersionContext} import sigma.ast.SType.AnyOps import sigma.ast._ import org.scalacheck.Gen @@ -14,7 +14,7 @@ import sigma.Extensions.ArrayOps import sigma.crypto.EcPointType import sigma.eval.SigmaDsl import sigma.util.Extensions.{BigIntegerOps, EcpOps, SigmaBooleanOps} -import sigmastate.interpreter.{CostAccumulator, CErgoTreeEvaluator} +import sigmastate.interpreter.{CErgoTreeEvaluator, CostAccumulator} import sigmastate.interpreter.CErgoTreeEvaluator.DefaultProfiler import sigmastate.utils.Helpers @@ -92,6 +92,32 @@ class DataSerializerSpecification extends SerializationSpecification { } } + def testOption[T <: SType](tpe: T) = { + implicit val wWrapped: Gen[T#WrappedType] = wrappedTypeGen(tpe) + val tT = Evaluation.stypeToRType(tpe) + + an[Exception] should be thrownBy ( + VersionContext.withVersions((VersionContext.V6SoftForkVersion - 1).toByte, 1) { + forAll { in: T#WrappedType => + roundtrip[SType](Some(in).asWrappedType, SOption(tpe)) + roundtrip[SOption[SCollection[T]]](Some(Colls.fromItems(in)(tT)), SOption(SCollectionType(tpe))) + } + }) + + VersionContext.withVersions(VersionContext.V6SoftForkVersion, 1) { + forAll { in: T#WrappedType => + roundtrip[SType](Some(in).asWrappedType, SOption(tpe)) + roundtrip[SOption[T]](None, SOption(tpe)) + roundtrip[SOption[T]](Some(in), SOption(tpe)) + roundtrip[SOption[SCollection[T]]](Some(Colls.fromItems(in)(tT)), SOption(SCollectionType(tpe))) + roundtrip[SCollection[SOption[T]]](Colls.fromItems(Option(in), None.asInstanceOf[Option[T#WrappedType]])(OptionType(tT)), SCollectionType(SOption(tpe))) + roundtrip[SOption[SOption[T]]](None, SOption(SOption(tpe))) + roundtrip[SOption[SOption[T]]](Some(Some(in)), SOption(SOption(tpe))) + roundtrip[SOption[SOption[T]]](Some(None), SOption(SOption(tpe))) + } + } + } + property("Data serialization round trip") { forAll { x: Byte => roundtrip[SByte.type](x, SByte) } forAll { x: Boolean => roundtrip[SBoolean.type](x, SBoolean) } @@ -105,6 +131,7 @@ class DataSerializerSpecification extends SerializationSpecification { forAll { x: Array[Byte] => roundtrip[SByteArray](x.toColl, SByteArray) } forAll { t: SPredefType => testCollection(t) } forAll { t: SPredefType => testTuples(t) } + forAll { t: SPredefType => testOption(t) } } property("Should check limits and fail") {