From 49dbe17ec980a627014e2262dfbf2b3912e89248 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 19 Feb 2019 13:41:20 +0200 Subject: [PATCH 01/30] add byteArrayToLong; --- .../scala/sigmastate/eval/RuntimeCosting.scala | 7 +++++++ .../scala/sigmastate/lang/SigmaBuilder.scala | 4 ++++ .../scala/sigmastate/lang/SigmaPredef.scala | 8 +++++--- .../serialization/ValueSerializer.scala | 1 + .../TransformersSerializationSpec.scala | 4 ++++ .../generators/TransformerGenerators.scala | 3 +++ src/test/scala/special/sigma/SigmaDslTest.scala | 17 ++++++++++++----- 7 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index 325d33e9f5..ab5dd400d2 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1520,6 +1520,13 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev val cost = inputC.cost + costOf(node) withDefaultSize(res, cost) +// case ByteArrayToLong(In(arr)) => +// val arrC = asRep[Costed[Coll[Byte]]](arr) +// val value = sigmaDslBuilder.byteArrayToLong(arrC.value) +// val size = arrC.dataSize +// val cost = arrC.cost + costOf(node) +// RCCostedPrim(value, cost, size) + case Xor(InCollByte(l), InCollByte(r)) => val values = colBuilder.xor(l.value, r.value) val sizes = r.sizes diff --git a/src/main/scala/sigmastate/lang/SigmaBuilder.scala b/src/main/scala/sigmastate/lang/SigmaBuilder.scala index 9cfc823722..95e2cdb4c9 100644 --- a/src/main/scala/sigmastate/lang/SigmaBuilder.scala +++ b/src/main/scala/sigmastate/lang/SigmaBuilder.scala @@ -78,6 +78,7 @@ trait SigmaBuilder { falseBranch: Value[T]): Value[T] def mkLongToByteArray(input: Value[SLong.type]): Value[SByteArray] + def mkByteArrayToLong(input: Value[SByteArray]): Value[SLong.type] def mkByteArrayToBigInt(input: Value[SByteArray]): Value[SBigInt.type] def mkUpcast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R): Value[R] def mkDowncast[T <: SNumericType, R <: SNumericType](input: Value[T], tpe: R): Value[R] @@ -360,6 +361,9 @@ class StdSigmaBuilder extends SigmaBuilder { override def mkLongToByteArray(input: Value[SLong.type]): Value[SByteArray] = LongToByteArray(input).withSrcCtx(currentSrcCtx.value) + override def mkByteArrayToLong(input: Value[SByteArray]): Value[SLong.type] = + ByteArrayToLong(input).withSrcCtx(currentSrcCtx.value) + override def mkByteArrayToBigInt(input: Value[SByteArray]): Value[SBigInt.type] = ByteArrayToBigInt(input).withSrcCtx(currentSrcCtx.value) diff --git a/src/main/scala/sigmastate/lang/SigmaPredef.scala b/src/main/scala/sigmastate/lang/SigmaPredef.scala index 92eaa5f985..dc1b885ab4 100644 --- a/src/main/scala/sigmastate/lang/SigmaPredef.scala +++ b/src/main/scala/sigmastate/lang/SigmaPredef.scala @@ -158,7 +158,9 @@ object SigmaPredef { val ByteArrayToLongFunc = PredefinedFunc("byteArrayToLong", Lambda(Vector("input" -> SByteArray), SLong, None), - undefined + { case (_, Seq(arg: Value[SByteArray]@unchecked)) => + mkByteArrayToLong(arg) + } ) val DecodePointFunc = PredefinedFunc("decodePoint", @@ -192,7 +194,7 @@ object SigmaPredef { val IsMemberFunc = PredefinedFunc("isMember", Lambda(Vector("tree" -> SAvlTree, "key" -> SByteArray, "proof" -> SByteArray), SBoolean, None), { case (_, Seq(tree: Value[SAvlTree.type]@unchecked, key: Value[SByteArray]@unchecked, - proof: Value[SByteArray]@unchecked)) => + proof: Value[SByteArray]@unchecked)) => mkIsMember(tree, key, proof) } ) @@ -200,7 +202,7 @@ object SigmaPredef { val TreeLookupFunc = PredefinedFunc("treeLookup", Lambda(Vector("tree" -> SAvlTree, "key" -> SByteArray, "proof" -> SByteArray), SOption[SByteArray], None), { case (_, Seq(tree: Value[SAvlTree.type]@unchecked, key: Value[SByteArray]@unchecked, - proof: Value[SByteArray]@unchecked)) => + proof: Value[SByteArray]@unchecked)) => mkTreeLookup(tree, key, proof) } ) diff --git a/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/src/main/scala/sigmastate/serialization/ValueSerializer.scala index 93b8bf21a0..92b2850850 100644 --- a/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/src/main/scala/sigmastate/serialization/ValueSerializer.scala @@ -90,6 +90,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { SimpleTransformerSerializer[SBox.type, SByteArray](ExtractIdCode, mkExtractId), SimpleTransformerSerializer[SBox.type, STuple](ExtractCreationInfoCode, mkExtractCreationInfo), SimpleTransformerSerializer[SLong.type, SByteArray](LongToByteArrayCode, mkLongToByteArray), + SimpleTransformerSerializer[SByteArray, SLong.type](ByteArrayToLongCode, mkByteArrayToLong), SimpleTransformerSerializer[SByteArray, SBigInt.type](ByteArrayToBigIntCode, mkByteArrayToBigInt), SimpleTransformerSerializer[SByteArray, SByteArray](CalcBlake2b256Code, mkCalcBlake2b256), SimpleTransformerSerializer[SByteArray, SByteArray](CalcSha256Code, mkCalcSha256), diff --git a/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala b/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala index 725257062c..3c12ce86fa 100644 --- a/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala +++ b/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala @@ -176,4 +176,8 @@ class TransformersSerializationSpec extends SerializationSpecification { property("BoolToSigmaProp: Serializer round trip") { forAll(boolToSigmaPropGen) { v => roundTripTest(v) } } + + property("ByteArrayToLong: Serializer round trip") { + forAll(byteArrayToLongGen) { roundTripTest(_) } + } } diff --git a/src/test/scala/sigmastate/serialization/generators/TransformerGenerators.scala b/src/test/scala/sigmastate/serialization/generators/TransformerGenerators.scala index 29d925c97c..2432531b44 100644 --- a/src/test/scala/sigmastate/serialization/generators/TransformerGenerators.scala +++ b/src/test/scala/sigmastate/serialization/generators/TransformerGenerators.scala @@ -278,4 +278,7 @@ trait TransformerGenerators { b <- booleanConstGen } yield mkBoolToSigmaProp(b).asInstanceOf[BoolToSigmaProp] + val byteArrayToLongGen: Gen[ByteArrayToLong] = + arbByteArrayConstant.arbitrary.map { v => + mkByteArrayToLong(v).asInstanceOf[ByteArrayToLong] } } diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index e266a795d4..c01d038544 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -2,13 +2,13 @@ package special.sigma import java.math.BigInteger +import com.google.common.primitives.Longs import org.scalatest.prop.PropertyChecks -import org.scalatest.{PropSpec, Matchers} +import org.scalatest.{Matchers, PropSpec} import org.scalacheck.{Arbitrary, Gen} -import sigma.types.CBoolean import sigmastate.helpers.SigmaTestingCommons import sigma.util.Extensions._ -import special.collection.Coll +import special.collection.{Builder, Coll} import special.collections.CollGens trait SigmaTypeGens { @@ -111,6 +111,13 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma } } - - + ignore("byteArrayToLong equivalence") { + // TODO enable after SigmaDslBuilder.byteArrayToLong is implemented (+ uncomment in RuntimeCosting) + val eq = checkEq(func[Coll[Byte],Long]("{ (x: Coll[Byte]) => byteArrayToLong(x) }")){ x => + Longs.fromByteArray(x.toArray) + } + forAll { x: Array[Byte] => + eq(Builder.DefaultCollBuilder.fromArray(x)) + } + } } From 0bc2c2b1bf2a8f05b38df2f62ac1fa282ee84387 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 19 Feb 2019 15:09:19 +0200 Subject: [PATCH 02/30] add xorOf; --- docs/LangSpec.md | 2 +- .../scala/sigmastate/eval/RuntimeCosting.scala | 14 ++++++++++++++ src/main/scala/sigmastate/lang/SigmaBuilder.scala | 4 ++++ src/main/scala/sigmastate/lang/SigmaPredef.scala | 2 +- .../scala/sigmastate/serialization/OpCodes.scala | 2 +- .../serialization/ValueSerializer.scala | 1 + src/main/scala/sigmastate/trees.scala | 15 +++++++++++++++ .../scala/sigmastate/lang/SigmaCompilerTest.scala | 11 +++++++++++ .../serialization/DeserializationResilience.scala | 2 +- .../TransformersSerializationSpec.scala | 6 ++++++ src/test/scala/special/sigma/SigmaDslTest.scala | 10 ++++++++++ 11 files changed, 65 insertions(+), 4 deletions(-) diff --git a/docs/LangSpec.md b/docs/LangSpec.md index c15195b06e..308d4724ac 100644 --- a/docs/LangSpec.md +++ b/docs/LangSpec.md @@ -787,7 +787,7 @@ def allOf(conditions: Coll[Boolean]): Boolean /** Returns true if at least on element of the conditions is true */ def anyOf(conditions: Coll[Boolean]): Boolean -/** Similar to allOf, but performing logical XOR operation instead of `||` +/** Similar to allOf, but performing logical XOR operation instead of `&&` * @since 2.0 */ def xorOf(conditions: Coll[Boolean]): Boolean diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index ab5dd400d2..8afe266e7f 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1415,6 +1415,20 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev withDefaultSize(res, cost) } +// case XorOf(input) => input match { +// case ConcreteCollection(items, tpe) => +// val itemsC = items.map(item => eval(adaptSigmaBoolean(item))) +// val res = sigmaDslBuilder.xorOf(colBuilder.fromItems(itemsC.map(_.value): _*)) +// val costs = colBuilder.fromItems(itemsC.map(_.cost): _*) +// val cost = costs.sum(intPlusMonoid) + perItemCostOf(node, costs.length) +// withDefaultSize(res, cost) +// case _ => +// val inputC = asRep[CostedColl[Boolean]](eval(input)) +// val res = sigmaDslBuilder.xorOf(inputC.value) +// val cost = inputC.cost + perItemCostOf(node, inputC.sizes.length) +// withDefaultSize(res, cost) +// } + case BinOr(l, r) => val lC = evalNode(ctx, env, l) val rC = RCostedThunk(Thunk(evalNode(ctx, env, r)), 0) diff --git a/src/main/scala/sigmastate/lang/SigmaBuilder.scala b/src/main/scala/sigmastate/lang/SigmaBuilder.scala index 95e2cdb4c9..9e3838a13c 100644 --- a/src/main/scala/sigmastate/lang/SigmaBuilder.scala +++ b/src/main/scala/sigmastate/lang/SigmaBuilder.scala @@ -46,6 +46,7 @@ trait SigmaBuilder { def mkOR(input: Value[SCollection[SBoolean.type]]): BoolValue def mkAND(input: Value[SCollection[SBoolean.type]]): BoolValue + def mkXorOf(input: Value[SCollection[SBoolean.type]]): BoolValue def mkAnyOf(input: Seq[Value[SBoolean.type]]): BoolValue def mkAllOf(input: Seq[Value[SBoolean.type]]): BoolValue @@ -312,6 +313,9 @@ class StdSigmaBuilder extends SigmaBuilder { override def mkAND(input: Value[SCollection[SBoolean.type]]): Value[SBoolean.type] = AND(input).withSrcCtx(currentSrcCtx.value) + override def mkXorOf(input: Value[SCollection[SBoolean.type]]): BoolValue = + XorOf(input).withSrcCtx(currentSrcCtx.value) + override def mkAnyOf(input: Seq[Value[SBoolean.type]]) = OR(input).withSrcCtx(currentSrcCtx.value) override def mkAllOf(input: Seq[Value[SBoolean.type]]) = diff --git a/src/main/scala/sigmastate/lang/SigmaPredef.scala b/src/main/scala/sigmastate/lang/SigmaPredef.scala index dc1b885ab4..1dc6f717b0 100644 --- a/src/main/scala/sigmastate/lang/SigmaPredef.scala +++ b/src/main/scala/sigmastate/lang/SigmaPredef.scala @@ -217,7 +217,7 @@ object SigmaPredef { val XorOfFunc = PredefinedFunc("xorOf", Lambda(Vector("conditions" -> SCollection(SBoolean)), SBoolean, None), - undefined + { case (_, Seq(col: Value[SCollection[SBoolean.type]]@unchecked)) => mkXorOf(col) } ) val SubstConstantsFunc = PredefinedFunc("substConstants", diff --git a/src/main/scala/sigmastate/serialization/OpCodes.scala b/src/main/scala/sigmastate/serialization/OpCodes.scala index 992c2d1c49..281e70d6ae 100644 --- a/src/main/scala/sigmastate/serialization/OpCodes.scala +++ b/src/main/scala/sigmastate/serialization/OpCodes.scala @@ -186,5 +186,5 @@ object OpCodes extends ValueCodes { val CollRotateRightCode : OpCode = (LastConstantCode + 141).toByte val ContextCode : OpCode = (LastConstantCode + 142).toByte - // reserved 143 (1) + val XorOfCode : OpCode = (LastConstantCode + 143).toByte } diff --git a/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/src/main/scala/sigmastate/serialization/ValueSerializer.scala index 92b2850850..cd33fc65a3 100644 --- a/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/src/main/scala/sigmastate/serialization/ValueSerializer.scala @@ -76,6 +76,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { ConcreteCollectionSerializer(mkConcreteCollection), LogicalTransformerSerializer(AndCode, mkAND), LogicalTransformerSerializer(OrCode, mkOR), + LogicalTransformerSerializer(XorOfCode, mkXorOf), TaggedVariableSerializer(mkTaggedVariable), GetVarSerializer(mkGetVar), MapCollectionSerializer(mkMapCollection), diff --git a/src/main/scala/sigmastate/trees.scala b/src/main/scala/sigmastate/trees.scala index 9238e05b93..4184bfc708 100644 --- a/src/main/scala/sigmastate/trees.scala +++ b/src/main/scala/sigmastate/trees.scala @@ -179,6 +179,21 @@ object OR { def apply(head: Value[SBoolean.type], tail: Value[SBoolean.type]*): OR = apply(head +: tail) } +/** Similar to allOf, but performing logical XOR operation instead of `&&` + */ +case class XorOf(input: Value[SCollection[SBoolean.type]]) + extends Transformer[SCollection[SBoolean.type], SBoolean.type] with NotReadyValueBoolean { + override val opCode: OpCode = XorOfCode + override val opType = SFunc(SCollection.SBooleanArray, SBoolean) +} + +object XorOf { + def apply(children: Seq[Value[SBoolean.type]]): XorOf = + XorOf(ConcreteCollection(children.toIndexedSeq)) + + def apply(head: Value[SBoolean.type], tail: Value[SBoolean.type]*): XorOf = apply(head +: tail) +} + /** * AND logical conjunction */ diff --git a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 0ad7da800e..59864f4162 100644 --- a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -553,4 +553,15 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen costerFail("Some(10)", 1, 1) } + property("byteArrayToLong") { + testMissingCosting("byteArrayToLong(longToByteArray(1L))", + ByteArrayToLong(LongToByteArray(LongConstant(1))) + ) + } + + property("xorOf") { + testMissingCosting("xorOf(Coll[Boolean](true, false))", + XorOf(Seq(TrueLeaf, FalseLeaf)) + ) + } } diff --git a/src/test/scala/sigmastate/serialization/DeserializationResilience.scala b/src/test/scala/sigmastate/serialization/DeserializationResilience.scala index 1381325c18..b47bb2092c 100644 --- a/src/test/scala/sigmastate/serialization/DeserializationResilience.scala +++ b/src/test/scala/sigmastate/serialization/DeserializationResilience.scala @@ -45,6 +45,6 @@ class DeserializationResilience extends SerializationSpecification { property("invalid op code") { an[InvalidOpCode] should be thrownBy - ValueSerializer.deserialize(Array.fill[Byte](1)(255.toByte)) + ValueSerializer.deserialize(Array.fill[Byte](1)(117.toByte)) } } diff --git a/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala b/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala index 3c12ce86fa..f8952055b3 100644 --- a/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala +++ b/src/test/scala/sigmastate/serialization/TransformersSerializationSpec.scala @@ -180,4 +180,10 @@ class TransformersSerializationSpec extends SerializationSpecification { property("ByteArrayToLong: Serializer round trip") { forAll(byteArrayToLongGen) { roundTripTest(_) } } + + property("XorOf: Serializer round trip") { + forAll(logicalExprTreeNodeGen(Seq(XorOf.apply))) { tree => + roundTripTest(tree) + } + } } diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index c01d038544..c0e80dc72d 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -120,4 +120,14 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma eq(Builder.DefaultCollBuilder.fromArray(x)) } } + + ignore("xorOf equivalence") { + // TODO enable after SigmaDslBuilder.xorOf is implemented (+ uncomment in RuntimeCosting) + val eq = checkEq(func[Coll[Boolean], Boolean]("{ (x: Coll[Boolean]) => xorOf(x) }")) { x => + x.toArray.distinct.length == 2 + } + forAll { x: Array[Boolean] => + eq(Builder.DefaultCollBuilder.fromArray(x)) + } + } } From 60fde65f00a2c9555210ecfa0e7c11539823e499 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 20 Feb 2019 09:33:12 +0200 Subject: [PATCH 03/30] add (de)serialization roundtrip into compilation pipeline in tests; --- .../helpers/SigmaTestingCommons.scala | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index f24b8a585e..f8ba518783 100644 --- a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -3,6 +3,7 @@ package sigmastate.helpers import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} import org.ergoplatform.ErgoScriptPredef.TrueProp +import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} import org.ergoplatform.{ErgoBox, ErgoLikeContext} import org.scalacheck.Arbitrary.arbByte import org.scalacheck.Gen @@ -16,9 +17,10 @@ import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, GroupElementConsta import sigmastate.eval.{CompiletimeCosting, Evaluation, IRContext} import sigmastate.interpreter.Interpreter.{ScriptEnv, ScriptNameProp} import sigmastate.interpreter.{CryptoConstants, Interpreter} +import sigmastate.interpreter.Interpreter.{ScriptEnv, ScriptNameProp} +import sigmastate.{SBoolean, SGroupElement, SType} import sigmastate.lang.{SigmaCompiler, TransformingSigmaBuilder} -import sigmastate.serialization.SigmaSerializer -import sigmastate.{SGroupElement, SType} +import sigmastate.serialization.{ErgoTreeSerializer, SigmaSerializer} import spire.util.Opt import scala.annotation.tailrec @@ -41,14 +43,24 @@ trait SigmaTestingCommons extends PropSpec val compiler = SigmaCompiler(TestnetNetworkPrefix, TransformingSigmaBuilder) + def checkSerializationRoundTrip(v: SValue): Unit = { + val compiledTreeBytes = ErgoTreeSerializer.DefaultSerializer.serializeWithSegregation(v) + withClue(s"(De)Serialization roundtrip failed for the tree:") { + ErgoTreeSerializer.DefaultSerializer.deserialize(compiledTreeBytes) shouldEqual v + } + } + def compile(env: ScriptEnv, code: String): Value[SType] = { - compiler.compile(env, code) + val tree = compiler.compile(env, code) + checkSerializationRoundTrip(tree) + tree } def compileWithCosting(env: ScriptEnv, code: String)(implicit IR: IRContext): Value[SType] = { val interProp = compiler.typecheck(env, code) val IR.Pair(calcF, _) = IR.doCosting(env, interProp) val tree = IR.buildTree(calcF) + checkSerializationRoundTrip(tree) tree } @@ -89,6 +101,8 @@ trait SigmaTestingCommons extends PropSpec val env = Interpreter.emptyEnv val interProp = compiler.typecheck(env, code) val IR.Pair(calcF, _) = IR.doCosting(env, interProp) + val tree = IR.buildTree(calcF) + checkSerializationRoundTrip(tree) val valueFun = IR.compile[tpeB.type](IR.getDataEnv, IR.asRep[IR.Context => tpeB.WrappedType](calcF)) (in: A) => { From 6f3f4e42e94df15a21a1c326d7333c5e4ed256a9 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 20 Feb 2019 11:44:23 +0200 Subject: [PATCH 04/30] fix numeric casts test (got reduced to constants); --- .../sigmastate/TestingInterpreterSpecification.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index 28cc126649..b3f4ad2c49 100644 --- a/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -203,10 +203,10 @@ class TestingInterpreterSpecification extends SigmaTestingCommons { property("Evaluate numeric casting ops") { def testWithCasting(castSuffix: String): Unit = { - testEval(s"Coll(1).size.toByte.$castSuffix == 1.$castSuffix") - testEval(s"Coll(1).size.toShort.$castSuffix == 1.$castSuffix") - testEval(s"Coll(1).size.toInt.$castSuffix == 1.$castSuffix") - testEval(s"Coll(1).size.toLong.$castSuffix == 1.$castSuffix") + testEval(s"OUTPUTS.size.toByte.$castSuffix == 0.$castSuffix") + testEval(s"OUTPUTS.size.toShort.$castSuffix == 0.$castSuffix") + testEval(s"OUTPUTS.size.toInt.$castSuffix == 0.$castSuffix") + testEval(s"OUTPUTS.size.toLong.$castSuffix == 0.$castSuffix") } testWithCasting("toByte") testWithCasting("toShort") From 9cc74ba83505d81468916e76ee51c3ac9fc96d90 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Wed, 20 Feb 2019 19:43:36 +0200 Subject: [PATCH 05/30] add MethodCall.typeSubst (map of concrete types for each generic type parameter); --- .../scala/sigmastate/eval/TreeBuilding.scala | 13 +- .../scala/sigmastate/lang/SigmaBuilder.scala | 17 +- .../scala/sigmastate/lang/SigmaTyper.scala | 21 ++- src/main/scala/sigmastate/lang/Terms.scala | 17 +- .../serialization/MethodCallSerializer.scala | 13 +- src/main/scala/sigmastate/types.scala | 17 +- .../sigmastate/lang/SigmaCompilerTest.scala | 146 +++++++++--------- .../MethodCallSerializerSpecification.scala | 12 +- .../CollectionOperationsSpecification.scala | 62 ++++---- 9 files changed, 171 insertions(+), 147 deletions(-) diff --git a/src/main/scala/sigmastate/eval/TreeBuilding.scala b/src/main/scala/sigmastate/eval/TreeBuilding.scala index 7cece04037..0488b76bd5 100644 --- a/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -24,7 +24,7 @@ import org.bouncycastle.math.ec.ECPoint import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.ProveDHTuple import sigmastate.interpreter.CryptoConstants.EcPointType -import sigmastate.lang.SigmaBuilder +import sigmastate.lang.{SigmaBuilder, SigmaTyper} trait TreeBuilding extends RuntimeCosting { IR: Evaluation => import Liftables._ @@ -237,13 +237,14 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => val args = argsSyms.map(_.asInstanceOf[Sym]).map(recurse) val col = recurse(colSym) val colTpe = elemToSType(colSym.elem).asCollection - val method = ((SCollection.methods.find(_.name == m.getName), args) match { + val (method, typeSubst) = (SCollection.methods.find(_.name == m.getName), args) match { case (Some(mth @ SCollection.FlatMapMethod), Seq(f)) => - mth.withConcreteTypes(Map(SCollection.tOV -> f.asFunc.tpe.tRange.asCollection.elemType)) - case (Some(mth), _) => mth + val typeSubst = Map(SCollection.tOV -> f.asFunc.tpe.tRange.asCollection.elemType) + (mth, typeSubst) + case (Some(mth), _) => (mth, SigmaTyper.emptySubst) case (None, _) => error(s"unknown method Coll.${m.getName}") - }).withConcreteTypes(Map(SCollection.tIV -> colTpe.elemType)) - builder.mkMethodCall(col, method, args.toIndexedSeq) + } + builder.mkMethodCall(col, method, args.toIndexedSeq, typeSubst + (SCollection.tIV -> colTpe.elemType)) case BoxM.value(box) => mkExtractAmount(recurse[SBox.type](box)) diff --git a/src/main/scala/sigmastate/lang/SigmaBuilder.scala b/src/main/scala/sigmastate/lang/SigmaBuilder.scala index 9e3838a13c..691a666d92 100644 --- a/src/main/scala/sigmastate/lang/SigmaBuilder.scala +++ b/src/main/scala/sigmastate/lang/SigmaBuilder.scala @@ -5,12 +5,12 @@ import java.math.BigInteger import org.ergoplatform.ErgoBox import org.ergoplatform.ErgoBox.RegisterId import sigmastate.SCollection.SByteArray -import sigmastate.Values.{StringConstant, FuncValue, FalseLeaf, Constant, SValue, TrueLeaf, BlockValue, ConstantNode, SomeValue, ConstantPlaceholder, BigIntValue, BoolValue, Value, SigmaPropValue, Tuple, GroupElementValue, TaggedVariableNode, SigmaBoolean, BlockItem, UnitConstant, ValUse, TaggedVariable, ConcreteCollection, NoneValue} +import sigmastate.Values.{BigIntValue, BlockItem, BlockValue, BoolValue, ConcreteCollection, Constant, ConstantNode, ConstantPlaceholder, FalseLeaf, FuncValue, GroupElementValue, NoneValue, SValue, SigmaBoolean, SigmaPropValue, SomeValue, StringConstant, TaggedVariable, TaggedVariableNode, TrueLeaf, Tuple, UnitConstant, ValUse, Value} import sigmastate._ import sigmastate.interpreter.CryptoConstants -import sigmastate.lang.Constraints.{TypeConstraint2, sameType2, onlyNumeric2} +import sigmastate.lang.Constraints.{TypeConstraint2, onlyNumeric2, sameType2} import sigmastate.basics.DLogProtocol.ProveDlog -import sigmastate.lang.Constraints.{TypeConstraint2, sameType2, onlyNumeric2} +import sigmastate.lang.Constraints.{TypeConstraint2, onlyNumeric2, sameType2} import sigmastate.lang.Terms._ import sigmastate.lang.exceptions.ConstraintFailed import sigmastate.serialization.OpCodes @@ -19,6 +19,7 @@ import scalan.Nullable import sigmastate.basics.ProveDHTuple import sigmastate.eval.CostingSigmaDslBuilder import sigmastate.interpreter.CryptoConstants.EcPointType +import sigmastate.lang.SigmaTyper.STypeSubst import special.sigma.{GroupElement, SigmaProp} import scala.util.DynamicVariable @@ -176,8 +177,9 @@ trait SigmaBuilder { args: IndexedSeq[Value[SType]], tpe: SType = NoType): Value[SType] def mkMethodCall(obj: Value[SType], - method: SMethod, - args: IndexedSeq[Value[SType]]): Value[SType] + method: SMethod, + args: IndexedSeq[Value[SType]], + typeSubst: STypeSubst): Value[SType] def mkLambda(args: IndexedSeq[(String,SType)], givenResType: SType, body: Option[Value[SType]]): Value[SFunc] @@ -544,8 +546,9 @@ class StdSigmaBuilder extends SigmaBuilder { override def mkMethodCall(obj: Value[SType], method: SMethod, - args: IndexedSeq[Value[SType]]): Value[SType] = - MethodCall(obj, method, args).withSrcCtx(currentSrcCtx.value) + args: IndexedSeq[Value[SType]], + typeSubst: STypeSubst = Map()): Value[SType] = + MethodCall(obj, method, args, typeSubst).withSrcCtx(currentSrcCtx.value) override def mkLambda(args: IndexedSeq[(String, SType)], givenResType: SType, diff --git a/src/main/scala/sigmastate/lang/SigmaTyper.scala b/src/main/scala/sigmastate/lang/SigmaTyper.scala index 78dc9adeb2..bd8107d750 100644 --- a/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/src/main/scala/sigmastate/lang/SigmaTyper.scala @@ -77,8 +77,8 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type with methods ${s.methods}", obj.sourceContext.toOption) s.method(n) match { case Some(method) if method.irBuilder.isDefined && !method.stype.isFunc => - method.irBuilder.flatMap(_.lift(builder, newObj, method, IndexedSeq())) - .getOrElse(mkMethodCall(newObj, method, IndexedSeq())) + method.irBuilder.flatMap(_.lift(builder, newObj, method, IndexedSeq(), emptySubst)) + .getOrElse(mkMethodCall(newObj, method, IndexedSeq(), emptySubst)) case _ => mkSelect(newObj, n, Some(tRes)) } @@ -117,9 +117,8 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe if (expectedArgs.length != newArgTypes.length || !expectedArgs.zip(newArgTypes).forall { case (ea, na) => ea == SAny || ea == na }) error(s"For method $n expected args: $expectedArgs; actual: $newArgTypes", sel.sourceContext) - val methodConcrType = method.withSType(concrFunTpe) - methodConcrType.irBuilder.flatMap(_.lift(builder, newObj, methodConcrType, newArgs)) - .getOrElse(mkMethodCall(newObj, methodConcrType, newArgs)) + method.irBuilder.flatMap(_.lift(builder, newObj, method, newArgs, subst)) + .getOrElse(mkMethodCall(newObj, method, newArgs, subst)) case _ => val newSelect = mkSelect(newObj, n, Some(concrFunTpe)).withSrcCtx(sel.sourceContext) mkApply(newSelect, newArgs) @@ -205,25 +204,25 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe else error(s"Invalid argument type for $m, expected $tColl but was ${r.tpe}", r.sourceContext) case (SCollection(method), _) => - val methodConcrType = method.stype match { + val typeSubst = method.stype match { case sfunc @ SFunc(_, _, _) => val newArgsTypes = newArgs.map(_.tpe) val actualTypes = newObj.tpe +: newArgsTypes unifyTypeLists(sfunc.tDom, actualTypes) match { case Some(subst) => val concrFunTpe = applySubst(sfunc, subst) - val newMethod = method.withSType(concrFunTpe) + val newMethod = method.copy(stype = concrFunTpe) val concrFunArgsTypes = concrFunTpe.asFunc.tDom.tail if (newArgsTypes != concrFunArgsTypes) error(s"Invalid method $newMethod argument type: expected $concrFunArgsTypes; actual: $newArgsTypes", mc.sourceContext) - newMethod + subst case None => error(s"Invalid argument type of method call $mc : expected ${sfunc.tDom}; actual: $actualTypes", mc.sourceContext) } - case _ => method + case _ => emptySubst } - methodConcrType.irBuilder.flatMap(_.lift(builder, newObj, methodConcrType, newArgs)) - .getOrElse(mkMethodCall(newObj, methodConcrType, newArgs)) + method.irBuilder.flatMap(_.lift(builder, newObj, method, newArgs, typeSubst)) + .getOrElse(mkMethodCall(newObj, method, newArgs, typeSubst)) case _ => throw new NonApplicableMethod(s"Unknown symbol $m, which is used as operation with arguments $newObj and $newArgs", mc.sourceContext.toOption) diff --git a/src/main/scala/sigmastate/lang/Terms.scala b/src/main/scala/sigmastate/lang/Terms.scala index a9dcd20842..b70308dfea 100644 --- a/src/main/scala/sigmastate/lang/Terms.scala +++ b/src/main/scala/sigmastate/lang/Terms.scala @@ -6,11 +6,12 @@ import sigmastate.SCollection.SByteArray import sigmastate.Values._ import sigmastate.utils.Overloading.Overload1 import sigmastate._ +import sigmastate.lang.SigmaTyper.STypeSubst import sigmastate.serialization.OpCodes import sigmastate.serialization.OpCodes.OpCode import sigmastate.lang.TransformingSigmaBuilder._ import sigmastate.utxo.CostTable.Cost -import sigmastate.utxo.{ExtractRegisterAs, Slice, SigmaPropIsProven} +import sigmastate.utxo.{ExtractRegisterAs, SigmaPropIsProven, Slice} import special.sigma.{AnyValue, TestValue} object Terms { @@ -126,12 +127,20 @@ object Terms { override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) } - /** Represents in ErgoTree an invocation of method of the object `obj` with arguments `args`.*/ - case class MethodCall(obj: Value[SType], method: SMethod, args: IndexedSeq[Value[SType]]) extends Value[SType] { + /** Represents in ErgoTree an invocation of method of the object `obj` with arguments `args`. + * @param obj object on which method will be invoked + * @param method method to be invoked + * @param args arguments passed to the method on invocation + * @param typeSubst a map of concrete type for each generic type parameter + */ + case class MethodCall(obj: Value[SType], + method: SMethod, + args: IndexedSeq[Value[SType]], + typeSubst: Map[STypeIdent, SType]) extends Value[SType] { override val opCode: OpCode = if (args.isEmpty) OpCodes.PropertyCallCode else OpCodes.MethodCallCode override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) override val tpe: SType = method.stype match { - case f: SFunc => f.tRange + case f: SFunc => f.tRange.withSubstTypes(typeSubst) case t => t } } diff --git a/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala b/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala index 36747e4a1b..18a05c0f33 100644 --- a/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala +++ b/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala @@ -2,10 +2,11 @@ package sigmastate.serialization import sigmastate.Values._ import sigmastate._ +import sigmastate.lang.SigmaTyper.STypeSubst import sigmastate.lang.Terms.MethodCall import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} -case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, IndexedSeq[Value[SType]]) => Value[SType]) +case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, IndexedSeq[Value[SType]], STypeSubst) => Value[SType]) extends ValueSerializer[MethodCall] { override def serialize(mc: MethodCall, w: SigmaByteWriter): Unit = { @@ -16,6 +17,8 @@ case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, Inde assert(mc.args.nonEmpty) w.putValues(mc.args) } + w.putUByte(mc.typeSubst.size) + mc.typeSubst.foreach { case (typeIdent, tpe) => w.putType(typeIdent).putType(tpe) } } override def parse(r: SigmaByteReader): Value[SType] = { @@ -27,6 +30,12 @@ case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, Inde } else IndexedSeq() val method = MethodCall.fromIds(typeId, methodId) - cons(obj, method, args) + val typeSubstSize = r.getUByte() + val xs = new Array[(STypeIdent, SType)](typeSubstSize) + for (i <- 0 until typeSubstSize) { + xs(i) = (r.getType().asInstanceOf[STypeIdent], r.getType()) + } + val typeSubst = xs.toMap + cons(obj, method, args, typeSubst) } } diff --git a/src/main/scala/sigmastate/types.scala b/src/main/scala/sigmastate/types.scala index 8ca5199b44..5ee4163b6b 100644 --- a/src/main/scala/sigmastate/types.scala +++ b/src/main/scala/sigmastate/types.scala @@ -26,8 +26,9 @@ import sigmastate.SByte.typeCode import sigmastate.SMethod.MethodCallIrBuilder import sigmastate.basics.DLogProtocol.ProveDlog import sigmastate.basics.ProveDHTuple +import sigmastate.lang.SigmaTyper.STypeSubst import sigmastate.utxo.ByIndex -import special.sigma.{Box, AvlTree, SigmaProp, wrapperType} +import special.sigma.{AvlTree, Box, SigmaProp, wrapperType} //import sigmastate.SNumericType._ import sigmastate.SSigmaProp.{IsProven, PropBytes} @@ -240,18 +241,14 @@ case class SMethod(objType: STypeCompanion, name: String, stype: SType, methodId: Byte, - irBuilder: Option[PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue]), SValue]]) { - - def withSType(newSType: SType): SMethod = copy(stype = newSType) - - def withConcreteTypes(subst: Map[STypeIdent, SType]): SMethod = - withSType(stype.withSubstTypes(subst)) + irBuilder: Option[PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue]]) { } object SMethod { - val MethodCallIrBuilder: Option[PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue]), SValue]] = Some { - case (builder, obj, method, args) => builder.mkMethodCall(obj, method, args.toIndexedSeq) + val MethodCallIrBuilder: Option[PartialFunction[(SigmaBuilder, SValue, SMethod, Seq[SValue], STypeSubst), SValue]] = Some { + case (builder, obj, method, args, tparamSubst) => + builder.mkMethodCall(obj, method, args.toIndexedSeq, tparamSubst) } def apply(objType: STypeCompanion, name: String, stype: SType, methodId: Byte): SMethod = @@ -693,7 +690,7 @@ object SCollection extends STypeCompanion with MethodByNameUnapply { val tV = STypeIdent("V") val SizeMethod = SMethod(this, "size", SInt, 1) val GetOrElseMethod = SMethod(this, "getOrElse", SFunc(IndexedSeq(SCollection(tIV), SInt, tIV), tIV, Seq(STypeParam(tIV))), 2, Some { - case (builder, obj, method, Seq(index, defaultValue)) => + case (builder, obj, _, Seq(index, defaultValue), _) => val index1 = index.asValue[SInt.type] val defaultValue1 = defaultValue.asValue[SType] builder.mkByIndex(obj.asValue[SCollection[SType]], index1, Some(defaultValue1)) diff --git a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 59864f4162..41bd4278a1 100644 --- a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -208,9 +208,9 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen testMissingCosting("Coll(1,2) << 2", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.BitShiftLeftMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(IntConstant(2)) - ) + SCollection.BitShiftLeftMethod, + Vector(IntConstant(2)), + Map(SCollection.tIV -> SInt)) ) } @@ -218,9 +218,9 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen testMissingCosting("Coll(1,2) >> 2", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.BitShiftRightMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(IntConstant(2)) - ) + SCollection.BitShiftRightMethod, + Vector(IntConstant(2)), + Map(SCollection.tIV -> SInt)) ) } @@ -239,16 +239,17 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen mkMethodCall( ConcreteCollection(TrueLeaf, FalseLeaf), SCollection.IndicesMethod, - Vector() + Vector(), + Map(SCollection.tIV -> SBoolean) ) } property("SCollection.flatMap") { comp("OUTPUTS.flatMap({ (out: Box) => Coll(out.value >= 1L) })") shouldBe mkMethodCall(Outputs, - SCollection.FlatMapMethod.withConcreteTypes(Map(SCollection.tIV -> SBox, SCollection.tOV -> SBoolean)), + SCollection.FlatMapMethod, Vector(FuncValue(1,SBox, - ConcreteCollection(Vector(GE(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), SBoolean)))) + ConcreteCollection(Vector(GE(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), SBoolean))), Map(SCollection.tIV -> SBox, SCollection.tOV -> SBoolean)) } property("SNumeric.toBytes") { @@ -274,7 +275,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen property("SOption.toColl") { testMissingCosting("getVar[Int](1).toColl", mkMethodCall(GetVarInt(1), - SOption.ToCollMethod.withConcreteTypes(Map(SOption.tT -> SInt)), IndexedSeq())) + SOption.ToCollMethod, IndexedSeq(), Map(SOption.tT -> SInt))) } property("SAvlTree.digest") { @@ -292,112 +293,110 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen ignore("SOption.map") { testMissingCosting("getVar[Int](1).map({(i: Int) => i + 1})", mkMethodCall(GetVarInt(1), - SOption.MapMethod.withConcreteTypes(Map(SOption.tT -> SInt, SOption.tR -> SInt)), + SOption.MapMethod, IndexedSeq(Terms.Lambda( Vector(("i", SInt)), SInt, - Some(Plus(Ident("i", SInt).asIntValue, IntConstant(1))))) - ) + Some(Plus(Ident("i", SInt).asIntValue, IntConstant(1))))), Map(SOption.tT -> SInt, SOption.tR -> SInt)) ) } ignore("SOption.filter") { testMissingCosting("getVar[Int](1).filter({(i: Int) => i > 0})", mkMethodCall(GetVarInt(1), - SOption.FilterMethod.withConcreteTypes(Map(SOption.tT -> SInt)), + SOption.FilterMethod, IndexedSeq(Terms.Lambda( Vector(("i", SInt)), SBoolean, - Some(GT(Ident("i", SInt).asIntValue, IntConstant(0))))) - ) + Some(GT(Ident("i", SInt).asIntValue, IntConstant(0))))), Map(SOption.tT -> SInt)) ) } property("SOption.flatMap") { testMissingCosting("getVar[Int](1).flatMap({(i: Int) => getVar[Int](2)})", mkMethodCall(GetVarInt(1), - SOption.FlatMapMethod.withConcreteTypes(Map(SOption.tT -> SInt, SOption.tR -> SInt)), + SOption.FlatMapMethod, IndexedSeq(Terms.Lambda( Vector(("i", SInt)), SOption(SInt), - Some(GetVarInt(2)))) - ) + Some(GetVarInt(2)))), + Map(SOption.tT -> SInt, SOption.tR -> SInt)) ) } property("SCollection.segmentLength") { comp("OUTPUTS.segmentLength({ (out: Box) => out.value >= 1L }, 0)") shouldBe mkMethodCall(Outputs, - SCollection.SegmentLengthMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SCollection.SegmentLengthMethod, Vector( FuncValue( Vector((1, SBox)), GE(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), IntConstant(0) - ) - ) + ), + Map(SCollection.tIV -> SBox)) } property("SCollection.indexWhere") { comp("OUTPUTS.indexWhere({ (out: Box) => out.value >= 1L }, 0)") shouldBe mkMethodCall(Outputs, - SCollection.IndexWhereMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SCollection.IndexWhereMethod, Vector( FuncValue( Vector((1, SBox)), GE(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), IntConstant(0) - ) - ) + ), + Map(SCollection.tIV -> SBox)) } property("SCollection.lastIndexWhere") { comp("OUTPUTS.lastIndexWhere({ (out: Box) => out.value >= 1L }, 1)") shouldBe mkMethodCall(Outputs, - SCollection.LastIndexWhereMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SCollection.LastIndexWhereMethod, Vector( FuncValue( Vector((1, SBox)), GE(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), IntConstant(1) - ) - ) + ), + Map(SCollection.tIV -> SBox)) } property("SCollection.patch") { comp("Coll(1, 2).patch(1, Coll(3), 1)") shouldBe mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.PatchMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(IntConstant(1), ConcreteCollection(IntConstant(3)), IntConstant(1)) - ) + SCollection.PatchMethod, + Vector(IntConstant(1), ConcreteCollection(IntConstant(3)), IntConstant(1)), + Map(SCollection.tIV -> SInt)) } property("SCollection.updated") { comp("Coll(1, 2).updated(1, 1)") shouldBe mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.UpdatedMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(IntConstant(1), IntConstant(1)) - ) + SCollection.UpdatedMethod, + Vector(IntConstant(1), IntConstant(1)), + Map(SCollection.tIV -> SInt)) } property("SCollection.updateMany") { comp("Coll(1, 2).updateMany(Coll(1), Coll(3))") shouldBe mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.UpdateManyMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(ConcreteCollection(IntConstant(1)), ConcreteCollection(IntConstant(3))) - ) + SCollection.UpdateManyMethod, + Vector(ConcreteCollection(IntConstant(1)), ConcreteCollection(IntConstant(3))), + Map(SCollection.tIV -> SInt)) } property("SCollection.unionSets") { testMissingCosting("Coll(1, 2).unionSets(Coll(1))", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.UnionSetsMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(ConcreteCollection(IntConstant(1))) - ) + SCollection.UnionSetsMethod, + Vector(ConcreteCollection(IntConstant(1))), + Map(SCollection.tIV -> SInt)) ) } @@ -405,9 +404,9 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen testMissingCosting("Coll(1, 2).diff(Coll(1))", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.DiffMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(ConcreteCollection(IntConstant(1))) - ) + SCollection.DiffMethod, + Vector(ConcreteCollection(IntConstant(1))), + Map(SCollection.tIV -> SInt)) ) } @@ -415,23 +414,23 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen testMissingCosting("Coll(1, 2).intersect(Coll(1))", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.IntersectMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(ConcreteCollection(IntConstant(1))) - ) + SCollection.IntersectMethod, + Vector(ConcreteCollection(IntConstant(1))), + Map(SCollection.tIV -> SInt)) ) } property("SCollection.prefixLength") { testMissingCosting("OUTPUTS.prefixLength({ (out: Box) => out.value >= 1L })", mkMethodCall(Outputs, - SCollection.PrefixLengthMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SCollection.PrefixLengthMethod, Vector( Terms.Lambda( Vector(("out",SBox)), SBoolean, Some(GE(ExtractAmount(Ident("out",SBox).asBox),LongConstant(1)))) - ) - ) + ), + Map(SCollection.tIV -> SBox)) ) } @@ -439,32 +438,32 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen comp("Coll(1, 2).indexOf(1, 0)") shouldBe mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.IndexOfMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(IntConstant(1), IntConstant(0)) - ) + SCollection.IndexOfMethod, + Vector(IntConstant(1), IntConstant(0)), + Map(SCollection.tIV -> SInt)) } property("SCollection.lastIndexOf") { testMissingCosting("Coll(1, 2).lastIndexOf(1, 0)", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.LastIndexOfMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(IntConstant(1), IntConstant(0)) - ) + SCollection.LastIndexOfMethod, + Vector(IntConstant(1), IntConstant(0)), + Map(SCollection.tIV -> SInt)) ) } property("SCollection.find") { testMissingCosting("OUTPUTS.find({ (out: Box) => out.value >= 1L })", mkMethodCall(Outputs, - SCollection.FindMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SCollection.FindMethod, Vector( Terms.Lambda( Vector(("out",SBox)), SBoolean, Some(GE(ExtractAmount(Ident("out",SBox).asBox),LongConstant(1)))) - ) - ) + ), + Map(SCollection.tIV -> SBox)) ) } @@ -482,9 +481,9 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen testMissingCosting("Coll(1, 2).startsWith(Coll(1), 1)", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.StartsWithMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(ConcreteCollection(IntConstant(1)), IntConstant(1)) - ) + SCollection.StartsWithMethod, + Vector(ConcreteCollection(IntConstant(1)), IntConstant(1)), + Map(SCollection.tIV -> SInt)) ) } @@ -492,9 +491,9 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen testMissingCosting("Coll(1, 2).endsWith(Coll(1))", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.EndsWithMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(ConcreteCollection(IntConstant(1))) - ) + SCollection.EndsWithMethod, + Vector(ConcreteCollection(IntConstant(1))), + Map(SCollection.tIV -> SInt)) ) } @@ -502,21 +501,21 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen comp("Coll(1, 2).zip(Coll(1, 1))") shouldBe mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.ZipMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), - Vector(ConcreteCollection(IntConstant(1), IntConstant(1))) - ) + SCollection.ZipMethod, + Vector(ConcreteCollection(IntConstant(1), IntConstant(1))), + Map(SCollection.tIV -> SInt)) } property("SCollection.partition") { comp("Coll(1, 2).partition({ (i: Int) => i > 0 })") shouldBe mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.PartitionMethod.withConcreteTypes(Map(SCollection.tIV -> SInt)), + SCollection.PartitionMethod, Vector(FuncValue( Vector((1, SInt)), GT(ValUse(1, SInt), IntConstant(0)) - )) - ) + )), + Map(SCollection.tIV -> SInt)) } property("SCollection.mapReduce") { @@ -524,8 +523,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen "Coll(1, 2).mapReduce({ (i: Int) => (i > 0, i.toLong) }, { (tl: (Long, Long)) => tl._1 + tl._2 })", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), - SCollection.MapReduceMethod.withConcreteTypes(Map( - SCollection.tIV -> SInt, SCollection.tK -> SBoolean, SCollection.tV -> SLong)), + SCollection.MapReduceMethod, Vector( Lambda(List(), Vector(("i", SInt)), @@ -543,8 +541,8 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen SelectField(Ident("tl", STuple(SLong, SLong)).asValue[STuple], 2).asInstanceOf[Value[SLong.type]]) ) ) - ) - ) + ), + Map(SCollection.tIV -> SInt, SCollection.tK -> SBoolean, SCollection.tV -> SLong)) ) } diff --git a/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala b/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala index 3f9802a873..2b285efdb7 100644 --- a/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala +++ b/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala @@ -1,13 +1,19 @@ package sigmastate.serialization -import sigmastate.SNumericType -import sigmastate.Values.IntConstant +import org.ergoplatform.Outputs +import sigmastate.Values.{FuncValue, ValUse} import sigmastate.lang.Terms.MethodCall +import sigmastate.utxo.ExtractScriptBytes +import sigmastate.{SBox, SByte, SCollection} class MethodCallSerializerSpecification extends SerializationSpecification { property("MethodCall deserialization round trip") { - val expr = MethodCall(IntConstant(10), SNumericType.getMethodByName("toByte"), IndexedSeq.empty) + val expr = MethodCall(Outputs, + SCollection.FlatMapMethod, + Vector(FuncValue(1, SBox, ExtractScriptBytes(ValUse(1, SBox)))), + Map(SCollection.tIV -> SBox, SCollection.tOV -> SByte) + ) roundTripTest(expr) } diff --git a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala index 33e6e9bc50..9771b5ca5d 100644 --- a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala +++ b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala @@ -7,6 +7,7 @@ import sigmastate._ import sigmastate.helpers.{ErgoLikeTestProvingInterpreter, SigmaTestingCommons} import sigmastate.lang.Terms._ import org.ergoplatform._ +import sigmastate.SCollection._ import sigmastate.interpreter.Interpreter.{ScriptNameProp, emptyEnv} import sigmastate.serialization.OpCodes._ @@ -440,10 +441,11 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { EQ( ByIndex( MethodCall(Outputs, - SCollection.FlatMapMethod.withConcreteTypes(Map(SCollection.tIV -> SBox, SCollection.tOV -> SByte)), + FlatMapMethod, Vector(FuncValue(1, SBox, ExtractScriptBytes(ValUse(1, SBox)) - )) + )), + Map(tIV -> SBox, tOV -> SByte) ).asCollection[SByte.type], IntConstant(0) ), @@ -456,9 +458,9 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { assertProof("OUTPUTS.map({ (b: Box) => b.value }).indexOf(1L, 0) == 0", EQ( MethodCall(MapCollection(Outputs, FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox)))), - SCollection.IndexOfMethod.withConcreteTypes(Map(SCollection.tIV -> SLong)), - Vector(LongConstant(1), IntConstant(0)) - ), + IndexOfMethod, + Vector(LongConstant(1), IntConstant(0)), + Map(tIV -> SLong)), IntConstant(0) ), IndexedSeq(1L, 1L)) @@ -466,7 +468,7 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ignore("indices") { assertProof("OUTPUTS.indices == Coll(0)", - EQ(MethodCall(Outputs, SCollection.IndicesMethod, Vector()), ConcreteCollection(IntConstant(0))), + EQ(MethodCall(Outputs, IndicesMethod, Vector(), Map()), ConcreteCollection(IntConstant(0))), IndexedSeq(1L, 1L)) } @@ -474,12 +476,12 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { assertProof("OUTPUTS.segmentLength({ (out: Box) => out.value == 1L }, 0) == 1", EQ( MethodCall(Outputs, - SCollection.SegmentLengthMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + SegmentLengthMethod, Vector( FuncValue(Vector((1, SBox)),EQ(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), IntConstant(0) - ) - ), + ), + Map(tIV -> SBox)), IntConstant(1)), IndexedSeq(1L, 2L)) } @@ -488,12 +490,12 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { assertProof("OUTPUTS.indexWhere({ (out: Box) => out.value == 1L }, 0) == 0", EQ( MethodCall(Outputs, - SCollection.IndexWhereMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + IndexWhereMethod, Vector( FuncValue(Vector((1, SBox)), EQ(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), IntConstant(0) - ) - ), + ), + Map(tIV -> SBox)), IntConstant(0)), IndexedSeq(1L, 2L)) } @@ -502,12 +504,12 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { assertProof("OUTPUTS.lastIndexWhere({ (out: Box) => out.value == 1L }, 1) == 0", EQ( MethodCall(Outputs, - SCollection.LastIndexWhereMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + LastIndexWhereMethod, Vector( FuncValue(Vector((1, SBox)), EQ(ExtractAmount(ValUse(1, SBox)), LongConstant(1))), IntConstant(1) - ) - ), + ), + Map(tIV -> SBox)), IntConstant(0)), IndexedSeq(1L, 2L)) } @@ -516,11 +518,11 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { assertProof("OUTPUTS.zip(Coll(1,2)).size == 2", EQ( SizeOf(MethodCall(Outputs, - SCollection.ZipMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + ZipMethod, Vector( ConcreteCollection(IntConstant(1), IntConstant(2)) - ) - ).asCollection[STuple]), + ), + Map(tIV -> SBox)).asCollection[STuple]), IntConstant(2)), IndexedSeq(1L, 2L)) } @@ -531,11 +533,11 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { SizeOf( SelectField( MethodCall(Outputs, - SCollection.PartitionMethod.withConcreteTypes(Map(SCollection.tIV -> SBox)), + PartitionMethod, Vector( FuncValue(Vector((1, SBox)), LT(ExtractAmount(ValUse(1, SBox)), LongConstant(2))) - ) - ).asValue[STuple], + ), + Map(tIV -> SBox)).asValue[STuple], 1 ).asCollection[SType] ), @@ -549,9 +551,9 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ByIndex( MethodCall( MapCollection(Outputs, FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox)))), - SCollection.PatchMethod.withConcreteTypes(Map(SCollection.tIV -> SLong)), - Vector(IntConstant(0), ConcreteCollection(LongConstant(3)), IntConstant(1)) - ).asCollection[SType], + PatchMethod, + Vector(IntConstant(0), ConcreteCollection(LongConstant(3)), IntConstant(1)), + Map(tIV -> SLong)).asCollection[SType], IntConstant(0) ), LongConstant(3)), @@ -564,9 +566,9 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ByIndex( MethodCall( MapCollection(Outputs, FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox)))), - SCollection.UpdatedMethod.withConcreteTypes(Map(SCollection.tIV -> SLong)), - Vector(IntConstant(0), LongConstant(3)) - ).asCollection[SType], + UpdatedMethod, + Vector(IntConstant(0), LongConstant(3)), + Map(tIV -> SLong)).asCollection[SType], IntConstant(0) ), LongConstant(3)), @@ -579,9 +581,9 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ByIndex( MethodCall( MapCollection(Outputs, FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox)))), - SCollection.UpdateManyMethod.withConcreteTypes(Map(SCollection.tIV -> SLong)), - Vector(ConcreteCollection(IntConstant(0)), ConcreteCollection(LongConstant(3))) - ).asCollection[SType], + UpdateManyMethod, + Vector(ConcreteCollection(IntConstant(0)), ConcreteCollection(LongConstant(3))), + Map(tIV -> SLong)).asCollection[SType], IntConstant(0) ), LongConstant(3)), From 699d55c12ccd36b6a1a6e60d3965ed2a3af91b34 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 09:20:02 +0200 Subject: [PATCH 06/30] fix SOption.method to have generic types (don't substitute for SOption.elemType on the fly); --- .../scala/sigmastate/lang/SigmaTyper.scala | 25 ++++++++++++------- src/main/scala/sigmastate/lang/Terms.scala | 2 +- src/main/scala/sigmastate/types.scala | 9 ++----- .../scala/sigmastate/FailingToProveSpec.scala | 2 +- .../helpers/SigmaTestingCommons.scala | 1 - .../sigmastate/lang/SigmaCompilerTest.scala | 20 ++++++++++----- 6 files changed, 34 insertions(+), 25 deletions(-) diff --git a/src/main/scala/sigmastate/lang/SigmaTyper.scala b/src/main/scala/sigmastate/lang/SigmaTyper.scala index bd8107d750..a8d179c337 100644 --- a/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/src/main/scala/sigmastate/lang/SigmaTyper.scala @@ -70,17 +70,24 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe val newObj = assignType(env, obj) newObj.tpe match { case s: SProduct => - val iField = s.methodIndex(n) - val tRes = if (iField != -1) { - s.methods(iField).stype - } else - throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type with methods ${s.methods}", obj.sourceContext.toOption) s.method(n) match { - case Some(method) if method.irBuilder.isDefined && !method.stype.isFunc => - method.irBuilder.flatMap(_.lift(builder, newObj, method, IndexedSeq(), emptySubst)) + case Some(method @ SMethod(SOption, _, stype, _, irBuilder)) if !stype.isFunc => + val optTypeSubst = Map(SOption.tT -> newObj.tpe.asOption.elemType) + val typeSubst = if (applySubst(stype, optTypeSubst) == stype) emptySubst else optTypeSubst + if (irBuilder.isDefined) + irBuilder.flatMap(_.lift(builder, newObj, method, IndexedSeq(), typeSubst)) + .getOrElse(mkMethodCall(newObj, method, IndexedSeq(), typeSubst)) + else + mkSelect(newObj, n, Some(applySubst(stype, typeSubst))) + + case Some(method @ SMethod(_, _, stype, _, irBuilder)) if irBuilder.isDefined && !stype.isFunc => + irBuilder.flatMap(_.lift(builder, newObj, method, IndexedSeq(), emptySubst)) .getOrElse(mkMethodCall(newObj, method, IndexedSeq(), emptySubst)) - case _ => - mkSelect(newObj, n, Some(tRes)) + + case Some(method) => + mkSelect(newObj, n, Some(method.stype)) + case None => + throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type with methods ${s.methods}", obj.sourceContext.toOption) } case t => error(s"Cannot get field '$n' in in the object $obj of non-product type $t", sel.sourceContext) diff --git a/src/main/scala/sigmastate/lang/Terms.scala b/src/main/scala/sigmastate/lang/Terms.scala index b70308dfea..253755ffa0 100644 --- a/src/main/scala/sigmastate/lang/Terms.scala +++ b/src/main/scala/sigmastate/lang/Terms.scala @@ -141,7 +141,7 @@ object Terms { override def opType: SFunc = SFunc(obj.tpe +: args.map(_.tpe), tpe) override val tpe: SType = method.stype match { case f: SFunc => f.tRange.withSubstTypes(typeSubst) - case t => t + case t => t.withSubstTypes(typeSubst) } } object MethodCall { diff --git a/src/main/scala/sigmastate/types.scala b/src/main/scala/sigmastate/types.scala index 5ee4163b6b..065717d9d9 100644 --- a/src/main/scala/sigmastate/types.scala +++ b/src/main/scala/sigmastate/types.scala @@ -111,7 +111,7 @@ object SType { * NOTE: in the current implementation only monomorphic methods are supported (without type parameters)*/ val types: Map[Byte, STypeCompanion] = Seq( SNumericType, SString, STuple, SGroupElement, SSigmaProp, SContext, - SAvlTree, SBox, SOption, SCollection + SAvlTree, SBox, SOption, SCollection, SBigInt, SOption ).map { t => (t.typeId, t) }.toMap implicit class STypeOps(val tpe: SType) extends AnyVal { @@ -574,12 +574,7 @@ case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct { } override def isConstantSize = elemType.isConstantSize def ancestors = Nil - override lazy val methods: Seq[SMethod] = { - val subst = Map(SOption.tT -> elemType) - SOption.methods.map { method => - method.copy(stype = SigmaTyper.applySubst(method.stype, subst)) - } - } + override def methods: Seq[SMethod] = SOption.methods override def toString = s"Option[$elemType]" } diff --git a/src/test/scala/sigmastate/FailingToProveSpec.scala b/src/test/scala/sigmastate/FailingToProveSpec.scala index 327503a487..0040f8c123 100644 --- a/src/test/scala/sigmastate/FailingToProveSpec.scala +++ b/src/test/scala/sigmastate/FailingToProveSpec.scala @@ -53,7 +53,7 @@ class FailingToProveSpec extends SigmaTestingCommons { val verifier = new ErgoLikeTestInterpreter() val env = Map.empty[String, Any] - val compiledScript = compile(env, + val compiledScript = compileWithCosting(env, s""" | { | diff --git a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index f8ba518783..ac4597d7ef 100644 --- a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -52,7 +52,6 @@ trait SigmaTestingCommons extends PropSpec def compile(env: ScriptEnv, code: String): Value[SType] = { val tree = compiler.compile(env, code) - checkSerializationRoundTrip(tree) tree } diff --git a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 41bd4278a1..68b0f62b3b 100644 --- a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -24,10 +24,18 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen private def comp(env: ScriptEnv, x: String): Value[SType] = compileWithCosting(env, x) private def comp(x: String): Value[SType] = compileWithCosting(env, x) private def compWOCosting(x: String): Value[SType] = compile(env, x) - private def compWOCosting(env: ScriptEnv, x: String): Value[SType] = compile(env, x) private def testMissingCosting(script: String, expected: SValue): Unit = { - compWOCosting(script) shouldBe expected + val tree = compWOCosting(script) + tree shouldBe expected + checkSerializationRoundTrip(tree) + // when implemented in coster this should be changed to a positive expectation + an [CosterException] should be thrownBy comp(env, script) + } + + private def testMissingCostingWOSerialization(script: String, expected: SValue): Unit = { + val tree = compWOCosting(script) + tree shouldBe expected // when implemented in coster this should be changed to a positive expectation an [CosterException] should be thrownBy comp(env, script) } @@ -313,7 +321,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("SOption.flatMap") { - testMissingCosting("getVar[Int](1).flatMap({(i: Int) => getVar[Int](2)})", + testMissingCostingWOSerialization("getVar[Int](1).flatMap({(i: Int) => getVar[Int](2)})", mkMethodCall(GetVarInt(1), SOption.FlatMapMethod, IndexedSeq(Terms.Lambda( @@ -421,7 +429,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("SCollection.prefixLength") { - testMissingCosting("OUTPUTS.prefixLength({ (out: Box) => out.value >= 1L })", + testMissingCostingWOSerialization("OUTPUTS.prefixLength({ (out: Box) => out.value >= 1L })", mkMethodCall(Outputs, SCollection.PrefixLengthMethod, Vector( @@ -454,7 +462,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("SCollection.find") { - testMissingCosting("OUTPUTS.find({ (out: Box) => out.value >= 1L })", + testMissingCostingWOSerialization("OUTPUTS.find({ (out: Box) => out.value >= 1L })", mkMethodCall(Outputs, SCollection.FindMethod, Vector( @@ -519,7 +527,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("SCollection.mapReduce") { - testMissingCosting( + testMissingCostingWOSerialization( "Coll(1, 2).mapReduce({ (i: Int) => (i > 0, i.toLong) }, { (tl: (Long, Long)) => tl._1 + tl._2 })", mkMethodCall( ConcreteCollection(IntConstant(1), IntConstant(2)), From 38995082f6e2f764c9d5fbf01d03c7dbff135496 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 11:48:41 +0200 Subject: [PATCH 07/30] fix build after rebase; --- src/main/scala/sigmastate/eval/RuntimeCosting.scala | 4 ++-- .../scala/sigmastate/helpers/SigmaTestingCommons.scala | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index 8afe266e7f..dcd73968b7 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1569,7 +1569,7 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev val res = sigmaDslBuilder.decodePoint(bytes.values) withDefaultSize(res, costOf(node)) - case Terms.MethodCall(obj, method, args) if obj.tpe.isCollectionLike => + case Terms.MethodCall(obj, method, args, _) if obj.tpe.isCollectionLike => val xsC = asRep[CostedColl[Any]](evalNode(ctx, env, obj)) val (argsVals, argsCosts) = args.map { case sfunc: Value[SFunc]@unchecked if sfunc.tpe.isFunc => @@ -1606,7 +1606,7 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev } withDefaultSize(value, cost) - case Terms.MethodCall(obj, method, args) if obj.tpe.isOption => + case Terms.MethodCall(obj, method, args, _) if obj.tpe.isOption => val optC = asRep[CostedOption[Any]](eval(obj)) val argsC = args.map(eval) (method.name, argsC) match { diff --git a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index ac4597d7ef..412b9aa0c9 100644 --- a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -1,6 +1,7 @@ package sigmastate.helpers import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix +import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoLikeContext, ErgoScriptPredef} import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} import org.ergoplatform.ErgoScriptPredef.TrueProp import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} @@ -11,6 +12,9 @@ import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} import org.scalatest.{Assertion, Matchers, PropSpec} import scalan.{Nullable, RType, TestContexts, TestUtils} import scorex.crypto.hash.Blake2b256 +import scorex.util._ +import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, GroupElementConstant, SValue, TrueLeaf, Value} +import sigmastate.eval.{CompiletimeCosting, Evaluation, IRContext} import scorex.util.serialization.{VLQByteStringReader, VLQByteStringWriter} import sigma.types.{IsPrimView, PrimViewType, View} import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, GroupElementConstant, SValue, Value} @@ -25,6 +29,10 @@ import spire.util.Opt import scala.annotation.tailrec import scala.language.implicitConversions +import scalan.{Nullable, RType, TestContexts, TestUtils} +import sigma.types.{IsPrimView, PrimViewType, View} +import sigmastate.serialization.ErgoTreeSerializer +import spire.util.Opt trait SigmaTestingCommons extends PropSpec with PropertyChecks From 4a2f89b87ac3c012e0fdd844c778e8b967a10560 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 11:52:40 +0200 Subject: [PATCH 08/30] enable tests for Coll.patch, .updated, .updateMany; --- .../utxo/CollectionOperationsSpecification.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala index 9771b5ca5d..bb3b7e2b9b 100644 --- a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala +++ b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala @@ -468,7 +468,7 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { ignore("indices") { assertProof("OUTPUTS.indices == Coll(0)", - EQ(MethodCall(Outputs, IndicesMethod, Vector(), Map()), ConcreteCollection(IntConstant(0))), + EQ(MethodCall(Outputs, IndicesMethod, Vector(), Map(tIV -> SBox)), ConcreteCollection(IntConstant(0))), IndexedSeq(1L, 1L)) } @@ -545,7 +545,7 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { IndexedSeq(1L, 2L)) } - ignore("patch") { + property("patch") { assertProof("OUTPUTS.map({ (b: Box) => b.value }).patch(0, Coll(3L), 1)(0) == 3L", EQ( ByIndex( @@ -560,7 +560,7 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { IndexedSeq(1L, 2L)) } - ignore("updated") { + property("updated") { assertProof("OUTPUTS.map({ (b: Box) => b.value }).updated(0, 3L)(0) == 3L", EQ( ByIndex( @@ -575,7 +575,7 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { IndexedSeq(1L, 2L)) } - ignore("updateMany") { + property("updateMany") { assertProof("OUTPUTS.map({ (b: Box) => b.value }).updateMany(Coll(0), Coll(3L))(0) == 3L", EQ( ByIndex( From cd0d4e857991af789515af629a31bf760e611beb Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 14:19:15 +0200 Subject: [PATCH 09/30] add BitOp serialization; --- .../scala/sigmastate/serialization/ValueSerializer.scala | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/src/main/scala/sigmastate/serialization/ValueSerializer.scala index cd33fc65a3..b989ef1c21 100644 --- a/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/src/main/scala/sigmastate/serialization/ValueSerializer.scala @@ -61,6 +61,12 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { TwoArgumentsSerializer(PlusCode, mkPlus[SNumericType]), TwoArgumentsSerializer(MinCode, mkMin[SNumericType]), TwoArgumentsSerializer(MaxCode, mkMax[SNumericType]), + TwoArgumentsSerializer(BitOrCode, mkBitOr[SNumericType]), + TwoArgumentsSerializer(BitAndCode, mkBitAnd[SNumericType]), + TwoArgumentsSerializer(BitXorCode, mkBitXor[SNumericType]), + TwoArgumentsSerializer(BitShiftLeftCode, mkBitShiftLeft[SNumericType]), + TwoArgumentsSerializer(BitShiftRightCode, mkBitShiftRight[SNumericType]), + TwoArgumentsSerializer(BitShiftRightZeroedCode, mkBitShiftRightZeroed[SNumericType]), CaseObjectSerialization(TrueCode, TrueLeaf), CaseObjectSerialization(FalseCode, FalseLeaf), SigmaPropIsProvenSerializer, From 6fbeb9bbc57cdeea293165a08fe84e0d1d2b9639 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 14:26:02 +0200 Subject: [PATCH 10/30] add BinXor serialization; --- src/main/scala/sigmastate/serialization/ValueSerializer.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/src/main/scala/sigmastate/serialization/ValueSerializer.scala index b989ef1c21..3b9d2bb2e7 100644 --- a/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/src/main/scala/sigmastate/serialization/ValueSerializer.scala @@ -50,6 +50,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { QuadrupleSerializer(TreeModificationsCode, mkTreeModifications), Relation2Serializer(BinOrCode, mkBinOr), Relation2Serializer(BinAndCode, mkBinAnd), + Relation2Serializer(BinXorCode, mkBinXor), QuadrupleSerializer[SBoolean.type, SLong.type, SLong.type, SLong.type](IfCode, mkIf), TwoArgumentsSerializer(XorCode, mkXor), TwoArgumentsSerializer(ExponentiateCode, mkExponentiate), From a8625a08baa8ef775474cfa711e263bb1315e7df Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 14:59:10 +0200 Subject: [PATCH 11/30] add LogicalNotSerializer; add OneArgumentOperationSerializer (Negation, BitInversion); --- .../serialization/LogicalNotSerializer.scala | 14 ++++++++++++++ .../OneArgumentOperationSerializer.scala | 13 +++++++++++++ .../sigmastate/serialization/ValueSerializer.scala | 5 ++++- src/main/scala/sigmastate/trees.scala | 11 +++++++---- 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 src/main/scala/sigmastate/serialization/LogicalNotSerializer.scala create mode 100644 src/main/scala/sigmastate/serialization/OneArgumentOperationSerializer.scala diff --git a/src/main/scala/sigmastate/serialization/LogicalNotSerializer.scala b/src/main/scala/sigmastate/serialization/LogicalNotSerializer.scala new file mode 100644 index 0000000000..01deb7af4c --- /dev/null +++ b/src/main/scala/sigmastate/serialization/LogicalNotSerializer.scala @@ -0,0 +1,14 @@ +package sigmastate.serialization + +import sigmastate.LogicalNot +import sigmastate.Values.BoolValue +import sigmastate.lang.Terms._ +import sigmastate.serialization.OpCodes.OpCode +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} + +case class LogicalNotSerializer(cons: BoolValue => BoolValue) + extends ValueSerializer[LogicalNot] { + override val opCode: OpCode = OpCodes.LogicalNotCode + override def serialize(obj: LogicalNot, w: SigmaByteWriter): Unit = w.putValue(obj.input) + override def parse(r: SigmaByteReader): BoolValue = cons(r.getValue().asBoolValue) +} diff --git a/src/main/scala/sigmastate/serialization/OneArgumentOperationSerializer.scala b/src/main/scala/sigmastate/serialization/OneArgumentOperationSerializer.scala new file mode 100644 index 0000000000..6cf9393926 --- /dev/null +++ b/src/main/scala/sigmastate/serialization/OneArgumentOperationSerializer.scala @@ -0,0 +1,13 @@ +package sigmastate.serialization + +import sigmastate.Values.{SValue, Value} +import sigmastate.lang.Terms._ +import sigmastate.serialization.OpCodes.OpCode +import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} +import sigmastate.{OneArgumentOperation, SNumericType, SType} + +case class OneArgumentOperationSerializer[T <: SType](opCode: OpCode, cons: Value[T] => SValue) + extends ValueSerializer[OneArgumentOperation[T, SType]] { + override def serialize(obj: OneArgumentOperation[T, SType], w: SigmaByteWriter): Unit = w.putValue(obj.input) + override def parse(r: SigmaByteReader): SValue = cons(r.getValue().asValue[T]) +} diff --git a/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/src/main/scala/sigmastate/serialization/ValueSerializer.scala index 3b9d2bb2e7..55ba97e83b 100644 --- a/src/main/scala/sigmastate/serialization/ValueSerializer.scala +++ b/src/main/scala/sigmastate/serialization/ValueSerializer.scala @@ -132,7 +132,10 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] { ModQArithOpSerializer(MinusModQCode, mkMinusModQ), SubstConstantsSerializer, CreateProveDlogSerializer(mkCreateProveDlog), - CreateProveDHTupleSerializer(mkCreateProveDHTuple) + CreateProveDHTupleSerializer(mkCreateProveDHTuple), + LogicalNotSerializer(mkLogicalNot), + OneArgumentOperationSerializer(NegationCode, mkNegation[SNumericType]), + OneArgumentOperationSerializer(BitInversionCode, mkBitInversion[SNumericType]), )) private def serializable(v: Value[SType]): Value[SType] = v match { diff --git a/src/main/scala/sigmastate/trees.scala b/src/main/scala/sigmastate/trees.scala index 4184bfc708..1bfb376102 100644 --- a/src/main/scala/sigmastate/trees.scala +++ b/src/main/scala/sigmastate/trees.scala @@ -406,6 +406,11 @@ sealed trait Triple[LIV <: SType, RIV <: SType, OV <: SType] extends NotReadyVal override def opType = SFunc(Vector(left.tpe, right.tpe), tpe) } +sealed trait OneArgumentOperation[IV <: SType, OV <: SType] extends NotReadyValue[OV] { + val input: Value[IV] + override def opType = SFunc(input.tpe, tpe) +} + // TwoArgumentsOperation sealed trait TwoArgumentsOperation[LIV <: SType, RIV <: SType, OV <: SType] extends Triple[LIV, RIV, OV] @@ -439,16 +444,14 @@ object ArithOp { } } -case class Negation[T <: SNumericType](input: Value[T]) extends NotReadyValue[T] { +case class Negation[T <: SNumericType](input: Value[T]) extends OneArgumentOperation[T, T] { override val opCode: OpCode = OpCodes.NegationCode override def tpe: T = input.tpe - override def opType: SFunc = SFunc(input.tpe, tpe) } -case class BitInversion[T <: SNumericType](input: Value[T]) extends NotReadyValue[T] { +case class BitInversion[T <: SNumericType](input: Value[T]) extends OneArgumentOperation[T, T] { override val opCode: OpCode = OpCodes.BitInversionCode override def tpe: T = input.tpe - override def opType: SFunc = SFunc(input.tpe, tpe) } case class BitOp[T <: SNumericType](left: Value[T], right: Value[T], opCode: OpCode) From be490bfabea98b136c96311ec1245b5997260788 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 15:31:49 +0200 Subject: [PATCH 12/30] add SigmaDslBuilder.byteArrayToLong; --- sigma-api/src/main/scala/special/sigma/SigmaDsl.scala | 1 + .../src/main/scala/special/sigma/SigmaDslOverArrays.scala | 2 ++ 2 files changed, 3 insertions(+) diff --git a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala index 08d1d0dc00..285d655162 100644 --- a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala @@ -491,6 +491,7 @@ trait SigmaDslBuilder { def byteArrayToBigInt(bytes: Coll[Byte]): BigInt def longToByteArray(l: Long): Coll[Byte] + def byteArrayToLong(bytes: Coll[Byte]): Long def proveDlog(g: GroupElement): SigmaProp def proveDHTuple(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement): SigmaProp diff --git a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala index f318501b5f..403dd68c1e 100644 --- a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala +++ b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala @@ -98,6 +98,8 @@ class TestSigmaDslBuilder extends SigmaDslBuilder { @NeverInline def longToByteArray(l: Long): Coll[Byte] = Colls.fromArray(Longs.toByteArray(l)) + @NeverInline + def byteArrayToLong(bytes: Coll[Byte]): Long = Longs.fromByteArray(bytes.toArray) @NeverInline def proveDlog(g: GroupElement): SigmaProp = MockProveDlog(true, Colls.emptyColl[Byte]) From 3745d2e6792afcaba7af6a493fdd62006b318375 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Thu, 21 Feb 2019 16:32:50 +0200 Subject: [PATCH 13/30] add SigmaDslBuilder.byteArrayToLong implementation (codegen); implement SigmaDslBuilder.byteArrayToLong in RuntimeCosting and TreeBuilding; --- .../resources/special/sigma/SigmaDsl.scalan | 1 + .../special/sigma/SigmaDslOverArrays.scalan | 1 + .../main/scala/special/sigma/SigmaDsl.scala | 1 + .../special/sigma/SigmaDslOverArrays.scala | 1 + .../special/sigma/impl/SigmaDslImpl.scala | 29 ++++++++++++++++++- .../sigma/impl/SigmaDslOverArraysImpl.scala | 20 +++++++++++++ .../sigmastate/eval/RuntimeCosting.scala | 12 ++++---- .../scala/sigmastate/eval/TreeBuilding.scala | 2 ++ .../scala/sigmastate/utxo/CostTable.scala | 1 + .../scala/special/sigma/SigmaDslTest.scala | 7 +++-- 10 files changed, 65 insertions(+), 10 deletions(-) diff --git a/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan b/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan index 46fe57d2ca..7514bcbe4a 100644 --- a/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan +++ b/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan @@ -195,6 +195,7 @@ package special.sigma { def sha256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]]; def byteArrayToBigInt(bytes: Rep[Coll[Byte]]): Rep[BigInt]; def longToByteArray(l: Rep[Long]): Rep[Coll[Byte]]; + def byteArrayToLong(bytes: Rep[Coll[Byte]]): Rep[Long]; def proveDlog(g: Rep[GroupElement]): Rep[SigmaProp]; def proveDHTuple(g: Rep[GroupElement], h: Rep[GroupElement], u: Rep[GroupElement], v: Rep[GroupElement]): Rep[SigmaProp]; def isMember(tree: Rep[AvlTree], key: Rep[Coll[Byte]], proof: Rep[Coll[Byte]]): Rep[Boolean]; diff --git a/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan b/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan index 3f4bd91ac5..b5961627cd 100644 --- a/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan +++ b/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan @@ -66,6 +66,7 @@ package special.sigma { @NeverInline def PubKey(base64String: Rep[String]): Rep[SigmaProp] = delayInvoke; @NeverInline def byteArrayToBigInt(bytes: Rep[Coll[Byte]]): Rep[BigInt] = delayInvoke; @NeverInline def longToByteArray(l: Rep[Long]): Rep[Coll[Byte]] = delayInvoke; + @NeverInline def byteArrayToLong(bytes: Rep[Coll[Byte]]): Rep[Long] = delayInvoke; @NeverInline def proveDlog(g: Rep[GroupElement]): Rep[SigmaProp] = delayInvoke; @NeverInline def proveDHTuple(g: Rep[GroupElement], h: Rep[GroupElement], u: Rep[GroupElement], v: Rep[GroupElement]): Rep[SigmaProp] = delayInvoke; @NeverInline def isMember(tree: Rep[AvlTree], key: Rep[Coll[Byte]], proof: Rep[Coll[Byte]]): Rep[Boolean] = delayInvoke; diff --git a/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala index 508fcc0eda..6b016610f1 100644 --- a/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala @@ -198,6 +198,7 @@ package special.sigma { def sha256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]]; def byteArrayToBigInt(bytes: Rep[Coll[Byte]]): Rep[BigInt]; def longToByteArray(l: Rep[Long]): Rep[Coll[Byte]]; + def byteArrayToLong(bytes: Rep[Coll[Byte]]): Rep[Long]; def proveDlog(g: Rep[GroupElement]): Rep[SigmaProp]; def proveDHTuple(g: Rep[GroupElement], h: Rep[GroupElement], u: Rep[GroupElement], v: Rep[GroupElement]): Rep[SigmaProp]; def isMember(tree: Rep[AvlTree], key: Rep[Coll[Byte]], proof: Rep[Coll[Byte]]): Rep[Boolean]; diff --git a/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala b/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala index a7f821555d..e9c3de7018 100644 --- a/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala +++ b/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala @@ -73,6 +73,7 @@ package special.sigma { @NeverInline def PubKey(base64String: Rep[String]): Rep[SigmaProp] = delayInvoke; @NeverInline def byteArrayToBigInt(bytes: Rep[Coll[Byte]]): Rep[BigInt] = delayInvoke; @NeverInline def longToByteArray(l: Rep[Long]): Rep[Coll[Byte]] = delayInvoke; + @NeverInline def byteArrayToLong(bytes: Rep[Coll[Byte]]): Rep[Long] = delayInvoke; @NeverInline def proveDlog(g: Rep[GroupElement]): Rep[SigmaProp] = delayInvoke; @NeverInline def proveDHTuple(g: Rep[GroupElement], h: Rep[GroupElement], u: Rep[GroupElement], v: Rep[GroupElement]): Rep[SigmaProp] = delayInvoke; @NeverInline def isMember(tree: Rep[AvlTree], key: Rep[Coll[Byte]], proof: Rep[Coll[Byte]]): Rep[Boolean] = delayInvoke; diff --git a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala index 21b99ad1da..a59bbd6747 100644 --- a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala +++ b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala @@ -4264,6 +4264,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { true, false, element[Coll[Byte]])) } + override def byteArrayToLong(bytes: Rep[Coll[Byte]]): Rep[Long] = { + asRep[Long](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("byteArrayToLong", classOf[Sym]), + List(bytes), + true, false, element[Long])) + } + override def proveDlog(g: Rep[GroupElement]): Rep[SigmaProp] = { asRep[SigmaProp](mkMethodCall(self, SigmaDslBuilderClass.getMethod("proveDlog", classOf[Sym]), @@ -4492,6 +4499,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { true, true, element[Coll[Byte]])) } + def byteArrayToLong(bytes: Rep[Coll[Byte]]): Rep[Long] = { + asRep[Long](mkMethodCall(source, + thisClass.getMethod("byteArrayToLong", classOf[Sym]), + List(bytes), + true, true, element[Long])) + } + def proveDlog(g: Rep[GroupElement]): Rep[SigmaProp] = { asRep[SigmaProp](mkMethodCall(source, thisClass.getMethod("proveDlog", classOf[Sym]), @@ -4579,7 +4593,7 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { override protected def collectMethods: Map[java.lang.reflect.Method, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(classOf[SigmaDslBuilder], classOf[SSigmaDslBuilder], Set( - "Colls", "Monoids", "Costing", "CostModel", "costBoxes", "costColWithConstSizedItem", "costOption", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "PubKey", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "proveDlog", "proveDHTuple", "isMember", "treeLookup", "treeModifications", "groupGenerator", "substConstants", "decodePoint", "BigInt", "toBigInteger" + "Colls", "Monoids", "Costing", "CostModel", "costBoxes", "costColWithConstSizedItem", "costOption", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "PubKey", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "isMember", "treeLookup", "treeModifications", "groupGenerator", "substConstants", "decodePoint", "BigInt", "toBigInteger" )) } @@ -4869,6 +4883,19 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { } } + object byteArrayToLong { + def unapply(d: Def[_]): Nullable[(Rep[SigmaDslBuilder], Rep[Coll[Byte]])] = d match { + case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] && method.getName == "byteArrayToLong" => + val res = (receiver, args(0)) + Nullable(res).asInstanceOf[Nullable[(Rep[SigmaDslBuilder], Rep[Coll[Byte]])]] + case _ => Nullable.None + } + def unapply(exp: Sym): Nullable[(Rep[SigmaDslBuilder], Rep[Coll[Byte]])] = exp match { + case Def(d) => unapply(d) + case _ => Nullable.None + } + } + object proveDlog { def unapply(d: Def[_]): Nullable[(Rep[SigmaDslBuilder], Rep[GroupElement])] = d match { case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] && method.getName == "proveDlog" => diff --git a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala index fded20baec..854d02baaa 100644 --- a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala +++ b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala @@ -314,6 +314,13 @@ object TestSigmaDslBuilder extends EntityObject("TestSigmaDslBuilder") { true, false, element[Coll[Byte]])) } + override def byteArrayToLong(bytes: Rep[Coll[Byte]]): Rep[Long] = { + asRep[Long](mkMethodCall(self, + thisClass.getMethod("byteArrayToLong", classOf[Sym]), + List(bytes), + true, false, element[Long])) + } + override def proveDlog(g: Rep[GroupElement]): Rep[SigmaProp] = { asRep[SigmaProp](mkMethodCall(self, thisClass.getMethod("proveDlog", classOf[Sym]), @@ -740,6 +747,19 @@ object TestSigmaDslBuilder extends EntityObject("TestSigmaDslBuilder") { } } + object byteArrayToLong { + def unapply(d: Def[_]): Nullable[(Rep[TestSigmaDslBuilder], Rep[Coll[Byte]])] = d match { + case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[TestSigmaDslBuilderElem] && method.getName == "byteArrayToLong" => + val res = (receiver, args(0)) + Nullable(res).asInstanceOf[Nullable[(Rep[TestSigmaDslBuilder], Rep[Coll[Byte]])]] + case _ => Nullable.None + } + def unapply(exp: Sym): Nullable[(Rep[TestSigmaDslBuilder], Rep[Coll[Byte]])] = exp match { + case Def(d) => unapply(d) + case _ => Nullable.None + } + } + object proveDlog { def unapply(d: Def[_]): Nullable[(Rep[TestSigmaDslBuilder], Rep[GroupElement])] = d match { case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[TestSigmaDslBuilderElem] && method.getName == "proveDlog" => diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index dcd73968b7..d521c54f10 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1534,12 +1534,12 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev val cost = inputC.cost + costOf(node) withDefaultSize(res, cost) -// case ByteArrayToLong(In(arr)) => -// val arrC = asRep[Costed[Coll[Byte]]](arr) -// val value = sigmaDslBuilder.byteArrayToLong(arrC.value) -// val size = arrC.dataSize -// val cost = arrC.cost + costOf(node) -// RCCostedPrim(value, cost, size) + case ByteArrayToLong(In(arr)) => + val arrC = asRep[Costed[Coll[Byte]]](arr) + val value = sigmaDslBuilder.byteArrayToLong(arrC.value) + val size = arrC.dataSize + val cost = arrC.cost + costOf(node) + RCCostedPrim(value, cost, size) case Xor(InCollByte(l), InCollByte(r)) => val values = colBuilder.xor(l.value, r.value) diff --git a/src/main/scala/sigmastate/eval/TreeBuilding.scala b/src/main/scala/sigmastate/eval/TreeBuilding.scala index 0488b76bd5..899c3d3bf8 100644 --- a/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -337,6 +337,8 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => mkTreeLookup(recurse(treeSym), recurse(keySym), recurse(proofCollSym)) case SDBM.longToByteArray(_, longSym) => mkLongToByteArray(recurse(longSym)) + case SDBM.byteArrayToLong(_, colSym) => + mkByteArrayToLong(recurse(colSym)) case SDBM.decodePoint(_, colSym) => mkDecodePoint(recurse(colSym)) diff --git a/src/main/scala/sigmastate/utxo/CostTable.scala b/src/main/scala/sigmastate/utxo/CostTable.scala index 4ce702c6f4..28af565b5d 100644 --- a/src/main/scala/sigmastate/utxo/CostTable.scala +++ b/src/main/scala/sigmastate/utxo/CostTable.scala @@ -208,6 +208,7 @@ object CostTable { ("TreeLookup_per_kb", "(AvlTree, Coll[Byte], Coll[Byte]) => Option[Coll[Byte]]", hashPerKb * 2), ("LongToByteArray", "(Long) => Coll[Byte]", castOp), + ("ByteArrayToLong", "(Coll[Byte]) => Long", castOp), ("ProveDlogEval", "(Unit) => SigmaProp", groupElementConst + constCost + 2 * expCost + multiplyGroup), diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index c0e80dc72d..df822aa328 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -111,13 +111,14 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma } } - ignore("byteArrayToLong equivalence") { - // TODO enable after SigmaDslBuilder.byteArrayToLong is implemented (+ uncomment in RuntimeCosting) + property("byteArrayToLong equivalence") { val eq = checkEq(func[Coll[Byte],Long]("{ (x: Coll[Byte]) => byteArrayToLong(x) }")){ x => Longs.fromByteArray(x.toArray) } forAll { x: Array[Byte] => - eq(Builder.DefaultCollBuilder.fromArray(x)) + whenever(x.size >= 8) { + eq(Builder.DefaultCollBuilder.fromArray(x)) + } } } From 9668338eeb29aeef3720e57ab2b98b3289ab0dad Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 07:26:08 +0200 Subject: [PATCH 14/30] disable (de)serialization roundtrip for ZProofBlock test; switched byteArrayToLong test to go through costing; --- src/test/scala/sigmastate/lang/SigmaCompilerTest.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 68b0f62b3b..6a75aace43 100644 --- a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -104,7 +104,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("ZKProof") { - testMissingCosting("ZKProof { sigmaProp(HEIGHT > 1000) }", + testMissingCostingWOSerialization("ZKProof { sigmaProp(HEIGHT > 1000) }", ZKProofBlock(BoolToSigmaProp(GT(Height, IntConstant(1000))))) } @@ -560,9 +560,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("byteArrayToLong") { - testMissingCosting("byteArrayToLong(longToByteArray(1L))", - ByteArrayToLong(LongToByteArray(LongConstant(1))) - ) + comp("byteArrayToLong(longToByteArray(1L))") shouldBe ByteArrayToLong(LongToByteArray(LongConstant(1))) } property("xorOf") { From 2f3cd15ea655f214e32d3e917aa9928be7918a62 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 10:04:28 +0200 Subject: [PATCH 15/30] add sigma API for xorOf, logicalNot, negation, logicalXor; --- .../src/main/scala/sigma/types/Types.scala | 19 +++++++++++++ .../main/scala/special/sigma/SigmaDsl.scala | 2 ++ .../src/main/scala/sigma/types/Types.scala | 6 +++++ .../special/sigma/SigmaDslOverArrays.scala | 3 +++ .../scala/special/sigma/SigmaDslTest.scala | 27 ++++++++++++++++++- 5 files changed, 56 insertions(+), 1 deletion(-) diff --git a/sigma-api/src/main/scala/sigma/types/Types.scala b/sigma-api/src/main/scala/sigma/types/Types.scala index ce636f440b..7d573cc626 100644 --- a/sigma-api/src/main/scala/sigma/types/Types.scala +++ b/sigma-api/src/main/scala/sigma/types/Types.scala @@ -13,6 +13,16 @@ trait Boolean extends PrimView[scala.Boolean] { * @since 2.0 */ def toByte: Byte + + /** Logical negation + * @since 2.0 + */ + def not: Boolean + + /** Logical XOR + * @since 2.0 + */ + def xor(y: Boolean): Boolean } trait Byte extends PrimView[scala.Byte] { @@ -20,6 +30,10 @@ trait Byte extends PrimView[scala.Byte] { def toInt: Int // def toLong: Long def + (y: Byte): Byte + /** Negation + * @since 2.0 + */ + def negate : Byte } trait Int extends PrimView[scala.Int] { @@ -48,6 +62,11 @@ trait Int extends PrimView[scala.Int] { * `this` is less than, equal to, or greater than `that`. */ def compareTo(that: Int): Int + + /** Negation + * @since 2.0 + */ + def negate : Int } diff --git a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala index 285d655162..868defa792 100644 --- a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala @@ -482,6 +482,8 @@ trait SigmaDslBuilder { def anyOf(conditions: Coll[Boolean]): Boolean def anyZK(conditions: Coll[SigmaProp]): SigmaProp + def xorOf(conditions: Coll[Boolean]): Boolean + def PubKey(base64String: String): SigmaProp def sigmaProp(b: Boolean): SigmaProp diff --git a/sigma-impl/src/main/scala/sigma/types/Types.scala b/sigma-impl/src/main/scala/sigma/types/Types.scala index 1629ebe6ee..9c71eb2d1f 100644 --- a/sigma-impl/src/main/scala/sigma/types/Types.scala +++ b/sigma-impl/src/main/scala/sigma/types/Types.scala @@ -6,12 +6,16 @@ import special.collection.{Coll, Builder} case class CBoolean(value: scala.Boolean) extends Boolean { override def toByte: Byte = CByte(if (value) 1 else 0) + override def not: Boolean = CBoolean(!value) + override def xor(y: Boolean): Boolean = CBoolean(value ^ y.value) } case class CByte(value: scala.Byte) extends Byte { override def toInt: Int = CInt(value.toInt) override def +(y: Byte): Byte = CByte(value.addExact(y.value)) + + override def negate : Byte = CByte((-value).toByte) } case class CInt(value: scala.Int) extends Int { @@ -40,5 +44,7 @@ case class CInt(value: scala.Int) extends Int { * `this` is less than, equal to, or greater than `that`. */ override def compareTo(that: Int): Int = CInt(if (value < that.value) -1 else if (value == that.value) 0 else 1) + + override def negate : Int = CInt(-value) } diff --git a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala index 403dd68c1e..ea8c580921 100644 --- a/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala +++ b/sigma-impl/src/main/scala/special/sigma/SigmaDslOverArrays.scala @@ -74,6 +74,9 @@ class TestSigmaDslBuilder extends SigmaDslBuilder { @NeverInline def anyZK(props: Coll[SigmaProp]): SigmaProp = MockSigma(props.exists(p => p.isValid)) + @NeverInline + override def xorOf(conditions: Coll[Boolean]): Boolean = conditions.toArray.distinct.length == 2 + @NeverInline def sigmaProp(b: Boolean): SigmaProp = MockSigma(b) diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index df822aa328..cc1bb7a0b2 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -122,7 +122,7 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma } } - ignore("xorOf equivalence") { + property("xorOf equivalence") { // TODO enable after SigmaDslBuilder.xorOf is implemented (+ uncomment in RuntimeCosting) val eq = checkEq(func[Coll[Boolean], Boolean]("{ (x: Coll[Boolean]) => xorOf(x) }")) { x => x.toArray.distinct.length == 2 @@ -131,4 +131,29 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma eq(Builder.DefaultCollBuilder.fromArray(x)) } } + + property("LogicalNot equivalence") { + val eq = checkEq(func[Boolean, Boolean]("{ (x: Boolean) => !x }")) { x => !x } + forAll { x: Boolean => eq(x) } + } + + property("Negation equivalence") { + val negByte = checkEq(func[Byte, Byte]("{ (x: Byte) => -x }")) { x => (-x).toByte } + forAll { x: Byte => negByte(x) } + val negShort = checkEq(func[Short, Short]("{ (x: Short) => -x }")) { x => (-x).toShort } + forAll { x: Short => negShort(x) } + val negInt = checkEq(func[Int, Int]("{ (x: Int) => -x }")) { x => -x } + forAll { x: Int => negInt(x) } + val negLong = checkEq(func[Long, Long]("{ (x: Long) => -x }")) { x => -x } + forAll { x: Long => negLong(x) } + val negBigInteger = checkEq(func[BigInteger, BigInteger]("{ (x: BigInt) => -x }")) { x => x.negate() } + forAll { x: scala.BigInt => negBigInteger(x.bigInteger) } + } + + property("BinXor(logical XOR) equivalence") { + val eq = checkEq(func[(Boolean, Boolean), Boolean]("{ (x: (Boolean, Boolean)) => x._1 ^ x._2 }")) { + x => x._1 ^ x._2 + } + forAll { x: (Boolean, Boolean) => eq(x) } + } } From edcf2c01e14df973f61b7c198c91f7e82ab299bc Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 10:14:15 +0200 Subject: [PATCH 16/30] add codegen for sigma API xorOf, logicalNot, negation, logicalXor; --- .../resources/special/sigma/SigmaDsl.scalan | 1 + .../special/sigma/SigmaDslOverArrays.scalan | 1 + .../main/scala/special/sigma/SigmaDsl.scala | 1 + .../special/sigma/SigmaDslOverArrays.scala | 1 + .../special/sigma/impl/SigmaDslImpl.scala | 29 ++++++++++++++++++- .../sigma/impl/SigmaDslOverArraysImpl.scala | 20 +++++++++++++ 6 files changed, 52 insertions(+), 1 deletion(-) diff --git a/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan b/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan index 7514bcbe4a..c55264a9f5 100644 --- a/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan +++ b/sigma-api/src/main/resources/special/sigma/SigmaDsl.scalan @@ -189,6 +189,7 @@ package special.sigma { def allZK(conditions: Rep[Coll[SigmaProp]]): Rep[SigmaProp]; def anyOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean]; def anyZK(conditions: Rep[Coll[SigmaProp]]): Rep[SigmaProp]; + def xorOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean]; def PubKey(base64String: Rep[String]): Rep[SigmaProp]; def sigmaProp(b: Rep[Boolean]): Rep[SigmaProp]; def blake2b256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]]; diff --git a/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan b/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan index b5961627cd..a8f00f76e4 100644 --- a/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan +++ b/sigma-impl/src/main/resources/special/sigma/SigmaDslOverArrays.scalan @@ -60,6 +60,7 @@ package special.sigma { @NeverInline def anyOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean] = delayInvoke; @NeverInline def allZK(props: Rep[Coll[SigmaProp]]): Rep[SigmaProp] = delayInvoke; @NeverInline def anyZK(props: Rep[Coll[SigmaProp]]): Rep[SigmaProp] = delayInvoke; + @NeverInline override def xorOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean] = delayInvoke; @NeverInline def sigmaProp(b: Rep[Boolean]): Rep[SigmaProp] = delayInvoke; @NeverInline def blake2b256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]] = delayInvoke; @NeverInline def sha256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]] = delayInvoke; diff --git a/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala index 6b016610f1..4c091538dc 100644 --- a/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-library/src/main/scala/special/sigma/SigmaDsl.scala @@ -192,6 +192,7 @@ package special.sigma { def allZK(conditions: Rep[Coll[SigmaProp]]): Rep[SigmaProp]; def anyOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean]; def anyZK(conditions: Rep[Coll[SigmaProp]]): Rep[SigmaProp]; + def xorOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean]; def PubKey(base64String: Rep[String]): Rep[SigmaProp]; def sigmaProp(b: Rep[Boolean]): Rep[SigmaProp]; def blake2b256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]]; diff --git a/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala b/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala index e9c3de7018..1f3800d67f 100644 --- a/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala +++ b/sigma-library/src/main/scala/special/sigma/SigmaDslOverArrays.scala @@ -67,6 +67,7 @@ package special.sigma { @NeverInline def anyOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean] = delayInvoke; @NeverInline def allZK(props: Rep[Coll[SigmaProp]]): Rep[SigmaProp] = delayInvoke; @NeverInline def anyZK(props: Rep[Coll[SigmaProp]]): Rep[SigmaProp] = delayInvoke; + @NeverInline override def xorOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean] = delayInvoke; @NeverInline def sigmaProp(b: Rep[Boolean]): Rep[SigmaProp] = delayInvoke; @NeverInline def blake2b256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]] = delayInvoke; @NeverInline def sha256(bytes: Rep[Coll[Byte]]): Rep[Coll[Byte]] = delayInvoke; diff --git a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala index a59bbd6747..b1c35d6bd6 100644 --- a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala +++ b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslImpl.scala @@ -4222,6 +4222,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { true, false, element[SigmaProp])) } + override def xorOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean] = { + asRep[Boolean](mkMethodCall(self, + SigmaDslBuilderClass.getMethod("xorOf", classOf[Sym]), + List(conditions), + true, false, element[Boolean])) + } + override def PubKey(base64String: Rep[String]): Rep[SigmaProp] = { asRep[SigmaProp](mkMethodCall(self, SigmaDslBuilderClass.getMethod("PubKey", classOf[Sym]), @@ -4457,6 +4464,13 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { true, true, element[SigmaProp])) } + def xorOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean] = { + asRep[Boolean](mkMethodCall(source, + thisClass.getMethod("xorOf", classOf[Sym]), + List(conditions), + true, true, element[Boolean])) + } + def PubKey(base64String: Rep[String]): Rep[SigmaProp] = { asRep[SigmaProp](mkMethodCall(source, thisClass.getMethod("PubKey", classOf[Sym]), @@ -4593,7 +4607,7 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { override protected def collectMethods: Map[java.lang.reflect.Method, MethodDesc] = { super.collectMethods ++ Elem.declaredMethods(classOf[SigmaDslBuilder], classOf[SSigmaDslBuilder], Set( - "Colls", "Monoids", "Costing", "CostModel", "costBoxes", "costColWithConstSizedItem", "costOption", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "PubKey", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "isMember", "treeLookup", "treeModifications", "groupGenerator", "substConstants", "decodePoint", "BigInt", "toBigInteger" + "Colls", "Monoids", "Costing", "CostModel", "costBoxes", "costColWithConstSizedItem", "costOption", "verifyZK", "atLeast", "allOf", "allZK", "anyOf", "anyZK", "xorOf", "PubKey", "sigmaProp", "blake2b256", "sha256", "byteArrayToBigInt", "longToByteArray", "byteArrayToLong", "proveDlog", "proveDHTuple", "isMember", "treeLookup", "treeModifications", "groupGenerator", "substConstants", "decodePoint", "BigInt", "toBigInteger" )) } @@ -4805,6 +4819,19 @@ object SigmaDslBuilder extends EntityObject("SigmaDslBuilder") { } } + object xorOf { + def unapply(d: Def[_]): Nullable[(Rep[SigmaDslBuilder], Rep[Coll[Boolean]])] = d match { + case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] && method.getName == "xorOf" => + val res = (receiver, args(0)) + Nullable(res).asInstanceOf[Nullable[(Rep[SigmaDslBuilder], Rep[Coll[Boolean]])]] + case _ => Nullable.None + } + def unapply(exp: Sym): Nullable[(Rep[SigmaDslBuilder], Rep[Coll[Boolean]])] = exp match { + case Def(d) => unapply(d) + case _ => Nullable.None + } + } + object PubKey { def unapply(d: Def[_]): Nullable[(Rep[SigmaDslBuilder], Rep[String])] = d match { case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[SigmaDslBuilderElem[_]] && method.getName == "PubKey" => diff --git a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala index 854d02baaa..457f072817 100644 --- a/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala +++ b/sigma-library/src/main/scala/special/sigma/impl/SigmaDslOverArraysImpl.scala @@ -272,6 +272,13 @@ object TestSigmaDslBuilder extends EntityObject("TestSigmaDslBuilder") { true, false, element[SigmaProp])) } + override def xorOf(conditions: Rep[Coll[Boolean]]): Rep[Boolean] = { + asRep[Boolean](mkMethodCall(self, + thisClass.getMethod("xorOf", classOf[Sym]), + List(conditions), + true, false, element[Boolean])) + } + override def sigmaProp(b: Rep[Boolean]): Rep[SigmaProp] = { asRep[SigmaProp](mkMethodCall(self, thisClass.getMethod("sigmaProp", classOf[Sym]), @@ -669,6 +676,19 @@ object TestSigmaDslBuilder extends EntityObject("TestSigmaDslBuilder") { } } + object xorOf { + def unapply(d: Def[_]): Nullable[(Rep[TestSigmaDslBuilder], Rep[Coll[Boolean]])] = d match { + case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[TestSigmaDslBuilderElem] && method.getName == "xorOf" => + val res = (receiver, args(0)) + Nullable(res).asInstanceOf[Nullable[(Rep[TestSigmaDslBuilder], Rep[Coll[Boolean]])]] + case _ => Nullable.None + } + def unapply(exp: Sym): Nullable[(Rep[TestSigmaDslBuilder], Rep[Coll[Boolean]])] = exp match { + case Def(d) => unapply(d) + case _ => Nullable.None + } + } + object sigmaProp { def unapply(d: Def[_]): Nullable[(Rep[TestSigmaDslBuilder], Rep[Boolean])] = d match { case MethodCall(receiver, method, args, _) if receiver.elem.isInstanceOf[TestSigmaDslBuilderElem] && method.getName == "sigmaProp" => From 24de39f60fae7b551f3f0e80139f0c6b9ea6f524 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 10:18:59 +0200 Subject: [PATCH 17/30] add xorOf handling in costing; --- .../sigmastate/eval/RuntimeCosting.scala | 26 +++++++++---------- .../scala/sigmastate/eval/TreeBuilding.scala | 2 ++ .../scala/sigmastate/utxo/CostTable.scala | 1 + .../scala/special/sigma/SigmaDslTest.scala | 1 - 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index d521c54f10..09d03efb3f 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1415,19 +1415,19 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev withDefaultSize(res, cost) } -// case XorOf(input) => input match { -// case ConcreteCollection(items, tpe) => -// val itemsC = items.map(item => eval(adaptSigmaBoolean(item))) -// val res = sigmaDslBuilder.xorOf(colBuilder.fromItems(itemsC.map(_.value): _*)) -// val costs = colBuilder.fromItems(itemsC.map(_.cost): _*) -// val cost = costs.sum(intPlusMonoid) + perItemCostOf(node, costs.length) -// withDefaultSize(res, cost) -// case _ => -// val inputC = asRep[CostedColl[Boolean]](eval(input)) -// val res = sigmaDslBuilder.xorOf(inputC.value) -// val cost = inputC.cost + perItemCostOf(node, inputC.sizes.length) -// withDefaultSize(res, cost) -// } + case XorOf(input) => input match { + case ConcreteCollection(items, tpe) => + val itemsC = items.map(item => eval(adaptSigmaBoolean(item))) + val res = sigmaDslBuilder.xorOf(colBuilder.fromItems(itemsC.map(_.value): _*)) + val costs = colBuilder.fromItems(itemsC.map(_.cost): _*) + val cost = costs.sum(intPlusMonoid) + perItemCostOf(node, costs.length) + withDefaultSize(res, cost) + case _ => + val inputC = asRep[CostedColl[Boolean]](eval(input)) + val res = sigmaDslBuilder.xorOf(inputC.value) + val cost = inputC.cost + perItemCostOf(node, inputC.sizes.length) + withDefaultSize(res, cost) + } case BinOr(l, r) => val lC = evalNode(ctx, env, l) diff --git a/src/main/scala/sigmastate/eval/TreeBuilding.scala b/src/main/scala/sigmastate/eval/TreeBuilding.scala index 899c3d3bf8..903dd9c389 100644 --- a/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -288,6 +288,8 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => mkOR(recurse(items)) case Def(SDBM.atLeast(_, bound, items)) => mkAtLeast(recurse(bound), recurse(items)) + case Def(SDBM.xorOf(_, items)) => + mkXorOf(recurse(items)) case SigmaM.and_bool_&&(In(prop), In(cond)) => SigmaAnd(Seq(prop.asSigmaProp, mkBoolToSigmaProp(cond.asBoolValue))) diff --git a/src/main/scala/sigmastate/utxo/CostTable.scala b/src/main/scala/sigmastate/utxo/CostTable.scala index 28af565b5d..7ea8c69ada 100644 --- a/src/main/scala/sigmastate/utxo/CostTable.scala +++ b/src/main/scala/sigmastate/utxo/CostTable.scala @@ -124,6 +124,7 @@ object CostTable { ("CalcBlake2b256_per_kb", "(Coll[Byte]) => Coll[Byte]", hashPerKb), ("CalcSha256_per_kb", "(Coll[Byte]) => Coll[Byte]", hashPerKb), ("Xor_per_kb", "(Coll[Byte],Coll[Byte]) => Coll[Byte]", hashPerKb / 2), + ("XorOf_per_item", "(Coll[Boolean]) => Boolean", logicCost), ("GT", "(T,T) => Boolean", comparisonCost), ("GE", "(T,T) => Boolean", comparisonCost), diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index cc1bb7a0b2..cf71ae7747 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -123,7 +123,6 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma } property("xorOf equivalence") { - // TODO enable after SigmaDslBuilder.xorOf is implemented (+ uncomment in RuntimeCosting) val eq = checkEq(func[Coll[Boolean], Boolean]("{ (x: Coll[Boolean]) => xorOf(x) }")) { x => x.toArray.distinct.length == 2 } From fcf3058162ed06fd5c2fdc67d43c226ea778d53d Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 14:02:14 +0200 Subject: [PATCH 18/30] add logicalNot full implementation; --- sigma-api/src/main/scala/sigma/types/Types.scala | 5 ----- sigma-impl/src/main/scala/sigma/types/Types.scala | 1 - src/main/scala/sigmastate/eval/RuntimeCosting.scala | 4 ++++ src/main/scala/sigmastate/eval/TreeBuilding.scala | 2 +- src/main/scala/sigmastate/utxo/CostTable.scala | 1 + src/test/scala/sigmastate/lang/SigmaCompilerTest.scala | 6 ++---- src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala | 4 ++-- .../sigmastate/utxo/CollectionOperationsSpecification.scala | 6 +++--- 8 files changed, 13 insertions(+), 16 deletions(-) diff --git a/sigma-api/src/main/scala/sigma/types/Types.scala b/sigma-api/src/main/scala/sigma/types/Types.scala index 7d573cc626..df78f61634 100644 --- a/sigma-api/src/main/scala/sigma/types/Types.scala +++ b/sigma-api/src/main/scala/sigma/types/Types.scala @@ -14,11 +14,6 @@ trait Boolean extends PrimView[scala.Boolean] { */ def toByte: Byte - /** Logical negation - * @since 2.0 - */ - def not: Boolean - /** Logical XOR * @since 2.0 */ diff --git a/sigma-impl/src/main/scala/sigma/types/Types.scala b/sigma-impl/src/main/scala/sigma/types/Types.scala index 9c71eb2d1f..30de09a964 100644 --- a/sigma-impl/src/main/scala/sigma/types/Types.scala +++ b/sigma-impl/src/main/scala/sigma/types/Types.scala @@ -6,7 +6,6 @@ import special.collection.{Coll, Builder} case class CBoolean(value: scala.Boolean) extends Boolean { override def toByte: Byte = CByte(if (value) 1 else 0) - override def not: Boolean = CBoolean(!value) override def xor(y: Boolean): Boolean = CBoolean(value ^ y.value) } diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index 09d03efb3f..d2c15e05a3 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1382,6 +1382,10 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev withDefaultSize(ApplyBinOp(binop, x.value, y.value), x.cost + y.cost + costOf(op)) } + case LogicalNot(input) => + val inputC = evalNode(ctx, env, input) + withDefaultSize(ApplyUnOp(Not, inputC.value), inputC.cost + costOf(node)) + // case ModQ(input) => // val inputC = asRep[Costed[WBigInteger]](eval(input)) // val v = inputC.value.modQ diff --git a/src/main/scala/sigmastate/eval/TreeBuilding.scala b/src/main/scala/sigmastate/eval/TreeBuilding.scala index 903dd9c389..3dcd587aa5 100644 --- a/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -90,7 +90,7 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => object IsLogicalUnOp { def unapply(op: UnOp[_,_]): Option[BoolValue => Value[SBoolean.type]] = op match { - case Not => Some({ v: BoolValue => builder.mkEQ(v, FalseLeaf) }) + case Not => Some({ v: BoolValue => builder.mkLogicalNot(v) }) case _ => None } } diff --git a/src/main/scala/sigmastate/utxo/CostTable.scala b/src/main/scala/sigmastate/utxo/CostTable.scala index 7ea8c69ada..c917079161 100644 --- a/src/main/scala/sigmastate/utxo/CostTable.scala +++ b/src/main/scala/sigmastate/utxo/CostTable.scala @@ -125,6 +125,7 @@ object CostTable { ("CalcSha256_per_kb", "(Coll[Byte]) => Coll[Byte]", hashPerKb), ("Xor_per_kb", "(Coll[Byte],Coll[Byte]) => Coll[Byte]", hashPerKb / 2), ("XorOf_per_item", "(Coll[Boolean]) => Boolean", logicCost), + ("LogicalNot", "(Boolean) => Boolean", logicCost), ("GT", "(T,T) => Boolean", comparisonCost), ("GE", "(T,T) => Boolean", comparisonCost), diff --git a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 6a75aace43..467ae0dfe4 100644 --- a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -173,7 +173,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("logicalNot") { - testMissingCosting("!true", LogicalNot(TrueLeaf)) + comp("!true") shouldBe LogicalNot(TrueLeaf) } property("Negation") { @@ -564,8 +564,6 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("xorOf") { - testMissingCosting("xorOf(Coll[Boolean](true, false))", - XorOf(Seq(TrueLeaf, FalseLeaf)) - ) + comp("xorOf(Coll[Boolean](true, false))") shouldBe XorOf(Seq(TrueLeaf, FalseLeaf)) } } diff --git a/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 6baadafd3f..316ba5cc7e 100644 --- a/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -416,7 +416,7 @@ class BasicOpsSpecification extends SigmaTestingCommons { // no value test("Def2", env, ext, "{ SELF.R8[Int].isDefined == false }", - EQ(ExtractRegisterAs[SInt.type](Self, R8).isDefined, FalseLeaf).toSigmaProp, + LogicalNot(ExtractRegisterAs[SInt.type](Self, R8).isDefined).toSigmaProp, true ) @@ -428,7 +428,7 @@ class BasicOpsSpecification extends SigmaTestingCommons { // there should be no variable with this id test("Def4", env, ext, "{ getVar[Int](99).isDefined == false }", - EQ(GetVarInt(99).isDefined, FalseLeaf).toSigmaProp, + LogicalNot(GetVarInt(99).isDefined).toSigmaProp, true ) } diff --git a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala index bb3b7e2b9b..118b74eb7d 100644 --- a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala +++ b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala @@ -351,15 +351,15 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { """OUTPUTS |.map({ (box: Box) => box.value }) |.fold(true, { (acc: Boolean, val: Long) => acc && (val < 0) }) == false""".stripMargin - val expectedPropTree = EQ( + val expectedPropTree = LogicalNot( Fold( MapCollection(Outputs, FuncValue(Vector((1, SBox)), ExtractAmount(ValUse(1, SBox)))), TrueLeaf, FuncValue(Vector((1, STuple(SBoolean, SLong))), BinAnd( SelectField(ValUse(1, STuple(SBoolean, SLong)), 1).asBoolValue, - LT(SelectField(ValUse(1, STuple(SBoolean, SLong)), 2), LongConstant(0))))), - FalseLeaf) + LT(SelectField(ValUse(1, STuple(SBoolean, SLong)), 2), LongConstant(0))))) + ) assertProof(code, expectedPropTree, outputBoxValues) } From 2abd8ec5a9f3309779b0875b63751e873b4c9828 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 16:24:49 +0200 Subject: [PATCH 19/30] add numeric negate full implementation; --- sigma-api/src/main/scala/sigma/types/Types.scala | 9 --------- .../src/main/scala/sigma/types/Types.scala | 4 ---- .../scala/sigmastate/eval/RuntimeCosting.scala | 16 ++++++++++++++++ .../scala/sigmastate/eval/TreeBuilding.scala | 11 ++++++++++- src/main/scala/sigmastate/trees.scala | 2 +- src/main/scala/sigmastate/utxo/CostTable.scala | 6 ++++++ .../sigmastate/helpers/SigmaTestingCommons.scala | 3 ++- 7 files changed, 35 insertions(+), 16 deletions(-) diff --git a/sigma-api/src/main/scala/sigma/types/Types.scala b/sigma-api/src/main/scala/sigma/types/Types.scala index df78f61634..080891d5e9 100644 --- a/sigma-api/src/main/scala/sigma/types/Types.scala +++ b/sigma-api/src/main/scala/sigma/types/Types.scala @@ -25,10 +25,6 @@ trait Byte extends PrimView[scala.Byte] { def toInt: Int // def toLong: Long def + (y: Byte): Byte - /** Negation - * @since 2.0 - */ - def negate : Byte } trait Int extends PrimView[scala.Int] { @@ -57,11 +53,6 @@ trait Int extends PrimView[scala.Int] { * `this` is less than, equal to, or greater than `that`. */ def compareTo(that: Int): Int - - /** Negation - * @since 2.0 - */ - def negate : Int } diff --git a/sigma-impl/src/main/scala/sigma/types/Types.scala b/sigma-impl/src/main/scala/sigma/types/Types.scala index 30de09a964..5b9528537f 100644 --- a/sigma-impl/src/main/scala/sigma/types/Types.scala +++ b/sigma-impl/src/main/scala/sigma/types/Types.scala @@ -13,8 +13,6 @@ case class CByte(value: scala.Byte) extends Byte { override def toInt: Int = CInt(value.toInt) override def +(y: Byte): Byte = CByte(value.addExact(y.value)) - - override def negate : Byte = CByte((-value).toByte) } case class CInt(value: scala.Int) extends Int { @@ -43,7 +41,5 @@ case class CInt(value: scala.Int) extends Int { * `this` is less than, equal to, or greater than `that`. */ override def compareTo(that: Int): Int = CInt(if (value < that.value) -1 else if (value == that.value) 0 else 1) - - override def negate : Int = CInt(-value) } diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index d2c15e05a3..c2ee9b18bf 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1448,6 +1448,22 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev val c = lC.cost + rC.cost + costOf(node) withDefaultSize(v, c) +// case BinXor(l, r) => +// val lC = evalNode(ctx, env, l) +// val rC = RCostedThunk(Thunk(evalNode(ctx, env, r)), 0) +// val v = sigmaDslBuilder.binXor(lC.value, rC.value) +// val c = lC.cost + rC.cost + costOf(node) +// withDefaultSize(v, c) + + case neg: Negation[t] => + val tpe = neg.input.tpe + val et = stypeToElem(tpe) + val op = NumericNegate(elemToNumeric(et))(et) + val inputC = evalNode(ctx, env, neg.input) + inputC match { case x: RCosted[a] => + withDefaultSize(ApplyUnOp(op, x.value), x.cost + costOf(neg)) + } + case SigmaAnd(items) => val itemsC = items.map(eval) val res = sigmaDslBuilder.allZK(colBuilder.fromItems(itemsC.map(_.value.asRep[SigmaProp]): _*)) diff --git a/src/main/scala/sigmastate/eval/TreeBuilding.scala b/src/main/scala/sigmastate/eval/TreeBuilding.scala index 3dcd587aa5..18084efa6c 100644 --- a/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -89,12 +89,19 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => } object IsLogicalUnOp { - def unapply(op: UnOp[_,_]): Option[BoolValue => Value[SBoolean.type]] = op match { + def unapply(op: UnOp[_,_]): Option[BoolValue => BoolValue] = op match { case Not => Some({ v: BoolValue => builder.mkLogicalNot(v) }) case _ => None } } + object IsNumericUnOp { + def unapply(op: UnOp[_,_]): Option[SValue => SValue] = op match { + case NumericNegate(_) => Some({ v: SValue => builder.mkNegation(v.asNumValue) }) + case _ => None + } + } + object IsContextProperty { def unapply(d: Def[_]): Option[SValue] = d match { case ContextM.HEIGHT(_) => Some(Height) @@ -204,6 +211,8 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => mkNode(x, y) case Def(ApplyUnOp(IsLogicalUnOp(mkNode), xSym)) => mkNode(recurse(xSym)) + case Def(ApplyUnOp(IsNumericUnOp(mkNode), xSym)) => + mkNode(recurse(xSym)) case CollM.apply(colSym, In(index)) => val col = recurse(colSym) diff --git a/src/main/scala/sigmastate/trees.scala b/src/main/scala/sigmastate/trees.scala index 1bfb376102..5630b591d2 100644 --- a/src/main/scala/sigmastate/trees.scala +++ b/src/main/scala/sigmastate/trees.scala @@ -444,7 +444,7 @@ object ArithOp { } } -case class Negation[T <: SNumericType](input: Value[T]) extends OneArgumentOperation[T, T] { +case class Negation[T <: SType](input: Value[T]) extends OneArgumentOperation[T, T] { override val opCode: OpCode = OpCodes.NegationCode override def tpe: T = input.tpe } diff --git a/src/main/scala/sigmastate/utxo/CostTable.scala b/src/main/scala/sigmastate/utxo/CostTable.scala index c917079161..2eb20b82fb 100644 --- a/src/main/scala/sigmastate/utxo/CostTable.scala +++ b/src/main/scala/sigmastate/utxo/CostTable.scala @@ -174,6 +174,12 @@ object CostTable { ("%", "(Int, Int) => Int", multiply), ("%", "(Long, Long) => Long", multiply), + ("Negation", "(Byte) => Byte", MinimalCost), + ("Negation", "(Short) => Short", MinimalCost), + ("Negation", "(Int) => Int", MinimalCost), + ("Negation", "(Long) => Long", MinimalCost), + ("Negation", "(BigInt) => BigInt", MinimalCost), + ("+", "(BigInt, BigInt) => BigInt", plusMinusBigInt), ("+_per_item", "(BigInt, BigInt) => BigInt", MinimalCost), diff --git a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index 412b9aa0c9..2f43eb70b8 100644 --- a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -98,10 +98,11 @@ trait SigmaTestingCommons extends PropSpec val tB = RType[B] val tpeA = Evaluation.rtypeToSType(tA) val tpeB = Evaluation.rtypeToSType(tB) + val getVarType = if (tpeA == SBigInt) "BigInt" else tA.name val code = s"""{ | val func = $func - | val res = func(getVar[${tA.name}](1).get) + | val res = func(getVar[$getVarType](1).get) | res |} """.stripMargin From f4e6ba6cb8dbbc0a2477485c0f28bf865c4dfc42 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 16:25:28 +0200 Subject: [PATCH 20/30] fix SigmaCompilerTest; --- src/test/scala/sigmastate/lang/SigmaCompilerTest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 467ae0dfe4..86223cc978 100644 --- a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -177,7 +177,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen } property("Negation") { - testMissingCosting("-HEIGHT", Negation(Height)) + comp("-HEIGHT") shouldBe Negation(Height) } property("BitInversion") { From 823e0cdf0eb77ec6653ff7c2e0b56ccea95ad81f Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 16:33:41 +0200 Subject: [PATCH 21/30] disable SigmaDslTest xor test; --- src/test/scala/special/sigma/SigmaDslTest.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index cf71ae7747..39e451d086 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -149,7 +149,8 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma forAll { x: scala.BigInt => negBigInteger(x.bigInteger) } } - property("BinXor(logical XOR) equivalence") { + ignore("BinXor(logical XOR) equivalence") { + // TODO implement val eq = checkEq(func[(Boolean, Boolean), Boolean]("{ (x: (Boolean, Boolean)) => x._1 ^ x._2 }")) { x => x._1 ^ x._2 } From 1daf28532bef0d2e5c45415225df7e6c55479cac Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 16:39:54 +0200 Subject: [PATCH 22/30] build fix after rebase; --- .../sigmastate/helpers/SigmaTestingCommons.scala | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index 2f43eb70b8..c1f5397d36 100644 --- a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -1,10 +1,8 @@ package sigmastate.helpers import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix -import org.ergoplatform.{ErgoAddressEncoder, ErgoBox, ErgoLikeContext, ErgoScriptPredef} import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} import org.ergoplatform.ErgoScriptPredef.TrueProp -import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} import org.ergoplatform.{ErgoBox, ErgoLikeContext} import org.scalacheck.Arbitrary.arbByte import org.scalacheck.Gen @@ -12,27 +10,19 @@ import org.scalatest.prop.{GeneratorDrivenPropertyChecks, PropertyChecks} import org.scalatest.{Assertion, Matchers, PropSpec} import scalan.{Nullable, RType, TestContexts, TestUtils} import scorex.crypto.hash.Blake2b256 -import scorex.util._ -import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, GroupElementConstant, SValue, TrueLeaf, Value} -import sigmastate.eval.{CompiletimeCosting, Evaluation, IRContext} import scorex.util.serialization.{VLQByteStringReader, VLQByteStringWriter} import sigma.types.{IsPrimView, PrimViewType, View} import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue, GroupElementConstant, SValue, Value} import sigmastate.eval.{CompiletimeCosting, Evaluation, IRContext} import sigmastate.interpreter.Interpreter.{ScriptEnv, ScriptNameProp} import sigmastate.interpreter.{CryptoConstants, Interpreter} -import sigmastate.interpreter.Interpreter.{ScriptEnv, ScriptNameProp} -import sigmastate.{SBoolean, SGroupElement, SType} import sigmastate.lang.{SigmaCompiler, TransformingSigmaBuilder} import sigmastate.serialization.{ErgoTreeSerializer, SigmaSerializer} +import sigmastate.{SGroupElement, SType, SBigInt} import spire.util.Opt import scala.annotation.tailrec import scala.language.implicitConversions -import scalan.{Nullable, RType, TestContexts, TestUtils} -import sigma.types.{IsPrimView, PrimViewType, View} -import sigmastate.serialization.ErgoTreeSerializer -import spire.util.Opt trait SigmaTestingCommons extends PropSpec with PropertyChecks From a81ffea128d8f938e121d6ed6938fbedbd2e3c92 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Fri, 22 Feb 2019 19:36:45 +0200 Subject: [PATCH 23/30] remove adaptSigmaBoolean in XorOf costing; use withDefaultSize in ByteArrayToLong costing; --- src/main/scala/sigmastate/eval/RuntimeCosting.scala | 5 ++--- src/main/scala/sigmastate/types.scala | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index c2ee9b18bf..263e1876d6 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1421,7 +1421,7 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev case XorOf(input) => input match { case ConcreteCollection(items, tpe) => - val itemsC = items.map(item => eval(adaptSigmaBoolean(item))) + val itemsC = items.map(item => eval(item)) val res = sigmaDslBuilder.xorOf(colBuilder.fromItems(itemsC.map(_.value): _*)) val costs = colBuilder.fromItems(itemsC.map(_.cost): _*) val cost = costs.sum(intPlusMonoid) + perItemCostOf(node, costs.length) @@ -1557,9 +1557,8 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev case ByteArrayToLong(In(arr)) => val arrC = asRep[Costed[Coll[Byte]]](arr) val value = sigmaDslBuilder.byteArrayToLong(arrC.value) - val size = arrC.dataSize val cost = arrC.cost + costOf(node) - RCCostedPrim(value, cost, size) + withDefaultSize(value, cost) case Xor(InCollByte(l), InCollByte(r)) => val values = colBuilder.xor(l.value, r.value) diff --git a/src/main/scala/sigmastate/types.scala b/src/main/scala/sigmastate/types.scala index 065717d9d9..3d82c0a36f 100644 --- a/src/main/scala/sigmastate/types.scala +++ b/src/main/scala/sigmastate/types.scala @@ -111,7 +111,7 @@ object SType { * NOTE: in the current implementation only monomorphic methods are supported (without type parameters)*/ val types: Map[Byte, STypeCompanion] = Seq( SNumericType, SString, STuple, SGroupElement, SSigmaProp, SContext, - SAvlTree, SBox, SOption, SCollection, SBigInt, SOption + SAvlTree, SBox, SOption, SCollection, SBigInt ).map { t => (t.typeId, t) }.toMap implicit class STypeOps(val tpe: SType) extends AnyVal { From 55eb2d198f02653908efd1485c65d9d93d370299 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Sat, 23 Feb 2019 06:34:32 +0200 Subject: [PATCH 24/30] add special.sigma.BigInt test (failing); revert hack for BigInt type in func; --- .../src/main/scala/special/sigma/package.scala | 1 + src/main/scala/sigmastate/eval/Evaluation.scala | 1 + .../helpers/SigmaTestingCommons.scala | 3 +-- src/test/scala/special/sigma/SigmaDslTest.scala | 17 ++++++++++++++--- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/sigma-api/src/main/scala/special/sigma/package.scala b/sigma-api/src/main/scala/special/sigma/package.scala index e9ca95b9cc..d564d2bd8f 100644 --- a/sigma-api/src/main/scala/special/sigma/package.scala +++ b/sigma-api/src/main/scala/special/sigma/package.scala @@ -11,6 +11,7 @@ package sigma { case class WrapperType[Wrapper](cWrapper: ClassTag[Wrapper]) extends RType[Wrapper] { override def classTag: ClassTag[Wrapper] = cWrapper + override def toString: String = cWrapper.toString } } diff --git a/src/main/scala/sigmastate/eval/Evaluation.scala b/src/main/scala/sigmastate/eval/Evaluation.scala index 882c0edbed..34ddc03658 100644 --- a/src/main/scala/sigmastate/eval/Evaluation.scala +++ b/src/main/scala/sigmastate/eval/Evaluation.scala @@ -465,6 +465,7 @@ object Evaluation { case StringType => SString case AnyType => SAny case BigIntegerRType => SBigInt + case BigIntRType => SBigInt case ECPointRType => SGroupElement case AvlTreeRType => SAvlTree case ot: OptionType[_] => sigmastate.SOption(rtypeToSType(ot.tA)) diff --git a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index c1f5397d36..4492c7a4d9 100644 --- a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -88,11 +88,10 @@ trait SigmaTestingCommons extends PropSpec val tB = RType[B] val tpeA = Evaluation.rtypeToSType(tA) val tpeB = Evaluation.rtypeToSType(tB) - val getVarType = if (tpeA == SBigInt) "BigInt" else tA.name val code = s"""{ | val func = $func - | val res = func(getVar[$getVarType](1).get) + | val res = func(getVar[${tA.name}](1).get) | res |} """.stripMargin diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index 39e451d086..d77892335f 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -8,10 +8,12 @@ import org.scalatest.{Matchers, PropSpec} import org.scalacheck.{Arbitrary, Gen} import sigmastate.helpers.SigmaTestingCommons import sigma.util.Extensions._ +import sigmastate.eval.CBigInt +import sigmastate.serialization.generators.ValueGenerators import special.collection.{Builder, Coll} import special.collections.CollGens -trait SigmaTypeGens { +trait SigmaTypeGens extends ValueGenerators { import Gen._; import Arbitrary._ import sigma.types._ val genBoolean = Arbitrary.arbBool.arbitrary.map(CBoolean(_): Boolean) @@ -22,6 +24,9 @@ trait SigmaTypeGens { val genInt = Arbitrary.arbInt.arbitrary.map(CInt(_): Int) implicit val arbInt = Arbitrary(genInt) + + val genBigInt = arbBigInteger.arbitrary.map(CBigInt(_): BigInt) + implicit val arbBigInt = Arbitrary(genBigInt) } /** This suite tests every method of every SigmaDsl type to be equivalent to @@ -145,8 +150,14 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma forAll { x: Int => negInt(x) } val negLong = checkEq(func[Long, Long]("{ (x: Long) => -x }")) { x => -x } forAll { x: Long => negLong(x) } - val negBigInteger = checkEq(func[BigInteger, BigInteger]("{ (x: BigInt) => -x }")) { x => x.negate() } - forAll { x: scala.BigInt => negBigInteger(x.bigInteger) } + // TODO add scala.BigInt test + } + + ignore("special.sigma.BigInt Negation equivalence") { + // TODO fix return type (java.math.BigInteger) + // TODO make negate() into a prefix method + val negBigInteger = checkEq(func[BigInt, BigInt]("{ (x: BigInt) => -x }")) { x => x.negate() } + forAll { x: BigInt => negBigInteger(x) } } ignore("BinXor(logical XOR) equivalence") { From 237de80470a2c35de96e02563a486a8c01fb9f1f Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Sat, 23 Feb 2019 07:19:17 +0200 Subject: [PATCH 25/30] fix byteArrayToLong and xorOf tests to test against SigmaDsl; --- .../main/scala/special/sigma/SigmaDsl.scala | 3 ++ .../scala/special/sigma/SigmaDslTest.scala | 32 +++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala index 868defa792..e6a3c23a66 100644 --- a/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala +++ b/sigma-api/src/main/scala/special/sigma/SigmaDsl.scala @@ -434,6 +434,8 @@ trait SigmaContract { def anyOf(conditions: Coll[Boolean]): Boolean = this.builder.anyOf(conditions) def anyZK(conditions: Coll[SigmaProp]): SigmaProp = this.builder.anyZK(conditions) + def xorOf(conditions: Coll[Boolean]): Boolean = this.builder.xorOf(conditions) + def PubKey(base64String: String): SigmaProp = this.builder.PubKey(base64String) def sigmaProp(b: Boolean): SigmaProp = this.builder.sigmaProp(b) @@ -443,6 +445,7 @@ trait SigmaContract { def byteArrayToBigInt(bytes: Coll[Byte]): BigInt = this.builder.byteArrayToBigInt(bytes) def longToByteArray(l: Long): Coll[Byte] = this.builder.longToByteArray(l) + def byteArrayToLong(bytes: Coll[Byte]): Long = this.builder.byteArrayToLong(bytes) def proveDlog(g: GroupElement): SigmaProp = this.builder.proveDlog(g) def proveDHTuple(g: GroupElement, h: GroupElement, u: GroupElement, v: GroupElement): SigmaProp = this.builder.proveDHTuple(g, h, u, v) diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index d77892335f..79c707cce6 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -3,6 +3,7 @@ package special.sigma import java.math.BigInteger import com.google.common.primitives.Longs +import org.ergoplatform.dsl.{SigmaContractSyntax, StdContracts, TestContractSpec} import org.scalatest.prop.PropertyChecks import org.scalatest.{Matchers, PropSpec} import org.scalacheck.{Arbitrary, Gen} @@ -31,7 +32,16 @@ trait SigmaTypeGens extends ValueGenerators { /** This suite tests every method of every SigmaDsl type to be equivalent to * the evaluation of the corresponding ErgoScript operation */ -class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with SigmaTestingCommons with CollGens with SigmaTypeGens { +class SigmaDslTest extends PropSpec + with PropertyChecks + with Matchers + with SigmaTestingCommons + with SigmaTypeGens + with SigmaContractSyntax + with StdContracts { suite => + + lazy val spec = TestContractSpec(suite)(new TestingIRContext) + implicit lazy val IR = new TestingIRContext { override val okPrintEvaluatedEntries: Boolean = false } @@ -80,7 +90,7 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma lazy val toAbs = checkEq(func[Int,Int]("{ (x: Int) => x.toAbs }"))(x => x.toAbs) lazy val compareTo = checkEq(func[(Int, Int), Int]("{ (x: (Int, Int)) => x._1.compareTo(x._2) }"))(x => x._1.compareTo(x._2)) - forAll(valGen) { x: Int => + forAll { x: Int => whenever(Byte.MinValue <= x && x <= scala.Byte.MaxValue) { toByte(x) } @@ -108,7 +118,7 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma import sigma.types._ val toByte = checkEq(func[Int,Byte]("{ (x: Int) => x.toByte }"))(x => x.toByte) lazy val compareTo = checkEq(func[(Int, Int), Int]("{ (x: (Int, Int)) => x._1.compareTo(x._2) }"))(x => x._1.compareTo(x._2)) - forAll(valGen) { in: scala.Int => + forAll { in: scala.Int => whenever(scala.Byte.MinValue <= in && in <= scala.Byte.MaxValue) { val x = CInt(in) toByte(x) @@ -116,12 +126,20 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma } } + ignore("longToByteArray equivalence") { + // TODO fix Array[Byte] != CollOverArray in result type comparison + val eq = checkEq(func[Long, Coll[Byte]]("{ (x: Long) => longToByteArray(x) }")){ x => + longToByteArray(x) + } + forAll { x: Long => eq(x) } + } + property("byteArrayToLong equivalence") { val eq = checkEq(func[Coll[Byte],Long]("{ (x: Coll[Byte]) => byteArrayToLong(x) }")){ x => - Longs.fromByteArray(x.toArray) + byteArrayToLong(x) } forAll { x: Array[Byte] => - whenever(x.size >= 8) { + whenever(x.length >= 8) { eq(Builder.DefaultCollBuilder.fromArray(x)) } } @@ -129,7 +147,7 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma property("xorOf equivalence") { val eq = checkEq(func[Coll[Boolean], Boolean]("{ (x: Coll[Boolean]) => xorOf(x) }")) { x => - x.toArray.distinct.length == 2 + xorOf(x) } forAll { x: Array[Boolean] => eq(Builder.DefaultCollBuilder.fromArray(x)) @@ -137,11 +155,13 @@ class SigmaDslTest extends PropSpec with PropertyChecks with Matchers with Sigma } property("LogicalNot equivalence") { + // TODO make a prefix method val eq = checkEq(func[Boolean, Boolean]("{ (x: Boolean) => !x }")) { x => !x } forAll { x: Boolean => eq(x) } } property("Negation equivalence") { + // TODO make a prefix method val negByte = checkEq(func[Byte, Byte]("{ (x: Byte) => -x }")) { x => (-x).toByte } forAll { x: Byte => negByte(x) } val negShort = checkEq(func[Short, Short]("{ (x: Short) => -x }")) { x => (-x).toShort } From 391cec0bc40fedaf2229ec4e5dafc52093f7f9c7 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Sun, 24 Feb 2019 18:33:45 +0200 Subject: [PATCH 26/30] make SOption a SGenericType; fix SFunc.typeParams to hold tpeParams; (de)serialize type idents in MethodCall.typeSubst using index in SFunc.tpeParams list; --- .../serialization/MethodCallSerializer.scala | 37 +++++++++++++------ src/main/scala/sigmastate/types.scala | 26 ++++++++++--- .../MethodCallSerializerSpecification.scala | 10 ++++- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala b/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala index 18a05c0f33..e3d9398b9d 100644 --- a/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala +++ b/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala @@ -3,7 +3,7 @@ package sigmastate.serialization import sigmastate.Values._ import sigmastate._ import sigmastate.lang.SigmaTyper.STypeSubst -import sigmastate.lang.Terms.MethodCall +import sigmastate.lang.Terms.{MethodCall, STypeParam} import sigmastate.utils.{SigmaByteReader, SigmaByteWriter} case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, IndexedSeq[Value[SType]], STypeSubst) => Value[SType]) @@ -17,25 +17,38 @@ case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, Inde assert(mc.args.nonEmpty) w.putValues(mc.args) } - w.putUByte(mc.typeSubst.size) - mc.typeSubst.foreach { case (typeIdent, tpe) => w.putType(typeIdent).putType(tpe) } + mc.method.stype match { + case genType: SGenericType => + w.putUByte(mc.typeSubst.size) + mc.typeSubst.foreach { case (ti, tpe) => + val tpIndex = genType.substitutedTypeParams.indexOf(STypeParam(ti)) + w.putUByte(tpIndex) + .putType(tpe) + } + case _ => w.putUByte(0) + } } override def parse(r: SigmaByteReader): Value[SType] = { val typeId = r.getByte() val methodId = r.getByte() val obj = r.getValue() - val args = if (opCode == OpCodes.MethodCallCode) { - r.getValues() - } - else IndexedSeq() + val args = if (opCode == OpCodes.MethodCallCode) r.getValues() else IndexedSeq() val method = MethodCall.fromIds(typeId, methodId) - val typeSubstSize = r.getUByte() - val xs = new Array[(STypeIdent, SType)](typeSubstSize) - for (i <- 0 until typeSubstSize) { - xs(i) = (r.getType().asInstanceOf[STypeIdent], r.getType()) + val typeSubst: STypeSubst = method.stype match { + case genType: SGenericType => + val typeSubstSize = r.getUByte() + val xs = new Array[(STypeIdent, SType)](typeSubstSize) + for (i <- 0 until typeSubstSize) { + val tpIndex = r.getUByte() + val ti = genType.substitutedTypeParams.apply(tpIndex).ident + xs(i) = (ti, r.getType()) + } + xs.toMap + case _ => + r.getUByte() // read 0 + Map() } - val typeSubst = xs.toMap cons(obj, method, args, typeSubst) } } diff --git a/src/main/scala/sigmastate/types.scala b/src/main/scala/sigmastate/types.scala index 3d82c0a36f..bd28e59ec9 100644 --- a/src/main/scala/sigmastate/types.scala +++ b/src/main/scala/sigmastate/types.scala @@ -232,7 +232,16 @@ trait SProduct extends SType { * e.g. Array[T], Option[T], etc.)*/ trait SGenericType { def typeParams: Seq[STypeParam] - def tparamSubst: Map[String, SType] + def tparamSubst: Map[STypeIdent, SType] + + lazy val substitutedTypeParams: Seq[STypeParam] = + typeParams.map { tp => + tparamSubst.getOrElse(tp.ident, tp.ident) match { + case v: STypeIdent => STypeParam(v) + case _ => tp + } + } + } /** Method info including name, arg type and result type. @@ -565,7 +574,8 @@ case object SUnit extends SPrimType { /** Type description of optional values. Instances of `Option` * are either constructed by `Some` or by `None` constructors. */ -case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct { +case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct with SGenericType { + import SOption._ override type WrappedType = Option[ElemType#WrappedType] override val typeCode: TypeCode = SOption.OptionTypeCode override def dataSize(v: SType#WrappedType) = { @@ -576,6 +586,8 @@ case class SOption[ElemType <: SType](elemType: ElemType) extends SProduct { def ancestors = Nil override def methods: Seq[SMethod] = SOption.methods override def toString = s"Option[$elemType]" + val typeParams: Seq[STypeParam] = Seq(STypeParam(tT)) + def tparamSubst: Map[STypeIdent, SType] = Map(tT -> elemType) } object SOption extends STypeCompanion { @@ -662,8 +674,8 @@ case class SCollectionType[T <: SType](elemType: T) extends SCollection[T] { arr.map(x => elemType.dataSize(x)).sum res } - def typeParams = SCollectionType.typeParams - def tparamSubst = Map(tIV.name -> elemType) + def typeParams: Seq[STypeParam] = SCollectionType.typeParams + def tparamSubst: Map[STypeIdent, SType] = Map(tIV -> elemType) override def methods = SCollection.methods override def toString = s"Coll[$elemType]" } @@ -921,10 +933,12 @@ case class SFunc(tDom: IndexedSeq[SType], tRange: SType, tpeParams: Seq[STypePa } override def dataSize(v: SType#WrappedType) = 8L import SFunc._ - val typeParams: Seq[STypeParam] = (tDom.zipWithIndex.map { case (t, i) => STypeParam(tD.name + (i + 1)) }) :+ STypeParam(tR.name) - val tparamSubst = typeParams.zip(tDom).map { case (p, t) => p.ident.name -> t }.toMap + (tR.name -> tRange) + val typeParams: Seq[STypeParam] = tpeParams + val tparamSubst: Map[STypeIdent, SType] = Map() // defined in MethodCall.typeSubst def getGenericType: SFunc = { + val typeParams: Seq[STypeParam] = tDom.zipWithIndex + .map { case (t, i) => STypeParam(tD.name + (i + 1)) } :+ STypeParam(tR.name) val ts = typeParams.map(_.ident) SFunc(ts.init.toIndexedSeq, ts.last, Nil) } diff --git a/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala b/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala index 2b285efdb7..47cd1f7ac0 100644 --- a/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala +++ b/src/test/scala/sigmastate/serialization/MethodCallSerializerSpecification.scala @@ -4,7 +4,7 @@ import org.ergoplatform.Outputs import sigmastate.Values.{FuncValue, ValUse} import sigmastate.lang.Terms.MethodCall import sigmastate.utxo.ExtractScriptBytes -import sigmastate.{SBox, SByte, SCollection} +import sigmastate._ class MethodCallSerializerSpecification extends SerializationSpecification { @@ -17,4 +17,12 @@ class MethodCallSerializerSpecification extends SerializationSpecification { roundTripTest(expr) } + property("MethodCall deserialization round trip (non-generic method)") { + val expr = MethodCall(Outputs, + SMethod(SCollection, "size", SInt, 1), + Vector(), + Map() + ) + roundTripTest(expr) + } } From eb4b3c177c7db1583329e177c030d971987af40f Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Mon, 25 Feb 2019 11:23:39 +0200 Subject: [PATCH 27/30] remove type param index in MethodCall serialization (only concrete types in the order of SGenericType.typeParams); fix Coll.zip() to include tOV type subst in TreeBuilding; --- src/main/scala/sigmastate/eval/TreeBuilding.scala | 9 ++++++--- .../serialization/MethodCallSerializer.scala | 11 ++++------- .../scala/sigmastate/lang/SigmaCompilerTest.scala | 2 +- .../utxo/CollectionOperationsSpecification.scala | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/scala/sigmastate/eval/TreeBuilding.scala b/src/main/scala/sigmastate/eval/TreeBuilding.scala index 18084efa6c..6e95ef23e6 100644 --- a/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -2,16 +2,16 @@ package sigmastate.eval import scala.collection.mutable.ArrayBuffer import sigmastate._ -import sigmastate.Values.{FuncValue, FalseLeaf, Constant, SValue, BlockValue, ConstantNode, SigmaPropConstant, BoolValue, Value, BooleanConstant, SigmaBoolean, ValDef, GroupElementConstant, ValUse, ConcreteCollection} +import sigmastate.Values.{BlockValue, BoolValue, BooleanConstant, ConcreteCollection, Constant, ConstantNode, EvaluatedCollection, FalseLeaf, FuncValue, GroupElementConstant, SValue, SigmaBoolean, SigmaPropConstant, ValDef, ValUse, Value} import sigmastate.serialization.OpCodes._ import org.ergoplatform._ import java.math.BigInteger -import org.ergoplatform.{Height, Outputs, Self, Inputs} +import org.ergoplatform.{Height, Inputs, Outputs, Self} import sigmastate._ import sigmastate.lang.Terms.{OperationId, ValueOps} import sigmastate.serialization.OpCodes._ -import sigmastate.serialization.{ValueSerializer, ConstantStore} +import sigmastate.serialization.{ConstantStore, ValueSerializer} import sigmastate.utxo.{CostTable, ExtractAmount, SizeOf} import ErgoLikeContext._ @@ -250,6 +250,9 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => case (Some(mth @ SCollection.FlatMapMethod), Seq(f)) => val typeSubst = Map(SCollection.tOV -> f.asFunc.tpe.tRange.asCollection.elemType) (mth, typeSubst) + case (Some(mth @ SCollection.ZipMethod), Seq(coll: EvaluatedCollection[_, _])) => + val typeSubst = Map(SCollection.tOV -> coll.elementType) + (mth, typeSubst) case (Some(mth), _) => (mth, SigmaTyper.emptySubst) case (None, _) => error(s"unknown method Coll.${m.getName}") } diff --git a/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala b/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala index e3d9398b9d..e7400fe6e1 100644 --- a/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala +++ b/src/main/scala/sigmastate/serialization/MethodCallSerializer.scala @@ -18,12 +18,10 @@ case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, Inde w.putValues(mc.args) } mc.method.stype match { - case genType: SGenericType => + case genType: SGenericType if mc.typeSubst.nonEmpty => w.putUByte(mc.typeSubst.size) - mc.typeSubst.foreach { case (ti, tpe) => - val tpIndex = genType.substitutedTypeParams.indexOf(STypeParam(ti)) - w.putUByte(tpIndex) - .putType(tpe) + genType.substitutedTypeParams.foreach { tp => + w.putType(mc.typeSubst(tp.ident)) } case _ => w.putUByte(0) } @@ -40,8 +38,7 @@ case class MethodCallSerializer(opCode: Byte, cons: (Value[SType], SMethod, Inde val typeSubstSize = r.getUByte() val xs = new Array[(STypeIdent, SType)](typeSubstSize) for (i <- 0 until typeSubstSize) { - val tpIndex = r.getUByte() - val ti = genType.substitutedTypeParams.apply(tpIndex).ident + val ti = genType.substitutedTypeParams(i).ident xs(i) = (ti, r.getType()) } xs.toMap diff --git a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala index 86223cc978..18a04502e5 100644 --- a/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala +++ b/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala @@ -511,7 +511,7 @@ class SigmaCompilerTest extends SigmaTestingCommons with LangTests with ValueGen ConcreteCollection(IntConstant(1), IntConstant(2)), SCollection.ZipMethod, Vector(ConcreteCollection(IntConstant(1), IntConstant(1))), - Map(SCollection.tIV -> SInt)) + Map(SCollection.tIV -> SInt, SCollection.tOV -> SInt)) } property("SCollection.partition") { diff --git a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala index 118b74eb7d..8ff041e292 100644 --- a/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala +++ b/src/test/scala/sigmastate/utxo/CollectionOperationsSpecification.scala @@ -522,7 +522,7 @@ class CollectionOperationsSpecification extends SigmaTestingCommons { Vector( ConcreteCollection(IntConstant(1), IntConstant(2)) ), - Map(tIV -> SBox)).asCollection[STuple]), + Map(tIV -> SBox, tOV -> SInt)).asCollection[STuple]), IntConstant(2)), IndexedSeq(1L, 2L)) } From 37804f692c1546f84e20a63db4bc5c8e3bacd142 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 26 Feb 2019 12:47:20 +0200 Subject: [PATCH 28/30] add Box.getReg handling via MethodCall throughout the whole pipeline --- .../sigmastate/eval/RuntimeCosting.scala | 13 +++++++- .../scala/sigmastate/eval/TreeBuilding.scala | 6 +++- .../scala/sigmastate/lang/SigmaTyper.scala | 30 +++++++++++++++++++ src/main/scala/sigmastate/types.scala | 9 ++++-- .../utxo/BasicOpsSpecification.scala | 15 ++++++++++ .../scala/special/sigma/SigmaDslTest.scala | 6 ++++ 6 files changed, 74 insertions(+), 5 deletions(-) diff --git a/src/main/scala/sigmastate/eval/RuntimeCosting.scala b/src/main/scala/sigmastate/eval/RuntimeCosting.scala index 263e1876d6..db53d6cdfc 100644 --- a/src/main/scala/sigmastate/eval/RuntimeCosting.scala +++ b/src/main/scala/sigmastate/eval/RuntimeCosting.scala @@ -1631,7 +1631,18 @@ trait RuntimeCosting extends SigmaLibrary with DataCosting with Slicing { IR: Ev (method.name, argsC) match { case (SOption.MapMethod.name, Seq(f)) => optC.map(asRep[Costed[Any => Any]](f)) case (SOption.FilterMethod.name, Seq(f)) => optC.filter(asRep[Costed[Any => Boolean]](f)) - case _ => error(s"method $method is not supported") + case _ => error(s"method $method is not supported in object $obj") + } + + case Terms.MethodCall(obj, method, args, typeSubst) if obj.tpe.isBox => + val boxC = asRep[CostedBox](eval(obj)) + val argsC = args.map(eval) + (method.name, argsC) match { + case (SBox.GetRegMethod.name, Seq(index)) => + val tpe = typeSubst(SBox.tT) + implicit val elem = stypeToElem(tpe).asElem[Any] + boxC.getReg(asRep[Int](index.value))(elem) + case _ => error(s"method $method is not supported in object $obj") } case _ => diff --git a/src/main/scala/sigmastate/eval/TreeBuilding.scala b/src/main/scala/sigmastate/eval/TreeBuilding.scala index 6e95ef23e6..9f17808d88 100644 --- a/src/main/scala/sigmastate/eval/TreeBuilding.scala +++ b/src/main/scala/sigmastate/eval/TreeBuilding.scala @@ -264,7 +264,11 @@ trait TreeBuilding extends RuntimeCosting { IR: Evaluation => mkExtractScriptBytes(box.asBox) case BoxM.getReg(In(box), regId, _) => val tpe = elemToSType(s.elem).asOption - mkExtractRegisterAs(box.asBox, ErgoBox.allRegisters(regId.asValue), tpe) + if (regId.isConst) + mkExtractRegisterAs(box.asBox, ErgoBox.allRegisters(regId.asValue), tpe) + else + builder.mkMethodCall(box, SBox.GetRegMethod, IndexedSeq(recurse(regId)), + Map(SBox.tT -> tpe.elemType)) case BoxM.creationInfo(In(box)) => mkExtractCreationInfo(box.asBox) case BoxM.id(In(box)) => diff --git a/src/main/scala/sigmastate/lang/SigmaTyper.scala b/src/main/scala/sigmastate/lang/SigmaTyper.scala index a8d179c337..c092cbf332 100644 --- a/src/main/scala/sigmastate/lang/SigmaTyper.scala +++ b/src/main/scala/sigmastate/lang/SigmaTyper.scala @@ -106,6 +106,36 @@ class SigmaTyper(val builder: SigmaBuilder, predefFuncRegistry: PredefinedFuncRe val res = mkGenLambda(tparams, args, newBody.fold(t)(_.tpe), newBody) res + case Apply(ApplyTypes(sel @ Select(obj, n, _), Seq(rangeTpe)), args) => + val newObj = assignType(env, obj) + val newArgs = args.map(assignType(env, _)) + obj.tpe match { + case p: SProduct => + p.method(n) match { + case Some(method @ SMethod(_, _, genFunTpe @ SFunc(_, _, _), _, _)) => + val subst = Map(genFunTpe.tpeParams.head.ident -> rangeTpe) + val concrFunTpe = applySubst(genFunTpe, subst) + val expectedArgs = concrFunTpe.asFunc.tDom.tail + val newArgTypes = newArgs.map(_.tpe) + if (expectedArgs.length != newArgTypes.length + || !expectedArgs.zip(newArgTypes).forall { case (ea, na) => ea == SAny || ea == na }) + error(s"For method $n expected args: $expectedArgs; actual: $newArgTypes", sel.sourceContext) + if (method.irBuilder.isDefined) { + method.irBuilder.flatMap(_.lift(builder, newObj, method, newArgs, subst)) + .getOrElse(mkMethodCall(newObj, method, newArgs, subst)) + } else { + val newSelect = mkSelect(newObj, n, Some(concrFunTpe)).withSrcCtx(sel.sourceContext) + mkApply(newSelect, newArgs) + } + case Some(method) => + error(s"Don't know how to handle method $method in obj $p", sel.sourceContext) + case None => + throw new MethodNotFound(s"Cannot find method '$n' in in the object $obj of Product type with methods ${p.methods}", obj.sourceContext.toOption) + } + case _ => + error(s"Cannot get field '$n' in in the object $obj of non-product type ${obj.tpe}", sel.sourceContext) + } + case app @ Apply(sel @ Select(obj, n, _), args) => val newSel = assignType(env, sel) val newArgs = args.map(assignType(env, _)) diff --git a/src/main/scala/sigmastate/types.scala b/src/main/scala/sigmastate/types.scala index bd28e59ec9..a205cc2c5e 100644 --- a/src/main/scala/sigmastate/types.scala +++ b/src/main/scala/sigmastate/types.scala @@ -118,6 +118,7 @@ object SType { def isCollectionLike: Boolean = tpe.isInstanceOf[SCollection[_]] def isCollection: Boolean = tpe.isInstanceOf[SCollectionType[_]] def isOption: Boolean = tpe.isInstanceOf[SOption[_]] + def isBox: Boolean = tpe.isInstanceOf[SBox.type] def isSigmaProp: Boolean = tpe.isInstanceOf[SSigmaProp.type] def isFunc : Boolean = tpe.isInstanceOf[SFunc] def isTuple: Boolean = tpe.isInstanceOf[STuple] @@ -990,7 +991,7 @@ case object SBox extends SProduct with SPredefType with STypeCompanion { override def isConstantSize = false def ancestors = Nil - private val tT = STypeIdent("T") + val tT = STypeIdent("T") def registers(idOfs: Int): Seq[SMethod] = { (1 to 10).map { i => SMethod(this, s"R$i", SFunc(IndexedSeq(), SOption(tT), Seq(STypeParam(tT))), (idOfs + i).toByte) @@ -1003,7 +1004,9 @@ case object SBox extends SProduct with SPredefType with STypeCompanion { val BytesWithNoRef = "bytesWithNoRef" val CreationInfo = "creationInfo" val TokensMethod = SMethod(this, "tokens", SCollectionType(STuple(SCollectionType(SByte), SLong)), 8, MethodCallIrBuilder) - // should be lazy to solve resursive initialization + val GetRegMethod = SMethod(this, "getReg", + SFunc(IndexedSeq(SBox, SInt), SOption(tT), Seq(STypeParam(tT))), 7, MethodCallIrBuilder) + // should be lazy to solve recursive initialization lazy val methods = Vector( SMethod(this, Value, SLong, 1), // see ExtractAmount SMethod(this, PropositionBytes, SCollectionType(SByte), 2), // see ExtractScriptBytes @@ -1011,7 +1014,7 @@ case object SBox extends SProduct with SPredefType with STypeCompanion { SMethod(this, BytesWithNoRef, SCollectionType(SByte), 4), // see ExtractBytesWithNoRef SMethod(this, Id, SCollectionType(SByte), 5), // see ExtractId SMethod(this, CreationInfo, STuple(SInt, SCollectionType(SByte)), 6), // see ExtractCreationInfo - SMethod(this, s"getReg", SFunc(IndexedSeq(SByte), SOption(tT), Seq(STypeParam(tT))), 7), + GetRegMethod, TokensMethod, ) ++ registers(8) } diff --git a/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala b/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala index 316ba5cc7e..2370701cd5 100644 --- a/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala +++ b/src/test/scala/sigmastate/utxo/BasicOpsSpecification.scala @@ -355,6 +355,21 @@ class BasicOpsSpecification extends SigmaTestingCommons { rootCause(_).isInstanceOf[InvalidType]) } + property("Box.getReg") { + test("Extract1", env, ext, + "{ SELF.getReg[Int](getVar[Int](intVar1).get + 4).get == 1}", + BoolToSigmaProp( + EQ( + MethodCall(Self, SBox.GetRegMethod, + IndexedSeq(Plus(GetVarInt(1).get, IntConstant(4))), Map(SBox.tT -> SInt) + ).asInstanceOf[Value[SOption[SType]]].get, + IntConstant(1) + ) + ), + true + ) + } + property("OptionGet success (SomeValue)") { test("Opt1", env, ext, "{ getVar[Int](intVar2).get == 2 }", diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index 79c707cce6..6c25113e79 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -187,4 +187,10 @@ class SigmaDslTest extends PropSpec } forAll { x: (Boolean, Boolean) => eq(x) } } + + ignore("Box.getReg equivalence") { + // TODO implement in SigmaDsl (interpreter test passes in BasicOpsSpec.Box.getReg test) +// val eq = checkEq(func[Box, Int]("{ (x: Box) => x.getReg[Int](1).get }")) { x => x.getReg(1).get } +// forAll { x: Box => eq(x) } + } } From 772a030940a1ca6af0e20f67dc8163ea069b90b8 Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 26 Feb 2019 15:58:46 +0200 Subject: [PATCH 29/30] fix build; --- src/main/scala/sigmastate/types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/sigmastate/types.scala b/src/main/scala/sigmastate/types.scala index a205cc2c5e..b0c945b037 100644 --- a/src/main/scala/sigmastate/types.scala +++ b/src/main/scala/sigmastate/types.scala @@ -1007,7 +1007,7 @@ case object SBox extends SProduct with SPredefType with STypeCompanion { val GetRegMethod = SMethod(this, "getReg", SFunc(IndexedSeq(SBox, SInt), SOption(tT), Seq(STypeParam(tT))), 7, MethodCallIrBuilder) // should be lazy to solve recursive initialization - lazy val methods = Vector( + lazy val methods: Vector[SMethod] = Vector( SMethod(this, Value, SLong, 1), // see ExtractAmount SMethod(this, PropositionBytes, SCollectionType(SByte), 2), // see ExtractScriptBytes SMethod(this, Bytes, SCollectionType(SByte), 3), // see ExtractBytes From 2a5524a9b61e01af4bab4613002ef883057da40f Mon Sep 17 00:00:00 2001 From: Denys Zadorozhnyi Date: Tue, 26 Feb 2019 17:29:39 +0200 Subject: [PATCH 30/30] fix and enable special.sigma.BigInt negation SigmaDsl test; --- sigma-api/src/main/scala/special/sigma/package.scala | 1 + .../scala/sigmastate/helpers/SigmaTestingCommons.scala | 7 ++++++- src/test/scala/special/sigma/SigmaDslTest.scala | 4 +--- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/sigma-api/src/main/scala/special/sigma/package.scala b/sigma-api/src/main/scala/special/sigma/package.scala index d564d2bd8f..d4e868c7f8 100644 --- a/sigma-api/src/main/scala/special/sigma/package.scala +++ b/sigma-api/src/main/scala/special/sigma/package.scala @@ -12,6 +12,7 @@ package sigma { case class WrapperType[Wrapper](cWrapper: ClassTag[Wrapper]) extends RType[Wrapper] { override def classTag: ClassTag[Wrapper] = cWrapper override def toString: String = cWrapper.toString + override def name: String = cWrapper.runtimeClass.getSimpleName } } diff --git a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala index 4492c7a4d9..f24a4f3d69 100644 --- a/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala +++ b/src/test/scala/sigmastate/helpers/SigmaTestingCommons.scala @@ -1,5 +1,7 @@ package sigmastate.helpers +import java.math.BigInteger + import org.ergoplatform.ErgoAddressEncoder.TestnetNetworkPrefix import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, TokenId} import org.ergoplatform.ErgoScriptPredef.TrueProp @@ -18,7 +20,8 @@ import sigmastate.interpreter.Interpreter.{ScriptEnv, ScriptNameProp} import sigmastate.interpreter.{CryptoConstants, Interpreter} import sigmastate.lang.{SigmaCompiler, TransformingSigmaBuilder} import sigmastate.serialization.{ErgoTreeSerializer, SigmaSerializer} -import sigmastate.{SGroupElement, SType, SBigInt} +import sigmastate.{SGroupElement, SType} +import special.sigma._ import spire.util.Opt import scala.annotation.tailrec @@ -120,6 +123,8 @@ trait SigmaTestingCommons extends PropSpec case Opt(pv) => pv case _ => x // cannot wrap, so just return as is } + case wt: WrapperType[_] if wt.classTag == reflect.classTag[BigInt] => + IR.sigmaDslBuilderValue.BigInt(x.asInstanceOf[BigInteger]) case _ => x // don't need to wrap } case _ => res diff --git a/src/test/scala/special/sigma/SigmaDslTest.scala b/src/test/scala/special/sigma/SigmaDslTest.scala index 6c25113e79..18a50525ba 100644 --- a/src/test/scala/special/sigma/SigmaDslTest.scala +++ b/src/test/scala/special/sigma/SigmaDslTest.scala @@ -170,11 +170,9 @@ class SigmaDslTest extends PropSpec forAll { x: Int => negInt(x) } val negLong = checkEq(func[Long, Long]("{ (x: Long) => -x }")) { x => -x } forAll { x: Long => negLong(x) } - // TODO add scala.BigInt test } - ignore("special.sigma.BigInt Negation equivalence") { - // TODO fix return type (java.math.BigInteger) + property("special.sigma.BigInt Negation equivalence") { // TODO make negate() into a prefix method val negBigInteger = checkEq(func[BigInt, BigInt]("{ (x: BigInt) => -x }")) { x => x.negate() } forAll { x: BigInt => negBigInteger(x) }