diff --git a/build.sbt b/build.sbt index d0dba6ec1c..526497cbe6 100644 --- a/build.sbt +++ b/build.sbt @@ -11,6 +11,7 @@ val testingDependencies = Seq( ) libraryDependencies ++= Seq( + "org.bouncycastle" % "bcprov-jdk15on" % "1.58", "com.typesafe.akka" %% "akka-actor" % "2.4.+", "org.scorexfoundation" %% "scorex-core" % "2.+", "org.bitbucket.inkytonik.kiama" %% "kiama" % "2.1.0" diff --git a/src/main/scala/sigmastate/Proof.scala b/src/main/scala/sigmastate/Proof.scala deleted file mode 100644 index bd73afbffd..0000000000 --- a/src/main/scala/sigmastate/Proof.scala +++ /dev/null @@ -1,21 +0,0 @@ -package sigmastate - -import scapi.sigma.rework.SigmaProtocol -import scorex.core.serialization.BytesSerializable -/* - -trait Proof[P <: SigmaProposition] extends BytesSerializable { - - //val challenge: ProofOfKnowledge.Message - //def verify(): Boolean - - val propCode: SigmaProposition.PropositionCode -} - -trait ProofOfKnowledge[SP <: SigmaProtocol[SP], CI <: SigmaProofOfKnowledgeTree[SP, _]] - extends Proof[CI] - -object ProofOfKnowledge { - type Message = Array[Byte] -} -*/ diff --git a/src/main/scala/sigmastate/interpreter/Interpreter.scala b/src/main/scala/sigmastate/interpreter/Interpreter.scala index d454a0e7d0..952c4052cf 100644 --- a/src/main/scala/sigmastate/interpreter/Interpreter.scala +++ b/src/main/scala/sigmastate/interpreter/Interpreter.scala @@ -20,8 +20,6 @@ import scapi.sigma.rework.FirstProverMessage import sigmastate.utxo.CostTable - - trait Interpreter { type CTX <: Context[CTX] type StateT <: StateTree @@ -29,7 +27,7 @@ trait Interpreter { type ProofT = UncheckedTree //todo: ProofT <: UncheckedTree ? - val dlogGroup: DlogGroup = new BcDlogECFp() + lazy val dlogGroup: DlogGroup = new BcDlogECFp() /** * Max cost of a script interpreter can accept @@ -55,24 +53,30 @@ trait Interpreter { } protected val relations: Strategy = everywherebu(rule[SigmaStateTree] { - case EQ(l: Value, r: Value) => BooleanLeaf.fromBoolean(l == r) - case NEQ(l: Value, r: Value) => BooleanLeaf.fromBoolean(l != r) - case GT(l: IntLeaf, r: IntLeaf) => BooleanLeaf.fromBoolean(l.value > r.value) - case GE(l: IntLeaf, r: IntLeaf) => BooleanLeaf.fromBoolean(l.value >= r.value) - case LT(l: IntLeaf, r: IntLeaf) => BooleanLeaf.fromBoolean(l.value < r.value) - case LE(l: IntLeaf, r: IntLeaf) => BooleanLeaf.fromBoolean(l.value <= r.value) + case EQ(l: Value, r: Value) if l.evaluated && r.evaluated => + BooleanLeafConstant.fromBoolean(l == r) + case NEQ(l: Value, r: Value) if l.evaluated && r.evaluated => + BooleanLeafConstant.fromBoolean(l != r) + case GT(l: IntLeafConstant, r: IntLeafConstant) => + BooleanLeafConstant.fromBoolean(l.value > r.value) + case GE(l: IntLeafConstant, r: IntLeafConstant) => + BooleanLeafConstant.fromBoolean(l.value >= r.value) + case LT(l: IntLeafConstant, r: IntLeafConstant) => + BooleanLeafConstant.fromBoolean(l.value < r.value) + case LE(l: IntLeafConstant, r: IntLeafConstant) => + BooleanLeafConstant.fromBoolean(l.value <= r.value) }) protected val operations: Strategy = everywherebu(rule[SigmaStateTree] { - case Plus(l: IntLeaf, r: IntLeaf) => IntLeaf(l.value + r.value) - case Minus(l: IntLeaf, r: IntLeaf) => IntLeaf(l.value - r.value) - case Xor(l: ByteArrayLeaf, r: ByteArrayLeaf) => + case Plus(l: IntLeafConstant, r: IntLeafConstant) => IntLeafConstant(l.value + r.value) + case Minus(l: IntLeafConstant, r: IntLeafConstant) => IntLeafConstant(l.value - r.value) + case Xor(l: ByteArrayLeafConstant, r: ByteArrayLeafConstant) => assert(l.value.length == r.value.length) - ByteArrayLeaf(Helpers.xor(l.value, r.value)) - case Append(l: ByteArrayLeaf, r: ByteArrayLeaf) => + ByteArrayLeafConstant(Helpers.xor(l.value, r.value)) + case Append(l: ByteArrayLeafConstant, r: ByteArrayLeafConstant) => require(l.value.length + r.value.length < 10000) //todo: externalize this maximum intermediate value length limit - ByteArrayLeaf(l.value ++ r.value) - case CalcBlake2b256(l: ByteArrayLeaf) => ByteArrayLeaf(Blake2b256(l.value)) + ByteArrayLeafConstant(l.value ++ r.value) + case c@CalcBlake2b256Inst(l: ByteArrayLeafConstant) if l.evaluated => c.function(l) }) protected val conjs: Strategy = everywherebu(rule[SigmaStateTree] { diff --git a/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala b/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala index dbe6c805f1..24c9acc490 100644 --- a/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala +++ b/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala @@ -30,7 +30,7 @@ trait ProverInterpreter extends Interpreter with AttributionCore { val secrets: Seq[SigmaProtocolPrivateInput[_]] - val contextExtenders: Map[Int, ByteArrayLeaf] + val contextExtenders: Map[Int, ByteArrayLeafConstant] def enrichContext(tree: SigmaStateTree): ContextExtension = { val targetName = CustomByteArray.getClass.getSimpleName.replace("$", "") @@ -106,6 +106,8 @@ trait ProverInterpreter extends Interpreter with AttributionCore { def prove(exp: SigmaStateTree, context: CTX, message: Array[Byte]): Try[ProverResult[ProofT]] = Try { val candidateProp = reduceToCrypto(exp, context).get + println(candidateProp) + val (cProp, ext) = (candidateProp.isInstanceOf[SigmaT] match { case true => (candidateProp, ContextExtension(Map())) case false => @@ -113,7 +115,7 @@ trait ProverInterpreter extends Interpreter with AttributionCore { //todo: no need for full reduction here probably (reduceToCrypto(candidateProp, context.withExtension(extension)).get, extension) }).ensuring { res => - res._1.isInstanceOf[BooleanLeaf] || + res._1.isInstanceOf[BooleanLeafConstant] || res._1.isInstanceOf[CAND] || res._1.isInstanceOf[COR] || res._1.isInstanceOf[DLogNode] || @@ -122,7 +124,7 @@ trait ProverInterpreter extends Interpreter with AttributionCore { ProverResult(cProp match { - case tree: BooleanLeaf => + case tree: BooleanLeafConstant => tree match { case TrueLeaf => NoProof case FalseLeaf => ??? diff --git a/src/main/scala/sigmastate/trees.scala b/src/main/scala/sigmastate/trees.scala index f28c00b988..0e008fa6e5 100644 --- a/src/main/scala/sigmastate/trees.scala +++ b/src/main/scala/sigmastate/trees.scala @@ -7,7 +7,9 @@ import scapi.sigma.{DiffieHellmanTupleNode, FirstDiffieHellmanTupleProverMessage import scapi.sigma.rework.{FirstProverMessage, SigmaProtocol, SigmaProtocolCommonInput, SigmaProtocolPrivateInput} import scorex.core.serialization.{BytesSerializable, Serializer} import scorex.core.transaction.box.proposition.ProofOfKnowledgeProposition +import scorex.crypto.hash.Blake2b256 import sigmastate.SigmaProposition.PropositionCode +import sigmastate.utxo.{BoxWithMetadata, Transformer, TransformerInstantiation} import sigmastate.utxo.CostTable.Cost @@ -45,7 +47,7 @@ trait SigmaProofOfKnowledgeTree[SP <: SigmaProtocol[SP], S <: SigmaProtocolPriva extends SigmaTree with ProofOfKnowledgeProposition[S] with SigmaProtocolCommonInput[SP] -case class OR(children: Seq[SigmaStateTree]) extends SigmaStateTree { +case class OR(children: Seq[SigmaStateTree]) extends NotReadyValueBoolean { override def cost: Int = children.map(_.cost).sum + children.length * Cost.OrPerChild + Cost.OrDeclaration } @@ -56,7 +58,7 @@ object OR { def apply(arg1: SigmaStateTree, arg2: SigmaStateTree, arg3: SigmaStateTree): OR = apply(Seq(arg1, arg2, arg3)) } -case class AND(children: Seq[SigmaStateTree]) extends SigmaStateTree { +case class AND(children: Seq[SigmaStateTree]) extends NotReadyValueBoolean { override def cost: Int = children.map(_.cost).sum + children.length * Cost.AndPerChild + Cost.AndDeclaration } @@ -65,154 +67,262 @@ object AND { } -trait Value extends StateTree +trait Value extends StateTree { + type WrappedValue -case class IntLeaf(value: Long) extends Value { - require(value >= 0) + def evaluated: Boolean = ??? +} + +trait EvaluatedValue[V <: Value] extends Value { + self: V => + + val value: V#WrappedValue + + override lazy val evaluated = true +} + +trait NotReadyValue[V <: Value] extends Value { + self: V => + override lazy val evaluated = false +} + +//todo: make PreservingNonNegativeIntLeaf for registers which value should be preserved? +sealed trait IntLeaf extends Value { + override type WrappedValue = Long override def cost: Int = 1 } -case class ByteArrayLeaf(value: Array[Byte]) extends Value { +case class IntLeafConstant(value: Long) extends IntLeaf with EvaluatedValue[IntLeaf] + +trait NotReadyValueIntLeaf extends IntLeaf with NotReadyValue[IntLeaf] + +case object Height extends NotReadyValueIntLeaf { + override def cost: Int = Cost.HeightAccess +} + +case object UnknownIntLeaf extends NotReadyValueIntLeaf + + +sealed trait ByteArrayLeaf extends Value { + override type WrappedValue = Array[Byte] +} + +case class ByteArrayLeafConstant(value: Array[Byte]) extends EvaluatedValue[ByteArrayLeaf] with ByteArrayLeaf { override def cost: Int = (value.length / 1024.0).ceil.round.toInt * Cost.ByteArrayPerKilobyte override def equals(obj: scala.Any): Boolean = obj match { - case ob: ByteArrayLeaf => value sameElements ob.value + case ob: ByteArrayLeafConstant => value sameElements ob.value case _ => false } } -case class PropLeaf(value: SigmaStateTree) extends Value { - override def cost: Int = value.cost + Cost.PropLeafDeclaration +trait NotReadyValueByteArray extends ByteArrayLeaf with NotReadyValue[ByteArrayLeaf] + + +//todo: merge with ByteArrayLeaf? + +sealed trait PropLeaf extends Value { + override type WrappedValue = Array[Byte] +} + +case class PropLeafConstant(value: Array[Byte]) extends EvaluatedValue[PropLeaf] with PropLeaf { + override def cost: Int = value.length + Cost.PropLeafDeclaration + + override def equals(obj: scala.Any): Boolean = obj match { + case ob: PropLeafConstant => value sameElements ob.value + case _ => false + } +} + +object PropLeafConstant { + def apply(value: BoxWithMetadata): PropLeafConstant = new PropLeafConstant(value.box.propositionBytes) + def apply(value: SigmaStateTree): PropLeafConstant = new PropLeafConstant(value.toString.getBytes) +} + +trait NotReadyValueProp extends PropLeaf with NotReadyValue[PropLeaf] + + +sealed trait BooleanLeaf extends Value { + override type WrappedValue = Boolean } -sealed abstract class BooleanLeaf(val value: Boolean) extends Value +sealed abstract class BooleanLeafConstant(val value: Boolean) extends BooleanLeaf with EvaluatedValue[BooleanLeaf] -object BooleanLeaf { - def fromBoolean(v: Boolean): BooleanLeaf = if (v) TrueLeaf else FalseLeaf +object BooleanLeafConstant { + def fromBoolean(v: Boolean): BooleanLeafConstant = if (v) TrueLeaf else FalseLeaf } -case object TrueLeaf extends BooleanLeaf(true) { +case object TrueLeaf extends BooleanLeafConstant(true) { override def cost: Int = Cost.ConstantNode } -case object FalseLeaf extends BooleanLeaf(false) { +case object FalseLeaf extends BooleanLeafConstant(false) { override def cost: Int = Cost.ConstantNode } +trait NotReadyValueBoolean extends BooleanLeaf with NotReadyValue[BooleanLeaf] -trait Variable[V <: Value] extends Value -case object Height extends Variable[IntLeaf] { - override def cost: Int = Cost.HeightAccess +sealed trait BoxLeaf extends Value { + override type WrappedValue = BoxWithMetadata +} + +case class BoxLeafConstant(value: BoxWithMetadata) extends BoxLeaf with EvaluatedValue[BoxLeaf]{ + override def cost: Int = 10 +} + +trait NotReadyValueBoxLeaf extends BoxLeaf with NotReadyValue[BoxLeaf] + + +trait CollectionLeaf[V <: Value] extends Value { + override type WrappedValue = Seq[V] +} + +case class ConcreteCollection[V <: Value](value: Seq[V]) extends CollectionLeaf[V] with EvaluatedValue[CollectionLeaf[V]] { + val cost = value.size + // lazy val isLazy = value.exists(_.isInstanceOf[NotReadyValue[_]] == true) + // override lazy val evaluated = !isLazy +} + +trait LazyCollection[V <: Value] extends CollectionLeaf[V] with NotReadyValue[LazyCollection[V]] + + +case object Inputs extends LazyCollection[BoxLeaf] { + val cost = 1 } -trait CustomVariable[V <: Value] extends Variable[Value] { +case object Outputs extends LazyCollection[BoxLeaf] { + val cost = 1 +} + + +trait CustomVariable[V <: Value] extends NotReadyValue[V] { + self: V => val id: Int } -case class CustomByteArray(override val id: Int) extends CustomVariable[ByteArrayLeaf] { +case class CustomByteArray(override val id: Int) extends CustomVariable[ByteArrayLeaf] with NotReadyValueByteArray { override def cost: Int = Cost.ByteArrayDeclaration } -sealed trait OneArgumentOperation extends StateTree { - val operand: SigmaStateTree + +sealed trait CalcBlake2b256 extends Transformer[ByteArrayLeaf, ByteArrayLeaf] with NotReadyValueByteArray { + override def function(bal: EvaluatedValue[ByteArrayLeaf]): ByteArrayLeaf = + ByteArrayLeafConstant(Blake2b256(bal.value)) + } -case class CalcBlake2b256(operand: SigmaStateTree) extends OneArgumentOperation { - override def cost: Int = operand.cost + Cost.Blake256bDeclaration +case class CalcBlake2b256Inst(input: ByteArrayLeaf) + extends CalcBlake2b256 with TransformerInstantiation[ByteArrayLeaf, ByteArrayLeaf]{ + + override def cost: Int = input.cost + Cost.Blake256bDeclaration } +case object CalcBlake2b256Fn extends CalcBlake2b256 { + override def cost: Int = Cost.Blake256bDeclaration + + override def instantiate(input: ByteArrayLeaf) = CalcBlake2b256Inst(input) + + override type M = this.type +} + + /** * A tree node with left and right descendants */ -sealed trait Triple extends StateTree { - val left: SigmaStateTree - val right: SigmaStateTree +sealed trait Triple[LIV <: Value, RIV <: Value, OV <: Value] extends NotReadyValue[OV] {self: OV => + val left: LIV + val right: RIV override def cost: Int = left.cost + right.cost + Cost.TripleDeclaration - //todo: define via F-Bounded polymorphism? - def withLeft(newLeft: SigmaStateTree): Relation + def withLeft(newLeft: LIV): Triple[LIV, RIV, OV] - def withRight(newRight: SigmaStateTree): Relation + def withRight(newRight: RIV): Triple[LIV, RIV, OV] } -sealed trait TwoArgumentsOperation extends Triple +sealed trait TwoArgumentsOperation[LIV <: Value, RIV <: Value, OV <: Value] extends Triple[LIV, RIV, OV]{self : OV => } -case class Plus(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { +case class Plus(override val left: IntLeaf, + override val right: IntLeaf) + extends TwoArgumentsOperation[IntLeaf, IntLeaf, IntLeaf] with NotReadyValueIntLeaf { - def withLeft(newLeft: SigmaStateTree): Plus = copy(left = newLeft) + def withLeft(newLeft: IntLeaf): Plus = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): Plus = copy(right = newRight) + def withRight(newRight: IntLeaf): Plus = copy(right = newRight) } -case class Minus(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): Minus = copy(left = newLeft) +case class Minus(override val left: IntLeaf, + override val right: IntLeaf) + extends TwoArgumentsOperation[IntLeaf, IntLeaf, IntLeaf] with NotReadyValueIntLeaf { - def withRight(newRight: SigmaStateTree): Minus = copy(right = newRight) + def withLeft(newLeft: IntLeaf): Minus = copy(left = newLeft) + + def withRight(newRight: IntLeaf): Minus = copy(right = newRight) } -case class Xor(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): Xor = copy(left = newLeft) +case class Xor(override val left: ByteArrayLeaf, + override val right: ByteArrayLeaf) + extends TwoArgumentsOperation[ByteArrayLeaf, ByteArrayLeaf, ByteArrayLeaf] with NotReadyValueByteArray { - def withRight(newRight: SigmaStateTree): Xor = copy(right = newRight) + def withLeft(newLeft: ByteArrayLeaf): Xor = copy(left = newLeft) + def withRight(newRight: ByteArrayLeaf): Xor = copy(right = newRight) } -case class Append(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): Append = copy(left = newLeft) +case class Append(override val left: ByteArrayLeaf, + override val right: ByteArrayLeaf) + extends TwoArgumentsOperation[ByteArrayLeaf, ByteArrayLeaf, ByteArrayLeaf] with NotReadyValueByteArray { + + def withLeft(newLeft: ByteArrayLeaf): Append = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): Append = copy(right = newRight) + def withRight(newRight: ByteArrayLeaf): Append = copy(right = newRight) } -sealed trait Relation extends Triple +sealed trait Relation[LIV <: Value, RIV <: Value] extends Triple[LIV, RIV, BooleanLeaf] with NotReadyValueBoolean + +case class LT(override val left: IntLeaf, + override val right: IntLeaf) extends Relation[IntLeaf, IntLeaf] { -case class LT(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): LT = copy(left = newLeft) + def withLeft(newLeft: IntLeaf): LT = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): LT = copy(right = newRight) + def withRight(newRight: IntLeaf): LT = copy(right = newRight) } -case class LE(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): LE = copy(left = newLeft) +case class LE(override val left: IntLeaf, + override val right: IntLeaf) extends Relation[IntLeaf, IntLeaf] { + def withLeft(newLeft: IntLeaf): LE = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): LE = copy(right = newRight) + def withRight(newRight: IntLeaf): LE = copy(right = newRight) } -case class GT(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): GT = copy(left = newLeft) +case class GT(override val left: IntLeaf, + override val right: IntLeaf) extends Relation[IntLeaf, IntLeaf] { + def withLeft(newLeft: IntLeaf): GT = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): GT = copy(right = newRight) + def withRight(newRight: IntLeaf): GT = copy(right = newRight) } -case class GE(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): GE = copy(left = newLeft) +case class GE(override val left: IntLeaf, + override val right: IntLeaf) extends Relation[IntLeaf, IntLeaf] { + def withLeft(newLeft: IntLeaf): GE = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): GE = copy(right = newRight) + def withRight(newRight: IntLeaf): GE = copy(right = newRight) } -case class EQ(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): EQ = copy(left = newLeft) +case class EQ[V<: Value](override val left: V, + override val right: V) extends Relation[V, V] { + def withLeft(newLeft: V): EQ[V] = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): EQ = copy(right = newRight) + def withRight(newRight: V): EQ[V] = copy(right = newRight) } -case class NEQ(override val left: SigmaStateTree, - override val right: SigmaStateTree) extends Relation { - def withLeft(newLeft: SigmaStateTree): NEQ = copy(left = newLeft) +case class NEQ[V<: Value](override val left: V, override val right: V) extends Relation[V, V] { + def withLeft(newLeft: V): NEQ[V] = copy(left = newLeft) - def withRight(newRight: SigmaStateTree): NEQ = copy(right = newRight) + def withRight(newRight: V): NEQ[V] = copy(right = newRight) } diff --git a/src/main/scala/sigmastate/utxo/Function.scala b/src/main/scala/sigmastate/utxo/Function.scala index 144136c982..f164325068 100644 --- a/src/main/scala/sigmastate/utxo/Function.scala +++ b/src/main/scala/sigmastate/utxo/Function.scala @@ -1,16 +1,19 @@ package sigmastate.utxo -import sigmastate.{Relation, StateTree} +import sigmastate.{Relation, StateTree, Value} import sigmastate.utxo.CostTable.Cost trait Function extends StateTree -case class TxHasOutput(relation: Relation*) extends Function { +/* +case class TxHasOutput(relation: Relation[Value, Value]*) extends Function { override val cost: Int = relation.length + Cost.TxHasOutputDeclaration -} +}*/ -case class TxOutput(outIndex: Int, relation: Relation*) extends Function { +//todo: replace with ByIndex on Collection +//todo: derive from BoxLeaf +case class TxOutput(outIndex: Int, relation: Relation[Value, Value]*) extends Function { override val cost: Int = relation.length + Cost.TxOutputDeclaration } diff --git a/src/main/scala/sigmastate/utxo/SelfVariable.scala b/src/main/scala/sigmastate/utxo/SelfVariable.scala index 3d193060c7..0421713d0e 100644 --- a/src/main/scala/sigmastate/utxo/SelfVariable.scala +++ b/src/main/scala/sigmastate/utxo/SelfVariable.scala @@ -1,17 +1,23 @@ package sigmastate.utxo -import sigmastate._ +import sigmastate.{NotReadyValueIntLeaf, _} import sigmastate.interpreter.{Context, ContextExtension} import sigmastate.utxo.CostTable.Cost +import sigmastate.utxo.SigmaStateBox.RegisterIdentifier import sigmastate.utxo.UtxoContext.Height +import org.bitbucket.inkytonik.kiama.rewriting.Rewriter.{everywherebu, rule} + + + case class BoxMetadata(creationHeight: Height, boxIndex: Short) -case class BowWithMetadata(box: SigmaStateBox, metadata: BoxMetadata) +case class BoxWithMetadata(box: SigmaStateBox, metadata: BoxMetadata) case class UtxoContext(currentHeight: Height, + boxesToSpend: Seq[BoxWithMetadata], spendingTransaction: SigmaStateTransaction, - self: BowWithMetadata, + self: BoxWithMetadata, override val extension: ContextExtension = ContextExtension(Map()) ) extends Context[UtxoContext] { override def withExtension(newExtension: ContextExtension): UtxoContext = this.copy(extension = newExtension) @@ -21,24 +27,235 @@ object UtxoContext { type Height = Long } -trait SelfVariable[V <: Value] extends Variable[V] { - override def cost: Int = Cost.SelfVariableDeclaration + +trait Transformer[IV <: Value, OV <: Value] extends NotReadyValue[OV] { + self: OV => + val abstractTransformation = true + + def function(input: EvaluatedValue[IV]): OV + + def instantiate(input: IV): TransformerInstantiation[IV, OV] +} + +trait TransformerInstantiation[IV <: Value, OV <: Value] extends Transformer[IV, OV] { + self: OV => + val input: IV + + def transformationReady: Boolean = input.evaluated + + override val abstractTransformation = false + + override def instantiate(input: IV) = this + + def evaluate(): OV = input match { + case ev: EvaluatedValue[IV] => function(ev) + case _: NotReadyValue[OV] => this + } +} + +case class MapCollection[IV <: Value, OV <: Value](input: CollectionLeaf[IV], mapper: Transformer[IV, OV]) + extends TransformerInstantiation[CollectionLeaf[IV], CollectionLeaf[OV]] with CollectionLeaf[OV] { + + override def transformationReady: Boolean = + input.evaluated && input.asInstanceOf[ConcreteCollection[IV]].value.forall(_.isInstanceOf[EvaluatedValue[_]]) + + override def function(cl: EvaluatedValue[CollectionLeaf[IV]]): CollectionLeaf[OV] = + ConcreteCollection(cl.value.map(el => mapper.function(el.asInstanceOf[EvaluatedValue[IV]]))) + + override def cost: Int = 1 + + override type M = this.type } -case object SelfHeight extends SelfVariable[IntLeaf] +case class Exists[IV <: Value](input: CollectionLeaf[IV], relations: Relation[_ <: Value, _ <: Value]*) + extends TransformerInstantiation[CollectionLeaf[IV], BooleanLeaf] with NotReadyValueBoolean { + + override def transformationReady: Boolean = + input.evaluated && input.asInstanceOf[ConcreteCollection[IV]].value.forall(_.isInstanceOf[EvaluatedValue[_]]) + + override val cost: Int = input.cost + relations.size + + override def function(input: EvaluatedValue[CollectionLeaf[IV]]): BooleanLeaf = { + def rl(arg: IV) = everywherebu(rule[Transformer[IV, _ <: Value]] { + case t: Transformer[IV, _] => t.instantiate(arg) + }) + + OR(input.value.map(el => rl(el)(AND(relations)).get.asInstanceOf[BooleanLeaf])) + } + + override type M = this.type +} -case object SelfAmount extends SelfVariable[IntLeaf] -case object SelfScript extends SelfVariable[PropLeaf] +trait Fold[IV <: Value] extends TransformerInstantiation[CollectionLeaf[IV], IV] with NotReadyValue[IV] { + self: IV => + val input: CollectionLeaf[IV] + val folder: (IV, IV) => IV + val zero: IV +} + +case class Sum(override val input: CollectionLeaf[IntLeaf]) extends Fold[IntLeaf] with NotReadyValueIntLeaf { + + override def transformationReady: Boolean = + input.evaluated && input.asInstanceOf[ConcreteCollection[IntLeaf]].value.forall(_.isInstanceOf[EvaluatedValue[_]]) + + val folder = { + case (s, i) => + (s, i) match { + case (si: IntLeafConstant, ii: IntLeafConstant) => IntLeafConstant(si.value + ii.value) + case _ => UnknownIntLeaf + } + }: (IntLeaf, IntLeaf) => IntLeaf + val zero = IntLeafConstant(0) + + override def function(input: EvaluatedValue[CollectionLeaf[IntLeaf]]): IntLeaf = ??? //todo: impl + + override type M = this.type +} + +sealed abstract class Extract[V <: Value] extends Transformer[BoxLeaf, V] { + self: V => + override def function(box: EvaluatedValue[BoxLeaf]): V + +} + +sealed trait ExtractHeight extends Extract[IntLeaf] with NotReadyValueIntLeaf { + override def cost: Int = 10 + + override type M = this.type + + override def function(box: EvaluatedValue[BoxLeaf]): IntLeaf = IntLeafConstant(box.value.metadata.creationHeight) +} + +case class ExtractHeightInst(input: BoxLeaf) extends ExtractHeight with TransformerInstantiation[BoxLeaf, IntLeaf] + +case object ExtractHeightFn extends ExtractHeight { + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, IntLeaf] = ExtractHeightInst(input) +} + + +sealed trait ExtractAmount extends Extract[IntLeaf] with NotReadyValueIntLeaf { + override def cost: Int = 10 + + override type M = this.type + + override def function(box: EvaluatedValue[BoxLeaf]): IntLeaf = IntLeafConstant(box.value.box.value) +} + +case class ExtractAmountInst(input: BoxLeaf) extends ExtractAmount with TransformerInstantiation[BoxLeaf, IntLeaf] -case object OutputAmount extends Variable[IntLeaf] { - override val cost: Int = Cost.OutputAmount +case object ExtractAmountFn extends ExtractAmount { + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, IntLeaf] = ExtractAmountInst(input) } -case object OutputScript extends Variable[PropLeaf] { - override val cost: Int = Cost.OutputScript + +sealed trait ExtractScript extends Extract[PropLeaf] with NotReadyValueProp { + override def cost: Int = 10 + + override type M = this.type + + override def function(box: EvaluatedValue[BoxLeaf]): PropLeaf = { + println(new String(box.value.box.propositionBytes)) + PropLeafConstant(box.value) + } +} + +case class ExtractScriptInst(input: BoxLeaf) extends ExtractScript with TransformerInstantiation[BoxLeaf, PropLeaf] + +case object ExtractScriptFn extends ExtractScript { + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, PropLeaf] = ExtractScriptInst(input) +} + + +sealed trait ExtractBytes extends Extract[ByteArrayLeaf] with NotReadyValueByteArray { + override def cost: Int = 10 + + override type M = this.type + + override def function(box: EvaluatedValue[BoxLeaf]): ByteArrayLeaf = ByteArrayLeafConstant(box.value.box.bytes) +} + +case class ExtractBytesInst(input: BoxLeaf) + extends ExtractBytes with TransformerInstantiation[BoxLeaf, ByteArrayLeaf] + +case object ExtractBytesFn extends ExtractBytes { + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, ByteArrayLeaf] = ExtractBytesInst(input) +} + + +abstract class ExtractRegisterAs[V <: Value] extends Extract[V] { + self: V => + val registerId: RegisterIdentifier + + override def cost: Int = 10 + + override type M = this.type + + override def function(box: EvaluatedValue[BoxLeaf]): V = box.value.box.get(registerId).get.asInstanceOf[V] +} + +case class ExtractRegisterAsIntLeafInst(input: BoxLeaf, registerId: RegisterIdentifier) + extends ExtractRegisterAs[IntLeaf] with TransformerInstantiation[BoxLeaf, IntLeaf] with NotReadyValueIntLeaf + +case class ExtractRegisterAsIntLeaf(registerId: RegisterIdentifier) + extends ExtractRegisterAs[IntLeaf] with NotReadyValueIntLeaf { + + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, IntLeaf] = + ExtractRegisterAsIntLeafInst(input, registerId) +} + + +case class ExtractRegisterAsBooleanLeafInst(input: BoxLeaf, registerId: RegisterIdentifier) + extends ExtractRegisterAs[BooleanLeaf] with TransformerInstantiation[BoxLeaf, BooleanLeaf] with NotReadyValueBoolean + +case class ExtractRegisterAsBooleanLeaf(registerId: RegisterIdentifier) + extends ExtractRegisterAs[BooleanLeaf] with NotReadyValueBoolean { + + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, BooleanLeaf] = + ExtractRegisterAsBooleanLeafInst(input, registerId) +} + + +case class ExtractRegisterAsByteArrayLeafInst(input: BoxLeaf, registerId: RegisterIdentifier) + extends ExtractRegisterAs[ByteArrayLeaf] with TransformerInstantiation[BoxLeaf, ByteArrayLeaf] with NotReadyValueByteArray + +case class ExtractRegisterAsByteArrayLeaf(registerId: RegisterIdentifier) + extends ExtractRegisterAs[ByteArrayLeaf] with NotReadyValueByteArray { + + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, ByteArrayLeaf] = + ExtractRegisterAsByteArrayLeafInst(input, registerId) +} + + +case class ExtractRegisterAsPropLeafInst(input: BoxLeaf, registerId: RegisterIdentifier) + extends ExtractRegisterAs[PropLeaf] with TransformerInstantiation[BoxLeaf, PropLeaf] with NotReadyValueProp + +case class ExtractRegisterAsPropLeaf(registerId: RegisterIdentifier) + extends ExtractRegisterAs[PropLeaf] with NotReadyValueProp { + + override def instantiate(input: BoxLeaf): TransformerInstantiation[BoxLeaf, PropLeaf] = + ExtractRegisterAsPropLeafInst(input, registerId) +} + +//todo: extract as box leaf + + +/* +todo: implement + +object Forall +object FoldLeft +object Append +object Slice +object ByIndex +*/ + +case object Self extends NotReadyValueBoxLeaf { + override def cost: Int = 10 + + override type M = this.type } -case object TxOutBytes extends Variable[ByteArrayLeaf] { +case object TxOutBytes extends NotReadyValueByteArray { override val cost: Int = Cost.TxOutBytes } \ No newline at end of file diff --git a/src/main/scala/sigmastate/utxo/SigmaStateTransaction.scala b/src/main/scala/sigmastate/utxo/SigmaStateTransaction.scala index 56eae33818..53025ab9ee 100644 --- a/src/main/scala/sigmastate/utxo/SigmaStateTransaction.scala +++ b/src/main/scala/sigmastate/utxo/SigmaStateTransaction.scala @@ -6,27 +6,58 @@ import scorex.core.serialization.Serializer import scorex.core.transaction.BoxTransaction import scorex.core.transaction.box.{Box, BoxUnlocker} import scorex.core.transaction.proof.Proof -import sigmastate.SigmaStateTree +import sigmastate._ +import sigmastate.utxo.SigmaStateBox.NonMandatoryIdentifier import scala.util.Try case class SigmaStateBox(override val value: Long, - override val proposition: SigmaStateTree) extends Box[SigmaStateTree] { + override val proposition: SigmaStateTree, + additionalRegisters: Map[NonMandatoryIdentifier, _ <: Value] = Map() + ) extends Box[SigmaStateTree] { + import sigmastate.utxo.SigmaStateBox._ + + def get(identifier: RegisterIdentifier): Option[_ <: Value] = { + identifier match { + case R1 => Some(IntLeafConstant(value)) + case R2 => Some(PropLeafConstant(propositionBytes)) + case n: NonMandatoryIdentifier => additionalRegisters.get(n) + } + } override lazy val id = ??? override type M = SigmaStateBox - //todo: implement + //todo: implement real + val propositionBytes = proposition.toString.getBytes + override def serializer: Serializer[SigmaStateBox] = new Serializer[SigmaStateBox] { override def toBytes(obj: SigmaStateBox): Array[Byte] = - Longs.toByteArray(obj.value) ++ obj.proposition.toString.getBytes + Longs.toByteArray(obj.value) ++ obj.propositionBytes override def parseBytes(bytes: Array[Byte]): Try[SigmaStateBox] = ??? } } +object SigmaStateBox { + sealed trait RegisterIdentifier + sealed trait NonMandatoryIdentifier extends RegisterIdentifier + + object R1 extends RegisterIdentifier + object R2 extends RegisterIdentifier + object R3 extends RegisterIdentifier with NonMandatoryIdentifier + object R4 extends RegisterIdentifier with NonMandatoryIdentifier + object R5 extends RegisterIdentifier with NonMandatoryIdentifier + object R6 extends RegisterIdentifier with NonMandatoryIdentifier + object R7 extends RegisterIdentifier with NonMandatoryIdentifier + object R8 extends RegisterIdentifier with NonMandatoryIdentifier + object R9 extends RegisterIdentifier with NonMandatoryIdentifier + object R10 extends RegisterIdentifier with NonMandatoryIdentifier +} + + class SigmaStateBoxUnlocker extends BoxUnlocker[SigmaStateTree] { override val closedBoxId: Array[Byte] = ??? override val boxKey: Proof[SigmaStateTree] = ??? @@ -44,4 +75,4 @@ case class SigmaStateTransaction(override val unlockers: Seq[SigmaStateBoxUnlock override def serializer: Serializer[SigmaStateTransaction] = ??? override def json: Json = ??? -} +} \ No newline at end of file diff --git a/src/main/scala/sigmastate/utxo/UtxoInterpreter.scala b/src/main/scala/sigmastate/utxo/UtxoInterpreter.scala index 3d3053d1ce..21c7ef0342 100644 --- a/src/main/scala/sigmastate/utxo/UtxoInterpreter.scala +++ b/src/main/scala/sigmastate/utxo/UtxoInterpreter.scala @@ -6,95 +6,75 @@ import org.bitbucket.inkytonik.kiama.rewriting.Rewriter._ import org.bitbucket.inkytonik.kiama.rewriting.Strategy import sigmastate.interpreter.{CostAccumulator, Interpreter} -import scala.collection.mutable - class UtxoInterpreter(override val maxCost: Int = CostTable.ScriptLimit) extends Interpreter { override type StateT = StateTree override type CTX = UtxoContext - private def replaceTxOut(idxOut: TxOutput, context: UtxoContext): AND = { - val ts = idxOut.relation.map { rel => - (rel.left, rel.right) match { - case (OutputAmount, _) | (_, OutputAmount) => OutputAmount -> rel - case (OutputScript, _) | (_, OutputScript) => OutputScript -> rel - case _ => ??? - } - } + def ssSubst(context: UtxoContext, cost: CostAccumulator): Strategy = everywherebu(rule[SigmaStateTree] { + case Inputs => ConcreteCollection(context.boxesToSpend.map(BoxLeafConstant.apply)) - val out = context.spendingTransaction.newBoxes(idxOut.outIndex) - val amount = out.value - val bindings = mutable.Map[Variable[_], Value]() - - val relations = ts.map { case (v, r) => - v match { - case OutputAmount => - bindings.put(OutputAmount, IntLeaf(amount)) - r - case OutputScript => - bindings.put(OutputScript, PropLeaf(out.proposition)) - r - case _ => ??? - } - } - val rels = relations.map { r => - val rl = r.left match { - case v: Variable[_] => - bindings.get(v) match { - case Some(value) => r.withLeft(value) - case None => r - } - case _ => r - } + case Outputs => ConcreteCollection(context.spendingTransaction.newBoxes + .zipWithIndex + .map { case (b, i) => BoxWithMetadata(b, BoxMetadata(context.currentHeight, i.toShort)) } + .map(BoxLeafConstant.apply)) - rl.right match { - case v: Variable[_] => - bindings.get(v) match { - case Some(value) => rl.withRight(value) - case None => rl - } - case _ => rl - } - } - AND(rels) - } + case Self => BoxLeafConstant(context.self) - def ssSubst(context: UtxoContext, cost: CostAccumulator): Strategy = everywherebu(rule[SigmaStateTree] { - case SelfScript => - val leaf = PropLeaf(context.self.box.proposition) - cost.addCost(leaf.cost).ensuring(_.isRight) - leaf //todo: cache the bytes as a lazy val in the transaction case TxOutBytes => - val outBytes = Bytes.concat(context.spendingTransaction.newBoxes.map(_.bytes):_*) - val leaf = ByteArrayLeaf(outBytes) + val outBytes = Bytes.concat(context.spendingTransaction.newBoxes.map(_.bytes): _*) + val leaf = ByteArrayLeafConstant(outBytes) cost.addCost(leaf.cost).ensuring(_.isRight) leaf - case idxOut: TxOutput => replaceTxOut(idxOut, context) + case e: Exists[_] if e.transformationReady => e.input match { + case c: ConcreteCollection[_] => e.function(c) + case _ => ??? + } - case hasOut: TxHasOutput => - val s = context.spendingTransaction.newBoxes.size - val outConditions = (0 until s).map { idx => - val out = TxOutput(idx, hasOut.relation:_*) - val and = replaceTxOut(out, context) - cost.addCost(and.cost).ensuring(_.isRight) - and + case m@MapCollection(coll, mapper) if m.transformationReady => + m.function(coll.asInstanceOf[ConcreteCollection[Value]]) + + case sum@Sum(coll) if sum.transformationReady => + coll.asInstanceOf[ConcreteCollection[IntLeaf]].value.foldLeft(sum.zero: IntLeaf) { case (s: IntLeaf, i: IntLeaf) => + sum.folder(s, i): IntLeaf } - cost.addCost(outConditions.length).ensuring(_.isRight) - OR(outConditions) + }) + + def functions(cost:CostAccumulator): Strategy = everywherebu(rule[Value]{ + //todo: reduce boilerplate below + case ex@ExtractScriptInst(box: BoxLeafConstant) => + val leaf = ex.function(box) + cost.addCost(leaf.cost).ensuring(_.isRight) + leaf + + case ex@ExtractHeightInst(box: BoxLeafConstant) => + ex.function(box) + + case ex@ExtractAmountInst(box: BoxLeafConstant) => + ex.function(box) + + case ex@ExtractBytesInst(box: BoxLeafConstant) => + val leaf = ex.function(box) + cost.addCost(leaf.cost).ensuring(_.isRight) + leaf + + case ex@ExtractRegisterAsIntLeafInst(box: BoxLeafConstant, rid) => + val leaf = ex.function(box) + cost.addCost(leaf.cost).ensuring(_.isRight) + leaf }) def varSubst(context: UtxoContext): Strategy = everywherebu( rule[Value] { - case Height => IntLeaf(context.currentHeight) - case SelfHeight => IntLeaf(context.self.metadata.creationHeight) - case SelfAmount => IntLeaf(context.self.box.value) + case Height => IntLeafConstant(context.currentHeight) }) override def specificPhases(tree: SigmaStateTree, context: UtxoContext, cost: CostAccumulator): SigmaStateTree = { val afterSs = ssSubst(context, cost)(tree).get.asInstanceOf[SigmaStateTree] - varSubst(context)(afterSs).get.asInstanceOf[SigmaStateTree] + val afterVar = varSubst(context)(afterSs).get.asInstanceOf[SigmaStateTree] + functions(cost)(afterVar).get.asInstanceOf[SigmaStateTree] } } \ No newline at end of file diff --git a/src/test/scala/sigmastate/TestingInterpreterSpecification.scala b/src/test/scala/sigmastate/TestingInterpreterSpecification.scala index 5d6cc4f64b..282badfa4a 100644 --- a/src/test/scala/sigmastate/TestingInterpreterSpecification.scala +++ b/src/test/scala/sigmastate/TestingInterpreterSpecification.scala @@ -27,7 +27,7 @@ object TestingInterpreter extends Interpreter with ProverInterpreter { override def specificPhases(tree: SigmaStateTree, context: TestingContext, cost: CostAccumulator): SigmaStateTree = everywherebu(rule[Value] { - case Height => IntLeaf(context.height) + case Height => IntLeafConstant(context.height) })(tree).get.asInstanceOf[SigmaStateTree] override lazy val secrets: Seq[DLogProverInput] = { @@ -36,7 +36,7 @@ object TestingInterpreter extends Interpreter with ProverInterpreter { Seq(DLogProverInput.random()._1, DLogProverInput.random()._1) } - override val contextExtenders: Map[Int, ByteArrayLeaf] = Map[Int, ByteArrayLeaf]() + override val contextExtenders: Map[Int, ByteArrayLeafConstant] = Map[Int, ByteArrayLeafConstant]() } class TestingInterpreterSpecification extends PropSpec @@ -55,13 +55,13 @@ class TestingInterpreterSpecification extends PropSpec val dk1 = DLogNode(DLogProverInput.random()._2.h) val env = TestingContext(h) - assert(reduceToCrypto(AND(GE(Height, IntLeaf(h - 1)), dk1), env).get.isInstanceOf[DLogNode]) - assert(reduceToCrypto(AND(GE(Height, IntLeaf(h)), dk1), env).get.isInstanceOf[DLogNode]) - assert(reduceToCrypto(AND(GE(Height, IntLeaf(h + 1)), dk1), env).get.isInstanceOf[FalseLeaf.type]) + assert(reduceToCrypto(AND(GE(Height, IntLeafConstant(h - 1)), dk1), env).get.isInstanceOf[DLogNode]) + assert(reduceToCrypto(AND(GE(Height, IntLeafConstant(h)), dk1), env).get.isInstanceOf[DLogNode]) + assert(reduceToCrypto(AND(GE(Height, IntLeafConstant(h + 1)), dk1), env).get.isInstanceOf[FalseLeaf.type]) - assert(reduceToCrypto(OR(GE(Height, IntLeaf(h - 1)), dk1), env).get.isInstanceOf[TrueLeaf.type]) - assert(reduceToCrypto(OR(GE(Height, IntLeaf(h)), dk1), env).get.isInstanceOf[TrueLeaf.type]) - assert(reduceToCrypto(OR(GE(Height, IntLeaf(h + 1)), dk1), env).get.isInstanceOf[DLogNode]) + assert(reduceToCrypto(OR(GE(Height, IntLeafConstant(h - 1)), dk1), env).get.isInstanceOf[TrueLeaf.type]) + assert(reduceToCrypto(OR(GE(Height, IntLeafConstant(h)), dk1), env).get.isInstanceOf[TrueLeaf.type]) + assert(reduceToCrypto(OR(GE(Height, IntLeafConstant(h + 1)), dk1), env).get.isInstanceOf[DLogNode]) } } } @@ -77,26 +77,26 @@ class TestingInterpreterSpecification extends PropSpec val env = TestingContext(h) assert(reduceToCrypto(OR( - AND(LE(Height, IntLeaf(h + 1)), AND(dk1, dk2)), - AND(GT(Height, IntLeaf(h + 1)), dk1) + AND(LE(Height, IntLeafConstant(h + 1)), AND(dk1, dk2)), + AND(GT(Height, IntLeafConstant(h + 1)), dk1) ), env).get.isInstanceOf[CAND]) assert(reduceToCrypto(OR( - AND(LE(Height, IntLeaf(h - 1)), AND(dk1, dk2)), - AND(GT(Height, IntLeaf(h - 1)), dk1) + AND(LE(Height, IntLeafConstant(h - 1)), AND(dk1, dk2)), + AND(GT(Height, IntLeafConstant(h - 1)), dk1) ), env).get.isInstanceOf[DLogNode]) assert(reduceToCrypto(OR( - AND(LE(Height, IntLeaf(h - 1)), AND(dk1, dk2)), - AND(GT(Height, IntLeaf(h + 1)), dk1) + AND(LE(Height, IntLeafConstant(h - 1)), AND(dk1, dk2)), + AND(GT(Height, IntLeafConstant(h + 1)), dk1) ), env).get.isInstanceOf[FalseLeaf.type]) assert(reduceToCrypto(OR(OR( - AND(LE(Height, IntLeaf(h - 1)), AND(dk1, dk2)), - AND(GT(Height, IntLeaf(h + 1)), dk1) - ), AND(GT(Height, IntLeaf(h - 1)), LE(Height, IntLeaf(h + 1)))), env).get.isInstanceOf[TrueLeaf.type]) + AND(LE(Height, IntLeafConstant(h - 1)), AND(dk1, dk2)), + AND(GT(Height, IntLeafConstant(h + 1)), dk1) + ), AND(GT(Height, IntLeafConstant(h - 1)), LE(Height, IntLeafConstant(h + 1)))), env).get.isInstanceOf[TrueLeaf.type]) } } @@ -110,8 +110,8 @@ class TestingInterpreterSpecification extends PropSpec val env2 = TestingContext(101) val prop = OR( - AND(LE(Height, IntLeaf(100)), AND(dk1, dk2)), - AND(GT(Height, IntLeaf(100)), dk1) + AND(LE(Height, IntLeafConstant(100)), AND(dk1, dk2)), + AND(GT(Height, IntLeafConstant(100)), dk1) ) val challenge = Array.fill(32)(Random.nextInt(100).toByte) @@ -138,7 +138,7 @@ class TestingInterpreterSpecification extends PropSpec val prop3 = AND(TrueLeaf, TrueLeaf) verify(prop3, env, proof, challenge).getOrElse(false) shouldBe true - val prop4 = GT(Height, IntLeaf(90)) + val prop4 = GT(Height, IntLeafConstant(90)) verify(prop4, env, proof, challenge).getOrElse(false) shouldBe true } @@ -157,7 +157,7 @@ class TestingInterpreterSpecification extends PropSpec val prop3 = AND(FalseLeaf, TrueLeaf) verify(prop3, env, proof, challenge).getOrElse(false) shouldBe false - val prop4 = GT(Height, IntLeaf(100)) + val prop4 = GT(Height, IntLeafConstant(100)) verify(prop4, env, proof, challenge).getOrElse(false) shouldBe false } @@ -165,7 +165,7 @@ class TestingInterpreterSpecification extends PropSpec val bytes = "hello world".getBytes val hash = Blake2b256(bytes) - val prop1 = EQ(CalcBlake2b256(ByteArrayLeaf(bytes)), ByteArrayLeaf(hash)) + val prop1 = EQ(CalcBlake2b256Inst(ByteArrayLeafConstant(bytes)), ByteArrayLeafConstant(hash)) val challenge = Array.fill(32)(Random.nextInt(100).toByte) val proof = NoProof @@ -173,11 +173,11 @@ class TestingInterpreterSpecification extends PropSpec verify(prop1, env, proof, challenge).getOrElse(false) shouldBe true - val prop2 = NEQ(CalcBlake2b256(ByteArrayLeaf(bytes)), ByteArrayLeaf(hash)) + val prop2 = NEQ(CalcBlake2b256Inst(ByteArrayLeafConstant(bytes)), ByteArrayLeafConstant(hash)) verify(prop2, env, proof, challenge).getOrElse(false) shouldBe false - val prop3 = EQ(CalcBlake2b256(ByteArrayLeaf(bytes)), ByteArrayLeaf(bytes)) + val prop3 = EQ(CalcBlake2b256Inst(ByteArrayLeafConstant(bytes)), ByteArrayLeafConstant(bytes)) verify(prop3, env, proof, challenge).getOrElse(false) shouldBe false } diff --git a/src/test/scala/sigmastate/utxo/BoxHelpers.scala b/src/test/scala/sigmastate/utxo/BoxHelpers.scala index fcd1c999a2..45692b934d 100644 --- a/src/test/scala/sigmastate/utxo/BoxHelpers.scala +++ b/src/test/scala/sigmastate/utxo/BoxHelpers.scala @@ -4,5 +4,5 @@ import sigmastate.SigmaStateTree object BoxHelpers { def boxWithMetadata(value: Int, proposition: SigmaStateTree, creationHeight: Int = 0, boxIndex: Short = 0) = - BowWithMetadata(SigmaStateBox(value, proposition), BoxMetadata(creationHeight, boxIndex)) + BoxWithMetadata(SigmaStateBox(value, proposition), BoxMetadata(creationHeight, boxIndex)) } \ No newline at end of file diff --git a/src/test/scala/sigmastate/utxo/SpamSpecification.scala b/src/test/scala/sigmastate/utxo/SpamSpecification.scala index 4fafab8688..f1f2ee9824 100644 --- a/src/test/scala/sigmastate/utxo/SpamSpecification.scala +++ b/src/test/scala/sigmastate/utxo/SpamSpecification.scala @@ -47,12 +47,12 @@ class SpamSpecification extends PropSpec val tag = Helpers.tagInt(ba) - val prover = new UtxoProvingInterpreter(CostTable.ScriptLimit * 10).withContextExtender(tag, ByteArrayLeaf(ba)) + val prover = new UtxoProvingInterpreter(CostTable.ScriptLimit * 10).withContextExtender(tag, ByteArrayLeafConstant(ba)) - val spamScript = EQ(CalcBlake2b256(CustomByteArray(tag)), CalcBlake2b256(CustomByteArray(tag))) + val spamScript = EQ(CalcBlake2b256Inst(CustomByteArray(tag)), CalcBlake2b256Inst(CustomByteArray(tag))) val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) val prt = prover.prove(spamScript, ctx, message) prt.isSuccess shouldBe true @@ -73,16 +73,16 @@ class SpamSpecification extends PropSpec val tag = Helpers.tagInt(ba) - val prover = new UtxoProvingInterpreter(CostTable.ScriptLimit * 10).withContextExtender(tag, ByteArrayLeaf(ba)) + val prover = new UtxoProvingInterpreter(CostTable.ScriptLimit * 10).withContextExtender(tag, ByteArrayLeafConstant(ba)) - val bigSubScript = (1 to 289).foldLeft(CalcBlake2b256(CustomByteArray(tag))) { case (script, _) => - CalcBlake2b256(script) + val bigSubScript = (1 to 289).foldLeft(CalcBlake2b256Inst(CustomByteArray(tag))) { case (script, _) => + CalcBlake2b256Inst(script) } - val spamScript = NEQ(bigSubScript, CalcBlake2b256(ByteArrayLeaf(Array.fill(32)(0: Byte)))) + val spamScript = NEQ(bigSubScript, CalcBlake2b256Inst(ByteArrayLeafConstant(Array.fill(32)(0: Byte)))) val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) val prt = prover.prove(spamScript, ctx, message) prt.isSuccess shouldBe true @@ -108,7 +108,7 @@ class SpamSpecification extends PropSpec //fake message, in a real-life a message is to be derived from a spending transaction val message = Blake2b256("Hello World") val fakeSelf = boxWithMetadata(0, TrueLeaf) - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) val publicImages = secret.publicImage +: simulated val prop = OR(publicImages) @@ -131,12 +131,14 @@ class SpamSpecification extends PropSpec whenever(orCnt > 10 && outCnt > 200) { val prover = new UtxoProvingInterpreter(maxCost = CostTable.ScriptLimit * 1000) - val propToCompare = OR((1 to orCnt).map(_ => IntLeaf(5))) + val propToCompare = OR((1 to orCnt).map(_ => IntLeafConstant(5))) - val spamProp = OR((1 until orCnt).map(_ => IntLeaf(5)) :+ IntLeaf(6)) + val spamProp = OR((1 until orCnt).map(_ => IntLeafConstant(5)) :+ IntLeafConstant(6)) val spamScript = - TxHasOutput(GE(OutputAmount, IntLeaf(10)), EQ(OutputScript, PropLeaf(propToCompare))) + Exists(Outputs, GE(ExtractAmountFn, IntLeafConstant(10)), + EQ(ExtractScriptFn, PropLeafConstant(propToCompare.toString.getBytes))) + val txOutputs = ((1 to outCnt) map (_ => SigmaStateBox(11, spamProp))) :+ SigmaStateBox(11, propToCompare) val tx = SigmaStateTransaction(Seq(), txOutputs) @@ -144,7 +146,7 @@ class SpamSpecification extends PropSpec //fake message, in a real-life a message is to be derived from a spending transaction val message = Blake2b256("Hello World") val fakeSelf = boxWithMetadata(0, propToCompare) - val ctx = UtxoContext(currentHeight = 100, spendingTransaction = tx, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 100, Seq(), spendingTransaction = tx, self = fakeSelf) val pt0 = System.currentTimeMillis() prover.prove(spamScript, ctx, message).map { proof => diff --git a/src/test/scala/sigmastate/utxo/UtxoInterpreterSpecification.scala b/src/test/scala/sigmastate/utxo/UtxoInterpreterSpecification.scala index 98a55779c7..cbe2c09329 100644 --- a/src/test/scala/sigmastate/utxo/UtxoInterpreterSpecification.scala +++ b/src/test/scala/sigmastate/utxo/UtxoInterpreterSpecification.scala @@ -19,6 +19,9 @@ class UtxoInterpreterSpecification extends PropSpec private val fakeSelf = boxWithMetadata(0, TrueLeaf) + //fake message, in a real-life a message is to be derived from a spending transaction + val fakeMessage = Blake2b256("Hello World") + property("PropLeaf EQ/NEQ") { val prover1 = new UtxoProvingInterpreter val prover2 = new UtxoProvingInterpreter @@ -28,13 +31,13 @@ class UtxoInterpreterSpecification extends PropSpec val h1 = prover1.dlogSecrets.head.publicImage.h val h2 = prover2.dlogSecrets.head.publicImage.h - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) - verifier.reduceToCrypto(EQ(PropLeaf(DLogNode(h1)), PropLeaf(DLogNode(h1))), ctx) + verifier.reduceToCrypto(EQ(PropLeafConstant(DLogNode(h1)), PropLeafConstant(DLogNode(h1))), ctx) .get.isInstanceOf[TrueLeaf.type] shouldBe true - verifier.reduceToCrypto(EQ(PropLeaf(DLogNode(h1)), PropLeaf(DLogNode(h2))), ctx) + verifier.reduceToCrypto(EQ(PropLeafConstant(DLogNode(h1)), PropLeafConstant(DLogNode(h2))), ctx) .get.isInstanceOf[FalseLeaf.type] shouldBe true } @@ -62,8 +65,8 @@ class UtxoInterpreterSpecification extends PropSpec val backerPubKey = backerProver.dlogSecrets.head.publicImage.h val projectPubKey = projectProver.dlogSecrets.head.publicImage.h - val timeout = IntLeaf(100) - val minToRaise = IntLeaf(1000) + val timeout = IntLeafConstant(100) + val minToRaise = IntLeafConstant(1000) // (height >= timeout /\ dlog_g backerKey) \/ (height < timeout /\ dlog_g projKey /\ has_output(amount >= minToRaise, proposition = dlog_g projKey) val crowdFundingScript = OR( @@ -72,14 +75,13 @@ class UtxoInterpreterSpecification extends PropSpec Seq( LT(Height, timeout), DLogNode(projectPubKey), - TxHasOutput(GE(OutputAmount, minToRaise), EQ(OutputScript, PropLeaf(DLogNode(projectPubKey)))) + Exists(Outputs, GE(ExtractAmountFn, minToRaise), EQ(ExtractScriptFn, PropLeafConstant(DLogNode(projectPubKey)))) ) ) ) val outputToSpend = SigmaStateBox(10, crowdFundingScript) - val outputWithMetadata = BowWithMetadata(outputToSpend, BoxMetadata(0, 0)) - val message = Blake2b256("Hello World") //normally message to be defined by spending transaction bytes + val outputWithMetadata = BoxWithMetadata(outputToSpend, BoxMetadata(0, 0)) //First case: height < timeout, project is able to claim amount of tokens not less than required threshold @@ -89,14 +91,14 @@ class UtxoInterpreterSpecification extends PropSpec //normally this transaction would invalid, but we're not checking it in this test val tx1 = SigmaStateTransaction(Seq(), Seq(tx1Output1, tx1Output2)) - val ctx1 = UtxoContext(currentHeight = timeout.value - 1, spendingTransaction = tx1, self = outputWithMetadata) + val ctx1 = UtxoContext(currentHeight = timeout.value - 1, Seq(), spendingTransaction = tx1, self = outputWithMetadata) //project is generating a proof and it is passing verification - val proofP = projectProver.prove(crowdFundingScript, ctx1, message).get.proof - verifier.verify(crowdFundingScript, ctx1, proofP, message).get shouldBe true + val proofP = projectProver.prove(crowdFundingScript, ctx1, fakeMessage).get.proof + verifier.verify(crowdFundingScript, ctx1, proofP, fakeMessage).get shouldBe true //backer can't generate a proof - backerProver.prove(crowdFundingScript, ctx1, message).isFailure shouldBe true + backerProver.prove(crowdFundingScript, ctx1, fakeMessage).isFailure shouldBe true //Second case: height < timeout, project is NOT able to claim amount of tokens not less than required threshold @@ -105,14 +107,14 @@ class UtxoInterpreterSpecification extends PropSpec val tx2Output2 = SigmaStateBox(1, DLogNode(projectPubKey)) val tx2 = SigmaStateTransaction(Seq(), Seq(tx2Output1, tx2Output2)) - val ctx2 = UtxoContext(currentHeight = timeout.value - 1, spendingTransaction = tx2, self = outputWithMetadata) + val ctx2 = UtxoContext(currentHeight = timeout.value - 1, Seq(), spendingTransaction = tx2, self = outputWithMetadata) //project cant' generate a proof - val proofP2Try = projectProver.prove(crowdFundingScript, ctx2, message) + val proofP2Try = projectProver.prove(crowdFundingScript, ctx2, fakeMessage) proofP2Try.isSuccess shouldBe false //backer can't generate a proof - val proofB2Try = backerProver.prove(crowdFundingScript, ctx2, message) + val proofB2Try = backerProver.prove(crowdFundingScript, ctx2, fakeMessage) proofB2Try.isSuccess shouldBe false //Third case: height >= timeout @@ -122,14 +124,14 @@ class UtxoInterpreterSpecification extends PropSpec val tx3Output2 = SigmaStateBox(1, DLogNode(projectPubKey)) val tx3 = SigmaStateTransaction(Seq(), Seq(tx3Output1, tx3Output2)) - val ctx3 = UtxoContext(currentHeight = timeout.value, spendingTransaction = tx3, self = outputWithMetadata) + val ctx3 = UtxoContext(currentHeight = timeout.value, Seq(), spendingTransaction = tx3, self = outputWithMetadata) //project cant' generate a proof - projectProver.prove(crowdFundingScript, ctx3, message).isFailure shouldBe true + projectProver.prove(crowdFundingScript, ctx3, fakeMessage).isFailure shouldBe true //backer is generating a proof and it is passing verification - val proofB = backerProver.prove(crowdFundingScript, ctx3, message).get.proof - verifier.verify(crowdFundingScript, ctx3, proofB, message).get shouldBe true + val proofB = backerProver.prove(crowdFundingScript, ctx3, fakeMessage).get.proof + verifier.verify(crowdFundingScript, ctx3, proofB, fakeMessage).get shouldBe true } @@ -144,7 +146,8 @@ class UtxoInterpreterSpecification extends PropSpec * (regular_script) ∨ * (height > (out.height + demurrage_period ) ∧ has_output(value >= out.value − demurrage_cost, script = out.script)) */ - property("Evaluation - Demurrage Example") { + //todo: fix + ignore("Evaluation - Demurrage Example") { val demurragePeriod = 100 val demurrageCost = 2 @@ -159,67 +162,74 @@ class UtxoInterpreterSpecification extends PropSpec val script = OR( regScript, AND( - GE(Height, Plus(SelfHeight, IntLeaf(demurragePeriod))), - TxHasOutput(GE(OutputAmount, Minus(SelfAmount, IntLeaf(demurrageCost))), EQ(OutputScript, SelfScript)) + GE(Height, Plus(ExtractHeightInst(Self), IntLeafConstant(demurragePeriod))), + Exists(Outputs, GE(ExtractAmountFn, Minus(ExtractAmountInst(Self), IntLeafConstant(demurrageCost))), + EQ(ExtractScriptFn, ExtractScriptInst(Self))) ) ) val outHeight = 100 val outValue = 10 - val message = Blake2b256("Hello World") //normally message to be defined by spending transaction bytes - //case 1: demurrage time hasn't come yet val tx1 = SigmaStateTransaction(Seq(), Seq(SigmaStateBox(outValue, script))) val ctx1 = UtxoContext( currentHeight = outHeight + demurragePeriod - 1, + Seq(), spendingTransaction = tx1, self = boxWithMetadata(outValue, script, outHeight)) //user can spend all the money - val uProof1 = userProver.prove(script, ctx1, message).get.proof - verifier.verify(script, ctx1, uProof1, message).get shouldBe true + val uProof1 = userProver.prove(script, ctx1, fakeMessage).get.proof + verifier.verify(script, ctx1, uProof1, fakeMessage).get shouldBe true //miner can't spend any money - verifier.verify(script, ctx1, NoProof, message).get shouldBe false + verifier.verify(script, ctx1, NoProof, fakeMessage).get shouldBe false //case 2: demurrage time has come val ctx2 = UtxoContext( currentHeight = outHeight + demurragePeriod, + Seq(), spendingTransaction = tx1, self = boxWithMetadata(outValue, script, outHeight)) //user can spend all the money - val uProof2 = userProver.prove(script, ctx1, message).get.proof - verifier.verify(script, ctx2, uProof2, message).get shouldBe true + val uProof2 = userProver.prove(script, ctx1, fakeMessage).get.proof + verifier.verify(script, ctx2, uProof2, fakeMessage).get shouldBe true //miner can spend "demurrageCost" tokens val tx3 = SigmaStateTransaction(Seq(), Seq(SigmaStateBox(outValue - demurrageCost, script))) val ctx3 = UtxoContext( currentHeight = outHeight + demurragePeriod, + Seq(), spendingTransaction = tx3, self = boxWithMetadata(outValue, script, outHeight)) - verifier.verify(script, ctx3, NoProof, message).get shouldBe true + + assert(ctx3.spendingTransaction.newBoxes.head.propositionBytes sameElements ctx3.self.box.propositionBytes) + + verifier.verify(script, ctx3, NoProof, fakeMessage).get shouldBe true //miner can't spend more val tx4 = SigmaStateTransaction(Seq(), Seq(SigmaStateBox(outValue - demurrageCost - 1, script))) val ctx4 = UtxoContext( currentHeight = outHeight + demurragePeriod, + Seq(), spendingTransaction = tx4, self = boxWithMetadata(outValue, script, outHeight)) - verifier.verify(script, ctx4, NoProof, message).get shouldBe false + verifier.verify(script, ctx4, NoProof, fakeMessage).get shouldBe false //miner can spend less val tx5 = SigmaStateTransaction(Seq(), Seq(SigmaStateBox(outValue - demurrageCost + 1, script))) val ctx5 = UtxoContext( currentHeight = outHeight + demurragePeriod, + Seq(), spendingTransaction = tx5, self = boxWithMetadata(outValue, script, outHeight)) - verifier.verify(script, ctx5, NoProof, message).get shouldBe true + verifier.verify(script, ctx5, NoProof, fakeMessage).get shouldBe true } /** @@ -228,36 +238,34 @@ class UtxoInterpreterSpecification extends PropSpec property("prover enriching context") { val prover = new UtxoProvingInterpreter val preimage = prover.contextExtenders.head._2.value - val prop = EQ(CalcBlake2b256(CustomByteArray(Helpers.tagInt(preimage))), ByteArrayLeaf(Blake2b256(preimage))) + val prop = EQ(CalcBlake2b256Inst(CustomByteArray(Helpers.tagInt(preimage))), ByteArrayLeafConstant(Blake2b256(preimage))) - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) - val pr = prover.prove(prop, ctx, message).get + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val pr = prover.prove(prop, ctx, fakeMessage).get val ctxv = ctx.withExtension(pr.extension) val verifier = new UtxoInterpreter - verifier.verify(prop, ctx, pr.proof, message).get shouldBe false //context w/out extensions - verifier.verify(prop, ctxv, pr.proof, message).get shouldBe true + verifier.verify(prop, ctx, pr.proof, fakeMessage).get shouldBe false //context w/out extensions + verifier.verify(prop, ctxv, pr.proof, fakeMessage).get shouldBe true } property("prover enriching context 2") { val prover = new UtxoProvingInterpreter val preimage1 = prover.contextExtenders.head._2.value val preimage2 = prover.contextExtenders.tail.head._2.value - val prop = EQ(CalcBlake2b256(Append(CustomByteArray(Helpers.tagInt(preimage2)), + val prop = EQ(CalcBlake2b256Inst(Append(CustomByteArray(Helpers.tagInt(preimage2)), CustomByteArray(Helpers.tagInt(preimage1))) - ), ByteArrayLeaf(Blake2b256(preimage2 ++ preimage1))) + ), ByteArrayLeafConstant(Blake2b256(preimage2 ++ preimage1))) - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) - val pr = prover.prove(prop, ctx, message).get + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val pr = prover.prove(prop, ctx, fakeMessage).get val ctxv = ctx.withExtension(pr.extension) val verifier = new UtxoInterpreter - verifier.verify(prop, ctx, pr.proof, message).get shouldBe false //context w/out extensions - verifier.verify(prop, ctxv, pr.proof, message).get shouldBe true + verifier.verify(prop, ctx, pr.proof, fakeMessage).get shouldBe false //context w/out extensions + verifier.verify(prop, ctxv, pr.proof, fakeMessage).get shouldBe true } property("prover enriching context - xor") { @@ -270,20 +278,19 @@ class UtxoInterpreterSpecification extends PropSpec val r = Base16.decode("bb6633db20") val prover = new UtxoProvingInterpreter() - .withContextExtender(k1, ByteArrayLeaf(v1)) - .withContextExtender(k2, ByteArrayLeaf(v2)) + .withContextExtender(k1, ByteArrayLeafConstant(v1)) + .withContextExtender(k2, ByteArrayLeafConstant(v2)) - val prop = EQ(Xor(CustomByteArray(k1), CustomByteArray(k2)), ByteArrayLeaf(r)) + val prop = EQ(Xor(CustomByteArray(k1), CustomByteArray(k2)), ByteArrayLeafConstant(r)) - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) - val pr = prover.prove(prop, ctx, message).get + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val pr = prover.prove(prop, ctx, fakeMessage).get val ctxv = ctx.withExtension(pr.extension) val verifier = new UtxoInterpreter - verifier.verify(prop, ctx, pr.proof, message).get shouldBe false //context w/out extensions - verifier.verify(prop, ctxv, pr.proof, message).get shouldBe true + verifier.verify(prop, ctx, pr.proof, fakeMessage).get shouldBe false //context w/out extensions + verifier.verify(prop, ctxv, pr.proof, fakeMessage).get shouldBe true } property("context enriching mixed w. crypto") { @@ -293,20 +300,19 @@ class UtxoInterpreterSpecification extends PropSpec val prop = AND( pubkey, - EQ(CalcBlake2b256(CustomByteArray(Helpers.tagInt(preimage))), ByteArrayLeaf(Blake2b256(preimage))) + EQ(CalcBlake2b256Inst(CustomByteArray(Helpers.tagInt(preimage))), ByteArrayLeafConstant(Blake2b256(preimage))) ) - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) - val pr = prover.prove(prop, ctx, message).get + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val pr = prover.prove(prop, ctx, fakeMessage).get val ctxv = ctx.withExtension(pr.extension) pr.proof.isInstanceOf[SchnorrNode] shouldBe true val verifier = new UtxoInterpreter - verifier.verify(prop, ctx, pr.proof, message).getOrElse(false) shouldBe false //context w/out extensions - verifier.verify(prop, ctxv, pr.proof, message).get shouldBe true + verifier.verify(prop, ctx, pr.proof, fakeMessage).getOrElse(false) shouldBe false //context w/out extensions + verifier.verify(prop, ctxv, pr.proof, fakeMessage).get shouldBe true } property("context enriching mixed w. crypto 2") { @@ -318,22 +324,21 @@ class UtxoInterpreterSpecification extends PropSpec val prop = AND( pubkey, EQ( - CalcBlake2b256(Append(CustomByteArray(Helpers.tagInt(preimage1)), CustomByteArray(Helpers.tagInt(preimage2)))), - ByteArrayLeaf(Blake2b256(preimage1 ++ preimage2)) + CalcBlake2b256Inst(Append(CustomByteArray(Helpers.tagInt(preimage1)), CustomByteArray(Helpers.tagInt(preimage2)))), + ByteArrayLeafConstant(Blake2b256(preimage1 ++ preimage2)) ) ) - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 0, spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) - val pr = prover.prove(prop, ctx, message).get + val ctx = UtxoContext(currentHeight = 0, Seq(), spendingTransaction = null, self = boxWithMetadata(0, TrueLeaf)) + val pr = prover.prove(prop, ctx, fakeMessage).get val ctxv = ctx.withExtension(pr.extension) pr.proof.isInstanceOf[SchnorrNode] shouldBe true val verifier = new UtxoInterpreter - verifier.verify(prop, ctx, pr.proof, message).getOrElse(false) shouldBe false //context w/out extensions - verifier.verify(prop, ctxv, pr.proof, message).get shouldBe true + verifier.verify(prop, ctx, pr.proof, fakeMessage).getOrElse(false) shouldBe false //context w/out extensions + verifier.verify(prop, ctxv, pr.proof, fakeMessage).get shouldBe true } /** @@ -353,7 +358,7 @@ class UtxoInterpreterSpecification extends PropSpec val verifier = new UtxoInterpreter val x = proverA.contextExtenders.head._2.value - val hx = ByteArrayLeaf(Blake2b256(x)) + val hx = ByteArrayLeafConstant(Blake2b256(x)) val height1 = 100000 val height2 = 50000 @@ -363,49 +368,46 @@ class UtxoInterpreterSpecification extends PropSpec //chain1 script val prop1 = OR( - AND(GT(Height, IntLeaf(height1 + deadlineA)), pubkeyA), - AND(pubkeyB, EQ(CalcBlake2b256(CustomByteArray(Helpers.tagInt(x))), hx)) + AND(GT(Height, IntLeafConstant(height1 + deadlineA)), pubkeyA), + AND(pubkeyB, EQ(CalcBlake2b256Inst(CustomByteArray(Helpers.tagInt(x))), hx)) ) //chain2 script val prop2 = OR( - AND(GT(Height, IntLeaf(height2 + deadlineB)), pubkeyB), - AND(pubkeyA, EQ(CalcBlake2b256(CustomByteArray(Helpers.tagInt(x))), hx)) + AND(GT(Height, IntLeafConstant(height2 + deadlineB)), pubkeyB), + AND(pubkeyA, EQ(CalcBlake2b256Inst(CustomByteArray(Helpers.tagInt(x))), hx)) ) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - //Preliminary checks: //B can't spend coins of A in chain1 (generate a valid proof) - val ctxf1 = UtxoContext(currentHeight = height1 + 1, spendingTransaction = null, self = fakeSelf) - proverB.prove(prop1, ctxf1, message).isSuccess shouldBe false + val ctxf1 = UtxoContext(currentHeight = height1 + 1, Seq(), spendingTransaction = null, self = fakeSelf) + proverB.prove(prop1, ctxf1, fakeMessage).isSuccess shouldBe false //A can't withdraw her coins in chain1 (generate a valid proof) - println(proverA.prove(prop1, ctxf1, message)) - proverA.prove(prop1, ctxf1, message).isFailure shouldBe true + println(proverA.prove(prop1, ctxf1, fakeMessage)) + proverA.prove(prop1, ctxf1, fakeMessage).isFailure shouldBe true //B cant't withdraw his coins in chain2 (generate a valid proof) - val ctxf2 = UtxoContext(currentHeight = height2 + 1, spendingTransaction = null, self = fakeSelf) - proverB.prove(prop2, ctxf2, message).isSuccess shouldBe false + val ctxf2 = UtxoContext(currentHeight = height2 + 1, Seq(), spendingTransaction = null, self = fakeSelf) + proverB.prove(prop2, ctxf2, fakeMessage).isSuccess shouldBe false //Successful run below: //A spends coins of B in chain2 - val ctx1 = UtxoContext(currentHeight = height2 + 1, spendingTransaction = null, self = fakeSelf) - val pr = proverA.prove(prop2, ctx1, message).get - verifier.verify(prop2, ctx1, pr, message).get shouldBe true + val ctx1 = UtxoContext(currentHeight = height2 + 1, Seq(), spendingTransaction = null, self = fakeSelf) + val pr = proverA.prove(prop2, ctx1, fakeMessage).get + verifier.verify(prop2, ctx1, pr, fakeMessage).get shouldBe true //B extracts preimage x of hx val t = pr.extension.values.head val (tx, bx) = (t._1, t._2) - val proverB2 = proverB.withContextExtender(tx, bx.asInstanceOf[ByteArrayLeaf]) + val proverB2 = proverB.withContextExtender(tx, bx.asInstanceOf[ByteArrayLeafConstant]) //B spends coins of A in chain1 with knowledge of x - val ctx2 = UtxoContext(currentHeight = height1 + 1, spendingTransaction = null, self = fakeSelf) - val pr2 = proverB2.prove(prop1, ctx2, message).get - verifier.verify(prop1, ctx2, pr2, message).get shouldBe true + val ctx2 = UtxoContext(currentHeight = height1 + 1, Seq(), spendingTransaction = null, self = fakeSelf) + val pr2 = proverB2.prove(prop1, ctx2, fakeMessage).get + verifier.verify(prop1, ctx2, pr2, fakeMessage).get shouldBe true } /** @@ -422,17 +424,15 @@ class UtxoInterpreterSpecification extends PropSpec val prop = OR(pubkeyA, pubkeyB) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prA, message).get shouldBe true + val prA = proverA.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prA, fakeMessage).get shouldBe true - val prB = proverB.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prB, message).get shouldBe true + val prB = proverB.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prB, fakeMessage).get shouldBe true - proverC.prove(prop, ctx, message).isFailure shouldBe true + proverC.prove(prop, ctx, fakeMessage).isFailure shouldBe true } property("simplest linear-sized ring signature (1-out-of-3 OR)") { @@ -447,18 +447,16 @@ class UtxoInterpreterSpecification extends PropSpec val prop = OR(pubkeyA, pubkeyB, pubkeyC) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prA, message).get shouldBe true + val prA = proverA.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prA, fakeMessage).get shouldBe true - val prB = proverB.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prB, message).get shouldBe true + val prB = proverB.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prB, fakeMessage).get shouldBe true - val prC = proverC.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prC, message).get shouldBe true + val prC = proverC.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prC, fakeMessage).get shouldBe true } //two secrets are known, nevertheless, one will be simulated @@ -474,12 +472,10 @@ class UtxoInterpreterSpecification extends PropSpec val prop = OR(Seq(pubkeyA1, pubkeyA2, pubkeyA3, pubkeyA4)) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prA, message).get shouldBe true + val prA = proverA.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prA, fakeMessage).get shouldBe true } property("complex sig scheme - OR of two ANDs") { @@ -497,22 +493,20 @@ class UtxoInterpreterSpecification extends PropSpec val prop = OR(AND(pubkeyA, pubkeyB), AND(pubkeyC, pubkeyD)) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - proverA.prove(prop, ctx, message).isFailure shouldBe true - proverB.prove(prop, ctx, message).isFailure shouldBe true - proverC.prove(prop, ctx, message).isFailure shouldBe true - proverD.prove(prop, ctx, message).isFailure shouldBe true + proverA.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverB.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverC.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverD.prove(prop, ctx, fakeMessage).isFailure shouldBe true val proverAB = proverA.withSecrets(Seq(proverB.dlogSecrets.head)) - val pr = proverAB.prove(prop, ctx, message).get - verifier.verify(prop, ctx, pr, message).get shouldBe true + val pr = proverAB.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr, fakeMessage).get shouldBe true val proverCD = proverC.withSecrets(Seq(proverD.dlogSecrets.head)) - val pr2 = proverCD.prove(prop, ctx, message).get - verifier.verify(prop, ctx, pr2, message).get shouldBe true + val pr2 = proverCD.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr2, fakeMessage).get shouldBe true } property("complex sig scheme - OR of AND and OR") { @@ -530,22 +524,20 @@ class UtxoInterpreterSpecification extends PropSpec val prop = OR(AND(pubkeyA, pubkeyB), OR(pubkeyC, pubkeyD)) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - proverA.prove(prop, ctx, message).isFailure shouldBe true - proverB.prove(prop, ctx, message).isFailure shouldBe true + proverA.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverB.prove(prop, ctx, fakeMessage).isFailure shouldBe true - val prC = proverC.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prC, message).get shouldBe true + val prC = proverC.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prC, fakeMessage).get shouldBe true - val prD = proverD.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prD, message).get shouldBe true + val prD = proverD.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prD, fakeMessage).get shouldBe true val proverAB = proverA.withSecrets(Seq(proverB.dlogSecrets.head)) - val pr = proverAB.prove(prop, ctx, message).get - verifier.verify(prop, ctx, pr, message).get shouldBe true + val pr = proverAB.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr, fakeMessage).get shouldBe true } property("complex sig scheme - AND of two ORs") { @@ -563,22 +555,20 @@ class UtxoInterpreterSpecification extends PropSpec val prop = AND(OR(pubkeyA, pubkeyB), OR(pubkeyC, pubkeyD)) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - proverA.prove(prop, ctx, message).isFailure shouldBe true - proverB.prove(prop, ctx, message).isFailure shouldBe true - proverC.prove(prop, ctx, message).isFailure shouldBe true - proverD.prove(prop, ctx, message).isFailure shouldBe true + proverA.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverB.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverC.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverD.prove(prop, ctx, fakeMessage).isFailure shouldBe true val proverAC = proverA.withSecrets(Seq(proverC.dlogSecrets.head)) - val pr = proverAC.prove(prop, ctx, message).get - verifier.verify(prop, ctx, pr, message).get shouldBe true + val pr = proverAC.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr, fakeMessage).get shouldBe true val proverBD = proverB.withSecrets(Seq(proverD.dlogSecrets.head)) - val pr2 = proverBD.prove(prop, ctx, message).get - verifier.verify(prop, ctx, pr2, message).get shouldBe true + val pr2 = proverBD.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr2, fakeMessage).get shouldBe true } property("complex sig scheme - AND of AND and OR") { @@ -596,25 +586,23 @@ class UtxoInterpreterSpecification extends PropSpec val prop = AND(AND(pubkeyA, pubkeyB), OR(pubkeyC, pubkeyD)) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - proverA.prove(prop, ctx, message).isFailure shouldBe true - proverB.prove(prop, ctx, message).isFailure shouldBe true - proverC.prove(prop, ctx, message).isFailure shouldBe true - proverD.prove(prop, ctx, message).isFailure shouldBe true + proverA.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverB.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverC.prove(prop, ctx, fakeMessage).isFailure shouldBe true + proverD.prove(prop, ctx, fakeMessage).isFailure shouldBe true val proverAB = proverA.withSecrets(Seq(proverB.dlogSecrets.head)) - proverAB.prove(prop, ctx, message).isFailure shouldBe true + proverAB.prove(prop, ctx, fakeMessage).isFailure shouldBe true val proverABC = proverAB.withSecrets(Seq(proverC.dlogSecrets.head)) - val prABC = proverABC.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prABC, message).get shouldBe true + val prABC = proverABC.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prABC, fakeMessage).get shouldBe true val proverABD = proverAB.withSecrets(Seq(proverC.dlogSecrets.head)) - val prABD = proverABD.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prABD, message).get shouldBe true + val prABD = proverABD.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prABD, fakeMessage).get shouldBe true } property("complex sig scheme - OR of two ORs") { @@ -632,21 +620,19 @@ class UtxoInterpreterSpecification extends PropSpec val prop = OR(OR(pubkeyA, pubkeyB), OR(pubkeyC, pubkeyD)) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prA, message).get shouldBe true + val prA = proverA.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prA, fakeMessage).get shouldBe true - val prB = proverB.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prB, message).get shouldBe true + val prB = proverB.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prB, fakeMessage).get shouldBe true - val prC = proverC.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prC, message).get shouldBe true + val prC = proverC.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prC, fakeMessage).get shouldBe true - val prD = proverD.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prD, message).get shouldBe true + val prD = proverD.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prD, fakeMessage).get shouldBe true } property("complex sig scheme - OR w. predicate") { @@ -659,21 +645,18 @@ class UtxoInterpreterSpecification extends PropSpec val pubkeyA = proverA.dlogSecrets.head.publicImage val pubkeyB = proverB.dlogSecrets.head.publicImage - val prop = OR(pubkeyA, pubkeyB, GT(Height, IntLeaf(500))) - - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") + val prop = OR(pubkeyA, pubkeyB, GT(Height, IntLeafConstant(500))) - val ctx1 = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx1, message).get - verifier.verify(prop, ctx1, prA, message).get shouldBe true - val prB = proverB.prove(prop, ctx1, message).get - verifier.verify(prop, ctx1, prB, message).get shouldBe true - proverC.prove(prop, ctx1, message).isFailure shouldBe true + val ctx1 = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) + val prA = proverA.prove(prop, ctx1, fakeMessage).get + verifier.verify(prop, ctx1, prA, fakeMessage).get shouldBe true + val prB = proverB.prove(prop, ctx1, fakeMessage).get + verifier.verify(prop, ctx1, prB, fakeMessage).get shouldBe true + proverC.prove(prop, ctx1, fakeMessage).isFailure shouldBe true - val ctx2 = UtxoContext(currentHeight = 501, spendingTransaction = null, self = fakeSelf) - val prC = proverC.prove(prop, ctx2, message).get - verifier.verify(prop, ctx2, prC, message).get shouldBe true + val ctx2 = UtxoContext(currentHeight = 501, Seq(), spendingTransaction = null, self = fakeSelf) + val prC = proverC.prove(prop, ctx2, fakeMessage).get + verifier.verify(prop, ctx2, prC, fakeMessage).get shouldBe true } property("complex sig scheme - OR of OR and AND w. predicate") { @@ -687,27 +670,25 @@ class UtxoInterpreterSpecification extends PropSpec val pubkeyB = proverB.dlogSecrets.head.publicImage val pubkeyC = proverC.dlogSecrets.head.publicImage - val prop = OR(OR(pubkeyA, pubkeyB), AND(pubkeyC, GT(Height, IntLeaf(500)))) + val prop = OR(OR(pubkeyA, pubkeyB), AND(pubkeyC, GT(Height, IntLeafConstant(500)))) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx1 = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx1 = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx1, message).get - verifier.verify(prop, ctx1, prA, message).get shouldBe true - val prB = proverB.prove(prop, ctx1, message).get - verifier.verify(prop, ctx1, prB, message).get shouldBe true - proverC.prove(prop, ctx1, message).isFailure shouldBe true + val prA = proverA.prove(prop, ctx1, fakeMessage).get + verifier.verify(prop, ctx1, prA, fakeMessage).get shouldBe true + val prB = proverB.prove(prop, ctx1, fakeMessage).get + verifier.verify(prop, ctx1, prB, fakeMessage).get shouldBe true + proverC.prove(prop, ctx1, fakeMessage).isFailure shouldBe true - val ctx2 = UtxoContext(currentHeight = 501, spendingTransaction = null, self = fakeSelf) + val ctx2 = UtxoContext(currentHeight = 501, Seq(), spendingTransaction = null, self = fakeSelf) - val prA2 = proverA.prove(prop, ctx2, message).get - verifier.verify(prop, ctx2, prA2, message).get shouldBe true - val prB2 = proverB.prove(prop, ctx2, message).get - verifier.verify(prop, ctx2, prB2, message).get shouldBe true - val prC2 = proverC.prove(prop, ctx2, message).get - verifier.verify(prop, ctx2, prC2, message).get shouldBe true + val prA2 = proverA.prove(prop, ctx2, fakeMessage).get + verifier.verify(prop, ctx2, prA2, fakeMessage).get shouldBe true + val prB2 = proverB.prove(prop, ctx2, fakeMessage).get + verifier.verify(prop, ctx2, prB2, fakeMessage).get shouldBe true + val prC2 = proverC.prove(prop, ctx2, fakeMessage).get + verifier.verify(prop, ctx2, prC2, fakeMessage).get shouldBe true } property("DH tuple"){ @@ -723,15 +704,13 @@ class UtxoInterpreterSpecification extends PropSpec val prop = DiffieHellmanTupleNode(ci.g, ci.h, ci.u, ci.v) val wrongProp = DiffieHellmanTupleNode(ci.g, ci.h, ci.u, ci.u) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val pr = prover.prove(prop, ctx, message).get - verifier.verify(prop, ctx, pr, message).get shouldBe true + val pr = prover.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr, fakeMessage).get shouldBe true - fakeProver.prove(prop, ctx, message).isSuccess shouldBe false - prover.prove(wrongProp, ctx, message).isSuccess shouldBe false + fakeProver.prove(prop, ctx, fakeMessage).isSuccess shouldBe false + prover.prove(wrongProp, ctx, fakeMessage).isSuccess shouldBe false } property("DH tuple - simulation"){ @@ -745,12 +724,10 @@ class UtxoInterpreterSpecification extends PropSpec val prop = OR(pubkeyA, pubdhB) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prA, message).get shouldBe true + val prA = proverA.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prA, fakeMessage).get shouldBe true } property("DH tuple and DLOG"){ @@ -764,14 +741,12 @@ class UtxoInterpreterSpecification extends PropSpec val prop = AND(pubkeyA, pubdhA) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 1, spendingTransaction = null, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 1, Seq(), spendingTransaction = null, self = fakeSelf) - val prA = proverA.prove(prop, ctx, message).get - verifier.verify(prop, ctx, prA, message).get shouldBe true + val prA = proverA.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, prA, fakeMessage).get shouldBe true - proverB.prove(prop, ctx, message).isSuccess shouldBe false + proverB.prove(prop, ctx, fakeMessage).isSuccess shouldBe false } property("mixing scenario w. timeout") { @@ -798,23 +773,83 @@ class UtxoInterpreterSpecification extends PropSpec val spendingTransaction = SigmaStateTransaction(Seq(), newBoxes) def mixingRequestProp(sender: DLogNode, timeout: Int) = OR( - AND(LE(Height, IntLeaf(timeout)), EQ(CalcBlake2b256(TxOutBytes), ByteArrayLeaf(properHash))), - AND(GT(Height, IntLeaf(timeout)), sender) + AND(LE(Height, IntLeafConstant(timeout)), EQ(CalcBlake2b256Inst(TxOutBytes), ByteArrayLeafConstant(properHash))), + AND(GT(Height, IntLeafConstant(timeout)), sender) ) - //fake message, in a real-life a message is to be derived from a spending transaction - val message = Blake2b256("Hello World") - val ctx = UtxoContext(currentHeight = 50, spendingTransaction, self = fakeSelf) + val ctx = UtxoContext(currentHeight = 50, Seq(), spendingTransaction, self = fakeSelf) //before timeout - val prA = proverA.prove(mixingRequestProp(pubkeyA, 100), ctx, message).get - verifier.verify(mixingRequestProp(pubkeyA, 100), ctx, prA, message).get shouldBe true - verifier.verify(mixingRequestProp(pubkeyB, 100), ctx, prA, message).get shouldBe true + val prA = proverA.prove(mixingRequestProp(pubkeyA, 100), ctx, fakeMessage).get + verifier.verify(mixingRequestProp(pubkeyA, 100), ctx, prA, fakeMessage).get shouldBe true + verifier.verify(mixingRequestProp(pubkeyB, 100), ctx, prA, fakeMessage).get shouldBe true //after timeout - val prA2 = proverA.prove(mixingRequestProp(pubkeyA, 40), ctx, message).get - println(prA2) - verifier.verify(mixingRequestProp(pubkeyA, 40), ctx, prA2, message).get shouldBe true - verifier.verify(mixingRequestProp(pubkeyB, 40), ctx, prA2, message).isSuccess shouldBe false + val prA2 = proverA.prove(mixingRequestProp(pubkeyA, 40), ctx, fakeMessage).get + verifier.verify(mixingRequestProp(pubkeyA, 40), ctx, prA2, fakeMessage).get shouldBe true + verifier.verify(mixingRequestProp(pubkeyB, 40), ctx, prA2, fakeMessage).isSuccess shouldBe false + } + + property("map + sum") { + val prover = new UtxoProvingInterpreter + val verifier = new UtxoInterpreter + + val pubkey = prover.dlogSecrets.head.publicImage + + val prop = AND(pubkey, GT(Sum(MapCollection(Outputs, ExtractAmountFn)), IntLeafConstant(20))) + + val newBox1 = SigmaStateBox(11, pubkey) + val newBox2 = SigmaStateBox(10, pubkey) + val newBoxes = Seq(newBox1, newBox2) + + val spendingTransaction = SigmaStateTransaction(Seq(), newBoxes) + + val ctx = UtxoContext(currentHeight = 50, Seq(), spendingTransaction, self = fakeSelf) + + val pr = prover.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr, fakeMessage) + } + + + ignore("map + sum + minus") { + + val prover = new UtxoProvingInterpreter + val verifier = new UtxoInterpreter + + val pubkey = prover.dlogSecrets.head.publicImage + + val prop = ??? //todo: fix AND(pubkey, GT(Sum(MapCollection(Outputs, Minus(ExtractAmountFn, IntLeafConstant(5)))), IntLeafConstant(20))) + + val newBox1 = SigmaStateBox(11, pubkey) + val newBox2 = SigmaStateBox(10, pubkey) + val newBoxes = Seq(newBox1, newBox2) + + val spendingTransaction = SigmaStateTransaction(Seq(), newBoxes) + + val ctx = UtxoContext(currentHeight = 50, Seq(), spendingTransaction, self = fakeSelf) + + val pr = prover.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr, fakeMessage) + } + + property("exists"){ + val prover = new UtxoProvingInterpreter + val verifier = new UtxoInterpreter + + val pubkey = prover.dlogSecrets.head.publicImage + + val prop = Exists(Outputs, GT(Plus(ExtractAmountFn, IntLeafConstant(5)), IntLeafConstant(10))) + + val newBox1 = SigmaStateBox(16, pubkey) + val newBox2 = SigmaStateBox(15, pubkey) + val newBoxes = Seq(newBox1, newBox2) + + val spendingTransaction = SigmaStateTransaction(Seq(), newBoxes) + + val ctx = UtxoContext(currentHeight = 50, Seq(), spendingTransaction, self = fakeSelf) + + val pr = prover.prove(prop, ctx, fakeMessage).get + verifier.verify(prop, ctx, pr, fakeMessage).get shouldBe true + //todo: finish } } \ No newline at end of file diff --git a/src/test/scala/sigmastate/utxo/UtxoProvingInterpreter.scala b/src/test/scala/sigmastate/utxo/UtxoProvingInterpreter.scala index 3e7d6615be..8b9b2b852c 100644 --- a/src/test/scala/sigmastate/utxo/UtxoProvingInterpreter.scala +++ b/src/test/scala/sigmastate/utxo/UtxoProvingInterpreter.scala @@ -4,7 +4,7 @@ import scapi.sigma.DLogProtocol.{DLogNode, DLogProverInput} import scapi.sigma.DiffieHellmanTupleProverInput import scapi.sigma.rework.SigmaProtocolPrivateInput import scorex.utils.Random -import sigmastate.ByteArrayLeaf +import sigmastate.ByteArrayLeafConstant import sigmastate.interpreter.ProverInterpreter import sigmastate.utils.Helpers @@ -25,18 +25,18 @@ class UtxoProvingInterpreter(override val maxCost: Int = CostTable.ScriptLimit) lazy val dhSecrets: Seq[DiffieHellmanTupleProverInput] = secrets.filter(_.isInstanceOf[DiffieHellmanTupleProverInput]).asInstanceOf[Seq[DiffieHellmanTupleProverInput]] - override lazy val contextExtenders: Map[Int, ByteArrayLeaf] = (1 to 10).map { i => + override lazy val contextExtenders: Map[Int, ByteArrayLeafConstant] = (1 to 10).map { i => val ba = Random.randomBytes(75) - Helpers.tagInt(ba) -> ByteArrayLeaf(ba) + Helpers.tagInt(ba) -> ByteArrayLeafConstant(ba) }.toMap - def withContextExtender(tag: Int, value: ByteArrayLeaf): UtxoProvingInterpreter = { + def withContextExtender(tag: Int, value: ByteArrayLeafConstant): UtxoProvingInterpreter = { val s = secrets val ce = contextExtenders new UtxoProvingInterpreter(maxCost) { override lazy val secrets = s - override lazy val contextExtenders: Map[Int, ByteArrayLeaf] = ce + (tag -> value) + override lazy val contextExtenders: Map[Int, ByteArrayLeafConstant] = ce + (tag -> value) } } @@ -46,7 +46,7 @@ class UtxoProvingInterpreter(override val maxCost: Int = CostTable.ScriptLimit) new UtxoProvingInterpreter(maxCost) { override lazy val secrets = s - override lazy val contextExtenders: Map[Int, ByteArrayLeaf] = ce + override lazy val contextExtenders: Map[Int, ByteArrayLeafConstant] = ce } } }