diff --git a/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Compilation.scala b/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Compilation.scala index aa1aacc..1d739c6 100644 --- a/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Compilation.scala +++ b/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Compilation.scala @@ -31,6 +31,7 @@ trait Compilation extends Parsing with Liftables { private def findContractDefDef(select: Select): String = { import scala.meta._ + // c.warn(s"getting file path for $select") val path = select.symbol.pos.source.file.file.toPath val source = new String(java.nio.file.Files.readAllBytes(path), "UTF-8") val input = Input.VirtualFile(path.toString, source) @@ -148,12 +149,15 @@ trait Compilation extends Parsing with Liftables { case BlockValue(IndexedSeq(), body) => body case v => v } + c.info(s"unwrapped BlockValue: $unwrappedBlockValue") + // c.untypecheck { q""" ErgoContract( $assembledContractBodyScalaTree, $unwrappedBlockValue ) """ + // } } def compileVerified[A, B](func: Expr[A => B]) = diff --git a/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Liftables.scala b/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Liftables.scala index b8a2c25..21d6db8 100644 --- a/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Liftables.scala +++ b/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Liftables.scala @@ -51,6 +51,7 @@ import sigmastate.utxo.{ } import scala.reflect.macros.whitebox +import sigmastate.ArithOp trait Liftables extends Types { val c: whitebox.Context @@ -102,10 +103,11 @@ trait Liftables extends Types { case IntConstant(v) => q"$svpack.IntConstant($v)" case ast: SigmaTransformer[_, _] => sigmaTransLiftable(ast) case ast: Transformer[_, _] => transLiftable(ast) + case ast: ArithOp[_] => arithOpLiftable(ast) // case ast: Relation[_, _] => relationLiftable(ast.asInstanceOf[Relation[SType, SType]]) case BlockValue(stats, res) => // TODO: WTF with list? - q"$svpack.BlockValue(${stats.toList}.toIndexedSeq, $res)" + q"$svpack.BlockValue(${stats.toList}.toIndexedSeq, $res).asInstanceOf[$svpack.Value[${res.tpe}]]" case ast: Value[_] if ast.tpe == SSigmaProp => sigmaPropLiftable(ast.asSigmaProp) // case ast: NotReadyValue[_] if ast.tpe == SBoolean => // notReadyBoolValueLiftable(ast.asInstanceOf[NotReadyValueBoolean]) @@ -170,6 +172,12 @@ trait Liftables extends Types { // case v @ _ => c.fail(s"no Liftable for Coll: $v") // } + implicit def arithOpLiftable[T <: SType]: Liftable[ArithOp[T]] = Liftable[ArithOp[T]] { + case ArithOp(l, r, opCode) => + q"$spack.ArithOp($l, $r, $opCode.asInstanceOf[sigmastate.serialization.OpCodes.OpCode])" + case v @ _ => c.fail(s"no Liftable for ArithOp: $v") + } + implicit def transLiftable[IV <: SType, OV <: SType]: Liftable[Transformer[IV, OV]] = Liftable[Transformer[IV, OV]] { case SizeOf(v) => q"$supack.SizeOf($v)" diff --git a/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Parsing.scala b/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Parsing.scala index d7b3457..d5bb5e0 100644 --- a/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Parsing.scala +++ b/compiler/src/main/scala/org/ergoplatform/compiler/compilation/Parsing.scala @@ -52,6 +52,9 @@ import special.sigma.SigmaContract import scala.collection.mutable import scala.reflect.api.Trees +import sigmastate.serialization.OpCodes +import sigmastate.ArithOp +import sigmastate.Values.LongConstant trait Parsing { this: Compilation => @@ -81,10 +84,12 @@ trait Parsing { val astParser: Parser[SValue] = Parser[SValue] { case q"$i: $typ" => astParser(i) + case `constParser`(v) => v case `contextApiParser`(v) => v case `contractApiParser`(v) => v case `sigmaTransParser`(v) => v case `relationParser`(v) => v + case `twoArgOpParser`(v) => v case `collApiParser`(v) => v case `boxApiParser`(v) => v case `sigmaPropApiParser`(v) => v @@ -98,7 +103,7 @@ trait Parsing { // case t: Tree => ScalaTree(t) } - // TODO: wtf? + // TODO: remove? var callArgToIdentMap: Map[String, String] = Map[String, String]() var valDefsMap: mutable.Map[String, (Int, SType)] = mutable.Map[String, (Int, SType)]() @@ -137,14 +142,16 @@ trait Parsing { val capturedValParser: Parser[ScalaTree] = Parser[ScalaTree] { // case t: Select if is[SigmaContractDsl](t.qualifier) => c.fail(s"add parsing of: $t") // case t: Select if is[SigmaContextDsl](t.qualifier) => c.fail(s"add parsing of: $t") - case i @ Ident(TermName(name)) if callArgToIdentMap.get(cname(name)).nonEmpty => - val newName = callArgToIdentMap(cname(name)) - val v = Ident(TermName(newName)) + case i @ Ident(TermName(name)) => + val newName = + if (callArgToIdentMap.get(cname(name)).nonEmpty) callArgToIdentMap(cname(name)) + else name + val v = Ident(TermName(newName)) c.info(s"Capturing converted($name -> $newName}): ${showRaw(v)}") ScalaTree(v, tpeToSType(i.tpe)) case t: Tree => - c.info(s"Capturing: ${showRaw(t)}") - ScalaTree(t, tpeToSType(t.tpe)) + c.fail(s"Capturing non-Ident node: ${showRaw(t)}") + // ScalaTree(t, tpeToSType(t.tpe)) } val sigmaTransParser: Parser[SigmaTransformer[_, _]] = Parser[SigmaTransformer[_, _]] { @@ -189,11 +196,27 @@ trait Parsing { case Apply(Select(astParser(l), TermName("$less")), Seq(astParser(r))) => LT(l, r) } + val twoArgOpParser: Parser[SValue] = Parser[SValue] { + case Apply(Select(astParser(l), TermName("$times")), Seq(astParser(r))) => + ArithOp(l, r, OpCodes.MultiplyCode) + case Apply(Select(astParser(l), TermName("$plus")), Seq(astParser(r))) => + ArithOp(l, r, OpCodes.PlusCode) + case Apply(Select(astParser(l), TermName("$minus")), Seq(astParser(r))) => + ArithOp(l, r, OpCodes.MinusCode) + } + val tupleParser: Parser[SValue] = Parser[SValue] { case q"$s._1" if isTypeTuple(s.tpe) => SelectField(astParser(s).asTuple, 1) case q"$s._2" if isTypeTuple(s.tpe) => SelectField(astParser(s).asTuple, 2) } + val constParser: Parser[SValue] = Parser[SValue] { + case Literal(ct @ c.universe.Constant(i)) if ct.tpe == IntTpe => + IntConstant(i.asInstanceOf[Int]) + case Literal(ct @ c.universe.Constant(i)) if ct.tpe == LongTpe => + LongConstant(i.asInstanceOf[Long]) + } + val intValueParser: Parser[IntValue] = Parser[IntValue] { case Literal(ct @ c.universe.Constant(i)) if ct.tpe == IntTpe => IntConstant(i.asInstanceOf[Int]) diff --git a/compiler/src/main/scala/org/ergoplatform/compiler/dsl/ErgoScriptCompilationDsl.scala b/compiler/src/main/scala/org/ergoplatform/compiler/dsl/ErgoScriptCompilationDsl.scala index 8300dac..d3eb9f0 100644 --- a/compiler/src/main/scala/org/ergoplatform/compiler/dsl/ErgoScriptCompilationDsl.scala +++ b/compiler/src/main/scala/org/ergoplatform/compiler/dsl/ErgoScriptCompilationDsl.scala @@ -16,6 +16,7 @@ trait ErgoScriptCompilationDsl { implicit private var IR: CompiletimeIRContext = new CompiletimeIRContext() private def compile(env: ScriptEnv, ergoScript: String): ErgoContract = { + IR.resetContext() val liftedEnv = env.mapValues { v => val tV = Evaluation.rtypeOf(v).get val elemTpe = Evaluation.rtypeToSType(tV) diff --git a/contracts-test/src/test/scala/org/ergoplatform/compiler/test/contracts/dex/DexPartialFilling.scala b/contracts-test/src/test/scala/org/ergoplatform/compiler/test/contracts/dex/DexPartialFilling.scala new file mode 100644 index 0000000..9fb5782 --- /dev/null +++ b/contracts-test/src/test/scala/org/ergoplatform/compiler/test/contracts/dex/DexPartialFilling.scala @@ -0,0 +1,83 @@ +package org.ergoplatform.compiler.test.contracts.dex + +import org.ergoplatform.compiler.{ErgoContract, ErgoScalaCompiler} +import special.collection.Coll +import special.sigma.{Context, SigmaContract, SigmaDslBuilder, SigmaProp} + +sealed abstract class DexPartialFilling extends SigmaContract { + + override def builder: SigmaDslBuilder = ??? + + def buyer( + ctx: Context, + buyerPk: SigmaProp, + tokenId: Coll[Byte], + tokenPrice: Long, + dexFeePerToken: Long + ): SigmaProp = { + import ctx._ + buyerPk || { + + val returnBox = OUTPUTS(0) + // val returnBox = OUTPUTS.filter { b => + // b.R4[Coll[Byte]].isDefined && b + // .R4[Coll[Byte]] + // .get == SELF.id && b.propositionBytes == buyerPk.propBytes + // }(0) + + val returnTokenData = returnBox.tokens(0) + val returnTokenId = returnTokenData._1 + val returnTokenAmount = returnTokenData._2 + val maxReturnTokenErgValue = returnTokenAmount * tokenPrice + val totalReturnErgValue = maxReturnTokenErgValue + returnBox.value + val expectedDexFee = dexFeePerToken * returnTokenAmount + + val foundNewOrderBoxes = OUTPUTS + // val foundNewOrderBoxes = OUTPUTS.filter { b => + // b.R4[Coll[Byte]].isDefined && b + // .R4[Coll[Byte]] + // .get == SELF.id && b.propositionBytes == SELF.propositionBytes + // } + + val coinsSecured = (SELF.value - expectedDexFee) == maxReturnTokenErgValue || { + foundNewOrderBoxes.size == 1 && foundNewOrderBoxes(0).value >= (SELF.value - totalReturnErgValue - expectedDexFee) + } + + val tokenIdIsCorrect = returnTokenId == tokenId + + tokenIdIsCorrect && returnTokenAmount >= 1 && coinsSecured + } + } + + // def seller(ctx: Context, ergAmount: Long, pkB: SigmaProp): SigmaProp = { + // import ctx._ + // pkB || ( + // OUTPUTS.size > 1 && + // OUTPUTS(1).R4[Coll[Byte]].isDefined + // ) && { + // val knownBoxId = OUTPUTS(1).R4[Coll[Byte]].get == SELF.id + // OUTPUTS(1).value >= ergAmount && + // knownBoxId && + // OUTPUTS(1).propositionBytes == pkB.propBytes + // } + // } +} + +object DexPartialFillingCompilation extends DexPartialFilling { + + def buyerContractInstance( + buyerPk: SigmaProp, + tokenId: Coll[Byte], + tokenPrice: Long, + dexFeePerToken: Long + ): ErgoContract = + ErgoScalaCompiler.contract { context: Context => + buyer(context, buyerPk, tokenId, tokenPrice, dexFeePerToken) + } + + // def sellerContractInstance(ergAmount: Long, pkB: SigmaProp): ErgoContract = + // ErgoScalaCompiler.contract { context: Context => + // seller(context, ergAmount, pkB) + // } + +}