Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V4.x pretty ergo tree #831

Draft
wants to merge 35 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
ae06ce8
fix-i778: throw more context data in exception
aslesarenko Apr 20, 2022
af1c323
fix-i778: more detailed message on assertion
aslesarenko Apr 20, 2022
20a9d63
fix-i778: more detailed message on assertion
aslesarenko Apr 20, 2022
60c5e7b
fix-i778: import cleanup
aslesarenko Apr 20, 2022
727b7b7
Merge pull request #793 from ScorexFoundation/fix-i778
aslesarenko May 4, 2022
934a8f5
i825-robovm: avoid usage of Java 8 getOrDefault method
aslesarenko Aug 23, 2022
f604672
i825-robovm: set HAS_SECRETS on CI env;
aslesarenko Aug 23, 2022
b513c0e
Merge pull request #826 from ScorexFoundation/i825-robovm
aslesarenko Aug 24, 2022
c2bb763
v5.x-tree-pretty-print: Basic tests for simple nodes (booleans/blocks…
jozanek Jun 7, 2022
a17436b
v5.x-tree-pretty-print: Introduce paiges library, indentation and wid…
jozanek Jun 8, 2022
0c3cf4b
v5.x-tree-pretty-print: Printer for global, outputs, exists and extra…
jozanek Jun 9, 2022
788bf43
v5.x-tree-pretty-print: Add test for BinOr/EQ nodes and SBoolean type.
jozanek Jun 11, 2022
eae3e31
v5.x-tree-pretty-print: Add test for substConstants, finish all Sigma…
jozanek Jun 11, 2022
444eda3
v5.x-tree-pretty-print: Downgrade paiges version, last one which uses…
jozanek Jun 11, 2022
4ca0380
v5.x-tree-pretty-print: Implement pretty printer for ergolike nodes.
jozanek Jun 13, 2022
67bf4ba
v5.x-tree-pretty-print: Implement pretty printer for Terms nodes.
jozanek Jun 13, 2022
72dc550
v5.x-tree-pretty-print: Implement pretty printer for Transformer nodes.
jozanek Jun 13, 2022
d677f1e
v5.x-tree-pretty-print: Implement pretty printer for nodes defined in…
jozanek Jun 14, 2022
2ceb537
v5.x-tree-pretty-print: Better indentation for if/function/block.
jozanek Jun 16, 2022
db9dfd3
v5.x-tree-pretty-print: Introduce methodDoc, slight refactoring.
jozanek Jun 16, 2022
e6d064e
v5.x-tree-pretty-print: Pretty print for min/max nodes and types: Str…
jozanek Jun 16, 2022
e7afa2b
v5.x-tree-pretty-print: Update comments, add test for deserialize.
jozanek Jun 17, 2022
704dbb0
v5.x-tree-pretty-print: Handle SUnit type.
jozanek Jun 17, 2022
befce0f
v5.x-tree-pretty-print: Proper indentation for blocks and functions.
jozanek Jun 17, 2022
36ecdf2
v5.x-tree-pretty-print: Values/arguments naming guess from type, pare…
jozanek Jun 20, 2022
f2f3b01
v5.x-tree-pretty-print: LogicalNegation node should also contain pare…
jozanek Jun 20, 2022
cd6d5d7
v5.x-tree-pretty-print: Customize indentation, fix closing parens for…
jozanek Jun 20, 2022
16e42dc
v5.x-tree-pretty-print: Update comments, use compile method from base…
jozanek Jun 20, 2022
17a830b
v5.x-tree-pretty-print: Add placeholder node test, explicit NoType, n…
jozanek Jun 27, 2022
bb74750
v5.x-tree-pretty-print: Tests for minerPubKey and groupGenerator node…
jozanek Jun 30, 2022
39e7989
v5.x-tree-pretty-print: Printer for Global/SFunc/NoType types.
jozanek Jul 1, 2022
9a5b928
v5.x-tree-pretty-print: Updated comments.
jozanek Jul 4, 2022
0c479a7
v5.x-tree-pretty-print: Wrap top level block in curly brackets, do no…
jozanek Jul 7, 2022
906b5f5
v5.x-tree-pretty-print: Default case for constants is to not cast at …
jozanek Aug 25, 2022
8e73b8a
v4.x-pretty-ergo-tree: Changes required after rebasing on v4.x
jozanek Oct 10, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ env:
jobs:
build:
name: Test and publish a snapshot
env:
HAS_SECRETS: ${{ secrets.SONATYPE_PASSWORD != '' }}
strategy:
matrix:
os: [ubuntu-latest]
Expand Down
3 changes: 2 additions & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ val kiama = "org.bitbucket.inkytonik.kiama" %% "kiama" % "2.1.0"
val fastparse = "com.lihaoyi" %% "fastparse" % "1.0.0"
val commonsIo = "commons-io" % "commons-io" % "2.5"
val commonsMath3 = "org.apache.commons" % "commons-math3" % "3.2"
val paiges = "org.typelevel" %% "paiges-core" % "0.3.0"

val testingDependencies = Seq(
"org.scalatest" %% "scalatest" % "3.0.5" % "test",
Expand Down Expand Up @@ -209,7 +210,7 @@ lazy val sigmastate = (project in file("sigmastate"))
.dependsOn(sigmaimpl % allConfigDependency, sigmalibrary % allConfigDependency)
.settings(libraryDefSettings)
.settings(libraryDependencies ++= Seq(
scorexUtil, kiama, fastparse, commonsMath3,
scorexUtil, kiama, fastparse, commonsMath3, paiges,
if (scalaVersion.value == scala211) circeCore211 else circeCore,
if (scalaVersion.value == scala211) circeGeneric211 else circeGeneric,
if (scalaVersion.value == scala211) circeParser211 else circeParser
Expand Down
47 changes: 37 additions & 10 deletions core/src/main/scala/scalan/Base.scala
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,14 @@ abstract class Base { scalan: Scalan =>
cfor(0)(_ < delta, _ + 1) { _ => tab.append(null) }
val sym = if (s == null) new SingleRef(d) else s
tab += sym
assert(tab.length == id + 1)
assert(tab.length == id + 1,
s"""tab.length == id + 1:
|tab.length = ${tab.length}
|id = $id
|s = $s
|d = $d
|sym = $sym
|""".stripMargin)
sym
}
}
Expand Down Expand Up @@ -781,7 +788,6 @@ abstract class Base { scalan: Scalan =>
if (sym == null) {
sym = createDefinition(optScope, newSym, d)
}
// assert(te.rhs == d, s"${if (te !) "Found" else "Created"} unequal definition ${te.rhs} with symbol ${te.sym.toStringWithType} for $d")
sym
}

Expand All @@ -792,15 +798,36 @@ abstract class Base { scalan: Scalan =>
* @return reference to `d` (which is `s`)
*/
protected def createDefinition[T](optScope: Nullable[ThunkScope], s: Ref[T], d: Def[T]): Ref[T] = {
assert(_symbolTable(d.nodeId).node.nodeId == d.nodeId)
assert(s.node eq d, s"Inconsistent Sym -> Def pair $s -> $d")
optScope match {
case Nullable(scope) =>
scope += s
case _ =>
_globalDefs.put(d, d)
try {
val nodeId = d.nodeId
val tableSym = _symbolTable(nodeId)
assert(tableSym.node.nodeId == nodeId)
assert(s.node eq d, s"Inconsistent Sym -> Def pair $s -> $d")
optScope match {
case Nullable(scope) =>
scope += s
case _ =>
_globalDefs.put(d, d)
}
s
} catch { case t: Throwable =>
val msg = new mutable.StringBuilder(
s"""optScope = $optScope
|s = $s
|d = $d""".stripMargin)
if (d != null) {
msg ++= s"\nd.nodeId = ${d.nodeId}"
val tableSym = _symbolTable(d.nodeId)
msg ++= s"\n_symbolTable(d.nodeId) = $tableSym"
if (tableSym != null) {
msg ++= s"\ntableSym.node = ${tableSym.node}"
if (tableSym.node != null) {
msg ++= s"\ntableSym.node.nodeId = ${tableSym.node.nodeId}"
}
}
}
throw new RuntimeException(msg.result(), t)
}
s
}

/**
Expand Down
6 changes: 5 additions & 1 deletion library-impl/src/main/scala/special/collection/Helpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ object Helpers {
var nValues = 0
while (i < arr.length) {
val (key, value) = m(arr(i))
val pos = keyPositions.getOrDefault(key, 0)
val pos = {
val p: Integer = keyPositions.get(key)
if (p == null) 0 else p.intValue()
}

if (pos == 0) {
keyPositions.put(key, nValues + 1)
keys += key
Expand Down
337 changes: 337 additions & 0 deletions sigmastate/src/main/scala/sigmastate/PrettyPrintErgoTree.scala

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions sigmastate/src/main/scala/sigmastate/Values.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ object Values {

def opName: String = this.getClass.getSimpleName

def toSigmaProp: SigmaPropValue = this match {
case b if b.tpe == SBoolean => BoolToSigmaProp(this.asBoolValue)
case p if p.tpe == SSigmaProp => p.asSigmaProp
case _ => sys.error(s"Expected SBoolean or SSigmaProp typed value, but was: $this")
}

/** Parser has some source information like line,column in the text. We need to keep it up until RuntimeCosting.
* The way to do this is to add Nullable property to every Value. Since Parser is always using SigmaBuilder
* to create nodes,
Expand Down Expand Up @@ -279,7 +285,7 @@ object Values {
override def costKind: PerItemCost
}

abstract class EvaluatedValue[+S <: SType] extends Value[S] {
sealed abstract class EvaluatedValue[+S <: SType] extends Value[S] {
val value: S#WrappedType
def opType: SFunc = {
val resType = tpe match {
Expand Down Expand Up @@ -680,6 +686,7 @@ object Values {
}


// TODO: Consider moving to trees.scala
trait NotReadyValueGroupElement extends NotReadyValue[SGroupElement.type] {
override def tpe = SGroupElement
}
Expand Down Expand Up @@ -707,6 +714,7 @@ object Values {
override def costKind: FixedCost = Constant.costKind
}

// TODO: Consider moving to trees.scala and make it sealed
trait NotReadyValueBoolean extends NotReadyValue[SBoolean.type] {
override def tpe = SBoolean
}
Expand Down Expand Up @@ -968,6 +976,7 @@ object Values {
override def costKind = ConcreteCollection.costKind
}

// TODO: Make sense to move to ErgoLikeContext?
trait LazyCollection[V <: SType] extends NotReadyValue[SCollection[V]]

implicit class CollectionOps[T <: SType](val coll: Value[SCollection[T]]) extends AnyVal {
Expand Down Expand Up @@ -1007,14 +1016,6 @@ object Values {
}
}

implicit class BoolValueOps(val b: BoolValue) extends AnyVal {
def toSigmaProp: SigmaPropValue = b match {
case b if b.tpe == SBoolean => BoolToSigmaProp(b)
case p if p.tpe == SSigmaProp => p.asSigmaProp
case _ => sys.error(s"Expected SBoolean or SSigmaProp typed value, but was: $b")
}
}

sealed trait BlockItem extends NotReadyValue[SType] {
def id: Int
def rhs: SValue
Expand Down
17 changes: 12 additions & 5 deletions sigmastate/src/main/scala/sigmastate/eval/IRContext.scala
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
package sigmastate.eval

import java.lang.{Math => JMath}
import sigmastate.SType
import sigmastate.Values.{Value, SValue}
import sigmastate.Values.{SValue, Value}
import sigmastate.interpreter.Interpreter.ScriptEnv
import sigmastate.lang.TransformingSigmaBuilder
import sigmastate.lang.exceptions.CostLimitException
import sigmastate.utils.Helpers
import sigmastate.utxo.CostTable

import java.util.concurrent.locks.ReentrantLock
import scala.util.Try

trait IRContext extends Evaluation with TreeBuilding {

override val builder = TransformingSigmaBuilder

/** Can be used to synchronize access to this IR object from multiple threads. */
val lock = new ReentrantLock()

/** Pass configuration which is used to turn-off constant propagation.
* @see `beginPass(noCostPropagationPass)` */
lazy val noConstPropagationPass = new DefaultPass(
Expand Down Expand Up @@ -125,9 +129,12 @@ trait IRContext extends Evaluation with TreeBuilding {
*/
def checkCostWithContext(ctx: SContext,
costF: Ref[((Context, (Int, Size[Context]))) => Int], maxCost: Long, initCost: Long): Try[Int] = Try {
val costFun = compile[(SContext, (Int, SSize[SContext])), Int, (Context, (Int, Size[Context])), Int](
getDataEnv, costF, Some(maxCost))
val (estimatedCost, accCost) = costFun((ctx, (0, Sized.sizeOf(ctx))))

val (estimatedCost, accCost) = Helpers.withReentrantLock(lock) { // protect mutable access to this IR
val costFun = compile[(SContext, (Int, SSize[SContext])), Int, (Context, (Int, Size[Context])), Int](
getDataEnv, costF, Some(maxCost))
costFun((ctx, (0, Sized.sizeOf(ctx))))
}

if (debugModeSanityChecks) {
if (estimatedCost != accCost)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -799,6 +799,7 @@ trait RuntimeCosting extends CostingRules { IR: IRContext =>
def isCostingProcess: Boolean = funUnderCosting != null

def stypeToElem[T <: SType](t: T): Elem[T#WrappedType] = (t match {
case SUnit => UnitElement
case SBoolean => BooleanElement
case SByte => ByteElement
case SShort => ShortElement
Expand All @@ -823,6 +824,7 @@ trait RuntimeCosting extends CostingRules { IR: IRContext =>
}).asInstanceOf[Elem[T#WrappedType]]

def elemToSType[T](e: Elem[T]): SType = e match {
case UnitElement => SUnit
case BooleanElement => SBoolean
case ByteElement => SByte
case ShortElement => SShort
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@ package sigmastate.interpreter

import java.util.concurrent.ExecutionException
import java.util.concurrent.atomic.AtomicInteger

import com.google.common.cache.{CacheBuilder, RemovalNotification, RemovalListener, LoadingCache, CacheLoader, CacheStats}
import com.google.common.cache.{CacheBuilder, CacheLoader, CacheStats, LoadingCache, RemovalListener, RemovalNotification}
import org.ergoplatform.settings.ErgoAlgos
import org.ergoplatform.validation.SigmaValidationSettings
import org.ergoplatform.validation.ValidationRules.{CheckCostFunc, CheckCalcFunc, trySoftForkable}
import org.ergoplatform.validation.ValidationRules.{CheckCalcFunc, CheckCostFunc, trySoftForkable}
import scalan.{AVHashMap, Nullable}
import sigmastate.Values
import sigmastate.Values.ErgoTree
import sigmastate.eval.{RuntimeIRContext, IRContext}
import sigmastate.eval.{IRContext, RuntimeIRContext}
import sigmastate.interpreter.Interpreter.{ReductionResult, WhenSoftForkReductionResult}
import sigmastate.serialization.ErgoTreeSerializer
import sigmastate.utils.Helpers
import sigmastate.utils.Helpers._
import spire.syntax.all.cfor

Expand Down Expand Up @@ -84,9 +84,11 @@ case class PrecompiledScriptReducer(scriptBytes: Seq[Byte])(implicit val IR: IRC
val estimatedCost = IR.checkCostWithContext(costingCtx, costF, maxCost, initCost).getOrThrow

// check calc
val calcF = costingRes.calcF
val calcCtx = context.toSigmaContext(isCost = false)
val res = Interpreter.calcResult(IR)(calcCtx, calcF)
val res = Helpers.withReentrantLock(IR.lock) { // protecting mutable access to IR instance
val calcF = costingRes.calcF
Interpreter.calcResult(IR)(calcCtx, calcF)
}
ReductionResult(SigmaDsl.toSigmaBoolean(res), estimatedCost)
}
}
Expand Down
2 changes: 1 addition & 1 deletion sigmastate/src/main/scala/sigmastate/trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ object CreateProveDHTuple extends ValueCompanion {
override val costKind = FixedCost(20)
}

trait SigmaTransformer[IV <: SigmaPropValue, OV <: SigmaPropValue] extends SigmaPropValue {
sealed trait SigmaTransformer[IV <: SigmaPropValue, OV <: SigmaPropValue] extends SigmaPropValue {
val items: Seq[IV]
}
trait SigmaTransformerCompanion extends ValueCompanion {
Expand Down
27 changes: 24 additions & 3 deletions sigmastate/src/main/scala/sigmastate/utils/Helpers.scala
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package sigmastate.utils

import java.util

import io.circe.Decoder
import org.ergoplatform.settings.ErgoAlgos
import sigmastate.eval.{Colls, SigmaDsl}
import sigmastate.interpreter.CryptoConstants.EcPointType
import special.collection.Coll
import special.sigma.GroupElement

import java.util
import java.util.concurrent.locks.Lock
import scala.reflect.ClassTag
import scala.util.{Failure, Try, Either, Success, Right}
import scala.util.{Either, Failure, Right, Success, Try}

object Helpers {
def xor(ba1: Array[Byte], ba2: Array[Byte]): Array[Byte] = ba1.zip(ba2).map(t => (t._1 ^ t._2).toByte)
Expand Down Expand Up @@ -158,6 +158,27 @@ object Helpers {
val bytes = ErgoAlgos.decodeUnsafe(base16String)
Colls.fromArray(bytes)
}

/**
* Executes the given block with a reentrant mutual exclusion Lock with the same basic
* behavior and semantics as the implicit monitor lock accessed using synchronized
* methods and statements in Java.
*
* Note, using this method has an advantage of having this method in a stack trace in case of
* an exception in the block.
* @param l lock object which should be acquired by the current thread before block can start executing
* @param block block of code which will be executed retaining the lock
* @return the value produced by the block
*/
def withReentrantLock[A](l: Lock)(block: => A): A = {
l.lock()
val res = try
block
finally {
l.unlock()
}
res
}
}

object Overloading {
Expand Down
5 changes: 3 additions & 2 deletions sigmastate/src/main/scala/sigmastate/utxo/transformers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ object Filter extends ValueCompanion {
}

/** Transforms a collection of values to a boolean (see [[Exists]], [[ForAll]]). */
trait BooleanTransformer[IV <: SType] extends Transformer[SCollection[IV], SBoolean.type] {
sealed trait BooleanTransformer[IV <: SType] extends Transformer[SCollection[IV], SBoolean.type] {
override val input: Value[SCollection[IV]]
val condition: Value[SFunc]
override def tpe = SBoolean
Expand Down Expand Up @@ -434,6 +434,7 @@ object ExtractBytes extends SimpleTransformerCompanion {
override def argInfos: Seq[ArgInfo] = ExtractBytesInfo.argInfos
}

// TODO: Rename to WithoutRef to be consistent with script
/** Extracts serialized bytes of this box's content, excluding transactionId and index of output. */
case class ExtractBytesWithNoRef(input: Value[SBox.type]) extends Extract[SByteArray] with NotReadyValueByteArray {
override def companion = ExtractBytesWithNoRef
Expand Down Expand Up @@ -523,7 +524,7 @@ object ExtractCreationInfo extends SimpleTransformerCompanion {
val OpType = SFunc(SBox, ResultType)
}

trait Deserialize[V <: SType] extends NotReadyValue[V]
sealed trait Deserialize[V <: SType] extends NotReadyValue[V]

/** Extracts context variable as Coll[Byte], deserializes it to script and then executes this script in the current context.
* The original `Coll[Byte]` of the script is available as `getVar[Coll[Byte]](id)`
Expand Down
Loading