From 0592cd85b7d4733612fb74faafc142b51f2689bc Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sun, 19 Dec 2021 16:27:45 +0300 Subject: [PATCH 01/14] v5.0-finalize: more tests for TypeSerializer --- sigmastate/src/main/scala/sigmastate/DataValueComparer.scala | 1 - .../sigmastate/serialization/TypeSerializerSpecification.scala | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sigmastate/src/main/scala/sigmastate/DataValueComparer.scala b/sigmastate/src/main/scala/sigmastate/DataValueComparer.scala index a85de65cc9..40f120f5b3 100644 --- a/sigmastate/src/main/scala/sigmastate/DataValueComparer.scala +++ b/sigmastate/src/main/scala/sigmastate/DataValueComparer.scala @@ -125,7 +125,6 @@ object DataValueComparer { final val OpDesc_EQ_COA_AvlTree = NamedDesc("EQ_COA_AvlTree") final val EQ_COA_AvlTree = OperationCostInfo(CostKind_EQ_COA_AvlTree, OpDesc_EQ_COA_AvlTree) - // TODO v5.0: update value after serialization is avoided to compute ErgoBox.id /** Equals two CollOverArray of Box type. */ final val CostKind_EQ_COA_Box = PerItemCost( baseCost = JitCost(15), perChunkCost = JitCost(5), chunkSize = 1) diff --git a/sigmastate/src/test/scala/sigmastate/serialization/TypeSerializerSpecification.scala b/sigmastate/src/test/scala/sigmastate/serialization/TypeSerializerSpecification.scala index ed4cda54ce..6521ec55f7 100644 --- a/sigmastate/src/test/scala/sigmastate/serialization/TypeSerializerSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/serialization/TypeSerializerSpecification.scala @@ -70,6 +70,8 @@ class TypeSerializerSpecification extends SerializationSpecification { } property("Specific types serialization roundtrip") { + roundtrip(SAny, Array[Byte](SAny.typeCode)) + roundtrip(SGlobal, Array[Byte](SGlobal.typeCode)) roundtrip(STuple(SCollection(SLong), SCollection(SLong)), Array[Byte](Pair1TypeCode, SLong.embedIn(CollectionTypeCode), SLong.embedIn(CollectionTypeCode))) roundtrip(STuple(SCollection(SLong), SOption(SLong)), From 5e782875c40c9e06fe4c1c78821517521abd28da Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sun, 19 Dec 2021 16:28:39 +0300 Subject: [PATCH 02/14] v5.0-finalize: removed CreateAvlTree operation --- .../scala/sigmastate/lang/SigmaBuilder.scala | 12 ------ .../scala/sigmastate/lang/SigmaPredef.scala | 16 -------- .../CreateAvlTreeSerializer.scala | 38 ------------------- .../serialization/ValueSerializer.scala | 1 - .../src/main/scala/sigmastate/trees.scala | 16 -------- 5 files changed, 83 deletions(-) delete mode 100644 sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala index 5b220109f8..a81135f7f1 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala @@ -144,11 +144,6 @@ abstract class SigmaBuilder { def mkCreateProveDlog(value: Value[SGroupElement.type]): SigmaPropValue - def mkCreateAvlTree(operationFlags: ByteValue, - digest: Value[SByteArray], - keyLength: IntValue, - valueLengthOpt: Value[SIntOption]): AvlTreeValue - /** Logically inverse to mkSigmaPropIsProven */ def mkBoolToSigmaProp(value: BoolValue): SigmaPropValue /** Logically inverse to mkBoolToSigmaProp */ @@ -531,13 +526,6 @@ class StdSigmaBuilder extends SigmaBuilder { override def mkCreateProveDlog(value: Value[SGroupElement.type]): SigmaPropValue = CreateProveDlog(value) - override def mkCreateAvlTree(operationFlags: ByteValue, - digest: Value[SByteArray], - keyLength: IntValue, - valueLengthOpt: Value[SIntOption]): AvlTreeValue = { - CreateAvlTree(operationFlags, digest, keyLength, valueLengthOpt) - } - override def mkBoolToSigmaProp(value: BoolValue): SigmaPropValue = BoolToSigmaProp(value).withSrcCtx(currentSrcCtx.value) diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala index 942a110d52..fb90aace2e 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala @@ -290,21 +290,6 @@ object SigmaPredef { Seq(ArgInfo("value", "element of elliptic curve group"))) ) - val AvlTreeFunc = PredefinedFunc("avlTree", - Lambda(Array("operationFlags" -> SByte, "digest" -> SByteArray, "keyLength" -> SInt, "valueLengthOpt" -> SIntOption), SAvlTree, None), - PredefFuncInfo( - { case (_, Seq(flags, digest, keyLength, valueLength)) => - mkCreateAvlTree(flags.asByteValue, digest.asByteArray, keyLength.asIntValue, valueLength.asOption[SInt.type]) - }), - OperationInfo(CreateAvlTree, - "Construct a new authenticated dictionary with given parameters and tree root digest.", - Seq( - ArgInfo("operationFlags", "flags of available operations"), - ArgInfo("digest", "hash of merkle tree root"), - ArgInfo("keyLength", "length of dictionary keys in bytes"), - ArgInfo("valueLengthOpt", "optional width of dictionary values in bytes"))) - ) - val SubstConstantsFunc = PredefinedFunc("substConstants", Lambda( Seq(paramT), @@ -391,7 +376,6 @@ object SigmaPredef { LongToByteArrayFunc, ProveDHTupleFunc, ProveDlogFunc, - AvlTreeFunc, SubstConstantsFunc, ExecuteFromVarFunc, ExecuteFromSelfRegFunc diff --git a/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala deleted file mode 100644 index ecbb37040e..0000000000 --- a/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala +++ /dev/null @@ -1,38 +0,0 @@ -package sigmastate.serialization - -import sigmastate.SCollection._ -import sigmastate.SOption.SIntOption -import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -import sigmastate._ -import sigmastate.Values._ -import sigmastate.lang.Terms.ValueOps -import sigmastate.utils.SigmaByteWriter.DataInfo - -// TODO refactor: remove not used -case class CreateAvlTreeSerializer( - cons: (ByteValue, Value[SByteArray], IntValue, Value[SIntOption]) => AvlTreeValue - ) - extends ValueSerializer[CreateAvlTree] -{ - import sigmastate.Operations.CreateAvlTreeInfo._ - override def opDesc = CreateAvlTree - val operationFlagsInfo: DataInfo[SValue] = operationFlagsArg - val digestInfo: DataInfo[SValue] = digestArg - val keyLengthInfo: DataInfo[SValue] = keyLengthArg - val valueLengthOptInfo: DataInfo[SValue] = valueLengthOptArg - - override def serialize(obj: CreateAvlTree, w: SigmaByteWriter): Unit = { - w.putValue(obj.operationFlags, operationFlagsInfo) - w.putValue(obj.digest, digestInfo) - w.putValue(obj.keyLength, keyLengthInfo) - w.putValue(obj.valueLengthOpt, valueLengthOptInfo) - } - - override def parse(r: SigmaByteReader) = { - val flags = r.getValue().asByteValue - val digest = r.getValue().asByteArray - val keyLength = r.getValue().asIntValue - val valueLength = r.getValue().asOption[SInt.type] - cons(flags, digest, keyLength, valueLength) - } -} diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala index af7b9a4a5e..b6773907e7 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala @@ -50,7 +50,6 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { Relation2Serializer(LE, mkLE[SType]), Relation2Serializer(EQ, mkEQ[SType]), Relation2Serializer(NEQ, mkNEQ[SType]), - CreateAvlTreeSerializer(mkCreateAvlTree), QuadrupleSerializer(TreeLookup, mkTreeLookup), Relation2Serializer(BinOr, mkBinOr), Relation2Serializer(BinAnd, mkBinAnd), diff --git a/sigmastate/src/main/scala/sigmastate/trees.scala b/sigmastate/src/main/scala/sigmastate/trees.scala index f5aba9686f..83ce291136 100644 --- a/sigmastate/src/main/scala/sigmastate/trees.scala +++ b/sigmastate/src/main/scala/sigmastate/trees.scala @@ -192,22 +192,6 @@ object CreateProveDlog extends ValueCompanion { val OpType = SFunc(SGroupElement, SSigmaProp) } -// TODO refactor: remove not used class -/** Construct a new authenticated dictionary with given parameters and tree root digest.*/ -case class CreateAvlTree(operationFlags: ByteValue, - digest: Value[SByteArray], - keyLength: IntValue, - valueLengthOpt: Value[SIntOption]) extends AvlTreeValue { - override def companion = CreateAvlTree - override def tpe = SAvlTree - override def opType = CreateAvlTree.OpType -} -object CreateAvlTree extends ValueCompanion { - override def opCode: OpCode = OpCodes.AvlTreeCode - override def costKind: CostKind = Value.notSupportedError(this, "costKind") - val OpType = SFunc(Array(SByte, SByteArray, SInt, SIntOption), SAvlTree) -} - /** ErgoTree operation to create a new SigmaProp value representing public key * of Diffie Hellman signature protocol. * Common input: (g,h,u,v)*/ From e0905cc8c135fc4a6669f04636dd888aaa11572b Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Sun, 19 Dec 2021 22:09:06 +0300 Subject: [PATCH 03/14] v5.0-finalize: negative test cases when not 1 argument in lambda --- .../sigmastate/eval/RuntimeCosting.scala | 4 +- .../sigmastate/interpreter/Interpreter.scala | 7 +- .../main/scala/sigmastate/lang/Terms.scala | 21 ++---- .../sigmastate/ErgoTreeSpecification.scala | 75 +++++++++++++++++++ .../helpers/SigmaTestingCommons.scala | 54 +++++++++---- .../generators/ObjectGenerators.scala | 2 +- 6 files changed, 126 insertions(+), 37 deletions(-) diff --git a/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala index f6ae303cf3..67bfa87b8f 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -977,11 +977,11 @@ trait RuntimeCosting extends CostingRules { IR: IRContext => implicit val tA = ct.tItem implicit val sizedA = Sized.typeToSized(tA) liftConst(Sized.sizeOf(x.asInstanceOf[special.collection.Coll[a]])) - case ct: OptionType[a] => // TODO cover with tests (1h) + case ct: OptionType[a] => // TODO cover with tests or remove in v5.x (1h) implicit val tA = ct.tA implicit val sizedA = Sized.typeToSized(tA) liftConst(Sized.sizeOf(x.asInstanceOf[Option[a]])) - case ct: PairType[a, b] => // TODO cover with tests (1h) + case ct: PairType[a, b] => // TODO cover with tests or remove in v5.x (1h) implicit val tA = ct.tFst implicit val tB = ct.tSnd implicit val sizedA = Sized.typeToSized(tA) diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala index d235566c05..e7e25ec6b1 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -575,9 +575,10 @@ trait Interpreter extends ScorexLogging { } } catch { case t: Throwable => - // TODO coverage: property("handle improper signature") doesn't lead to exception - // because the current implementation of parseAndComputeChallenges doesn't check - // signature format + // TODO cover with tests + // NOTE, property("handle improper signature") doesn't lead to exception + // because the current implementation of parseAndComputeChallenges doesn't throw + // an exception log.warn("Improper signature: ", t); false } diff --git a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala index b6673b32d3..9e59bfed84 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala @@ -2,12 +2,12 @@ package sigmastate.lang import org.bitbucket.inkytonik.kiama.rewriting.Rewriter._ import scalan.Nullable -import sigmastate.SCollection.{SIntArray, SByteArray} +import sigmastate.SCollection.{SByteArray, SIntArray} import sigmastate.Values._ import sigmastate.utils.Overloading.Overload1 import sigmastate._ -import sigmastate.interpreter.ErgoTreeEvaluator -import sigmastate.interpreter.ErgoTreeEvaluator.DataEnv +import sigmastate.interpreter.{ErgoTreeEvaluator, Interpreter} +import sigmastate.interpreter.ErgoTreeEvaluator.{DataEnv, error} import sigmastate.serialization.OpCodes import sigmastate.serialization.OpCodes.OpCode import sigmastate.lang.TransformingSigmaBuilder._ @@ -140,21 +140,12 @@ object Terms { protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { addCost(Apply.costKind) - if (args.isEmpty) { - // TODO coverage - val fV = func.evalTo[() => Any](env) - fV() - } - else if (args.length == 1) { + if (args.length == 1) { val fV = func.evalTo[Any => Any](env) val argV = args(0).evalTo[Any](env) fV(argV) - } - else { - // TODO coverage - val f = func.evalTo[Seq[Any] => Any](env) - val argsV = args.map(a => a.evalTo[Any](env)) - f(argsV) + } else { + Interpreter.error(s"Function must have 1 argument, but was: $this") } } } diff --git a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 49460488ed..0f2805d0f0 100644 --- a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -8,6 +8,7 @@ import sigmastate.Values._ import sigmastate.lang.SourceContext import special.sigma.SigmaTestingData import sigmastate.lang.Terms._ +import sigmastate.lang.exceptions.{CosterException, InterpreterException} import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer import sigmastate.utxo.{DeserializeContext, DeserializeRegister} @@ -439,4 +440,78 @@ class ErgoTreeSpecification extends SigmaTestingData { } } } + + implicit def IR = new TestingIRContext + implicit def cs = compilerSettingsInTests + implicit def es = evalSettings + + property("lambda with 0 arguments") { + val expr = Apply(FuncValue(Vector(), IntConstant(1)), IndexedSeq()) + + // old v4.x interpreter + assertExceptionThrown( + { + val oldF = funcFromExpr[Int, Int]("({ (x: Int) => 1 })()", expr) + }, + exceptionLike[CosterException]("Don't know how to evalNode") + ) + + // new v5.0 interpreter + val newF = funcJitFromExpr[Int, Int]("({ (x: Int) => 1 })()", expr) + assertExceptionThrown( + { + val x = 100 // any value which is not used anyway + val (y, _) = newF.apply(x) + }, + exceptionLike[InterpreterException]("Function must have 1 argument, but was:") + ) + } + + + property("lambda with one argument") { + val expr = Apply( + FuncValue(Vector((1, SInt)), Negation(ValUse(1, SInt))), + IndexedSeq(IntConstant(1))) + val script = "({ (x: Int) => -x })(1)" + + val x = 1 + + { // old v4.x interpreter + val oldF = funcFromExpr[Int, Int](script, expr) + val (y, _) = oldF.apply(x) + y shouldBe -1 + } + + { // new v5.0 interpreter + val newF = funcJitFromExpr[Int, Int](script, expr) + val (y, _) = newF.apply(x) + y shouldBe -1 + } + } + + property("lambda with 2 and more arguments") { + val expr = Apply( + FuncValue(Vector((1, SInt), (2, SInt)), Plus(ValUse(1, SInt), ValUse(2, SInt))), + IndexedSeq(IntConstant(1), IntConstant(1)) + ) + val script = "{ (x: Int, y: Int) => x + y }" + + // old v4.x interpreter + assertExceptionThrown( + { + val oldF = funcFromExpr[(Int, Int), Int](script, expr) + }, + exceptionLike[CosterException]("Don't know how to evalNode") + ) + + // ndw v5.0 interpreter + val newF = funcJitFromExpr[(Int, Int), Int](script, expr) + assertExceptionThrown( + { + val (y, _) = newF.apply((1, 1)) + }, + exceptionLike[InterpreterException]("Function must have 1 argument, but was:") + ) + + } } diff --git a/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index 875f787355..08c13aed63 100644 --- a/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/sigmastate/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -194,8 +194,8 @@ trait SigmaTestingCommons extends PropSpec * @param funcScript source code of the function * @param bindings additional context variables */ - def func[A: RType, B: RType] - (funcScript: String, bindings: VarBinding*) + def funcFromExpr[A: RType, B: RType] + (funcScript: String, expr: SValue, bindings: VarBinding*) (implicit IR: IRContext, compilerSettings: CompilerSettings): CompiledFunc[A, B] = { import IR._ @@ -203,23 +203,21 @@ trait SigmaTestingCommons extends PropSpec val tA = RType[A] val env = Interpreter.emptyEnv - val compiledTree = compileTestScript[A](env, funcScript) - // The following is done as part of Interpreter.verify() val (costF, valueFun) = { - val costingRes = getCostingResult(env, compiledTree) + val costingRes = getCostingResult(env, expr) val calcF = costingRes.calcF val tree = IR.buildTree(calcF) // sanity check that buildTree is reverse to buildGraph (see doCostingEx) if (tA != special.sigma.ContextRType) { - if (tree != compiledTree) { + if (tree != expr) { println(s"Result of buildTree:") val prettyTree = SigmaPPrint(tree, height = 150) println(prettyTree) println(s"compiledTree:") - val prettyCompiledTree = SigmaPPrint(compiledTree, height = 150) + val prettyCompiledTree = SigmaPPrint(expr, height = 150) println(prettyCompiledTree) assert(prettyTree.plainText == prettyCompiledTree.plainText, "Failed sanity check that buildTree is reverse to buildGraph") @@ -241,8 +239,25 @@ trait SigmaTestingCommons extends PropSpec val (res, _) = valueFun(sigmaCtx) (res.asInstanceOf[B], GivenCost(JitCost(estimatedCost))) } - val Terms.Apply(funcVal, _) = compiledTree.asInstanceOf[SValue] - CompiledFunc(funcScript, bindings, funcVal, compiledTree, f) + val Terms.Apply(funcVal, _) = expr.asInstanceOf[SValue] + CompiledFunc(funcScript, bindings, funcVal, expr, f) + } + + /** Returns a Scala function which is equivalent to the given function script. + * The script is embedded into valid ErgoScript which is then compiled to + * [[sigmastate.Values.Value]] tree. + * Limitations: + * 1) DeserializeContext, ConstantPlaceholder is not supported + * @param funcScript source code of the function + * @param bindings additional context variables + */ + def func[A: RType, B: RType] + (funcScript: String, bindings: VarBinding*) + (implicit IR: IRContext, + compilerSettings: CompilerSettings): CompiledFunc[A, B] = { + val env = Interpreter.emptyEnv + val compiledTree = compileTestScript[A](env, funcScript) + funcFromExpr[A, B](funcScript, compiledTree, bindings:_*) } def evalSettings = ErgoTreeEvaluator.DefaultEvalSettings @@ -256,14 +271,12 @@ trait SigmaTestingCommons extends PropSpec |""".stripMargin) } - def funcJit[A: RType, B: RType] - (funcScript: String, bindings: VarBinding*) + def funcJitFromExpr[A: RType, B: RType] + (funcScript: String, expr: SValue, bindings: VarBinding*) (implicit IR: IRContext, evalSettings: EvalSettings, compilerSettings: CompilerSettings): CompiledFunc[A, B] = { val tA = RType[A] - val compiledTree = compileTestScript[A](Interpreter.emptyEnv, funcScript) - val f = (in: A) => { implicit val cA: ClassTag[A] = tA.classTag val (_, sigmaCtx) = createContexts(in, bindings) @@ -276,7 +289,7 @@ trait SigmaTestingCommons extends PropSpec coster = accumulator, evalSettings.profilerOpt.getOrElse(DefaultProfiler), evalSettings) val (res, actualTime) = BenchmarkUtil.measureTimeNano( - evaluator.evalWithCost[B](ErgoTreeEvaluator.EmptyDataEnv, compiledTree)) + evaluator.evalWithCost[B](ErgoTreeEvaluator.EmptyDataEnv, expr)) val costDetails = if (evalSettings.costTracingEnabled) { val trace: Seq[CostItem] = evaluator.getCostTrace() val costDetails = TracedCost(trace, Some(actualTime)) @@ -294,8 +307,17 @@ trait SigmaTestingCommons extends PropSpec } (res.value, costDetails) } - val Terms.Apply(funcVal, _) = compiledTree.asInstanceOf[SValue] - CompiledFunc(funcScript, bindings, funcVal, compiledTree, f) + val Terms.Apply(funcVal, _) = expr.asInstanceOf[SValue] + CompiledFunc(funcScript, bindings, funcVal, expr, f) + } + + def funcJit[A: RType, B: RType] + (funcScript: String, bindings: VarBinding*) + (implicit IR: IRContext, + evalSettings: EvalSettings, + compilerSettings: CompilerSettings): CompiledFunc[A, B] = { + val compiledTree = compileTestScript[A](Interpreter.emptyEnv, funcScript) + funcJitFromExpr(funcScript, compiledTree, bindings:_*) } /** Creates a specialized (faster) version which can be used to benchmark performance of diff --git a/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala b/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala index 04728c3ae1..607a008cc0 100644 --- a/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala +++ b/sigmastate/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala @@ -617,7 +617,7 @@ trait ObjectGenerators extends TypeGenerators } yield ValUse(id, tpe) val blockValueGen: Gen[BlockValue] = for { - items <- Gen.nonEmptyListOf(valDefGen) + items <- Gen.listOf(valDefGen) } yield BlockValue(items.toIndexedSeq, EQ( SizeOf(Tuple(items.toIndexedSeq.map(valDef => ValUse(valDef.id, valDef.tpe)))), From 92ef08e8c65b9e7bfcaa9fcc3110a70bebf3efdc Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 20 Dec 2021 01:22:42 +0300 Subject: [PATCH 04/14] v5.0-finalize: more // TODO cover --- .../main/scala/sigmastate/UnprovenTree.scala | 2 - .../src/main/scala/sigmastate/Values.scala | 39 ++++--------------- .../main/scala/sigmastate/lang/Terms.scala | 2 +- .../sigmastate/ErgoTreeSpecification.scala | 10 ++--- .../sigmastate/lang/SigmaCompilerTest.scala | 2 +- 5 files changed, 14 insertions(+), 41 deletions(-) diff --git a/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala b/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala index fb828ed127..5e291685ad 100644 --- a/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala +++ b/sigmastate/src/main/scala/sigmastate/UnprovenTree.scala @@ -201,8 +201,6 @@ case class UnprovenDiffieHellmanTuple(override val proposition: ProveDHTuple, override def withPosition(updatedPosition: NodePosition) = this.copy(position = updatedPosition) } -// TODO coverage (8h): write a test that restores the tree from this string and check that the result is equal, -// in order to make sure this conversion is unambiguous object FiatShamirTree { /** Prefix byte which is put before the other ProofTreeConjecture serialized bytes. */ val internalNodePrefix: Byte = 0 diff --git a/sigmastate/src/main/scala/sigmastate/Values.scala b/sigmastate/src/main/scala/sigmastate/Values.scala index 17d18bbffa..28f88c2ae2 100644 --- a/sigmastate/src/main/scala/sigmastate/Values.scala +++ b/sigmastate/src/main/scala/sigmastate/Values.scala @@ -3,7 +3,6 @@ package sigmastate import java.math.BigInteger import java.util import java.util.Objects - import org.bitbucket.inkytonik.kiama.rewriting.Rewriter.{count, everywherebu, strategy} import org.ergoplatform.settings.ErgoAlgos import org.ergoplatform.validation.ValidationException @@ -11,7 +10,7 @@ import scalan.{Nullable, RType} import scalan.util.CollectionUtil._ import sigmastate.SCollection.{SByteArray, SIntArray} import sigmastate.interpreter.CryptoConstants.EcPointType -import sigmastate.interpreter.{CompanionDesc, CryptoConstants, ErgoTreeEvaluator, NamedDesc} +import sigmastate.interpreter.{CompanionDesc, CryptoConstants, ErgoTreeEvaluator, Interpreter, NamedDesc} import sigmastate.serialization.{ConstantStore, OpCodes, _} import sigmastate.serialization.OpCodes._ import sigmastate.TrivialProp.{FalseProp, TrueProp} @@ -885,18 +884,14 @@ object Values { * * @param items source collection of expressions */ - case class Tuple(items: IndexedSeq[Value[SType]]) extends EvaluatedValue[STuple] with EvaluatedCollection[SAny.type, STuple] { + case class Tuple(items: IndexedSeq[Value[SType]]) extends Value[STuple] { override def companion = Tuple - override def elementType = SAny override lazy val tpe = STuple(items.map(_.tpe)) - override lazy val value = { // TODO coverage - val xs = items.cast[EvaluatedValue[SAny.type]].map(_.value) - Colls.fromArray(xs.toArray(SAny.classTag.asInstanceOf[ClassTag[SAny.WrappedType]]))(RType.AnyType) - } + override def opType: SFunc = ??? protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { // in v5.0 version we support only tuples of 2 elements to be equivalent with v4.x if (items.length != 2) - error(s"Invalid tuple $this") + Interpreter.error(s"Invalid tuple $this") val item0 = items(0) val x = item0.evalTo[Any](env) @@ -1175,13 +1170,7 @@ object Values { protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = { addCost(FuncValue.costKind) - if (args.length == 0) { - // TODO coverage - () => { - body.evalTo[Any](env) - } - } - else if (args.length == 1) { + if (args.length == 1) { val arg0 = args(0) (vArg: Any) => { Value.checkType(arg0._2, vArg) @@ -1194,22 +1183,8 @@ object Values { Value.checkType(body, res) res } - } - else { - // TODO coverage - (vArgs: Seq[Any]) => { - var env1 = env - val len = args.length - cfor(0)(_ < len, _ + 1) { i => - val id = args(i)._1 - val v = vArgs(i) - E.addFixedCost(FuncValue.AddToEnvironmentDesc_CostKind, - FuncValue.AddToEnvironmentDesc) { - env1 = env1 + (id -> v) - } - } - body.evalTo[Any](env1) - } + } else { + Interpreter.error(s"Function must have 1 argument, but was: $this") } } } diff --git a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala index 9e59bfed84..b3d5fb3680 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/Terms.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/Terms.scala @@ -145,7 +145,7 @@ object Terms { val argV = args(0).evalTo[Any](env) fV(argV) } else { - Interpreter.error(s"Function must have 1 argument, but was: $this") + Interpreter.error(s"Function application must have 1 argument, but was: $this") } } } diff --git a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 0f2805d0f0..626452140a 100644 --- a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -445,7 +445,7 @@ class ErgoTreeSpecification extends SigmaTestingData { implicit def cs = compilerSettingsInTests implicit def es = evalSettings - property("lambda with 0 arguments") { + property("Apply with 0 arguments") { val expr = Apply(FuncValue(Vector(), IntConstant(1)), IndexedSeq()) // old v4.x interpreter @@ -463,12 +463,12 @@ class ErgoTreeSpecification extends SigmaTestingData { val x = 100 // any value which is not used anyway val (y, _) = newF.apply(x) }, - exceptionLike[InterpreterException]("Function must have 1 argument, but was:") + exceptionLike[InterpreterException]("Function application must have 1 argument, but was:") ) } - property("lambda with one argument") { + property("Apply with one argument") { val expr = Apply( FuncValue(Vector((1, SInt)), Negation(ValUse(1, SInt))), IndexedSeq(IntConstant(1))) @@ -489,7 +489,7 @@ class ErgoTreeSpecification extends SigmaTestingData { } } - property("lambda with 2 and more arguments") { + property("Apply with 2 and more arguments") { val expr = Apply( FuncValue(Vector((1, SInt), (2, SInt)), Plus(ValUse(1, SInt), ValUse(2, SInt))), IndexedSeq(IntConstant(1), IntConstant(1)) @@ -510,7 +510,7 @@ class ErgoTreeSpecification extends SigmaTestingData { { val (y, _) = newF.apply((1, 1)) }, - exceptionLike[InterpreterException]("Function must have 1 argument, but was:") + exceptionLike[InterpreterException]("Function application must have 1 argument, but was:") ) } diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index e9f1557ab9..ec9a528635 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -141,7 +141,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ObjectGe } property("deserialize") { - def roundtrip[T <: SType](c: EvaluatedValue[T], typeSig: String) = { + def roundtrip[T <: SType](c: Value[T], typeSig: String) = { val bytes = ValueSerializer.serialize(c) val str = Base58.encode(bytes) comp(env, s"deserialize[$typeSig](" + "\"" + str + "\")") shouldBe c From dcc0ad9f17b795f76ee5ed0ff0cb4b907a2eb866 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 20 Dec 2021 11:28:52 +0300 Subject: [PATCH 05/14] v5.0-finalize: removed CreateAvlTree operation (part 2) --- sigmastate/src/main/scala/sigmastate/Operations.scala | 9 --------- .../serialization/ConstantPlaceholderSerializer.scala | 3 ++- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/sigmastate/src/main/scala/sigmastate/Operations.scala b/sigmastate/src/main/scala/sigmastate/Operations.scala index 51ae277757..dfae1395d8 100644 --- a/sigmastate/src/main/scala/sigmastate/Operations.scala +++ b/sigmastate/src/main/scala/sigmastate/Operations.scala @@ -153,15 +153,6 @@ object Operations { val argInfos: Seq[ArgInfo] = Array(indexArg) } - object CreateAvlTreeInfo extends InfoObject { - private val func = predefinedOps.funcs("avlTree") - val operationFlagsArg: ArgInfo = func.argInfo("operationFlags") - val digestArg: ArgInfo = func.argInfo("digest") - val keyLengthArg: ArgInfo = func.argInfo("keyLength") - val valueLengthOptArg: ArgInfo = func.argInfo("valueLengthOpt") - val argInfos: Seq[ArgInfo] = Array(operationFlagsArg, digestArg, keyLengthArg, valueLengthOptArg) - } - object CreateProveDHTupleInfo extends InfoObject { private val func = predefinedOps.funcs("proveDHTuple") val gArg: ArgInfo = func.argInfo("g") diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ConstantPlaceholderSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ConstantPlaceholderSerializer.scala index e9c19ca9e5..b02b5e64e9 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ConstantPlaceholderSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ConstantPlaceholderSerializer.scala @@ -6,10 +6,11 @@ import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} case class ConstantPlaceholderSerializer(cons: (Int, SType) => Value[SType]) extends ValueSerializer[ConstantPlaceholder[SType]] { + import sigmastate.Operations.ConstantPlaceholderInfo._ override def opDesc = ConstantPlaceholder override def serialize(obj: ConstantPlaceholder[SType], w: SigmaByteWriter): Unit = { - w.putUInt(obj.id) + w.putUInt(obj.id, indexArg) } override def parse(r: SigmaByteReader): Value[SType] = { From 16414f86347287b06604a92e6873a585db721f4b Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 20 Dec 2021 17:38:55 +0300 Subject: [PATCH 06/14] v5.0-finalize: more test cases for Tuple and Option in register --- .../src/main/scala/sigmastate/eval/Zero.scala | 2 +- .../serialization/DataSerializer.scala | 1 - .../special/sigma/SigmaDslSpecification.scala | 53 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/sigmastate/src/main/scala/sigmastate/eval/Zero.scala b/sigmastate/src/main/scala/sigmastate/eval/Zero.scala index a29ef6f769..a8fea6df60 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/Zero.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/Zero.scala @@ -19,7 +19,7 @@ case class CZero[T](zero: T) extends Zero[T] trait ZeroLowPriority { implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) - implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(Some(Zero.zeroOf[T])) + implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(None) implicit def pairIsZero[A: Zero, B: Zero]: Zero[(A,B)] = CZero(Zero[A].zero, Zero[B].zero) } object Zero extends ZeroLowPriority { diff --git a/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala index 00629cef4c..1176bf1673 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala @@ -98,7 +98,6 @@ object DataSerializer { new String(bytes, StandardCharsets.UTF_8) case SBigInt => val size: Short = r.getUShort().toShort - // TODO HF (2h): replace with validation rule to enable soft-forkability if (size > SBigInt.MaxSizeInBytes) { throw new SerializerException(s"BigInt value doesn't not fit into ${SBigInt.MaxSizeInBytes} bytes: $size") } diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala index 2a1eb160c7..563f5e5f79 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala @@ -3865,6 +3865,13 @@ class SigmaDslSpecification extends SigmaDslTesting ErgoBox.R4 -> ByteArrayConstant(Coll(1.toByte)) ))) + val box3 = SigmaDsl.Box(testBox(20, TrueTree, 0, Seq(), Map( + ErgoBox.R4 -> Constant((10, 20L).asInstanceOf[SType#WrappedType], STuple(SInt, SLong)), + ErgoBox.R5 -> Constant((10, Some(20L)).asInstanceOf[SType#WrappedType], STuple(SInt, SOption(SLong))) + // TODO HF (1h): uncomment after DataSerializer support of Option type + // ErgoBox.R6 -> Constant[SOption[SInt.type]](Option(10), SOption(SInt)), + ))) + verifyCases( Seq( (box1, Expected(Success(1.toByte), cost = 36253)), @@ -3941,6 +3948,52 @@ class SigmaDslSpecification extends SigmaDslTesting Vector((1, SBox)), OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R9, SOption(SAvlTree))) ))) + + verifyCases( + Seq( + (box3, Expected(Success(10), cost = 36468)) + ), + existingFeature((x: Box) => x.R4[(Int, Long)].get._1, + "{ (x: Box) => x.R4[(Int, Long)].get._1 }", + FuncValue( + Array((1, SBox)), + SelectField.typed[Value[SInt.type]]( + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R4, SOption(SPair(SInt, SLong)))), + 1.toByte + ) + ))) + + verifyCases( + Seq( + (box3, Expected(Success(10), cost = 36468)) + ), + existingFeature((x: Box) => x.R5[(Int, Option[Long])].get._1, + "{ (x: Box) => x.R5[(Int, Option[Long])].get._1 }", + FuncValue( + Array((1, SBox)), + SelectField.typed[Value[SInt.type]]( + OptionGet(ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R5, SOption(SPair(SInt, SOption(SLong))))), + 1.toByte + ) + ))) + + // TODO HF (1h): uncomment after DataSerializer support of Option type + // verifyCases( + // Seq( + // (box3, Expected(Success(20L), cost = 36468)) + // ), + // existingFeature((x: Box) => x.R5[(Int, Option[Long])].get._2.get, + // "{ (x: Box) => x.R5[(Int, Option[Long])].get._2.get }", + // FuncValue( + // Array((1, SBox)), + // OptionGet( + // SelectField.typed[Value[SOption[SLong.type]]]( + // OptionGet( + // ExtractRegisterAs(ValUse(1, SBox), ErgoBox.R5, SOption(SPair(SInt, SOption(SLong)))) + // ), + // 2.toByte + // )) + // ))) } def existingPropTest[A: RType, B: RType](propName: String, scalaFunc: A => B) = { From 985ce41e2e44d82a44ac57b700c7c735f855428e Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 21 Dec 2021 00:08:07 +0300 Subject: [PATCH 07/14] v5.0-finalize: mark all TODOs accoring to target release (v5.0, v5.x, v6.0) --- .../special/collection/CollsOverArrays.scala | 2 +- .../special/sigma/SigmaDslOverArrays.scala | 2 +- .../org/ergoplatform/ErgoLikeContext.scala | 2 + .../ergoplatform/ErgoLikeInterpreter.scala | 6 +- .../src/main/scala/sigmastate/Values.scala | 4 +- .../sigmastate/eval/CostingDataContext.scala | 2 +- .../interpreter/ErgoTreeEvaluator.scala | 11 ---- .../sigmastate/interpreter/Interpreter.scala | 2 +- .../serialization/DataSerializer.scala | 2 +- .../serialization/ErgoTreeSerializer.scala | 15 +++-- .../serialization/ModQArithOpSerializer.scala | 2 +- .../serialization/ModQSerializer.scala | 2 +- .../src/main/scala/sigmastate/trees.scala | 2 +- .../src/main/scala/sigmastate/types.scala | 6 +- .../scala/sigmastate/utxo/transformers.scala | 4 +- .../ErgoLikeTransactionSpec.scala | 2 +- .../sigmastate/ErgoTreeSpecification.scala | 2 +- .../sigmastate/lang/SigmaBinderTest.scala | 2 +- .../special/sigma/SigmaDslSpecification.scala | 65 ++++++++++--------- .../scala/special/sigma/SigmaDslTesting.scala | 5 +- .../special/sigma/SigmaTestingData.scala | 2 +- 21 files changed, 71 insertions(+), 71 deletions(-) diff --git a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala index ad78776d4f..6f91fee975 100644 --- a/library-impl/src/main/scala/special/collection/CollsOverArrays.scala +++ b/library-impl/src/main/scala/special/collection/CollsOverArrays.scala @@ -211,7 +211,7 @@ class CollOverArrayBuilder extends CollBuilder { override def Monoids: MonoidBuilder = new MonoidBuilderInst @inline override def pairColl[@specialized A, @specialized B](as: Coll[A], bs: Coll[B]): PairColl[A, B] = { - // TODO HF (2h): use minimal length and slice longer collection + // TODO v6.0 (2h): use minimal length and slice longer collection // The current implementation doesn't check the case when `as` and `bs` have different lengths. // in which case the implementation of `PairOfCols` has inconsistent semantics of `map`, `exists` etc methods. // To fix the problem, the longer collection have to be truncated (which is consistent diff --git a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala index d1180960a7..04b70cab04 100644 --- a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala +++ b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala @@ -40,7 +40,7 @@ class TestSigmaDslBuilder extends SigmaDslBuilder { @NeverInline override def xorOf(conditions: Coll[Boolean]): Boolean = { - // TODO HF (2h): see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/640 + // This is buggy version used in v4.x interpreter (for ErgoTrees v0, v1) conditions.toArray.distinct.length == 2 } diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala index c57ccf2d53..4fd8feafd9 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeContext.scala @@ -165,6 +165,8 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData, } val vars = contextVars(varMap ++ extensions) val avlTree = CAvlTree(lastBlockUtxoRoot) + // so selfBox is never one of the `inputs` instances + // as result selfBoxIndex is always (erroneously) returns -1 in ErgoTree v0, v1 val selfBox = boxesToSpend(selfIndex).toTestBox(isCost) val ergoTreeVersion = currentErgoTreeVersion.getOrElse( Interpreter.error(s"Undefined context property: currentErgoTreeVersion")) diff --git a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala index 7e54647a06..a4bda1a54d 100644 --- a/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala +++ b/sigmastate/src/main/scala/org/ergoplatform/ErgoLikeInterpreter.scala @@ -30,11 +30,11 @@ class ErgoLikeInterpreter(implicit val IR: IRContext) extends Interpreter { else Some(outVal) case _ => - // TODO HF (1h): this case is not possible because `ErgoBox.get` + // this case is not possible because `ErgoBox.get` // returns lookups values from `additionalRegisters` Map with values // of type EvaluatedValue, which are always Constant nodes in practice. - // Also, this branch is never executed so can be safely removed - // (better as part of the HF) + // Also, this branch is never executed so can be safely removed, but + // we keep it here explicitly for clarity None } }.orElse(d.default) diff --git a/sigmastate/src/main/scala/sigmastate/Values.scala b/sigmastate/src/main/scala/sigmastate/Values.scala index 28f88c2ae2..1a305d0ad3 100644 --- a/sigmastate/src/main/scala/sigmastate/Values.scala +++ b/sigmastate/src/main/scala/sigmastate/Values.scala @@ -918,8 +918,8 @@ object Values { trait OptionValue[T <: SType] extends Value[SOption[T]] { } - // TODO HF (4h): SomeValue and NoneValue are not used in ErgoTree and can be - // either removed or implemented in v4.x + // TODO v6.0 (4h): SomeValue and NoneValue are not used in ErgoTree and can be + // either removed or implemented in v6.0 case class SomeValue[T <: SType](x: Value[T]) extends OptionValue[T] { override def companion = SomeValue val tpe = SOption(x.tpe) diff --git a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala index 2913341708..c691b9a294 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/CostingDataContext.scala @@ -352,7 +352,7 @@ case class CostingBox(isCost: Boolean, val ebox: ErgoBox) extends Box with Wrapp override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj != null && ( obj match { case obj: Box => util.Arrays.equals(id.toArray, obj.id.toArray) - // TODO v5.0 HF: return false when obj in not Box instead of throwing an exception + // TODO v5.0: return false when obj in not Box instead of throwing an exception })) } diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala b/sigmastate/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala index 05c983f4cd..64a7b1c7c5 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala @@ -375,17 +375,6 @@ object ErgoTreeEvaluator { isMeasureOperationTime = false, isMeasureScriptTime = false) - /** Helper method to compute cost details for the given method call. */ - def calcCost(mc: MethodCall, obj: Any, args: Array[Any]) - (implicit E: ErgoTreeEvaluator): CostDetails = { - // add approximated cost of invoked method (if specified) - val cost = mc.method.costFunc match { - case Some(costFunc) => costFunc(E, mc, obj, args) - case _ => CostDetails.ZeroCost // TODO v5.0: throw exception if not defined - } - cost - } - /** Evaluator currently is being executed on the current thread. * This variable is set in a single place, specifically in the `eval` method of * [[ErgoTreeEvaluator]]. diff --git a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala index e7e25ec6b1..22bbada8ff 100644 --- a/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/sigmastate/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -396,7 +396,7 @@ trait Interpreter extends ScorexLogging { proof: Array[Byte], message: Array[Byte]): Try[VerificationResult] = { val res = Try { - // TODO v5.0: the condition below should be revised if necessary + // TODO v6.0: the condition below should be revised if necessary // The following conditions define behavior which depend on the version of ergoTree // This works in addition to more fine-grained soft-forkability mechanism implemented // using ValidationRules (see trySoftForkable method call here and in reduceToCrypto). diff --git a/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala index 1176bf1673..ca426e8e7a 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/DataSerializer.scala @@ -76,7 +76,7 @@ object DataSerializer { i += 1 } - // TODO HF (3h): support Option[T] (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659) + // TODO v6.0 (3h): support Option[T] (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659) case _ => sys.error(s"Don't know how to serialize ($v, $tpe)") } diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala index 7fbacb0c54..be10087703 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ErgoTreeSerializer.scala @@ -268,7 +268,15 @@ class ErgoTreeSerializer { val (header, _, constants, treeBytes) = deserializeHeaderWithTreeBytes(r) val w = SigmaSerializer.startWriter() w.put(header) - w.putUInt(constants.length) // TODO HF (3h): this should not be serialized when segregation is off + + // TODO v5.0 (3h): the following `constants.length` should not be serialized when + // segregation is off in the `header`, because in this case there is no `constants` + // section in the ErgoTree serialization format. Thus, applying this + // `substituteConstants` for non-segregated trees will return non-parsable ErgoTree + // bytes. This can be fixed in v5.0 based on using context.currentErgoTreeVersion, + // when this method is executed as part of SubstConstants operation. + w.putUInt(constants.length) + val constantSerializer = ConstantSerializer(DeserializationSigmaBuilder) constants.zipWithIndex.foreach { @@ -279,10 +287,9 @@ class ErgoTreeSerializer { val valW = SigmaSerializer.startWriter(constantStore) valW.putValue(newVal) val newConsts = constantStore.getAll - assert(newConsts.length == 1) + require(newConsts.length == 1) val newConst = newConsts.head - // TODO HF (1h): replace assert with require - assert(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") + require(c.tpe == newConst.tpe, s"expected new constant to have the same ${c.tpe} tpe, got ${newConst.tpe}") constantSerializer.serialize(newConst, w) case (c, _) => constantSerializer.serialize(c, w) diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala index 26d70281f3..b8d8d07093 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala @@ -6,7 +6,7 @@ import sigmastate.utils.SigmaByteWriter.DataInfo import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} import sigmastate.{ModQArithOpCompanion, SType, ModQArithOp} -// TODO HF (2h): make sure it is covered with tests +// TODO v6.0 (2h): make sure it is covered with tests case class ModQArithOpSerializer(override val opDesc: ModQArithOpCompanion, cons: (BigIntValue, BigIntValue) => BigIntValue) extends ValueSerializer[ModQArithOp] { val leftInfo: DataInfo[SValue] = opDesc.argInfos(0) diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala index dee613490c..f48a6e6388 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ModQSerializer.scala @@ -5,7 +5,7 @@ import sigmastate.lang.Terms._ import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} import sigmastate.{ModQ, SType} -// TODO HF (2h): make sure it is covered with tests +// TODO v6.0 (2h): make sure it is covered with tests object ModQSerializer extends ValueSerializer[ModQ] { override def opDesc = ModQ diff --git a/sigmastate/src/main/scala/sigmastate/trees.scala b/sigmastate/src/main/scala/sigmastate/trees.scala index 83ce291136..69d7b73e94 100644 --- a/sigmastate/src/main/scala/sigmastate/trees.scala +++ b/sigmastate/src/main/scala/sigmastate/trees.scala @@ -1077,7 +1077,7 @@ object BitOp { } } -// TODO HF (24h): implement modular operations +// TODO v6.0 (24h): implement modular operations case class ModQ(input: Value[SBigInt.type]) extends NotReadyValue[SBigInt.type] { override def companion = ModQ diff --git a/sigmastate/src/main/scala/sigmastate/types.scala b/sigmastate/src/main/scala/sigmastate/types.scala index f66a37be9d..128abc0c3e 100644 --- a/sigmastate/src/main/scala/sigmastate/types.scala +++ b/sigmastate/src/main/scala/sigmastate/types.scala @@ -822,7 +822,7 @@ object SNumericType extends STypeCompanion { /** Array of all numeric types ordered by number of bytes in the representation. */ final val allNumericTypes = Array(SByte, SShort, SInt, SLong, SBigInt) - // TODO HF (4h): this typeId is now shadowed by SGlobal.typeId + // TODO v6.0 (4h): this typeId is now shadowed by SGlobal.typeId // see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667 override def typeId: TypeCode = 106: Byte @@ -1723,7 +1723,7 @@ object SCollection extends STypeCompanion with MethodByNameUnapply { .withIRInfo(MethodCallIrBuilder, javaMethodOf[Coll[_], Any, Int]("indexOf")) .withInfo(MethodCall, "") - // TODO v5.0: optimize using specialization for numeric and predefined types + // TODO mainnet v5.0: optimize using specialization for numeric and predefined types /** Implements evaluation of Coll.indexOf method call ErgoTree node. * Called via reflection based on naming convention. * @see SMethod.evalMethod @@ -2421,7 +2421,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType { E.addSeqCost(InsertIntoAvlTree_Info, nItems) { () => val insert = Insert(ADKey @@ key.toArray, ADValue @@ value.toArray) val insertRes = bv.performOneOperation(insert) - // TODO v5.0: throwing exception is not consistent with update semantics + // TODO v6.0: throwing exception is not consistent with update semantics // however it preserves v4.0 semantics if (insertRes.isFailure) { Interpreter.error(s"Incorrect insert for $tree (key: $key, value: $value, digest: ${tree.digest}): ${insertRes.failed.get}}") diff --git a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala index c3b18a12f0..fa67a31f26 100644 --- a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala +++ b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala @@ -412,7 +412,7 @@ object ExtractScriptBytes extends SimpleTransformerCompanion { val OpType = SFunc(SBox, SByteArray) override def opCode: OpCode = OpCodes.ExtractScriptBytesCode - // TODO v5.0: ensure the following is true + // TODO v5.x: ensure the following is true /** The cost is fixed and doesn't include serialization of ErgoTree because * the ErgoTree is expected to be constructed with non-null propositionBytes. * This is (and must be) guaranteed by ErgoTree deserializer. @@ -437,7 +437,7 @@ object ExtractBytes extends SimpleTransformerCompanion { override def opCode: OpCode = OpCodes.ExtractBytesCode /** The cost is fixed and doesn't include serialization of ErgoBox because * the ErgoBox is expected to be constructed with non-null `bytes`. - * TODO v5.0: This is not, but must be guaranteed by ErgoBox deserializer. */ + * TODO v5.x: This is not currently, but must be guaranteed by lazy ErgoBox deserializer. */ override val costKind = FixedCost(JitCost(12)) override def argInfos: Seq[ArgInfo] = ExtractBytesInfo.argInfos } diff --git a/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala index 9c92c7b74f..894010b454 100644 --- a/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala +++ b/sigmastate/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala @@ -95,7 +95,7 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting { ErgoAlgos.decodeUnsafe(token1).toColl -> 10000000L, ErgoAlgos.decodeUnsafe(token2).toColl -> 500L ).map(identity).toConstant - // TODO HF (16h): fix collections equality and remove map(identity) + // TODO v6.0 (16h): fix collections equality and remove map(identity) // (PairOfColl should be equal CollOverArray but now it is not) res shouldBe exp } diff --git a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala index 626452140a..7a028248e5 100644 --- a/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala +++ b/sigmastate/src/test/scala/sigmastate/ErgoTreeSpecification.scala @@ -271,7 +271,7 @@ class ErgoTreeSpecification extends SigmaTestingData { { import SSigmaProp._ (SSigmaProp.typeId, Seq( MInfo(1, PropBytesMethod), - MInfo(2, IsProvenMethod) // TODO HF (3h): this method must be removed + MInfo(2, IsProvenMethod) // TODO v5.x (3h): this method must be removed ), true) }, { import SBox._ diff --git a/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala index a283cbb07d..ad8db1ef17 100644 --- a/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala +++ b/sigmastate/src/test/scala/sigmastate/lang/SigmaBinderTest.scala @@ -142,7 +142,7 @@ class SigmaBinderTest extends PropSpec with PropertyChecks with Matchers with La If(EQ(IntConstant(10), IntConstant(11)), IntConstant(2), IntConstant(3))) } - // TODO HF (4h): SomeValue and NoneValue are not used in ErgoTree and can be + // TODO v6.0 (4h): SomeValue and NoneValue are not used in ErgoTree and can be // either removed or implemented in v4.x property("Option constructors") { bind(env, "None") shouldBe NoneValue(NoType) diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala index 563f5e5f79..010274bd3b 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala @@ -2300,7 +2300,7 @@ class SigmaDslSpecification extends SigmaDslTesting (BigIntMaxValue, BigIntMinValue) -> expect(false), (BigIntMaxValue, -47.toBigInt) -> expect(false), (BigIntMaxValue, BigIntMaxValue) -> expect(false), - (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v5.0: reject this overlimit cases + (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases (BigIntOverlimit, BigIntOverlimit) -> expect(false) ) @@ -2316,7 +2316,7 @@ class SigmaDslSpecification extends SigmaDslTesting property("BigInt LE, GE") { val o = NumericOps.BigIntIsExactOrdering - // TODO HF: this values have bitCount == 255 (see to256BitValueExact) + // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact) val BigIntMinValue = CBigInt(new BigInteger("-7F" + "ff" * 31, 16)) val BigIntMaxValue = CBigInt(new BigInteger("7F" + "ff" * 31, 16)) val BigIntOverlimit = CBigInt(new BigInteger("7F" + "ff" * 33, 16)) @@ -2359,7 +2359,7 @@ class SigmaDslSpecification extends SigmaDslTesting (BigIntMaxValue, BigIntMinValue) -> expect(false), (BigIntMaxValue, -47.toBigInt) -> expect(false), (BigIntMaxValue, BigIntMaxValue) -> expect(true), - (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v5.0: reject this overlimit cases + (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases (BigIntOverlimit, BigIntOverlimit) -> expect(true) ) @@ -2371,15 +2371,15 @@ class SigmaDslSpecification extends SigmaDslTesting } property("BigInt methods equivalence (new features)") { - // TODO HF (2h): the behavior of `upcast` for BigInt is different from all other Numeric types + // TODO v6.0 (2h): the behavior of `upcast` for BigInt is different from all other Numeric types // The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. - // It makes sense to fix this inconsistency as part of HF + // It makes sense to fix this inconsistency as part of upcoming forks assertExceptionThrown( SBigInt.upcast(CBigInt(new BigInteger("0", 16)).asInstanceOf[AnyVal]), _.getMessage.contains("Cannot upcast value") ) - // TODO HF (2h): the behavior of `downcast` for BigInt is different from all other Numeric types + // TODO v6.0 (2h): the behavior of `downcast` for BigInt is different from all other Numeric types // The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree. // It makes sense to fix this inconsistency as part of HF assertExceptionThrown( @@ -2672,7 +2672,7 @@ class SigmaDslSpecification extends SigmaDslTesting MethodCall(ValUse(1, SGroupElement), SGroupElement.getMethodByName("negate"), Vector(), Map()) ))) - // TODO HF (3h): related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + // TODO v6.0 (3h): related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 // val isIdentity = existingFeature({ (x: GroupElement) => x.isIdentity }, // "{ (x: GroupElement) => x.isIdentity }") @@ -2722,7 +2722,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ))) } else { - // TODO v5.0: add test case with `exp` MethodCall + // TODO mainnet v5.0: add test case with `exp` MethodCall } if (lowerMethodCallsInTests) { @@ -2770,7 +2770,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ))) } else { - //TODO v5.0: add test case with `exp` MethodCall + //TODO mainnet v5.0: add test case with `exp` MethodCall } } @@ -3284,7 +3284,7 @@ class SigmaDslSpecification extends SigmaDslTesting val invalidKvs = Colls.fromItems((invalidKey -> value)) // NOTE, insertProof is based on `key` val input = (tree, (invalidKvs, insertProof)) val (res, _) = insert.checkEquality(input).getOrThrow - res.isDefined shouldBe true // TODO HF: should it really be true? (looks like a bug) + res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) insert.checkVerify(input, Expected(value = Success(res), cost = 38501)) } @@ -3417,7 +3417,7 @@ class SigmaDslSpecification extends SigmaDslTesting val invalidKvs = Colls.fromItems((key -> invalidValue)) val input = (tree, (invalidKvs, updateProof)) val (res, _) = update.checkEquality(input).getOrThrow - res.isDefined shouldBe true // TODO HF: should it really be true? (looks like a bug) + res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) update.checkVerify(input, Expected(value = Success(res), cost = cost)) } @@ -3694,7 +3694,7 @@ class SigmaDslSpecification extends SigmaDslTesting "{ (x: Box) => x.creationInfo }", FuncValue(Vector((1, SBox)), ExtractCreationInfo(ValUse(1, SBox))))) - // TODO HF (2h): fix collections equality and remove map(identity) + // TODO v6.0 (2h): fix collections equality and remove map(identity) // (PairOfColl should be equal CollOverArray) verifyCases( Seq( @@ -3718,7 +3718,7 @@ class SigmaDslSpecification extends SigmaDslTesting } property("Box properties equivalence (new features)") { - // TODO HF (4h): related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416 + // TODO v6.0 (4h): related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416 val getReg = newFeature((x: Box) => x.getReg[Int](1).get, "{ (x: Box) => x.getReg[Int](1).get }") @@ -3868,7 +3868,7 @@ class SigmaDslSpecification extends SigmaDslTesting val box3 = SigmaDsl.Box(testBox(20, TrueTree, 0, Seq(), Map( ErgoBox.R4 -> Constant((10, 20L).asInstanceOf[SType#WrappedType], STuple(SInt, SLong)), ErgoBox.R5 -> Constant((10, Some(20L)).asInstanceOf[SType#WrappedType], STuple(SInt, SOption(SLong))) - // TODO HF (1h): uncomment after DataSerializer support of Option type + // TODO v6.0 (1h): uncomment after DataSerializer support of Option type // ErgoBox.R6 -> Constant[SOption[SInt.type]](Option(10), SOption(SInt)), ))) @@ -3977,7 +3977,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ))) - // TODO HF (1h): uncomment after DataSerializer support of Option type + // TODO v6.0 (1h): uncomment after DataSerializer support of Option type // verifyCases( // Seq( // (box3, Expected(Success(20L), cost = 36468)) @@ -4279,7 +4279,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ), height = 11, - selfBox = input.copy(), // TODO HF (2h): in 3.x implementation selfBox is never the same instance as input (see toSigmaContext) + selfBox = input.copy(), // in 3.x, 4.x implementation selfBox is never the same instance as input (see toSigmaContext) lastBlockUtxoRootHash = CAvlTree( AvlTreeData( ADDigest @@ (ErgoAlgos.decodeUnsafe("54d23dd080006bdb56800100356080935a80ffb77e90b800057f00661601807f17")), @@ -4479,6 +4479,7 @@ class SigmaDslSpecification extends SigmaDslTesting )), preGeneratedSamples = Some(samples)) + // TODO v5.0: fix selfBoxIndex for ET v2 verifyCases( Seq((ctx, Expected(Success(-1), cost = 36318))), existingFeature({ (x: Context) => x.selfBoxIndex }, @@ -4494,7 +4495,7 @@ class SigmaDslSpecification extends SigmaDslTesting )), preGeneratedSamples = Some(samples)) - // TODO HF (2h): see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603 + // TODO v5.0 (2h): see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/603 samples.foreach { c => ctx.selfBoxIndex shouldBe -1 } @@ -4530,7 +4531,7 @@ class SigmaDslSpecification extends SigmaDslTesting existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey }), preGeneratedSamples = Some(samples)) -// TODO HF (2h): implement support of Option[T] in DataSerializer +// TODO v6.0 (2h): implement support of Option[T] in DataSerializer // this will allow passing optional values in registers and also in constants // testCases2( // Seq( @@ -4637,7 +4638,7 @@ class SigmaDslSpecification extends SigmaDslTesting throw expectedError }, scalaFuncNew = { (x: Context) => - // TODO HF: this is expected in v5.0 + // TODO v5.0: this is expected in v5.0 val dataBox = x.dataInputs(0) val ok = if (x.OUTPUTS(0).R5[Long].get == 1L) { dataBox.R4[Long].get <= x.SELF.value @@ -5089,7 +5090,7 @@ class SigmaDslSpecification extends SigmaDslTesting } property("xorOf equivalence") { - // TODO HF (3h): see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/640 + // TODO v5.0 (3h): see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/640 verifyCases( { def success[T](v: T, c: Int) = Expected(Success(v), c) @@ -5296,10 +5297,10 @@ class SigmaDslSpecification extends SigmaDslTesting ) ))) } else { - // TODO v5.0: add test case with `exp` MethodCall + // TODO mainnet v5.0: add test case with `exp` MethodCall } - // TODO HF (2h): fix semantics when the left collection is longer + // TODO v6.0 (2h): fix semantics when the left collection is longer if (lowerMethodCallsInTests) { verifyCases( { @@ -5331,7 +5332,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ))) } else { - // TODO v5.0: add test case with `SGlobal.xor` MethodCall + // TODO mainnet v5.0: add test case with `SGlobal.xor` MethodCall } } @@ -5862,7 +5863,7 @@ class SigmaDslSpecification extends SigmaDslTesting preGeneratedSamples = Some(samples)) } - // TODO HF (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 property("Coll find method equivalence") { val find = newFeature((x: Coll[Int]) => x.find({ (v: Int) => v > 0 }), "{ (x: Coll[Int]) => x.find({ (v: Int) => v > 0} ) }") @@ -5871,7 +5872,7 @@ class SigmaDslSpecification extends SigmaDslTesting } } - // TODO HF (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/418 + // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/418 property("Coll bitwise methods equivalence") { val shiftRight = newFeature( { (x: Coll[Boolean]) => @@ -5883,7 +5884,7 @@ class SigmaDslSpecification extends SigmaDslTesting } } - // TODO HF (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 + // TODO v6.0 (3h): https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479 property("Coll diff methods equivalence") { val diff = newFeature((x: (Coll[Int], Coll[Int])) => x._1.diff(x._2), "{ (x: (Coll[Int], Coll[Int])) => x._1.diff(x._2) }") @@ -6115,7 +6116,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ))) } else { - // TODO v5.0: add test case with `SCollection.getOrElse` MethodCall + // TODO mainnet v5.0: add test case with `SCollection.getOrElse` MethodCall } } @@ -6345,7 +6346,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) ))) } else { - // TODO v5.0: add test case with `SCollection.append` MethodCall + // TODO mainnet v5.0: add test case with `SCollection.append` MethodCall } } @@ -6484,7 +6485,7 @@ class SigmaDslSpecification extends SigmaDslTesting ) )) } - // TODO HF (3h): implement Option.fold + // TODO v6.0 (3h): implement Option.fold property("Option new methods") { val isEmpty = newFeature({ (x: Option[Long]) => x.isEmpty }, "{ (x: Option[Long]) => x.isEmpty }") @@ -6874,7 +6875,7 @@ class SigmaDslSpecification extends SigmaDslTesting preGeneratedSamples = Some(Seq())) } - // TODO HF (3h): implement allZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 + // TODO v6.0 (3h): implement allZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 property("allZK equivalence") { lazy val allZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.allZK(x), "{ (x: Coll[SigmaProp]) => allZK(x) }") @@ -6883,7 +6884,7 @@ class SigmaDslSpecification extends SigmaDslTesting } } - // TODO HF (3h): implement anyZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 + // TODO v6.0 (3h): implement anyZK func https://github.com/ScorexFoundation/sigmastate-interpreter/issues/543 property("anyZK equivalence") { lazy val anyZK = newFeature((x: Coll[SigmaProp]) => SigmaDsl.anyZK(x), "{ (x: Coll[SigmaProp]) => anyZK(x) }") @@ -6997,7 +6998,7 @@ class SigmaDslSpecification extends SigmaDslTesting Seq( (Helpers.decodeBytes(""), 0) -> Expected(new java.nio.BufferUnderflowException()), - // TODO HF (2h): fix for trees without segregation flag + // TODO v5.0 (2h): fix for trees without segregation flag // NOTE: constants count is serialized erroneously in the following 2 cases (Coll(t1.bytes:_*), 0) -> success(Helpers.decodeBytes("000008d3")), (Helpers.decodeBytes("000008d3"), 0) -> success(Helpers.decodeBytes("00000008d3")), diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala index 144e086c3b..d9e704a169 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslTesting.scala @@ -585,7 +585,7 @@ class SigmaDslTesting extends PropSpec } else { // new cost expectation is specified, compare it with the actual result -// TODO v5.0: uncomment to enable test vectors +// TODO mainnet v5.0: uncomment to enable test vectors // funcRes.foreach { case (_, newCost) => // if (newCost.trace != expectedTrace) { // printCostDetails(script, newCost) @@ -681,8 +681,9 @@ class SigmaDslTesting extends PropSpec implicit val cs = compilerSettingsInTests val oldImpl = () => func[A, B](script) - val newImpl = oldImpl // funcJit[A, B](script) // TODO HF (16h): use actual new implementation here + val newImpl = oldImpl // funcJit[A, B](script) // TODO v6.0 (16h): use actual new implementation here + /** In v5.x this method just checks the old implementations fails on the new feature. */ override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = { val oldRes = Try(oldF(input)) oldRes.isFailure shouldBe true diff --git a/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala b/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala index a27ab7afdb..7e73a0ae09 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaTestingData.scala @@ -128,7 +128,7 @@ trait SigmaTestingData extends SigmaTestingCommons with ObjectGenerators { def createBigIntMaxValue(): BigInt = BigIntMaxValue_instances.getNext - // TODO HF: this values have bitCount == 255 (see to256BitValueExact) + // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact) val BigIntMinValue = CBigInt(new BigInteger("-7F" + "ff" * 31, 16)) val BigIntMaxValue = createBigIntMaxValue() val BigIntOverlimit = CBigInt(new BigInteger("7F" + "ff" * 33, 16)) From cf54654d5918bb0abd4b8f7a8046cc67e001e89d Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 21 Dec 2021 11:11:16 +0300 Subject: [PATCH 08/14] v5.0-finalize: fixed CollectionUtil.joinSeq method + tests --- .../scala/scalan/util/CollectionUtil.scala | 13 ++++++++-- .../scalan/util/CollectionUtilTests.scala | 24 +++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/common/src/main/scala/scalan/util/CollectionUtil.scala b/common/src/main/scala/scalan/util/CollectionUtil.scala index 2e32794b96..7df4b6638a 100644 --- a/common/src/main/scala/scalan/util/CollectionUtil.scala +++ b/common/src/main/scala/scalan/util/CollectionUtil.scala @@ -50,6 +50,10 @@ object CollectionUtil { } } + /** Group the given sequence of pairs by first values as keys. + * @param kvs sequence of values which is traversed once + * @return a multimap with ArrayBuffer of values for each key. + */ def createMultiMap[K,V](kvs: GenIterable[(K,V)]): Map[K, ArrayBuffer[V]] = { val res = HashMap.empty[K, ArrayBuffer[V]] kvs.foreach { case (k, v) => @@ -62,12 +66,17 @@ object CollectionUtil { res.toMap } - // TODO optimize: using cfor and avoiding allocations + /** Perform relational inner join of two sequences using the given key projections. */ def joinSeqs[O, I, K](outer: GenIterable[O], inner: GenIterable[I])(outKey: O=>K, inKey: I=>K): GenIterable[(O,I)] = { val kvs = createMultiMap(inner.map(i => (inKey(i), i))) val res = outer.flatMap(o => { val ko = outKey(o) - kvs(ko).map(i => (o,i)) + kvs.get(ko) match { + case Some(inners) => + inners.map(i => (o,i)) + case None => + Nil + } }) res } diff --git a/common/src/test/scala/scalan/util/CollectionUtilTests.scala b/common/src/test/scala/scalan/util/CollectionUtilTests.scala index ef5ebf17ca..116f192d6e 100644 --- a/common/src/test/scala/scalan/util/CollectionUtilTests.scala +++ b/common/src/test/scala/scalan/util/CollectionUtilTests.scala @@ -38,6 +38,30 @@ class CollectionUtilTests extends BaseTests { def joinPairs(l: Seq[(String,Int)], r: Seq[(String,Int)]) = outerJoinSeqs(l, r)(l => l._1, r => r._1)((_,l) => l._2, (_,r) => r._2, (k,l,r) => l._2 + r._2) + test("joinSeqs") { + def key(p : (Int, String)): Int = p._1 + + { + val res = CollectionUtil.joinSeqs( + outer = Seq(1 -> "o1", 1 -> "o1"), + inner = Seq(1 -> "i1", 2 -> "i2"))(key, key) + res shouldBe Seq( + (1 -> "o1") -> (1 -> "i1"), + (1 -> "o1") -> (1 -> "i1") + ) + } + + { // same as above, but swapping inner and outer + val res = CollectionUtil.joinSeqs( + outer = Seq(1 -> "o1", 2 -> "o2"), + inner = Seq(1 -> "i1", 1 -> "i1"))(key, key) + res shouldBe Seq( + (1 -> "o1") -> (1 -> "i1"), + (1 -> "o1") -> (1 -> "i1") + ) + } + } + test("outerJoin maps") { val left = Map(1 -> 1, 2 -> 2, 3 -> 3) val right = Map(2 -> 2, 3 -> 3, 4 -> 4) From b13986cd757f588a1e6643b958bc37513bc2ea51 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 21 Dec 2021 13:42:25 +0300 Subject: [PATCH 09/14] v5.0-finalize: fixed test --- .../src/test/scala/special/sigma/SigmaDslSpecification.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala index 010274bd3b..672e3087f3 100644 --- a/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala +++ b/sigmastate/src/test/scala/special/sigma/SigmaDslSpecification.scala @@ -7009,7 +7009,7 @@ class SigmaDslSpecification extends SigmaDslTesting (Coll(t3.bytes:_*), 0) -> success(Helpers.decodeBytes("100108d27300")), (Helpers.decodeBytes("100108d37300"), 0) -> success(Helpers.decodeBytes("100108d27300")), (Coll(t3.bytes:_*), 1) -> success(Helpers.decodeBytes("100108d37300")), - (Coll(t4.bytes:_*), 0) -> Expected(new AssertionError("assertion failed: expected new constant to have the same SInt$ tpe, got SSigmaProp")) + (Coll(t4.bytes:_*), 0) -> Expected(new IllegalArgumentException("requirement failed: expected new constant to have the same SInt$ tpe, got SSigmaProp")) ) }, existingFeature( From 81ac4cb13ba06dd469d06b953cf79795be1e901b Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 21 Dec 2021 18:00:09 +0300 Subject: [PATCH 10/14] v5.0-finalize: identifying the problem with Coll.append --- .../scala/scalan/util/CollectionUtil.scala | 18 +++++- .../scalan/util/CollectionUtilTests.scala | 13 ++-- core/src/main/scala/scalan/TypeDescs.scala | 5 +- .../special/collections/CollsTests.scala | 64 +++++++++++++++++-- .../scala/sigmastate/utxo/transformers.scala | 9 ++- 5 files changed, 94 insertions(+), 15 deletions(-) diff --git a/common/src/main/scala/scalan/util/CollectionUtil.scala b/common/src/main/scala/scalan/util/CollectionUtil.scala index 7df4b6638a..c0f5b6e423 100644 --- a/common/src/main/scala/scalan/util/CollectionUtil.scala +++ b/common/src/main/scala/scalan/util/CollectionUtil.scala @@ -12,10 +12,11 @@ import scala.reflect.ClassTag object CollectionUtil { + /** @deprecated shouldn't be used other than for backwards compatibility with v3.x, v4.x. */ def concatArrays[T](xs: Array[T], ys: Array[T]): Array[T] = { val len = xs.length + ys.length val result = (xs match { - case arr: Array[AnyRef] => new Array[AnyRef](len) + case arr: Array[AnyRef] => new Array[AnyRef](len) // creates an array with invalid type descriptor (i.e. when T == Tuple2) case arr: Array[Byte] => new Array[Byte](len) case arr: Array[Short] => new Array[Short](len) case arr: Array[Int] => new Array[Int](len) @@ -30,6 +31,21 @@ object CollectionUtil { result } + /** Concatenates two arrays into a new resulting array. + * All items of both arrays are copied to the result using System.arraycopy. + * This method takes ClassTag to create proper resulting array. + * Can be used in v5.0 and above. + */ + def concatArrays_v5[T:ClassTag](arr1: Array[T], arr2: Array[T]): Array[T] = { + val l1 = arr1.length + val l2 = arr2.length + val length: Int = l1 + l2 + val result: Array[T] = new Array[T](length) + System.arraycopy(arr1, 0, result, 0, l1) + System.arraycopy(arr2, 0, result, l1, l2) + result + } + def deepHashCode[T](arr: Array[T]): Int = arr match { case arr: Array[AnyRef] => util.Arrays.deepHashCode(arr) case arr: Array[Byte] => util.Arrays.hashCode(arr) diff --git a/common/src/test/scala/scalan/util/CollectionUtilTests.scala b/common/src/test/scala/scalan/util/CollectionUtilTests.scala index 116f192d6e..9471093cad 100644 --- a/common/src/test/scala/scalan/util/CollectionUtilTests.scala +++ b/common/src/test/scala/scalan/util/CollectionUtilTests.scala @@ -25,10 +25,15 @@ class CollectionUtilTests extends BaseTests { val zs = concatArrays(xs, ys) assertResult(Array[Byte](1, 2, 3, 4, 5, 6))(zs) -// val jxs = Array[JByte](new JByte(1), new JByte(2), new JByte(3)) -// val jys = Array[JByte](new JByte(4), new JByte(5), new JByte(6)) -// val jzs = concatArrays(jxs, jys) -// assertResult(Array[Byte](1, 2, 3, 4, 5, 6))(jzs) + val pairs = xs.zip(ys) + // this reproduces the problem which takes place in v3.x, v4.x (ErgoTree v0, v1) + an[ClassCastException] should be thrownBy(concatArrays(pairs, pairs)) + + // and this is the fix in v5.0 + concatArrays_v5(pairs, pairs) shouldBe Array((1, 4), (2, 5), (3, 6), (1, 4), (2, 5), (3, 6)) + + val xOpts = xs.map(Option(_)) + concatArrays_v5(xOpts, xOpts) shouldBe Array(Some(1), Some(2), Some(3), Some(1), Some(2), Some(3)) } def join(l: Map[Int,Int], r: Map[Int,Int]) = diff --git a/core/src/main/scala/scalan/TypeDescs.scala b/core/src/main/scala/scalan/TypeDescs.scala index 5f3a4536bb..6ade17a110 100644 --- a/core/src/main/scala/scalan/TypeDescs.scala +++ b/core/src/main/scala/scalan/TypeDescs.scala @@ -55,7 +55,7 @@ abstract class TypeDescs extends Base { self: Scalan => case class RMethodDesc(method: Method) extends MethodDesc case class WMethodDesc(wrapSpec: WrapSpec, method: Method) extends MethodDesc -// TODO benchmark this version agains the version below +// TODO optimize: benchmark this version agains the version below // def getSourceValues(dataEnv: DataEnv, forWrapper: Boolean, stagedValue: AnyRef, out: DBuffer[AnyRef]): Unit = { // import OverloadHack._ // stagedValue match { @@ -154,7 +154,7 @@ abstract class TypeDescs extends Base { self: Scalan => protected def collectMethods: Map[Method, MethodDesc] = Map() // TODO optimize: all implementations protected lazy val methods: Map[Method, MethodDesc] = collectMethods - // TODO benchamrk against the version below it + // TODO optimize: benchamrk against the version below it // def invokeUnlifted(mc: MethodCall, dataEnv: DataEnv): AnyRef = { // val srcArgs = DBuffer.ofSize[AnyRef](mc.args.length + 10) // with some spare space to have only single allocation // val res = methods.get(mc.method) match { @@ -244,7 +244,6 @@ abstract class TypeDescs extends Base { self: Scalan => m.getName } - // TODO optimize /** Build a mapping between methods of staged class and the corresponding methods of source class. * The methods are related using names. * The computed mapping can be used to project MethodCalls IR nodes back to the corresponding diff --git a/library/src/test/scala/special/collections/CollsTests.scala b/library/src/test/scala/special/collections/CollsTests.scala index ba3ddd1210..5cb453cc90 100644 --- a/library/src/test/scala/special/collections/CollsTests.scala +++ b/library/src/test/scala/special/collections/CollsTests.scala @@ -5,11 +5,14 @@ import special.collection.{Coll, PairOfCols, CollOverArray, CReplColl} import org.scalacheck.Gen import org.scalatest.{PropSpec, Matchers} import org.scalatest.prop.PropertyChecks +import scalan.RType class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGens { testSuite => import Gen._ import special.collection.ExtensionMethods._ + def squared[A](f: A => A): ((A, A)) => (A, A) = (p: (A, A)) => (f(p._1), f(p._2)) + property("Coll.indices") { val minSuccess = MinSuccessful(30) forAll(collGen, collGen, minSuccess) { (col1: Coll[Int], col2: Coll[Int]) => @@ -20,8 +23,47 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen } } - // TODO col1.zip(col2).length shouldBe col1.arr.zip(col2.arr).length - property("Coll.zip") { + // TODO v6.0: make sure forall: T, col: Coll[T] => col.length shouldBe col.toArray.length + // The above equality should hold for all possible collection instances + property("Coll.length") { + def equalLength[A: RType](xs: Coll[A]) = { + val arr = xs.toArray + xs.length shouldBe arr.length + xs.zip(xs).length shouldBe arr.zip(arr).length + xs.zip(xs.append(xs)).length shouldBe arr.zip(arr ++ arr).length + xs.append(xs).zip(xs).length shouldBe (arr ++ arr).zip(arr).length + } + + def equalLengthMapped[A: RType](xs: Coll[A], f: A => A) = { + val arr = xs.toArray + val ys = xs.map(f) + ys.length shouldBe xs.length + ys.length shouldBe arr.map(f).length + + equalLength(ys) + equalLength(xs.append(ys)) + equalLength(ys.append(xs)) + } + + forAll(MinSuccessful(100)) { xs: Coll[Int] => + val arr = xs.toArray + equalLength(xs) + equalLengthMapped(xs, inc) + + equalLength(xs.append(xs)) + equalLengthMapped(xs.append(xs), inc) + + val pairs = xs.zip(xs) + equalLength(pairs) + an[ClassCastException] should be thrownBy { + equalLengthMapped(pairs, squared(inc)) // due to problem with append + } + + equalLength(pairs.append(pairs)) + an[ClassCastException] should be thrownBy { + equalLengthMapped(pairs.append(pairs), squared(inc)) // due to problem with append + } + } } property("Coll.flatMap") { @@ -198,6 +240,15 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen } property("Coll.append") { + + { // this shows how the problem with CollectionUtil.concatArrays manifests itself in Coll.append + val xs = builder.fromItems(1, 2) + val pairs = xs.zip(xs) + val ys = pairs.map(squared(inc)) // this map transforms PairOfCols to CollOverArray + // due to the problem with concatArrays + an[ClassCastException] should be thrownBy (ys.append(ys)) + } + forAll(collGen, collGen, valGen, MinSuccessful(50)) { (col1, col2, v) => { @@ -212,13 +263,18 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen { val repl1 = builder.replicate(col1.length, v) val repl2 = builder.replicate(col2.length, v) + assert(repl1.isInstanceOf[CReplColl[Int]]) + val arepl = repl1.append(repl2) assert(arepl.isInstanceOf[CReplColl[Int]]) arepl.toArray shouldBe (repl1.toArray ++ repl2.toArray) val pairs1 = repl1.zip(repl1) + assert(pairs1.isInstanceOf[PairOfCols[Int, Int]]) + val pairs2 = repl2.zip(repl2) val apairs = pairs1.append(pairs2) + assert(apairs.isInstanceOf[PairOfCols[Int, Int]]) apairs.toArray shouldBe (pairs1.toArray ++ pairs2.toArray) apairs match { @@ -266,10 +322,6 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen res.toArray shouldBe col.toArray.reverse val pairs = col.zip(col) pairs.reverse.toArray shouldBe pairs.toArray.reverse -// TODO should work -// val c1 = col.asInstanceOf[Coll[Any]] -// val appended = c1.append(c1) -// appended.toArray shouldBe (c1.toArray ++ c1.toArray) } } diff --git a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala index fa67a31f26..c42e27af4d 100644 --- a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala +++ b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala @@ -12,6 +12,7 @@ import sigmastate.Operations._ import sigmastate.eval.{Evaluation, SigmaDsl} import sigmastate.interpreter.ErgoTreeEvaluator import sigmastate.interpreter.ErgoTreeEvaluator.{DataEnv, error} +import sigmastate.interpreter.Interpreter.JitActivationVersion import sigmastate.lang.exceptions.InterpreterException import special.collection.Coll import special.sigma.{Box, SigmaProp} @@ -69,7 +70,13 @@ case class Append[IV <: SType](input: Value[SCollection[IV]], col2: Value[SColle val inputV = input.evalTo[Coll[IV#WrappedType]](env) val col2V = col2.evalTo[Coll[IV#WrappedType]](env) addSeqCost(Append.costKind, inputV.length + col2V.length) { () => - inputV.append(col2V) + if (E.context.currentErgoTreeVersion >= JitActivationVersion) { + // TODO v5.0: use append_v5 instead + inputV.append(col2V) + } else { + // using old erroneous method of v0, v1 + inputV.append(col2V) + } } } } From 68365862ac5548a644d91f9b062a38b1bebcfeef Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Tue, 4 Jan 2022 22:10:10 +0300 Subject: [PATCH 11/14] v5.0-finalize-part1: rollback CreateAvlTree --- .../main/scala/sigmastate/Operations.scala | 9 +++++ .../scala/sigmastate/lang/SigmaBuilder.scala | 12 ++++++ .../scala/sigmastate/lang/SigmaPredef.scala | 16 ++++++++ .../CreateAvlTreeSerializer.scala | 38 +++++++++++++++++++ .../serialization/ValueSerializer.scala | 1 + .../src/main/scala/sigmastate/trees.scala | 16 ++++++++ 6 files changed, 92 insertions(+) create mode 100644 sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala diff --git a/sigmastate/src/main/scala/sigmastate/Operations.scala b/sigmastate/src/main/scala/sigmastate/Operations.scala index dfae1395d8..51ae277757 100644 --- a/sigmastate/src/main/scala/sigmastate/Operations.scala +++ b/sigmastate/src/main/scala/sigmastate/Operations.scala @@ -153,6 +153,15 @@ object Operations { val argInfos: Seq[ArgInfo] = Array(indexArg) } + object CreateAvlTreeInfo extends InfoObject { + private val func = predefinedOps.funcs("avlTree") + val operationFlagsArg: ArgInfo = func.argInfo("operationFlags") + val digestArg: ArgInfo = func.argInfo("digest") + val keyLengthArg: ArgInfo = func.argInfo("keyLength") + val valueLengthOptArg: ArgInfo = func.argInfo("valueLengthOpt") + val argInfos: Seq[ArgInfo] = Array(operationFlagsArg, digestArg, keyLengthArg, valueLengthOptArg) + } + object CreateProveDHTupleInfo extends InfoObject { private val func = predefinedOps.funcs("proveDHTuple") val gArg: ArgInfo = func.argInfo("g") diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala index a81135f7f1..5b220109f8 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaBuilder.scala @@ -144,6 +144,11 @@ abstract class SigmaBuilder { def mkCreateProveDlog(value: Value[SGroupElement.type]): SigmaPropValue + def mkCreateAvlTree(operationFlags: ByteValue, + digest: Value[SByteArray], + keyLength: IntValue, + valueLengthOpt: Value[SIntOption]): AvlTreeValue + /** Logically inverse to mkSigmaPropIsProven */ def mkBoolToSigmaProp(value: BoolValue): SigmaPropValue /** Logically inverse to mkBoolToSigmaProp */ @@ -526,6 +531,13 @@ class StdSigmaBuilder extends SigmaBuilder { override def mkCreateProveDlog(value: Value[SGroupElement.type]): SigmaPropValue = CreateProveDlog(value) + override def mkCreateAvlTree(operationFlags: ByteValue, + digest: Value[SByteArray], + keyLength: IntValue, + valueLengthOpt: Value[SIntOption]): AvlTreeValue = { + CreateAvlTree(operationFlags, digest, keyLength, valueLengthOpt) + } + override def mkBoolToSigmaProp(value: BoolValue): SigmaPropValue = BoolToSigmaProp(value).withSrcCtx(currentSrcCtx.value) diff --git a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala index fb90aace2e..942a110d52 100644 --- a/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala +++ b/sigmastate/src/main/scala/sigmastate/lang/SigmaPredef.scala @@ -290,6 +290,21 @@ object SigmaPredef { Seq(ArgInfo("value", "element of elliptic curve group"))) ) + val AvlTreeFunc = PredefinedFunc("avlTree", + Lambda(Array("operationFlags" -> SByte, "digest" -> SByteArray, "keyLength" -> SInt, "valueLengthOpt" -> SIntOption), SAvlTree, None), + PredefFuncInfo( + { case (_, Seq(flags, digest, keyLength, valueLength)) => + mkCreateAvlTree(flags.asByteValue, digest.asByteArray, keyLength.asIntValue, valueLength.asOption[SInt.type]) + }), + OperationInfo(CreateAvlTree, + "Construct a new authenticated dictionary with given parameters and tree root digest.", + Seq( + ArgInfo("operationFlags", "flags of available operations"), + ArgInfo("digest", "hash of merkle tree root"), + ArgInfo("keyLength", "length of dictionary keys in bytes"), + ArgInfo("valueLengthOpt", "optional width of dictionary values in bytes"))) + ) + val SubstConstantsFunc = PredefinedFunc("substConstants", Lambda( Seq(paramT), @@ -376,6 +391,7 @@ object SigmaPredef { LongToByteArrayFunc, ProveDHTupleFunc, ProveDlogFunc, + AvlTreeFunc, SubstConstantsFunc, ExecuteFromVarFunc, ExecuteFromSelfRegFunc diff --git a/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala new file mode 100644 index 0000000000..ecbb37040e --- /dev/null +++ b/sigmastate/src/main/scala/sigmastate/serialization/CreateAvlTreeSerializer.scala @@ -0,0 +1,38 @@ +package sigmastate.serialization + +import sigmastate.SCollection._ +import sigmastate.SOption.SIntOption +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigmastate._ +import sigmastate.Values._ +import sigmastate.lang.Terms.ValueOps +import sigmastate.utils.SigmaByteWriter.DataInfo + +// TODO refactor: remove not used +case class CreateAvlTreeSerializer( + cons: (ByteValue, Value[SByteArray], IntValue, Value[SIntOption]) => AvlTreeValue + ) + extends ValueSerializer[CreateAvlTree] +{ + import sigmastate.Operations.CreateAvlTreeInfo._ + override def opDesc = CreateAvlTree + val operationFlagsInfo: DataInfo[SValue] = operationFlagsArg + val digestInfo: DataInfo[SValue] = digestArg + val keyLengthInfo: DataInfo[SValue] = keyLengthArg + val valueLengthOptInfo: DataInfo[SValue] = valueLengthOptArg + + override def serialize(obj: CreateAvlTree, w: SigmaByteWriter): Unit = { + w.putValue(obj.operationFlags, operationFlagsInfo) + w.putValue(obj.digest, digestInfo) + w.putValue(obj.keyLength, keyLengthInfo) + w.putValue(obj.valueLengthOpt, valueLengthOptInfo) + } + + override def parse(r: SigmaByteReader) = { + val flags = r.getValue().asByteValue + val digest = r.getValue().asByteArray + val keyLength = r.getValue().asIntValue + val valueLength = r.getValue().asOption[SInt.type] + cons(flags, digest, keyLength, valueLength) + } +} diff --git a/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala index b6773907e7..af7b9a4a5e 100644 --- a/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/sigmastate/src/main/scala/sigmastate/serialization/ValueSerializer.scala @@ -50,6 +50,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { Relation2Serializer(LE, mkLE[SType]), Relation2Serializer(EQ, mkEQ[SType]), Relation2Serializer(NEQ, mkNEQ[SType]), + CreateAvlTreeSerializer(mkCreateAvlTree), QuadrupleSerializer(TreeLookup, mkTreeLookup), Relation2Serializer(BinOr, mkBinOr), Relation2Serializer(BinAnd, mkBinAnd), diff --git a/sigmastate/src/main/scala/sigmastate/trees.scala b/sigmastate/src/main/scala/sigmastate/trees.scala index 69d7b73e94..0eb73e64e8 100644 --- a/sigmastate/src/main/scala/sigmastate/trees.scala +++ b/sigmastate/src/main/scala/sigmastate/trees.scala @@ -192,6 +192,22 @@ object CreateProveDlog extends ValueCompanion { val OpType = SFunc(SGroupElement, SSigmaProp) } +// TODO refactor: remove not used class +/** Construct a new authenticated dictionary with given parameters and tree root digest.*/ +case class CreateAvlTree(operationFlags: ByteValue, + digest: Value[SByteArray], + keyLength: IntValue, + valueLengthOpt: Value[SIntOption]) extends AvlTreeValue { + override def companion = CreateAvlTree + override def tpe = SAvlTree + override def opType = CreateAvlTree.OpType +} +object CreateAvlTree extends ValueCompanion { + override def opCode: OpCode = OpCodes.AvlTreeCode + override def costKind: CostKind = Value.notSupportedError(this, "costKind") + val OpType = SFunc(Array(SByte, SByteArray, SInt, SIntOption), SAvlTree) +} + /** ErgoTree operation to create a new SigmaProp value representing public key * of Diffie Hellman signature protocol. * Common input: (g,h,u,v)*/ From ef7eb6a5e255c78380e06a7e126f1089644f9bad Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Mon, 24 Jan 2022 10:44:38 +0100 Subject: [PATCH 12/14] v5.0-finalize-part1: rollback change in Append --- .../src/main/scala/sigmastate/utxo/transformers.scala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala index c42e27af4d..9de189d54f 100644 --- a/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala +++ b/sigmastate/src/main/scala/sigmastate/utxo/transformers.scala @@ -70,13 +70,7 @@ case class Append[IV <: SType](input: Value[SCollection[IV]], col2: Value[SColle val inputV = input.evalTo[Coll[IV#WrappedType]](env) val col2V = col2.evalTo[Coll[IV#WrappedType]](env) addSeqCost(Append.costKind, inputV.length + col2V.length) { () => - if (E.context.currentErgoTreeVersion >= JitActivationVersion) { - // TODO v5.0: use append_v5 instead inputV.append(col2V) - } else { - // using old erroneous method of v0, v1 - inputV.append(col2V) - } } } } From eb4781181f050f1352de3cc36357bd3d4cd0e55e Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Wed, 26 Jan 2022 18:01:47 +0100 Subject: [PATCH 13/14] v5.0-finalize-part1: rollback changes in optionIsZero --- sigmastate/src/main/scala/sigmastate/eval/Zero.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sigmastate/src/main/scala/sigmastate/eval/Zero.scala b/sigmastate/src/main/scala/sigmastate/eval/Zero.scala index a8fea6df60..a29ef6f769 100644 --- a/sigmastate/src/main/scala/sigmastate/eval/Zero.scala +++ b/sigmastate/src/main/scala/sigmastate/eval/Zero.scala @@ -19,7 +19,7 @@ case class CZero[T](zero: T) extends Zero[T] trait ZeroLowPriority { implicit def collIsZero[T: Zero: RType]: Zero[Coll[T]] = CZero(Colls.emptyColl[T]) - implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(None) + implicit def optionIsZero[T: Zero]: Zero[Option[T]] = CZero(Some(Zero.zeroOf[T])) implicit def pairIsZero[A: Zero, B: Zero]: Zero[(A,B)] = CZero(Zero[A].zero, Zero[B].zero) } object Zero extends ZeroLowPriority { From 5abc74e13db15965b020a11856b883a1b5fedd73 Mon Sep 17 00:00:00 2001 From: Alexander Slesarenko Date: Fri, 28 Jan 2022 17:52:45 +0100 Subject: [PATCH 14/14] v5.0-finalize-part1: fix tests --- library/src/test/scala/special/collections/CollsTests.scala | 6 ------ 1 file changed, 6 deletions(-) diff --git a/library/src/test/scala/special/collections/CollsTests.scala b/library/src/test/scala/special/collections/CollsTests.scala index 5cb453cc90..b016bd7cff 100644 --- a/library/src/test/scala/special/collections/CollsTests.scala +++ b/library/src/test/scala/special/collections/CollsTests.scala @@ -55,14 +55,8 @@ class CollsTests extends PropSpec with PropertyChecks with Matchers with CollGen val pairs = xs.zip(xs) equalLength(pairs) - an[ClassCastException] should be thrownBy { - equalLengthMapped(pairs, squared(inc)) // due to problem with append - } equalLength(pairs.append(pairs)) - an[ClassCastException] should be thrownBy { - equalLengthMapped(pairs.append(pairs), squared(inc)) // due to problem with append - } } }