diff --git a/build.sbt b/build.sbt
index 06bf2e6f14..31d769e1e2 100644
--- a/build.sbt
+++ b/build.sbt
@@ -17,7 +17,7 @@ lazy val commonSettings = Seq(
scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 13)) =>
- Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-release", "8")
+ Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-Wconf:src=src_managed/.*:silent", "-release", "8")
case Some((2, 12)) =>
Seq("-Ywarn-unused:_,imports", "-Ywarn-unused:imports", "-release", "8")
case Some((2, 11)) =>
@@ -282,7 +282,7 @@ lazy val interpreterJS = interpreter.js
},
Compile / npmDependencies ++= Seq(
"sigmajs-crypto-facade" -> sigmajsCryptoFacadeVersion,
- "@fleet-sdk/common" -> "0.1.0-alpha.14"
+ "@fleet-sdk/common" -> "0.1.3"
)
)
diff --git a/common/shared/src/main/scala/scalan/TypeDesc.scala b/common/shared/src/main/scala/scalan/TypeDesc.scala
index c7578a2ea8..98ecb1ec4c 100644
--- a/common/shared/src/main/scala/scalan/TypeDesc.scala
+++ b/common/shared/src/main/scala/scalan/TypeDesc.scala
@@ -35,9 +35,6 @@ object RType {
case ClassTag.Short => ShortType
case ClassTag.Int => IntType
case ClassTag.Long => LongType
- case ClassTag.Char => CharType
- case ClassTag.Float => FloatType
- case ClassTag.Double => DoubleType
case ClassTag.Unit => UnitType
case _ => GeneralType[A](ctA)
}).asInstanceOf[RType[A]]
@@ -72,10 +69,6 @@ object RType {
implicit val ShortType : RType[Short] = PrimitiveType[Short] (ClassTag.Short, Array.emptyShortArray)
implicit val IntType : RType[Int] = PrimitiveType[Int] (ClassTag.Int, Array.emptyIntArray)
implicit val LongType : RType[Long] = PrimitiveType[Long] (ClassTag.Long, Array.emptyLongArray)
- // TODO v5.x: optimize: remove Char, Float, Double types, they are not supported and will never be
- implicit val CharType : RType[Char] = PrimitiveType[Char] (ClassTag.Char, Array.emptyCharArray)
- implicit val FloatType : RType[Float] = PrimitiveType[Float] (ClassTag.Float, Array.emptyFloatArray)
- implicit val DoubleType : RType[Double] = PrimitiveType[Double] (ClassTag.Double, Array.emptyDoubleArray)
implicit val UnitType : RType[Unit] = PrimitiveType[Unit] (ClassTag.Unit, Array[Unit]()(ClassTag.Unit))
implicit case object StringType extends RType[String] {
diff --git a/common/shared/src/main/scala/scalan/util/CollectionUtil.scala b/common/shared/src/main/scala/scalan/util/CollectionUtil.scala
index b9b79dd65c..0ee4030e2c 100644
--- a/common/shared/src/main/scala/scalan/util/CollectionUtil.scala
+++ b/common/shared/src/main/scala/scalan/util/CollectionUtil.scala
@@ -128,6 +128,9 @@ object CollectionUtil {
}
implicit class AnyOps[A](val x: A) extends AnyVal {
+ /** Performs a specified action on the source value and returns the result. */
+ def perform(action: A => A): A = action(x)
+
/** Traverses the tree structure in a depth-first manner using the provided function to generate child nodes.
*
* @param f a function that takes a node of type A and returns a list of its children
diff --git a/common/shared/src/test/scala/scalan/BaseTests.scala b/common/shared/src/test/scala/scalan/BaseTests.scala
index 64a8c7f615..e015ff948e 100644
--- a/common/shared/src/test/scala/scalan/BaseTests.scala
+++ b/common/shared/src/test/scala/scalan/BaseTests.scala
@@ -34,7 +34,8 @@ abstract class BaseShouldTests extends AnyFlatSpec with TestUtils {
}
}
- protected implicit def convertToInAndIgnoreMethods2(resultOfStringPassedToVerb: ResultOfStringPassedToVerb) =
+ protected implicit def convertToInAndIgnoreMethods2(
+ resultOfStringPassedToVerb: ResultOfStringPassedToVerb): InAndIgnoreMethods2 =
new InAndIgnoreMethods2(resultOfStringPassedToVerb)
}
diff --git a/core-lib/shared/src/main/scala/special/collection/package.scala b/core-lib/shared/src/main/scala/special/collection/package.scala
index 826c1bd3b5..b8be71be23 100644
--- a/core-lib/shared/src/main/scala/special/collection/package.scala
+++ b/core-lib/shared/src/main/scala/special/collection/package.scala
@@ -20,5 +20,14 @@ package object collection {
/** Implicit resolution of `Coll[A]` type descriptor, given a descriptor of `A`. */
implicit def collRType[A](implicit tA: RType[A]): RType[Coll[A]] = CollType[A](tA)
+ /** Conversion to underlying descriptor class.
+ * Allows syntax like
+ *
+ * ```val tColl: RType[Coll[A]] = ...; tColl.tItem```
+ *
+ * where `tItem` is a method of `CollType`, but is not defined on `RType`.
+ */
+ implicit def downcastCollType[A](ct: RType[Coll[A]]): CollType[A] = ct.asInstanceOf[CollType[A]]
+
implicit val collBuilderRType: RType[CollBuilder] = RType.fromClassTag(classTag[CollBuilder])
}
diff --git a/core-lib/shared/src/main/scala/special/sigma/SigmaDsl.scala b/core-lib/shared/src/main/scala/special/sigma/SigmaDsl.scala
index 186aa59bd7..b1f27f0899 100644
--- a/core-lib/shared/src/main/scala/special/sigma/SigmaDsl.scala
+++ b/core-lib/shared/src/main/scala/special/sigma/SigmaDsl.scala
@@ -192,8 +192,7 @@ trait BigInt {
def |(that: BigInt): BigInt = or(that)
}
-/** Base class for points on elliptic curves.
- */
+/** Base class for points on elliptic curves. */
trait GroupElement {
/** Checks if the provided element is an identity element. */
def isIdentity: Boolean
@@ -381,7 +380,6 @@ trait Box {
trait AvlTree {
/** Returns digest of the state represented by this tree.
* Authenticated tree digest = root hash bytes ++ tree height
- * @since 2.0
*/
def digest: Coll[Byte]
@@ -529,10 +527,8 @@ trait AvlTreeVerifier {
}
-/** Only header fields that can be predicted by a miner.
- * @since 2.0
- */
-trait PreHeader { // Testnet2
+/** Only header fields that can be predicted by a miner. */
+trait PreHeader {
/** Block version, to be increased on every soft and hardfork. */
def version: Byte
@@ -556,9 +552,7 @@ trait PreHeader { // Testnet2
def votes: Coll[Byte]
}
-/** Represents data of the block header available in Sigma propositions.
- * @since 2.0
- */
+/** Represents data of the block header available in Sigma propositions. */
trait Header {
/** Bytes representation of ModifierId of this Header */
def id: Coll[Byte]
@@ -572,7 +566,7 @@ trait Header {
/** Hash of ADProofs for transactions in a block */
def ADProofsRoot: Coll[Byte] // Digest32. Can we build AvlTree out of it?
- /** AvlTree) of a state after block application */
+ /** AvlTree of a state after block application */
def stateRoot: AvlTree
/** Root hash (for a Merkle tree) of transactions in a block. */
diff --git a/core-lib/shared/src/test/scala/special/TypesTests.scala b/core-lib/shared/src/test/scala/special/TypesTests.scala
index 13b562d84c..b0c6ce5808 100644
--- a/core-lib/shared/src/test/scala/special/TypesTests.scala
+++ b/core-lib/shared/src/test/scala/special/TypesTests.scala
@@ -10,8 +10,8 @@ class TypesTests extends BaseTests {
def test[A](t: RType[A], n: String) = {
t.name shouldBe n
}
- test(tupleRType(Array(IntType, LongType, RType[(String, Double)], RType[Option[Boolean]])),
- "(Int, Long, (String, Double), Option[Boolean])")
+ test(tupleRType(Array(IntType, LongType, RType[(Byte, special.sigma.BigInt)], RType[Option[Boolean]])),
+ "(Int, Long, (Byte, BigInt), Option[Boolean])")
}
test("RType implements equality") {
diff --git a/core-lib/shared/src/test/scala/special/collections/CollsTests.scala b/core-lib/shared/src/test/scala/special/collections/CollsTests.scala
index 6e2271fd9e..f1fe708a7f 100644
--- a/core-lib/shared/src/test/scala/special/collections/CollsTests.scala
+++ b/core-lib/shared/src/test/scala/special/collections/CollsTests.scala
@@ -433,24 +433,6 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers
checkColls(repl, coll)
}
- forAll(charGen, indexGen, minSuccess) { (x, n) =>
- val repl = builder.replicate(n, x)
- val coll = builder.fromArray(Array.fill(n)(x))
-
- checkColls(repl, coll)
- }
- forAll(floatGen, indexGen, minSuccess) { (x, n) =>
- val repl = builder.replicate(n, x)
- val coll = builder.fromArray(Array.fill(n)(x))
-
- checkColls(repl, coll)
- }
- forAll (doubleGen, indexGen, minSuccess) { (x, n) =>
- val repl = builder.replicate(n, x)
- val coll = builder.fromArray(Array.fill(n)(x))
-
- checkColls(repl, coll)
- }
forAll (indexGen, minSuccess) { (n) =>
val replTrue = builder.replicate(n, true)
val collTrue = builder.fromArray(Array.fill(n)(true))
@@ -479,13 +461,13 @@ class CollsTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers
checkColls(repl, coll)
}
- forAll (byteGen, doubleGen, intGen, indexGen, minSuccess) { (b, d, i, n) =>
+ forAll (byteGen, longGen, intGen, indexGen, minSuccess) { (b, d, i, n) =>
val repl = builder.replicate(n, (b, (i, (d, b))))
- val coll = builder.fromArray(Array.fill[(Byte, (Int, (Double, Byte)))](n)((b, (i, (d, b)))))
+ val coll = builder.fromArray(Array.fill[(Byte, (Int, (Long, Byte)))](n)((b, (i, (d, b)))))
checkColls(repl, coll)
}
- forAll (byteGen, doubleGen, intGen, indexGen, indexGen, minSuccess) { (b, d, i, n, m) =>
+ forAll (byteGen, longGen, intGen, indexGen, indexGen, minSuccess) { (b, d, i, n, m) =>
val repl = builder.replicate(n, (b, ((i, (("string", builder.replicate(m, n)), Array(1, 2, 3, 4))), (d, b))))
val coll = builder.fromArray(Array.fill(n)((b, ((i, (("string", builder.fromArray(Array.fill(m)(n))), Array(1, 2, 3, 4))), (d, b)))))
diff --git a/docs/Costing.md b/docs/Costing.md
deleted file mode 100644
index a48b6d9b84..0000000000
--- a/docs/Costing.md
+++ /dev/null
@@ -1,446 +0,0 @@
-
-## Estimation of ErgoTree computational complexity
-
-### Background
-
-To prevent DDoS attacks every script in a blockchain have to be checked for complexity limits.
-This estimation happens during block/transaction validation for every guarding script of every input box.
-Script can be executed iff its estimated complexity in a given `Context` is less than a `limit` value.
-
-### Contract execution context
-
-Transaction `tx` is validated as part of the block.
-Every input box `ib` in `tx` contains a property `propBytes` with serialized ErgoTree of the contract.
-During validation `propBytes` property is deserialized to ErgoTree `tree` which is executed.
-The box `ib` itself is accessed via `SELF` property of the `Context` data structure.
-Besides `Context` execution of a contract depends on votable `ProtocolParameters` data, which contains
-global parameters which can be set up by miners following a voting protocol.
-
-### Costing Rules
-
-The following constants are used in cost and size calculations.
-
-Constant Name | Description
---------------|------------
-GroupSize | Number of bytes to represent any group element as byte array
-
-The following table shows the rules for calculating cost and size for a result of each operation
-based on costs and sizes of the operations arguments.
-The operations names are given by the node classes of ErgoTree.
-
-
-Operation | Cost in time units, Size in bytes
----------------------|-----------------------------------
-`ProveDLog` | CT("ProveDlogEval"), GroupSize
-`ProveDHTuple` | CT("ProveDHTupleEval"), GroupSize * 4
-x,y: BigInt; x op y where op in ("+", "-") | cost(x) + cost(y) + CT("op"), MaxSizeInBytes
-
-
-### Asymptotic complexity of the costing algorithm
-
-For a given input box `ib` the algorithm consists of the following steps (details of each
-step are given in later sections):
-
-`#` | Step | Complexity
-----|-------------------------------------------------------------------|-----------
-1 | Check that `val len = propBytes.length; len < MaxPropBytesSize` | `O(1)`
-2 | Deserialize `propBytes` to ErgoTree `tree` with `N` nodes | `O(len) and N = O(len)`
-3 | Recursively traverse `tree` and build costed graph `graphC` with `M <= N` nodes | `O(N)`
-4 | Split `graphC` into calculation function `calcF` and cost estimation function `costF` | `O(M)`
-5 | Topologically sort nodes of `costF` for execution (Tarjan algorithm) | `O(M)`
-6 | Iterate over sorted nodes of `costF` and execute primitive ops | `O(M)`
-
-#### Overview of Costing Process
-
-
-Deserialized ErgoTree have to be translated into two related functions:
-1) `calcF: Context => SigmaBoolean` - script calculation function, which produces Sigma tree for
-further proof generation (when new `tx` is created) or proof verification (when `tx` is verified)
-2) `costF: Context => Int` - cost estimation function, which by construction is closely connected
-with `calcF` and allows to compute execution complexity of `calcF` in a given context.
-
-_Costing Process_ or simply _costing_ is the process of obtaining two functions `calcF` and `costF`
-for a given deserialized ErgoTree.
-
-The key feature of the costing algorithm is that in many cases the functions `calcF` and `costF` can be
-constructed for a given ErgoTree once and for all Context. This is statically verifiable property
-which can be ensured by the compiler of ErgoTree.
-
-If context independent costing is not possible, the corresponding bit should be setup in script header.
-In this case _context-dependent costing_ should be performed for the script during transaction validation.
-Context-dependent costing can use data in the `Context` to construct `calcF` and `costF` functions.
-This is necessary to achieve better cost approximations in complex scripts.
-
-Costing process is divided into two steps:
-1) Building of _Costed Graph_ `graphC` (see [below](#BuildingCostedGraph))
-2) Splitting of the `graphC` into `calcF` and `costF` functions (see [below](#SplittingCostedGraph))
-
-### Deserialization (Steps 1, 2 of costing algorithm)
-
-Deserializer should check that serialized byte array is of limited size, otherwise
-out-of-memory attack is possible. `MaxPropBytesSize` is a protocol parameter (see `ProtocolParameters`).
-During deserialization another parameter `MaxTreeDepth` is checked to limit depth of ErgoTree and thus
-avoid stack-overflow attack.
-
-### Building Costed Graph (Step 3)
-
-
-Costed Graph is a graph-based intermediate representatin (IR) which is created from the deserialized
-ErgoTree. Implementation of Costed Graph is based on Scalan/Special framework.
-See [Scalan idioms](https://github.com/scalan/scalan.github.io/blob/master/idioms.md) for details.
-
-#### Costed Values
-
-
-The nodes of the costed graph `graphC` are _costed values_ of type `Costed[T]`.
-```scala
-trait Costed[Val] {
- def value: Val // the value which is costed
- def cost: Int // the cost estimation to obtain value
- def dataSize: Long // the size estimation of the value in bytes
-}
-```
-Every costed value `valC: Costed[T]` is a container of the value along with additional data to
-represent cost and size (costing information, costing properties).
-
-Note, that `cost` and `dataSize` are independent parameters because some _costed_ values may have
-very small `dataSize`, but at the same time very high `cost`, e.g. result of contract may be `true`
-boolean value whose `dataSize` is 1 byte, but its `cost` is the cost of executing the whole contract.
-The opposite is also possible. For example a context variable of `Coll[Byte]` type have `cost` equal 0,
-but may have very big `dataSize`.
-
-From this perspective Costed Graph `graphC` is a data flow graph between costed values.
-Costed graph represents both execution of values and simultaneous execution of `cost` and `dataSize`
-estimations for all intermediate values.
-
-The `cost` and `dataSize` computations depend on operations in the contract.
-Consider the following contract fragment where we multiply two big integers:
-```
-val x: BigInt = ...
-val y: BigInt = ...
-val z = x * y
-```
-In the costed graph it corresponds to the following fragment:
-
-```scala
-val xC: Costed[BigInt] = ...
-val yC: Costed[BigInt]= ...
-val zC = new Costed {
- val value = xC.value * yC.value
- val dataSize = xC.dataSize + yC.dataSize + 1L
- val cost = xC.cost + yC.cost + costOf("*_per_item") * this.dataSize
-}
-```
-For the example above note the following properties:
-1) both `cost` and `dataSize` depend on costing information from arguments
-2) resulting `zC.cost` depend on `dataSize` of arguments
-3) neigher `cost` nor `dataSize` depend on argument values, i.e. costing properties of result
-can be approximated using costing properties of arguments along.
-
-The property 3) turns out to be very important, because many operations have this property
-which leads to the possibility to do context-independent costing.
-
-Depending on type `T` costed values have specialized representations, given by descendants of
-the type `Costed[T]`. The following subsections describe possible specialization in detail.
-
-##### Costed Values of Primitive Type
-
-The simplest case is when `T` is primitive.
-In this case cost information is represented by class `CCostedPrim[T]` derived from trait `CostedPrim[T]`.
-Separation into class and closely related trait is technical implementation detail, we will omit traits
-in the following sections for brevity. The trait always have the same public methods as class.
-```scala
-trait CostedPrim[Val] extends Costed[Val]
-class CCostedPrim[Val](val value: Val, val cost: Int, val dataSize: Long) extends CostedPrim[Val]
-```
-This representation of costs is used for intermediate values of type `Boolean`, `Byte`, `Short`, `Int`,
-`Long`, `BigInt`, `GroupElement` and `SigmaProp` types.
-These types represent atomic values of constant or limited size.
-The following table summarizes this
-
-Type | Data Size, bytes
----------------|-----------------
-`Boolean` | == 1
-`Byte` | == 1
-`Short` | == 2
-`Int` | == 4
-`Long` | == 8
-`BigInt` | <= 32
-`GroupElement` | <= 32
-`SigmaProp` | <= 32
-
-Constants, context variables and registers of primitive types are represented in costed graph `graphC`
-using `CCostedPrim` class. For these cases costed properties as computed from actual data values.
-For example having Int literal in the contract
-
-`val x: Int = 10`
-
-costing algorithm constructs the following graph
-
-`val xC: Costed[Int] = CCostedPrim(10, costOf("Const:() => Int"), sizeOf[Int])`
-
-Here `costOf("Const:() => Int")` is an operation which requests `CostModel` for the cost of using
-a constant value in a script.
-
-##### Costed Values of Tuple type
-
-If `L` and `R` types, then costed value of type `(L,R)` is represented by the following
-specializations of `Costed[(L,R)]` type
-```scala
-class CCostedPair[L,R](val l: Costed[L], val r: Costed[R]) extends CostedPair[L,R] {
- def value: (L,R) = (l.value, r.value)
- def cost: Int = l.cost + r.cost + ConstructTupleCost
- def dataSize: Long = l.dataSize + r.dataSize
-}
-```
-Thus, for every intermediate value of type `(L,R)` we assume it has two components
-which are both costed values and accessible via properties `l` and `r` of
-`CostedPair` trait.
-
-For each constant, context variable and register access the corresponding `CCostedPair`
-is computed from actual data values by recursively deconstructing them down to primitive
-types.
-For example for the constant `(10, (10L, true))` the following costed value will be created
-in costed graph
-```scala
-CCostedPair(
- CCostedPrim(10, costOf("Const:() => Int"), sizeOf[Int]),
- CCostedPair(
- CCostedPrim(10L, costOf("Const:() => Long"), sizeOf[Long]),
- CCostedPrim(true, costOf("Const:() => Boolean"), sizeOf[Boolean])))
-```
-
-##### Costed Values of Coll Type
-
-If `Item` is a type of array element, then costed value of type `Coll[Item]` is
-represented by the following specializations of `Costed[Coll[Item]]` type
-```scala
-class CCostedColl[Item](
- val values: Coll[Item], val costs: Coll[Int],
- val sizes: Coll[Long], val valuesCost: Int) extends CostedColl[Item] {
- def value: Coll[Item] = values
- def cost: Int = valuesCost + costs.sum
- def dataSize: Long = sizes.sum
- def mapCosted[Res](f: Costed[Item] => Costed[Res]): CostedColl[Res] = rewritableMethod
- def foldCosted[B](zero: Costed[B], op: Costed[(B, Item)] => Costed[B]): Costed[B] = rewritableMethod
-}
-```
-For constant, context variables and registers values of `Coll` type the costing information
-of `CCostedColl` is computed from actual data.
-
-Note methods `mapCosted` and `foldCosted`, these methods represent costed version of original
-collection methods. Note the methods are defined as rewritable, meaning their implementation
-require special rewriting rule. See section [Rewrite Rules](#RewriteRules)
-
-#### Building Costed Graph
-
-Given an environment `envVals` and ErgoTree `tree` a Costed Graph can be obtained as
-[reified lambda](https://github.com/scalan/scalan.github.io/blob/master/idioms.md#Idiom4) of type
-`Ref[Context => Costed[T#WrappedType]]`
-This transformation is implemented as shown in the following `buildCostedGraph` method, which can
-be found in `RuntimeCosting.scala` file.
-```scala
-def buildCostedGraph[T <: SType](envVals: Map[Any, SValue], tree: Value[T]): Ref[Context => Costed[T#WrappedType]] =
- fun { ctx: Ref[Context] => // here ctx represents data context
- val ctxC = RCCostedContext(ctx) // data context is wrapped into Costed value container
- val env = envVals.mapValues(v => evalNode(ctxC, Map(), v)) // do costing of environment
- val res = evalNode(ctxC, env, tree) // traverse tree recursively applying costing rules
- res
- }
-```
-Note that function `evalNode` applies the [costing rules](#CostingRules) recursively for tree nodes
-(of `sigmastate.Values.Value[T]` type). Those rules are executed in the `fun` block and
-as result all the created graph nodes belong to the resulting costed graph
-
-#### Costing Rules
-
-
-In order to build costed graph, the algorithm have to recursively traverse ErgoTree.
-For each node of ErgoTree, separate _costing rule_ is applied using `evalNode` method
-whose structure is show below.
-```scala
-type RCosted[A] = Ref[Costed[A]]
-type CostingEnv = Map[Any, RCosted[_]]
-def evalNode[T <: SType](ctx: Ref[CostedContext], env: CostingEnv, node: Value[T]): RCosted[T#WrappedType] = {
- def eval[T <: SType](node: Value[T]) = evalNode(ctx, env, node)
- object In { def unapply(v: SValue): Nullable[RCosted[Any]] = Nullable(evalNode(ctx, env, v)) }
- ...
- node match {
- case Node1(In(arg1),...,In(argK)) => rhs1(arg1,...,argK)
- ...
- case NodeN(In(arg1),...,In(argK)) => rhsN(arg1,...,argK)
- }
-}
-```
-Here `In` is a helper extractor which recursively apply `evalNode` for each argument so that variables
-`arg1,...,argK` represent results of costing of the corresponding subtree.
-The right hand side of each rule (`rhs1,...rhsN`) contains operations with
-[Ref types](https://github.com/scalan/scalan.github.io/blob/master/idioms.md#Idiom3), the effect of their
-execution is creation of new graph nodes which become part of resulting costed graph.
-
-Following is an example of a simple costing rule to introduce basic concepts
-(it can be found in RuntimeCosting.scala file).
-```scala
- case sigmastate.MultiplyGroup(In(_l), In(_r)) =>
- val l = asRep[Costed[GroupElement]](_l) // type cast to an expected Ref type
- val r = asRep[Costed[GroupElement]](_r)
- val value = l.value.add(r.value) // value sub-rule
- val cost = l.cost + r.cost + costOf(node) // cost sub-rule
- val size = CryptoConstants.groupSize.toLong // size sub-rule
- RCCostedPrim(value, cost, size)
-```
-The rule first recognizes specific ErgoTree node, then recursively each argument is costed
-so that the result is bound with variables `_l` and `_r`.
-Right hand side starts with typecasting costed subgraphs to expected types. This operation is safe
-because input ErgoTree is type checked. These typecasts also make the rest of the rules typesafe,
-which is ensured by Scala compiler checking correctness of the operations.
-Using costed arguments `l` and `r` the rule contains three sub-rules.
-_Value rule_ implements calculation of the resulting value and essentially translates
-MultiplyGroup operation into operation in the costed graph.
-_Cost rule_ defines formula of the cost for `MultiplyGroup` operation (by adding to the costed graph).
-_Size rule_ defines formula of the size for `MultiplyGroup` operation
-And finally `value`, `cost` and `size` are packed into costed value which represent current tree node
-in the costed graph.
-
-The rule above has the simples form and applicable to most simple operations. However some operations
-require rules which don't fall into this default patterns.
-Following is an example rule for `MapCollection` tree node, which makes recursive costing of arguments
-explicit by using `eval` helper and also employ other idioms of staged evaluation.
-```scala
- case MapCollection(input, id, mapper) =>
- val eIn = stypeToElem(input.tpe.elemType) // translate sigma type to Special type descriptor
- val xs = asRep[CostedColl[Any]](eval(input)) // recursively build subgraph for input argument
- implicit val eAny = xs.elem.asInstanceOf[CostedElem[Coll[Any],_]].eVal.eA
- assert(eIn == eAny, s"Types should be equal: but $eIn != $eAny")
- val mapperC = fun { x: Ref[Costed[Any]] => // x argument is already costed
- evalNode(ctx, env + (id -> x), mapper) // associate id in the tree with x node of the graph
- }
- val res = xs.mapCosted(mapperC) // call costed method of costed collection
- res
-```
-Observe that this rule basically translates mapping operation over collection into invocation of
-the method `mapCosted` on costed collection value `xs` with appropriately prepared argument `mapperC`.
-Because `xs` has [Ref type](https://github.com/scalan/scalan.github.io/blob/master/idioms.md#Idiom3)
-this invocation has an effect of adding new `MethodCall(xs, "mapCosted", Seq(mapperC))` node to the graph.
-This new node is immediately catched by the rewriting rule (see [Rewrite Rules](#RewriteRules) section) which further transforms it into final
-nodes of resulting costed graph. Such separation makes the whole algorithm more modular.
-
-### Spliting Costed Graph (Step 4)
-
-
-Costed Graph represents simultaneous calculation of original contract and cost information along contract's data flow.
-However in order to perform cost estimation before contract calculation we need to separate contract calculation
-operations of the Costed Graph from cost and data size computing operations.
-
-After building a Costed Graph
-```scala
-val graphC: Ref[Context => SType#WrappedValue] = buildCostedGraph(env, tree)
-```
-we can perform _splitting_ by using the function `split` to obtain `calcF` and `costF` functions
-```scala
-val Pair(calcF: Ref[Context => SType#WrappedValue], costF: Ref[Context => Int]) = split(graphC)
-```
-Both `calcF` and `costF` take `Context` as its argument and both represented as
-[reified lambdas](https://github.com/scalan/scalan.github.io/blob/master/idioms.md#Idiom4) of
-Scalan/Special IR.
-
-This _splitting_ function is generic and defined as shown below
-```scala
- def split[T,R](f: Ref[T => Costed[R]]): Ref[(T => R, T => Int)] = {
- val calc = fun { x: Ref[T] => val y = f(x); val res = y.value; res }
- val cost = fun { x: Ref[T] => f(x).cost }
- Pair(calc, cost)
- }
-```
-In order to understand how this function works first observe, that this function is from
-reified lambda to a pair of reified lambdas, thus it performs transformation of one graph to a pair of
-new graphs.
-Second, consider the `fun` block in the definition of `calc` and the application `f(x)` inside the block.
-Because `f` is a reified lambda then according to staged evaluation semantics its application to `x`
-has an effect of inlining, i.e. unfolding the graph of `f` with `x` substituted for `f`'s argument into
-the block of `fun`.
-Third, remember that Costed Graph have nodes of types derived from `Costed` depending of the type of value,
-e.g. for primitive type `CCostedPrim(v, c, s)` node is added to the graph.
-This is described in [Costed Values](#CostedValues) section.
-And forth, recall that typical costing rule have the following formulas for calculation of costed values.
-```scala
- val value = l.value.add(r.value) // value sub-rule
- val cost = l.cost + r.cost + costOf(node) // cost sub-rule
- val size = CryptoConstants.groupSize.toLong // size sub-rule
- RCCostedPrim(value, cost, size)
-```
-Combined with third point and assuming
-```scala
- l = new CCostedPrim(v1, c1, s1)
- r = new CCostedPrim(v2, c2, s2)
-```
-we can conclude that `l.value.add(r.value)` will evaluate to `v1.add(v2)` and similarly
-`l.cost + r.cost + costOf(node)` will evaluate to `c1 + c2 + costOf(node)`.
-
-```scala
- val value = v1.add(v2) // value sub-rule
- val cost = c1 + c2 + costOf(node) // cost sub-rule
- val size = CryptoConstants.groupSize.toLong // size sub-rule
- RCCostedPrim(value, cost, size)
-```
-In other words, resuting node of the cosing rule doesn't depend on intermediate costed nodes.
-Such intermediate nodes (like `CCostedPrim` above) become dead and are not used in values calculation.
-This is the key insight in the implementation of function `split`.
-
-Now, keeping above in mind, after the graph of `f` is unfolded `y` represents resulting node.
-Thus, `value` property is called on the costed node `y` which has type `Costed`.
-The resulting symbol obtained by execution of `y.value` becomes a resulting node of the
-`calc` graph. After the block of `fun` operator is executed, dead-code-elimination is performed by `fun`
-using simple collection of reachable nodes of the graph starting from resulting node `res`.
-
-Thus, by construction, the body of `calc` contains only operations necessary to execute contract, and have no
-vestiges of costing and sizing operations.
-
-### Real World Complications
-
-Here we discuss some complications in the algorithm caused by the diversity of real world examples.
-The main motivation here is to keep main algorithm generic and simple, and encapsulate all complexity
-of the specific cases into reusable modules.
-
-
-#### Rewrite Rules
-
-[Rewrite rules](https://github.com/scalan/scalan.github.io/blob/master/idioms.md#Idiom6) is the mechanism
-to hook into graph building process and perform on the fly substitution of
-specific sub-graphs with equivalent but different sub-graphs.
-The following rule uses auto-generated extractor `mapCosted` which recognizes invocations of method
-`CostedColl.mapCosted` (Remember, this method was used in costing rule for `MapCollection` tree node).
-
-```scala
-override def rewriteDef[T](d: Def[T]): Ref[_] = {
- val CCM = CostedCollMethods
- d match {
- case CCM.mapCosted(xs: RCostedColl[a], _f: RCostedFunc[_, b]) =>
- val f = asRep[Costed[a] => Costed[b]](_f)
- val (calcF, costF, sizeF) = splitCostedFunc[a, b](f)
- val vals = xs.values.map(calcF)
- val mRes = AllMarking(element[Int])
- val mCostF = sliceAnalyzer.analyzeFunc(costF, mRes)
- implicit val eA = xs.elem.eItem
- implicit val eB = f.elem.eRange.eVal
-
- val costs = mCostF.mDom match {
- case PairMarking(markA,_) if markA.isEmpty =>
- val slicedCostF = fun { in: Ref[(Int, Long)] => costF(Pair(variable[a], in)) }
- xs.costs.zip(xs.sizes).map(slicedCostF)
- case _ =>
- xs.values.zip(xs.costs.zip(xs.sizes)).map(costF)
- }
- val tpeB = elemToSType(eB)
- val sizes = if (tpeB.isConstantSize) {
- colBuilder.replicate(xs.sizes.length, typeSize(tpeB))
- } else
- xs.sizes.map(sizeF)
- RCCostedColl(vals, costs, sizes, xs.valuesCost)
- case _ => super.rewriteDef(d)
- }
-}
-```
-Method `rewriteDef` is defined in `Scalan` cake trait and can be overridden following _stackable overrides_
-pattern (calling super.rewriteDef for the default case). This specific rule is defined in `RuntimeCosting`
-trait.
diff --git a/docs/ergoscript-compiler.md b/docs/ergoscript-compiler.md
new file mode 100644
index 0000000000..e50f7d6a00
--- /dev/null
+++ b/docs/ergoscript-compiler.md
@@ -0,0 +1,56 @@
+
+# ErgoScript Compiler
+
+Sigma frontend implements the following pipeline:
+
+`SourceCode` --> `parse` --> `bind` -> `typecheck` -> `buildGraph` -> `buildTree` -> `ErgoTree`
+
+Here:
+- `SourceCode` - a string of unicode characters
+- `parse` - method `SigmaCompiler.parse`
+- `bind` - method `SigmaBinder.bind`
+- `typecheck` - method `SigmaTyper.typecheck`
+- `buildGraph` - method `IRContext.buildGraph`
+- `buildTree` - method `IRContext.buildTree`
+- `ErgoTree` - an intermediate representation which can be processed by Sigma [Interpreter](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/master/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala#L46)
+
+## Parser
+`parse` takes a string and produces abstract syntax tree, AST, of a Sigma expression represented by `Value` type in Scala.
+
+In case of any errors it throws `ParserException`
+
+## Binder
+`SigmaBinder` takes an AST of successfully parsed Sigma expression and resolves
+global variables and predefined functions that are looked up in the provided environment.
+Binder transforms environment values of predefined Scala types (such as Int, Boolean, Box, etc.)
+into constant nodes (IntConstant, BoxConstant, etc) of the corresponding type. (See also `Constant` class)
+
+In case of any error it throws `BinderException`
+
+## Typer
+`SigmaTyper` takes an AST from the output of `SigmaBinder` and assigns types
+to all tree nodes. Since AST is immutable data structure the typer produces a new tree.
+
+Type assignment is performed by `assignType` tree transformation which assign correct types for all
+tree nodes.
+
+In case of any error it throws `TyperException`
+
+## Graph Building
+
+`IRContext.buildGraph` takes an AST from the output of `SigmaTyper` and builds a graph where nodes are operations and edges are dependencies between operations.
+During graph building the following optimizations are performed:
+- constant propagation
+- common subexpression elimination
+- dead code elimination
+
+## Tree Building
+
+`IRContext.buildTree` takes a graph from the output of `IRContext.buildGraph` and builds the resulting ErgoTree.
+
+## IR contexts
+
+- `IRContext` - the main interface of graph IR which mixes in both GraphBuilding and TreeBuilding traits.
+ Since v5.0 it is not used by Interpreter and thus not part of consensus.
+
+- `CompiletimeIRContext` - the main implementation of IRContext
diff --git a/docs/notes.md b/docs/notes.md
index cc75f1065d..7bea09d887 100644
--- a/docs/notes.md
+++ b/docs/notes.md
@@ -3,13 +3,13 @@
These dependencies can be removed with refactoring
-| Jar | Size, Kb |
-|---------------|---------------|
-| - jline-2.14.3.jar | 268 |
-| cats-core_2.12-1.4.0.jar | 4400 |
-| - cats-kernel_2.12-1.4.0.jar | 3200 |
-| - algebra_2.12-0.7.0.jar | 1100 |
-| - spire-macros_2.12-0.14.1.jar | 73 |
+| Jar | Size, Kb |
+|--------------------------------|----------|
+| - jline-3.10.0.jar | 715 |
+| cats-core_2.12-2.1.0.jar | 4900 |
+| - cats-kernel_2.12-1.4.0.jar | 3500 |
+| - algebra_2.12-2.0.0-M2.jar | 1400 |
+| - spire-macros_2.12-0.14.1.jar | 79 |
diff --git a/docs/releasenotes.md b/docs/releasenotes.md
deleted file mode 100644
index e34cfa8562..0000000000
--- a/docs/releasenotes.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# v2.2
-
-- soft-forkability for cost estimation (#503)
-- optimization of cost estimation rules (#523)
-- new Context parameters (initCost, costLimit)
-- implemented fast complexity measure of box propositions
- (to fail-fast on too complex scripts #537)
-- implemented test cases to check fast script rejections of
- of either oversized of too costly scripts
-
-# v2.1
-- soft-forkability for language evolution (add types, operations) #500
-- ErgoTree language specification (abstract syntax, typing rules, semantics, serialization format) #495
-- ErgoTree IR extended with metadata to generate specification appendixes.
-- more operations implemented (zip #498, element-wise xor for collections #494, flatMap #493,
- plusModQ, minusModQ #488, logical xor #478, filter #471, Option.map #469, groupGenerator #440)
-- security improvements (like limiting ErgoTree depth during deserialization #459 #482)
-- ICO and LETS examples #450
-- final version of ErgoScript white paper (#410)
-- improvements in scorex-util serialization
-- various fixes code cleanup and better test coverage (#504, #508, #515)
\ No newline at end of file
diff --git a/docs/sigma-dsl.md b/docs/sigma-dsl.md
index ee9691186b..e9ca0ba0ff 100644
--- a/docs/sigma-dsl.md
+++ b/docs/sigma-dsl.md
@@ -6,10 +6,8 @@
code directly in Scala IDE (e.g. IntelliJ IDEA) and copy-paste code snippets
between SigmaDsl and SigmaScript.
Special Scala macros can also be used to automatically translate SigmaDsl to
- Sigma byte code.
-
-SigmaDsl is implemented as Scala library using [Special](https://github.com/scalan/special)
-framework.
+ Sigma byte code. Some prototype has been implemented [here](https://github.com/ergoplatform/ergo-scala-compiler)
## See also
-[Special](https://github.com/scalan/special)
+
+[ergo-scala-compiler](https://github.com/ergoplatform/ergo-scala-compiler)
diff --git a/docs/sigma-front-end.md b/docs/sigma-front-end.md
deleted file mode 100644
index 693ca98089..0000000000
--- a/docs/sigma-front-end.md
+++ /dev/null
@@ -1,55 +0,0 @@
-
-# Sigma language front-end
-
-Sigma frontend implements the following pipeline:
-
-SourceCode --> `Parser` --> `Binder` -> `Typer` -> `CompiletimeCosting` -> `TreeBuilding` -> ErgoTree
-
-Here:
-- SourceCode is a string of unicode characters
-- ErgoTree is an intermediate representation which can be processed by Sigma [Interpreter](https://github.com/ScorexFoundation/sigmastate-interpreter/blob/master/src/main/scala/sigmastate/interpreter/Interpreter.scala)
-
-## Parser
-`SigmaParser` takes a string and produces abstract syntax tree, AST, of a Sigma expression represented by `Value` type in Scala.
-
-In case of any errors it throws `ParserException`
-
-## Binder
-`SigmaBinder` takes an AST of successfully parsed Sigma expression and resolves
-global variables and predefined functions that are looked up in the provided environment..
-Binder transforms environment values of predefined Scala types (such as Int, Boolean, Box, etc.)
-into constant nodes (IntConstant, BoxConstant, etc) of the corresponding type. (See also `Constant` class)
-
-In case of any error it throws `BinderException`
-
-## Typer
-`SigmaTyper` takes an AST from the output of `SigmaBinder` and assigns types
-to all tree nodes. Since AST is immutable data structure the typer produces a new tree.
-
-Type assignment is performed by `assignType` tree transformation which assign correct types for all
-tree nodes.
-
-In case of any error it throws `TyperException`
-
-## Costing
-
-See Costing.md for detailed description of costing process.
-
-## TreeBuilding
-
-
-## IR contexts
-There are following IR contexts.
-
-Context class | Description
-----------------------|------------
- IRContext | Generic context which includes extends Evaluation, RuntimeCosting and TreeBuilding
- RuntimeIRContext | context which should be used during transaction validation
- CompiletimeIRContext | context which should be used during ErgoScript compilation
-
-The reason to have different contexts is to limit RuntimeIRContext to support only those nodes which can be serialized as part of ErgoTree.
-This doesn't include all ErgoScript nodes which CompiletimeIRContext can handle.
-For example, compileWithCosting should use IR context with CompiletimeCosting mixed in.
-However, Interpreter takes as input compiled or deserialized ErgoTree, so it can work without CompiletimeCosting mixed in into IR context.
-
-
diff --git a/docs/soft-fork-log.md b/docs/soft-fork-log.md
deleted file mode 100644
index 072e177827..0000000000
--- a/docs/soft-fork-log.md
+++ /dev/null
@@ -1,38 +0,0 @@
-
-## A log of changes leading to soft-fork
-
-This list should be updated every time something soft-forkable is added.
-
-### Changes in v2.1
-
- - new type (SGlobal.typeCode = 106)
- - new method (SGlobal.groupGenerator.methodId = 1)
- - new method (SAvlTree.updateDigest.methodId = 15)
- - removed GroupElement.nonce (changed codes of getEncoded, exp, multiply, negate)
- - change in Coll.filter serialization format (removed tagged variable id, changed condition type)
-
-### Changes in v2.2
-
-#### Changes in ErgoConstants
- MaxTokens 4 -> 255
- MaxPropositionBytes 64K -> 4K
- SizeBoxBytesWithoutRefsMax 64K -> 4K
- MaxSigmaPropSizeInBytes 1K (added because SigmaProp.isConstantSize == true)
- MaxLoopLevelInCostFunction 1 (added and checked)
-
-#### ComplexityTable added
-
-#### Changes in CostTable
-
-MinimalCost = 10 (1)
-interpreterInitCost = 10000 (added)
-perGraphNodeCost = 200 (added)
-val costFactor: Double = 2d (added)
-constCost = 10 (1)
-lambdaCost = 10 (1)
-plusMinus = 10 (2)
-comparisonCost = 10 (3)
-lambdaInvoke = 30 (added)
-concreteCollectionItemCost = 10 (added) // since each item is a separate graph node
-logicCost = 10 (2)
-castOp = 10 (5)
\ No newline at end of file
diff --git a/graph-ir/shared/src/main/scala/scalan/Base.scala b/graph-ir/shared/src/main/scala/scalan/Base.scala
index b105665f14..22e01da17f 100644
--- a/graph-ir/shared/src/main/scala/scalan/Base.scala
+++ b/graph-ir/shared/src/main/scala/scalan/Base.scala
@@ -301,16 +301,13 @@ abstract class Base { scalan: Scalan =>
def lift(srcF: SA => SB): Ref[A => B] = FuncConst[SA,SB,A,B](srcF)
}
- implicit lazy val BooleanIsLiftable = asLiftable[Boolean,Boolean](BooleanElement.liftable)
- implicit lazy val ByteIsLiftable = asLiftable[Byte,Byte](ByteElement.liftable)
- implicit lazy val ShortIsLiftable = asLiftable[Short,Short](ShortElement.liftable)
- implicit lazy val IntIsLiftable = asLiftable[Int,Int](IntElement.liftable)
- implicit lazy val LongIsLiftable = asLiftable[Long,Long](LongElement.liftable)
- implicit lazy val StringIsLiftable = asLiftable[String,String](StringElement.liftable)
- implicit lazy val FloatIsLiftable = asLiftable[Float,Float](FloatElement.liftable)
- implicit lazy val DoubleIsLiftable = asLiftable[Double,Double](DoubleElement.liftable)
- implicit lazy val UnitIsLiftable = asLiftable[Unit,Unit](UnitElement.liftable)
- implicit lazy val CharIsLiftable = asLiftable[Char,Char](CharElement.liftable)
+ implicit lazy val BooleanIsLiftable: Liftable[Boolean, Boolean] = asLiftable[Boolean,Boolean](BooleanElement.liftable)
+ implicit lazy val ByteIsLiftable: Liftable[Byte, Byte] = asLiftable[Byte,Byte](ByteElement.liftable)
+ implicit lazy val ShortIsLiftable: Liftable[Short, Short] = asLiftable[Short,Short](ShortElement.liftable)
+ implicit lazy val IntIsLiftable: Liftable[Int, Int] = asLiftable[Int,Int](IntElement.liftable)
+ implicit lazy val LongIsLiftable: Liftable[Long, Long] = asLiftable[Long,Long](LongElement.liftable)
+ implicit lazy val StringIsLiftable: Liftable[String, String] = asLiftable[String,String](StringElement.liftable)
+ implicit lazy val UnitIsLiftable: Liftable[Unit, Unit] = asLiftable[Unit,Unit](UnitElement.liftable)
implicit def PairIsLiftable[SA,SB,A,B]
(implicit lA: Liftable[SA, A], lB: Liftable[SB, B]): Liftable[(SA, SB), (A, B)] =
diff --git a/graph-ir/shared/src/main/scala/scalan/TypeDescs.scala b/graph-ir/shared/src/main/scala/scalan/TypeDescs.scala
index 48cb0a0dc9..57fc2a1a52 100644
--- a/graph-ir/shared/src/main/scala/scalan/TypeDescs.scala
+++ b/graph-ir/shared/src/main/scala/scalan/TypeDescs.scala
@@ -367,11 +367,8 @@ abstract class TypeDescs extends Base { self: Scalan =>
implicit val ShortElement: Elem[Short] = new BaseElemLiftable(0.toShort, ShortType)
implicit val IntElement: Elem[Int] = new BaseElemLiftable(0, IntType)
implicit val LongElement: Elem[Long] = new BaseElemLiftable(0L, LongType)
- implicit val FloatElement: Elem[Float] = new BaseElemLiftable(0.0F, FloatType)
- implicit val DoubleElement: Elem[Double] = new BaseElemLiftable(0.0, DoubleType)
implicit val UnitElement: Elem[Unit] = new BaseElemLiftable((), UnitType)
implicit val StringElement: Elem[String] = new BaseElemLiftable("", StringType)
- implicit val CharElement: Elem[Char] = new BaseElemLiftable('\u0000', CharType)
/** Implicitly defines element type for pairs. */
implicit final def pairElement[A, B](implicit ea: Elem[A], eb: Elem[B]): Elem[(A, B)] =
diff --git a/graph-ir/shared/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala b/graph-ir/shared/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala
index 4b69cfe957..e5e7f333e6 100644
--- a/graph-ir/shared/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala
+++ b/graph-ir/shared/src/main/scala/wrappers/scala/impl/WOptionsImpl.scala
@@ -90,7 +90,7 @@ class WOptionCls extends EntityObject("WOption") {
case class WOptionAdapter[A](source: Ref[WOption[A]])
extends Node with WOption[A]
with Def[WOption[A]] {
- implicit lazy val eA = source.elem.typeArgs("A")._1.asInstanceOf[Elem[A]]
+ implicit lazy val eA: Elem[A] = source.elem.typeArgs("A")._1.asInstanceOf[Elem[A]]
val resultType: Elem[WOption[A]] = element[WOption[A]]
override def transform(t: Transformer) = WOptionAdapter[A](t(source))
diff --git a/graph-ir/shared/src/main/scala/wrappers/scalan/impl/WRTypesImpl.scala b/graph-ir/shared/src/main/scala/wrappers/scalan/impl/WRTypesImpl.scala
index dfbd0f3111..66bbf9ed81 100644
--- a/graph-ir/shared/src/main/scala/wrappers/scalan/impl/WRTypesImpl.scala
+++ b/graph-ir/shared/src/main/scala/wrappers/scalan/impl/WRTypesImpl.scala
@@ -67,7 +67,7 @@ class WRTypeCls extends EntityObject("WRType") {
case class WRTypeAdapter[A](source: Ref[WRType[A]])
extends Node with WRType[A]
with Def[WRType[A]] {
- implicit lazy val eA = source.elem.typeArgs("A")._1.asInstanceOf[Elem[A]]
+ implicit lazy val eA: Elem[A] = source.elem.typeArgs("A")._1.asInstanceOf[Elem[A]]
val resultType: Elem[WRType[A]] = element[WRType[A]]
override def transform(t: Transformer) = WRTypeAdapter[A](t(source))
diff --git a/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala b/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala
index d23407e7d7..ba5460e517 100644
--- a/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala
+++ b/interpreter/js/src/main/scala/sigmastate/crypto/Platform.scala
@@ -199,8 +199,12 @@ object Platform {
override def infinity(): crypto.Ecp =
new Ecp(ctx.getInfinity())
- override def decodePoint(encoded: Array[Byte]): crypto.Ecp =
+ override def decodePoint(encoded: Array[Byte]): crypto.Ecp = {
+ if (encoded(0) == 0) {
+ return infinity()
+ }
new Ecp(ctx.decodePoint(Base16.encode(encoded)))
+ }
override def generator: crypto.Ecp =
new Ecp(ctx.getGenerator())
diff --git a/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala b/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala
index 9fc4e43d86..63839e67ef 100644
--- a/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala
+++ b/interpreter/js/src/test/scala/sigmastate/crypto/CryptoFacadeJsSpec.scala
@@ -2,7 +2,6 @@ package sigmastate.crypto
import org.scalatest.matchers.should.Matchers
import org.scalatest.propspec.AnyPropSpec
-import scorex.util.encode.Base16
import scala.scalajs.js
import scala.scalajs.js.typedarray.Uint8Array
diff --git a/interpreter/jvm/src/main/scala/sigmastate/crypto/Platform.scala b/interpreter/jvm/src/main/scala/sigmastate/crypto/Platform.scala
index dcd5deabde..3b74ba4714 100644
--- a/interpreter/jvm/src/main/scala/sigmastate/crypto/Platform.scala
+++ b/interpreter/jvm/src/main/scala/sigmastate/crypto/Platform.scala
@@ -187,7 +187,7 @@ object Platform {
case _: Int => tpe == SInt
case _: Long => tpe == SLong
case _: BigInt => tpe == SBigInt
- case _: String => tpe == SString // TODO v6.0: remove this case
+ case _: String => tpe == SString // TODO v6.0: remove this case (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
case _: GroupElement => tpe.isGroupElement
case _: SigmaProp => tpe.isSigmaProp
case _: AvlTree => tpe.isAvlTree
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala
index 37a6e96193..dadd0d5c7a 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBox.scala
@@ -1,11 +1,11 @@
package org.ergoplatform
-import scorex.utils.{Ints, Shorts}
-import org.ergoplatform.ErgoBox.{NonMandatoryRegisterId, Token}
+import org.ergoplatform.ErgoBox.{AdditionalRegisters, Token}
import org.ergoplatform.settings.ErgoAlgos
import scorex.crypto.authds.ADKey
-import scorex.crypto.hash.{Blake2b256, Digest32}
+import scorex.crypto.hash.Blake2b256
import scorex.util._
+import scorex.utils.{Ints, Shorts}
import sigmastate.SCollection.SByteArray
import sigmastate.SType.AnyOps
import sigmastate.Values._
@@ -13,12 +13,10 @@ import sigmastate._
import sigmastate.eval.Extensions._
import sigmastate.eval._
import sigmastate.serialization.SigmaSerializer
-import sigmastate.utils.{SigmaByteReader, SigmaByteWriter, Helpers}
+import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter}
import sigmastate.utxo.ExtractCreationInfo
import special.collection._
-import scala.runtime.ScalaRunTime
-
/**
* Box (aka coin, or an unspent output) is a basic concept of a UTXO-based cryptocurrency. In Bitcoin, such an object
* is associated with some monetary value (arbitrary, but with predefined precision, so we use integer arithmetic to
@@ -37,6 +35,8 @@ import scala.runtime.ScalaRunTime
* A transaction is unsealing a box. As a box can not be open twice, any further valid transaction can not be linked
* to the same box.
*
+ * Note, private constructor can only be used from within the ErgoBox companion object, e.g. by deserializer.
+ *
* @param value - amount of money associated with the box
* @param ergoTree - guarding script, which should be evaluated to true in order to open this box
* @param additionalTokens - secondary tokens the box contains
@@ -46,17 +46,28 @@ import scala.runtime.ScalaRunTime
* @param creationHeight - height when a transaction containing the box was created.
* This height is declared by user and should not exceed height of the block,
* containing the transaction with this box.
+ * @param _bytes - serialized bytes of the box when not `null`
* HOTSPOT: don't beautify the code of this class
*/
-class ErgoBox(
+class ErgoBox private (
override val value: Long,
override val ergoTree: ErgoTree,
- override val additionalTokens: Coll[Token] = Colls.emptyColl[Token],
- override val additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map.empty,
+ override val additionalTokens: Coll[Token],
+ override val additionalRegisters: AdditionalRegisters,
val transactionId: ModifierId,
val index: Short,
- override val creationHeight: Int
+ override val creationHeight: Int,
+ _bytes: Array[Byte]
) extends ErgoBoxCandidate(value, ergoTree, creationHeight, additionalTokens, additionalRegisters) {
+ /** This is public constructor has the same parameters as the private primary constructor, except bytes. */
+ def this(value: Long,
+ ergoTree: ErgoTree,
+ additionalTokens: Coll[Token] = Colls.emptyColl[Token],
+ additionalRegisters: AdditionalRegisters = Map.empty,
+ transactionId: ModifierId,
+ index: Short,
+ creationHeight: Int) =
+ this(value, ergoTree, additionalTokens, additionalRegisters, transactionId, index, creationHeight, null)
import ErgoBox._
@@ -72,11 +83,15 @@ class ErgoBox(
}
}
- // TODO optimize: avoid serialization by implementing lazy box deserialization
/** Serialized content of this box.
* @see [[ErgoBox.sigmaSerializer]]
*/
- lazy val bytes: Array[Byte] = ErgoBox.sigmaSerializer.toBytes(this)
+ lazy val bytes: Array[Byte] = {
+ if (_bytes != null)
+ _bytes // bytes provided by deserializer
+ else
+ ErgoBox.sigmaSerializer.toBytes(this)
+ }
override def equals(arg: Any): Boolean = arg match {
case x: ErgoBox => java.util.Arrays.equals(id, x.id)
@@ -136,7 +151,7 @@ object ErgoBox {
/** Represents id of optional registers of a box. */
sealed abstract class NonMandatoryRegisterId(override val number: Byte) extends RegisterId
- type AdditionalRegisters = Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]]
+ type AdditionalRegisters = scala.collection.Map[NonMandatoryRegisterId, EvaluatedValue[_ <: SType]]
object R0 extends MandatoryRegisterId(0, "Monetary value, in Ergo tokens")
object R1 extends MandatoryRegisterId(1, "Guarding script")
@@ -199,10 +214,16 @@ object ErgoBox {
}
override def parse(r: SigmaByteReader): ErgoBox = {
- val ergoBoxCandidate = ErgoBoxCandidate.serializer.parse(r)
+ val start = r.position
+ val c = ErgoBoxCandidate.serializer.parse(r)
val transactionId = r.getBytes(ErgoLikeTransaction.TransactionIdBytesSize).toModifierId
val index = r.getUShort()
- ergoBoxCandidate.toBox(transactionId, index.toShort)
+ val end = r.position
+ val len = end - start
+ r.position = start
+ val boxBytes = r.getBytes(len) // also moves position back to end
+ new ErgoBox(c.value, c.ergoTree, c.additionalTokens, c.additionalRegisters,
+ transactionId, index.toShort, c.creationHeight, boxBytes)
}
}
}
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala
index 8d5765caf5..da1fdc94fd 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoBoxCandidate.scala
@@ -38,7 +38,7 @@ class ErgoBoxCandidate(val value: Long,
val ergoTree: ErgoTree,
val creationHeight: Int,
val additionalTokens: Coll[Token] = Colls.emptyColl,
- val additionalRegisters: Map[NonMandatoryRegisterId, _ <: EvaluatedValue[_ <: SType]] = Map())
+ val additionalRegisters: AdditionalRegisters = Map())
extends ErgoBoxAssets {
/** Transforms this tree to a proposition, substituting the constants if the constant
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala
index 3bf455ae6f..3b356fd4ec 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeContext.scala
@@ -200,11 +200,11 @@ class ErgoLikeContext(val lastBlockUtxoRoot: AvlTreeData,
lastBlockUtxoRoot, headers, preHeader, dataBoxes, boxesToSpend, spendingTransaction,
selfIndex, extension, validationSettings, costLimit, initCost,
activatedScriptVersion)
- var hashCode = 0
+ var h = 0
cfor(0)(_ < state.length, _ + 1) { i =>
- hashCode = 31 * hashCode + state(i).hashCode
+ h = 31 * h + state(i).hashCode
}
- hashCode
+ h
}
override def toString = s"ErgoLikeContext(lastBlockUtxoRoot=$lastBlockUtxoRoot, headers=$headers, preHeader=$preHeader, dataBoxes=$dataBoxes, boxesToSpend=$boxesToSpend, spendingTransaction=$spendingTransaction, selfIndex=$selfIndex, extension=$extension, validationSettings=$validationSettings, costLimit=$costLimit, initCost=$initCost, activatedScriptVersion=$activatedScriptVersion)"
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala
index 5ad3e08207..f5b8a2c15f 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/ErgoLikeTransaction.scala
@@ -50,7 +50,6 @@ trait ErgoLikeTransactionTemplate[IT <: UnsignedInput] {
lazy val inputIds: IndexedSeq[ADKey] = inputs.map(_.boxId)
- override def toString = s"ErgoLikeTransactionTemplate(dataInputs=$dataInputs, inputs=$inputs, outputCandidates=$outputCandidates)"
}
@@ -76,6 +75,8 @@ class UnsignedErgoLikeTransaction(override val inputs: IndexedSeq[UnsignedInput]
}
override def hashCode(): Int = id.hashCode()
+
+ override def toString = s"UnsignedErgoLikeTransaction(dataInputs=$dataInputs, inputs=$inputs, outputCandidates=$outputCandidates)"
}
object UnsignedErgoLikeTransaction {
@@ -105,6 +106,7 @@ class ErgoLikeTransaction(override val inputs: IndexedSeq[Input],
}
override def hashCode(): Int = id.hashCode()
+ override def toString = s"ErgoLikeTransaction(dataInputs=$dataInputs, inputs=$inputs, outputCandidates=$outputCandidates)"
}
object ErgoLikeTransactionSerializer extends SigmaSerializer[ErgoLikeTransaction, ErgoLikeTransaction] {
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaConstants.scala b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaConstants.scala
index ff1251ae10..be745d8d7d 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/SigmaConstants.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/SigmaConstants.scala
@@ -66,10 +66,6 @@ object SigmaConstants {
"Max children count should not be greater than provided value") {
}
- object ScriptCostLimit extends SizeConstant[Int](1000000, 12,
- "Maximum execution cost of a script") {
- }
-
object MaxLoopLevelInCostFunction extends SizeConstant[Int](1, 13,
"Maximum allowed loop level in a cost function") {
}
@@ -96,7 +92,6 @@ object SigmaConstants {
MaxTupleLength,
MaxHeaders,
MaxChildrenCountForAtLeastOp,
- ScriptCostLimit,
MaxLoopLevelInCostFunction,
VotesArraySize,
AutolykosPowSolutionNonceArraySize
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettings.scala b/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettings.scala
index 6314ea0b20..e59575a272 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettings.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettings.scala
@@ -48,7 +48,7 @@ abstract class SigmaValidationSettings extends Iterable[(Short, (ValidationRule,
def isSoftFork(ruleId: Short, ve: ValidationException): Boolean = {
val infoOpt = get(ruleId)
infoOpt match {
- case Some((_, ReplacedRule(newRuleId))) => true
+ case Some((_, ReplacedRule(_))) => true
case Some((rule, status)) => rule.isSoftFork(this, rule.id, status, ve.args)
case None => false
}
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala b/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala
index 1e2b8b352a..667d8f4989 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializer.scala
@@ -2,9 +2,9 @@ package org.ergoplatform.validation
import sigmastate.serialization.SigmaSerializer
import sigmastate.utils.{SigmaByteReader, SigmaByteWriter}
-import scalan.util.Extensions.{IntOps,LongOps}
+import scalan.util.Extensions.{IntOps, LongOps}
+import sigmastate.exceptions.SerializerException
-// TODO v5.x: remove unused class and related json encoders
/** The rules are serialized ordered by ruleId.
* This serializer preserves roundtrip identity `deserialize(serialize(_)) = identity`
* however it may not preserve `serialize(deserialize(_)) = identity` */
@@ -13,9 +13,9 @@ object SigmaValidationSettingsSerializer extends SigmaSerializer[SigmaValidation
override def serialize(settings: SigmaValidationSettings, w: SigmaByteWriter): Unit = {
val rules = settings.toArray.sortBy(_._1)
w.putUInt(rules.length)
- rules.foreach { r =>
- w.putUShort(r._1)
- RuleStatusSerializer.serialize(r._2._2, w)
+ rules.foreach { case (id, (_, status)) =>
+ w.putUShort(id)
+ RuleStatusSerializer.serialize(status, w)
}
}
@@ -27,10 +27,12 @@ object SigmaValidationSettingsSerializer extends SigmaSerializer[SigmaValidation
val status = RuleStatusSerializer.parse(r)
ruleId -> status
}
- val initVs = ValidationRules.currentSettings
- val res = parsed
- .filter(pair => initVs.get(pair._1).isDefined)
- .foldLeft(initVs) { (vs, rule) => vs.updated(rule._1, rule._2) }
+ val map = parsed.map { case (id, status) =>
+ val (rule, _) = ValidationRules.currentSettings.get(id)
+ .getOrElse(throw SerializerException(s"Rule with id $id is not registered"))
+ id -> (rule, status)
+ }.toMap
+ val res = new MapSigmaValidationSettings(map)
res
}
}
diff --git a/interpreter/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala b/interpreter/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala
index 1110916e03..2573d3f592 100644
--- a/interpreter/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala
+++ b/interpreter/shared/src/main/scala/org/ergoplatform/validation/ValidationRules.scala
@@ -1,9 +1,9 @@
package org.ergoplatform.validation
import scalan.util.Extensions.toUByte
-import sigmastate.Values.{SValue, ErgoTree}
+import sigmastate.Values.{ErgoTree, SValue}
import sigmastate._
-import sigmastate.exceptions.{InvalidOpCode, SerializerException, ReaderPositionLimitExceeded, SigmaException, InterpreterException}
+import sigmastate.exceptions.{InterpreterException, InvalidOpCode, ReaderPositionLimitExceeded, SerializerException, SigmaException}
import sigmastate.serialization.OpCodes.OpCode
import sigmastate.serialization.TypeSerializer.embeddableIdToType
import sigmastate.serialization.{OpCodes, ValueSerializer}
diff --git a/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala
index 07ff33c5bc..f003586d85 100644
--- a/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/SigSerializer.scala
@@ -5,13 +5,14 @@ import scorex.util.encode.Base16
import sigmastate.Values.SigmaBoolean
import sigmastate.basics.DLogProtocol.{ProveDlog, SecondDLogProverMessage}
import sigmastate.basics.VerifierMessage.Challenge
-import sigmastate.basics.{SecondDiffieHellmanTupleProverMessage, ProveDHTuple, CryptoConstants}
+import sigmastate.basics.{CryptoConstants, ProveDHTuple, SecondDHTupleProverMessage}
import sigmastate.interpreter.ErgoTreeEvaluator.{fixedCostOp, perItemCostOp}
import sigmastate.interpreter.{ErgoTreeEvaluator, NamedDesc, OperationCostInfo}
import sigmastate.serialization.SigmaSerializer
import sigmastate.util.safeNewArray
import sigmastate.utils.{Helpers, SigmaByteReader, SigmaByteWriter}
import debox.cfor
+import sigmastate.eval.Extensions.ArrayOps
import sigmastate.exceptions.SerializerException
/** Contains implementation of signature (aka proof) serialization.
@@ -61,7 +62,7 @@ class SigSerializer {
w: SigmaByteWriter,
writeChallenge: Boolean): Unit = {
if (writeChallenge) {
- w.putBytes(node.challenge)
+ w.putBytes(node.challenge.toArray)
}
node match {
case dl: UncheckedSchnorr =>
@@ -184,7 +185,7 @@ class SigSerializer {
// Verifier Step 2: Let e_0 be the challenge in the node here (e_0 is called "challenge" in the code)
val challenge = if (challengeOpt == null) {
Challenge @@ readBytesChecked(r, hashSize,
- hex => warn(s"Invalid challenge in: $hex"))
+ hex => warn(s"Invalid challenge in: $hex")).toColl
} else {
challengeOpt
}
@@ -203,7 +204,7 @@ class SigSerializer {
fixedCostOp(ParseChallenge_ProveDHT) {
val z_bytes = readBytesChecked(r, order, hex => warn(s"Invalid z bytes for $dh: $hex"))
val z = BigIntegers.fromUnsignedByteArray(z_bytes)
- UncheckedDiffieHellmanTuple(dh, None, challenge, SecondDiffieHellmanTupleProverMessage(z))
+ UncheckedDiffieHellmanTuple(dh, None, challenge, SecondDHTupleProverMessage(z))
}
case and: CAND =>
@@ -223,18 +224,18 @@ class SigSerializer {
// Read all the children but the last and compute the XOR of all the challenges including e_0
val nChildren = or.children.length
val children = safeNewArray[UncheckedSigmaTree](nChildren)
- val xorBuf = challenge.clone()
+ val xorBuf = challenge.toArray.clone()
val iLastChild = nChildren - 1
cfor(0)(_ < iLastChild, _ + 1) { i =>
val parsedChild = parseAndComputeChallenges(or.children(i), r, null)
children(i) = parsedChild
- Helpers.xorU(xorBuf, parsedChild.challenge) // xor it into buffer
+ Helpers.xorU(xorBuf, parsedChild.challenge.toArray) // xor it into buffer
}
val lastChild = or.children(iLastChild)
// use the computed XOR for last child's challenge
children(iLastChild) = parseAndComputeChallenges(
- lastChild, r, challengeOpt = Challenge @@ xorBuf)
+ lastChild, r, challengeOpt = Challenge @@ xorBuf.toColl)
COrUncheckedNode(challenge, children)
@@ -248,13 +249,13 @@ class SigSerializer {
val polynomial = perItemCostOp(ParsePolynomial, nCoefs) { () =>
val coeffBytes = readBytesChecked(r, hashSize * nCoefs,
hex => warn(s"Invalid coeffBytes for $th: $hex"))
- GF2_192_Poly.fromByteArray(challenge, coeffBytes)
+ GF2_192_Poly.fromByteArray(challenge.toArray, coeffBytes)
}
val children = safeNewArray[UncheckedSigmaTree](nChildren)
cfor(0)(_ < nChildren, _ + 1) { i =>
val c = perItemCostOp(EvaluatePolynomial, nCoefs) { () =>
- Challenge @@ polynomial.evaluate((i + 1).toByte).toByteArray
+ Challenge @@ polynomial.evaluate((i + 1).toByte).toByteArray.toColl
}
children(i) = parseAndComputeChallenges(th.children(i), r, c)
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala b/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala
index 365a1f85a7..4b4b28fa0d 100644
--- a/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/UncheckedTree.scala
@@ -1,10 +1,9 @@
package sigmastate
-import java.util.Arrays
import sigmastate.basics.DLogProtocol.{FirstDLogProverMessage, ProveDlog, SecondDLogProverMessage}
import sigmastate.basics.VerifierMessage.Challenge
import sigmastate.Values.SigmaBoolean
-import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, ProveDHTuple, SecondDiffieHellmanTupleProverMessage}
+import sigmastate.basics.{FirstDHTupleProverMessage, ProveDHTuple, SecondDHTupleProverMessage}
import sigmastate.crypto.GF2_192_Poly
sealed trait UncheckedTree extends ProofTree
@@ -12,114 +11,46 @@ sealed trait UncheckedTree extends ProofTree
case object NoProof extends UncheckedTree
sealed trait UncheckedSigmaTree extends UncheckedTree {
- val challenge: Array[Byte]
+ val challenge: Challenge
}
-trait UncheckedConjecture extends UncheckedSigmaTree with ProofTreeConjecture {
+trait UncheckedConjecture extends UncheckedSigmaTree with ProofTreeConjecture
- override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match {
- case x: UncheckedConjecture =>
- Arrays.equals(challenge, x.challenge) && children == x.children
- case _ => false
- })
+trait UncheckedLeaf extends UncheckedSigmaTree with ProofTreeLeaf
- override def hashCode(): Int =
- 31 * Arrays.hashCode(challenge) + children.hashCode()
-}
-
-trait UncheckedLeaf[SP <: SigmaBoolean] extends UncheckedSigmaTree with ProofTreeLeaf {
- val proposition: SigmaBoolean
-}
-
-case class UncheckedSchnorr(override val proposition: ProveDlog,
- override val commitmentOpt: Option[FirstDLogProverMessage],
- override val challenge: Challenge,
- secondMessage: SecondDLogProverMessage)
- extends UncheckedLeaf[ProveDlog] {
-
- override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match {
- case x: UncheckedSchnorr =>
- // NOTE, proposition is not compared because it is included into challenge
- // like `challenge = hash(prop ++ msg)`
- commitmentOpt == x.commitmentOpt &&
- Arrays.equals(challenge, x.challenge) &&
- secondMessage == x.secondMessage
- case _ => false
- })
-
- override def hashCode(): Int = {
- var h = commitmentOpt.hashCode()
- h = 31 * h + Arrays.hashCode(challenge)
- h = 31 * h + secondMessage.hashCode()
- h
- }
-}
-
-
-case class UncheckedDiffieHellmanTuple(override val proposition: ProveDHTuple,
- override val commitmentOpt: Option[FirstDiffieHellmanTupleProverMessage],
- override val challenge: Challenge,
- secondMessage: SecondDiffieHellmanTupleProverMessage)
- extends UncheckedLeaf[ProveDHTuple] {
+case class UncheckedSchnorr(
+ override val proposition: ProveDlog,
+ override val commitmentOpt: Option[FirstDLogProverMessage],
+ override val challenge: Challenge,
+ secondMessage: SecondDLogProverMessage
+) extends UncheckedLeaf
- override def equals(obj: Any): Boolean = (this eq obj.asInstanceOf[AnyRef]) || (obj match {
- case x: UncheckedDiffieHellmanTuple =>
- // NOTE, proposition is not compared because it is included into challenge
- // like `challenge = hash(prop ++ msg)`
- commitmentOpt == x.commitmentOpt &&
- Arrays.equals(challenge, x.challenge) &&
- secondMessage == x.secondMessage
- case _ => false
- })
-
- override def hashCode(): Int = {
- var h = commitmentOpt.hashCode()
- h = 31 * h + Arrays.hashCode(challenge)
- h = 31 * h + secondMessage.hashCode()
- h
- }
-}
-
-case class CAndUncheckedNode(override val challenge: Challenge,
- override val children: Seq[UncheckedSigmaTree])
- extends UncheckedConjecture {
+case class UncheckedDiffieHellmanTuple(
+ override val proposition: ProveDHTuple,
+ override val commitmentOpt: Option[FirstDHTupleProverMessage],
+ override val challenge: Challenge,
+ secondMessage: SecondDHTupleProverMessage
+) extends UncheckedLeaf
+case class CAndUncheckedNode(
+ override val challenge: Challenge,
+ override val children: Seq[UncheckedSigmaTree]) extends UncheckedConjecture {
override val conjectureType = ConjectureType.AndConjecture
}
-
-case class COrUncheckedNode(override val challenge: Challenge,
- override val children: Seq[UncheckedSigmaTree]) extends UncheckedConjecture {
-
+case class COrUncheckedNode(
+ override val challenge: Challenge,
+ override val children: Seq[UncheckedSigmaTree]) extends UncheckedConjecture {
override val conjectureType = ConjectureType.OrConjecture
-
}
-case class CThresholdUncheckedNode(override val challenge: Challenge,
- override val children: Seq[UncheckedSigmaTree],
- k: Integer,
- polynomialOpt: Option[GF2_192_Poly]) extends UncheckedConjecture {
+case class CThresholdUncheckedNode(
+ override val challenge: Challenge,
+ override val children: Seq[UncheckedSigmaTree],
+ k: Integer,
+ polynomialOpt: Option[GF2_192_Poly]) extends UncheckedConjecture {
require(children.length <= 255) // Our polynomial arithmetic can take only byte inputs
require(k >= 0 && k <= children.length)
override val conjectureType = ConjectureType.ThresholdConjecture
-
- override def canEqual(other: Any) = other.isInstanceOf[CThresholdUncheckedNode]
-
- override def equals(other: Any) = (this eq other.asInstanceOf[AnyRef]) || (other match {
- case other: CThresholdUncheckedNode =>
- Arrays.equals(challenge, other.challenge) &&
- children == other.children &&
- k == other.k &&
- polynomialOpt == other.polynomialOpt
- case _ => false
- })
-
- override def hashCode(): Int = {
- var h = Arrays.hashCode(challenge)
- h = 31 * h + children.hashCode
- h = 31 * h + k.hashCode()
- h = 31 * h + polynomialOpt.hashCode()
- h
- }
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala b/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala
index 6e600b927b..a01c937a1f 100644
--- a/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/UnprovenTree.scala
@@ -4,7 +4,7 @@ import java.math.BigInteger
import sigmastate.Values.{ErgoTree, SigmaBoolean, SigmaPropConstant}
import sigmastate.basics.DLogProtocol.{FirstDLogProverMessage, ProveDlog}
import sigmastate.basics.VerifierMessage.Challenge
-import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, FirstProverMessage, ProveDHTuple}
+import sigmastate.basics.{FirstDHTupleProverMessage, FirstProverMessage, ProveDHTuple}
import sigmastate.interpreter.{ErgoTreeEvaluator, NamedDesc, OperationCostInfo}
import sigmastate.interpreter.ErgoTreeEvaluator.fixedCostOp
import sigmastate.serialization.ErgoTreeSerializer.DefaultSerializer
@@ -25,7 +25,7 @@ object ConjectureType extends Enumeration {
trait ProofTree extends Product
trait ProofTreeLeaf extends ProofTree {
- val proposition: SigmaBoolean
+ val proposition: SigmaLeaf
val commitmentOpt: Option[FirstProverMessage]
}
@@ -102,7 +102,7 @@ sealed trait UnprovenTree extends ProofTree {
/**
* Challenge used by the prover.
*/
- val challengeOpt: Option[Array[Byte]]
+ val challengeOpt: Option[Challenge]
def withChallenge(challenge: Challenge): UnprovenTree
@@ -187,7 +187,7 @@ case class UnprovenSchnorr(override val proposition: ProveDlog,
}
case class UnprovenDiffieHellmanTuple(override val proposition: ProveDHTuple,
- override val commitmentOpt: Option[FirstDiffieHellmanTupleProverMessage],
+ override val commitmentOpt: Option[FirstDHTupleProverMessage],
randomnessOpt: Option[BigInteger],
override val challengeOpt: Option[Challenge] = None,
override val simulated: Boolean,
diff --git a/interpreter/shared/src/main/scala/sigmastate/Values.scala b/interpreter/shared/src/main/scala/sigmastate/Values.scala
index 1e9114a782..840df3ab56 100644
--- a/interpreter/shared/src/main/scala/sigmastate/Values.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/Values.scala
@@ -755,7 +755,6 @@ object Values {
val dhtSerializer = ProveDHTupleSerializer(ProveDHTuple.apply)
val dlogSerializer = ProveDlogSerializer(ProveDlog.apply)
- // TODO v5.x: control maxTreeDepth same as in deserialize
override def serialize(data: SigmaBoolean, w: SigmaByteWriter): Unit = {
w.put(data.opCode)
data match {
@@ -878,31 +877,6 @@ object Values {
def apply(items: Value[SType]*): Tuple = Tuple(items.toIndexedSeq)
}
- trait OptionValue[T <: SType] extends Value[SOption[T]] {
- }
-
- // TODO v6.0 (4h): SomeValue and NoneValue are not used in ErgoTree and can be
- // either removed or implemented in v6.0
- case class SomeValue[T <: SType](x: Value[T]) extends OptionValue[T] {
- override def companion = SomeValue
- val tpe = SOption(x.tpe)
- def opType = SFunc(x.tpe, tpe)
- }
- object SomeValue extends ValueCompanion {
- override val opCode = SomeValueCode
- override def costKind: CostKind = Constant.costKind
- }
-
- case class NoneValue[T <: SType](elemType: T) extends OptionValue[T] {
- override def companion = NoneValue
- val tpe = SOption(elemType)
- def opType = SFunc(elemType, tpe)
- }
- object NoneValue extends ValueCompanion {
- override val opCode = NoneValueCode
- override def costKind: CostKind = Constant.costKind
- }
-
/** ErgoTree node which converts a collection of expressions into a collection of data
* values. Each data value of the resulting collection is obtained by evaluating the
* corresponding expression in `items`. All items must have the same type.
diff --git a/interpreter/shared/src/main/scala/sigmastate/basics/DLogProtocol.scala b/interpreter/shared/src/main/scala/sigmastate/basics/DLogProtocol.scala
index 492b054b0e..9b801d7ad1 100644
--- a/interpreter/shared/src/main/scala/sigmastate/basics/DLogProtocol.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/basics/DLogProtocol.scala
@@ -22,8 +22,7 @@ object DLogProtocol {
}
/** Construct a new SigmaBoolean value representing public key of discrete logarithm signature protocol. */
- case class ProveDlog(value: EcPointType)
- extends SigmaProofOfKnowledgeLeaf[DLogSigmaProtocol, DLogProverInput] {
+ case class ProveDlog(value: EcPointType) extends SigmaLeaf {
override def size: Int = 1
override val opCode: OpCode = OpCodes.ProveDlogCode
/** Serialized bytes of the elliptic curve point (using GroupElementSerializer). */
@@ -43,7 +42,7 @@ object DLogProtocol {
}
case class DLogProverInput(w: BigInteger)
- extends SigmaProtocolPrivateInput[DLogSigmaProtocol, ProveDlog] {
+ extends SigmaProtocolPrivateInput[ProveDlog] {
import CryptoConstants.dlogGroup
@@ -79,7 +78,7 @@ object DLogProtocol {
}
- object DLogInteractiveProver {
+ object DLogInteractiveProver extends SigmaProtocolProver {
import CryptoConstants.secureRandom
def firstMessage(): (BigInteger, FirstDLogProverMessage) = {
@@ -92,12 +91,7 @@ object DLogProtocol {
}
def secondMessage(privateInput: DLogProverInput, rnd: BigInteger, challenge: Challenge): SecondDLogProverMessage = {
- import CryptoConstants.dlogGroup
-
- val q: BigInteger = dlogGroup.order
- val e: BigInteger = new BigInteger(1, challenge)
- val ew: BigInteger = e.multiply(privateInput.w).mod(q)
- val z: BigInteger = rnd.add(ew).mod(q)
+ val z = responseToChallenge(privateInput, rnd, challenge)
SecondDLogProverMessage(z)
}
@@ -109,7 +103,7 @@ object DLogProtocol {
val z = BigIntegers.createRandomInRange(BigInteger.ZERO, qMinusOne, secureRandom)
//COMPUTE a = g^z*h^(-e) (where -e here means -e mod q)
- val e: BigInteger = new BigInteger(1, challenge)
+ val e: BigInteger = new BigInteger(1, challenge.toArray)
val minusE = dlogGroup.order.subtract(e)
val hToE = dlogGroup.exponentiate(publicInput.value, minusE)
val gToZ = dlogGroup.exponentiate(dlogGroup.generator, z)
@@ -137,7 +131,7 @@ object DLogProtocol {
dlogGroup.multiplyGroupElements(
dlogGroup.exponentiate(g, secondMessage.z.underlying()),
- dlogGroup.inverseOf(dlogGroup.exponentiate(h, new BigInteger(1, challenge))))
+ dlogGroup.inverseOf(dlogGroup.exponentiate(h, new BigInteger(1, challenge.toArray))))
}
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala b/interpreter/shared/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala
index 08e9dd2ab7..e39508bf4f 100644
--- a/interpreter/shared/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/basics/DiffieHellmanTupleProtocol.scala
@@ -14,12 +14,12 @@ import special.sigma.SigmaProp
trait DiffieHellmanTupleProtocol extends SigmaProtocol[DiffieHellmanTupleProtocol] {
- override type A = FirstDiffieHellmanTupleProverMessage
- override type Z = SecondDiffieHellmanTupleProverMessage
+ override type A = FirstDHTupleProverMessage
+ override type Z = SecondDHTupleProverMessage
}
case class DiffieHellmanTupleProverInput(w: BigInteger, commonInput: ProveDHTuple)
- extends SigmaProtocolPrivateInput[DiffieHellmanTupleProtocol, ProveDHTuple] {
+ extends SigmaProtocolPrivateInput[ProveDHTuple] {
override lazy val publicImage: ProveDHTuple = commonInput
}
@@ -41,8 +41,12 @@ object DiffieHellmanTupleProverInput {
}
}
-//a = g^r, b = h^r
-case class FirstDiffieHellmanTupleProverMessage(a: CryptoConstants.EcPointType, b: CryptoConstants.EcPointType)
+/** First message of Diffie Hellman tuple sigma protocol.
+ * @param a commitment to secret randomness `a = g^r`, where `g` is default generator of the group
+ * @param b commitment to secret randomness `b = h^r`, where `h` is another random generator of the group
+ * @see createRandomGenerator in [[sigmastate.basics.CryptoConstants.dlogGroup]]
+ */
+case class FirstDHTupleProverMessage(a: EcPointType, b: EcPointType)
extends FirstProverMessage {
override type SP = DiffieHellmanTupleProtocol
@@ -52,17 +56,21 @@ case class FirstDiffieHellmanTupleProverMessage(a: CryptoConstants.EcPointType,
}
}
-//z = r + ew mod q
-case class SecondDiffieHellmanTupleProverMessage(z: BigInteger) extends SecondProverMessage {
-
+/** Represents a response for challenge in Diffie Hellman tuple sigma protocol.
+ * @param z responce to the challenge computed as `(r + ew) mod q`, where
+ * `r` is the prover's randomness,
+ * `e` challenge from the verifier (also computed by the prover in non-interactive case),
+ * `w` is the prover's secret.
+ * `q` is the group order
+ */
+case class SecondDHTupleProverMessage(z: BigInteger) extends SecondProverMessage {
override type SP = DiffieHellmanTupleProtocol
-
}
/** Construct a new SigmaProp value representing public key of Diffie Hellman signature protocol.
* Common input: (g,h,u,v) */
case class ProveDHTuple(gv: EcPointType, hv: EcPointType, uv: EcPointType, vv: EcPointType)
- extends SigmaProofOfKnowledgeLeaf[DiffieHellmanTupleProtocol, DiffieHellmanTupleProverInput] {
+ extends SigmaLeaf {
override val opCode: OpCode = OpCodes.ProveDiffieHellmanTupleCode
override def size: Int = 4 // one node for each EcPoint
lazy val g = gv
@@ -83,38 +91,47 @@ object ProveDHTupleProp {
}
}
-object DiffieHellmanTupleInteractiveProver {
+object DiffieHellmanTupleInteractiveProver extends SigmaProtocolProver {
import CryptoConstants.dlogGroup
- def firstMessage(publicInput: ProveDHTuple): (BigInteger, FirstDiffieHellmanTupleProverMessage) = {
+ /** Create a commitment to randomness r and a first message of sigma protocol. */
+ def firstMessage(publicInput: ProveDHTuple): (BigInteger, FirstDHTupleProverMessage) = {
val qMinusOne = dlogGroup.order.subtract(BigInteger.ONE)
val r = BigIntegers.createRandomInRange(BigInteger.ZERO, qMinusOne, dlogGroup.secureRandom)
val a = dlogGroup.exponentiate(publicInput.g, r)
val b = dlogGroup.exponentiate(publicInput.h, r)
- r -> FirstDiffieHellmanTupleProverMessage(a, b)
+ r -> FirstDHTupleProverMessage(a, b)
}
+ /** Creates second message of sigma protocol, which is a response for the challenge from the verifier.
+ *
+ * @param privateInput private input of the prover (secret)
+ * @param rnd random number generated by the prover (secret random number used to
+ * compute commitment)
+ * @param challenge challenge from the verifier (also computed by the prover in non-interactive case)
+ * @return second message from the prover
+ */
def secondMessage(privateInput: DiffieHellmanTupleProverInput,
rnd: BigInteger,
- challenge: Challenge): SecondDiffieHellmanTupleProverMessage = {
- val q: BigInteger = dlogGroup.order
- val e: BigInteger = new BigInteger(1, challenge)
- val ew: BigInteger = e.multiply(privateInput.w).mod(q)
- val z: BigInteger = rnd.add(ew).mod(q)
- SecondDiffieHellmanTupleProverMessage(z)
+ challenge: Challenge): SecondDHTupleProverMessage = {
+ val z = responseToChallenge(privateInput, rnd, challenge)
+ SecondDHTupleProverMessage(z)
}
- def simulate(publicInput: ProveDHTuple, challenge: Challenge):
- (FirstDiffieHellmanTupleProverMessage, SecondDiffieHellmanTupleProverMessage) = {
+ /** Simulates messages of the sigma protocol which are indistinquishable from generated
+ * by the real prover. */
+ def simulate(publicInput: ProveDHTuple, challenge: Challenge): (FirstDHTupleProverMessage, SecondDHTupleProverMessage) = {
val qMinusOne = dlogGroup.order.subtract(BigInteger.ONE)
- //SAMPLE a random z <- Zq
+ //SAMPLE a random z <- Zq, this will be the simulated response to the challenge
val z = BigIntegers.createRandomInRange(BigInteger.ZERO, qMinusOne, dlogGroup.secureRandom)
// COMPUTE a = g^z*u^(-e) and b = h^z*v^{-e} (where -e here means -e mod q)
- val e: BigInteger = new BigInteger(1, challenge)
+ // in real prover we compute commitments from random number and them compute response to the challenge,
+ // but here we do in opposite direction, use random response and compute commitments from it
+ val e: BigInteger = new BigInteger(1, challenge.toArray)
val minusE = dlogGroup.order.subtract(e)
val hToZ = dlogGroup.exponentiate(publicInput.h, z)
val gToZ = dlogGroup.exponentiate(publicInput.g, z)
@@ -122,7 +139,7 @@ object DiffieHellmanTupleInteractiveProver {
val vToMinusE = dlogGroup.exponentiate(publicInput.v, minusE)
val a = dlogGroup.multiplyGroupElements(gToZ, uToMinusE)
val b = dlogGroup.multiplyGroupElements(hToZ, vToMinusE)
- FirstDiffieHellmanTupleProverMessage(a, b) -> SecondDiffieHellmanTupleProverMessage(z)
+ FirstDHTupleProverMessage(a, b) -> SecondDHTupleProverMessage(z)
}
/**
@@ -133,14 +150,14 @@ object DiffieHellmanTupleInteractiveProver {
*
* g^z = a*u^e, h^z = b*v^e => a = g^z/u^e, b = h^z/v^e
*
- * @param proposition
- * @param challenge
- * @param secondMessage
+ * @param proposition proposition "I know DH tuple"
+ * @param challenge challenge from verifier
+ * @param secondMessage prover's response to the challenge
* @return
*/
def computeCommitment(proposition: ProveDHTuple,
challenge: Challenge,
- secondMessage: SecondDiffieHellmanTupleProverMessage): (EcPointType, EcPointType) = {
+ secondMessage: SecondDHTupleProverMessage): (EcPointType, EcPointType) = {
val g = proposition.g
val h = proposition.h
@@ -149,7 +166,7 @@ object DiffieHellmanTupleInteractiveProver {
val z = secondMessage.z
- val e = new BigInteger(1, challenge)
+ val e = new BigInteger(1, challenge.toArray)
val gToZ = dlogGroup.exponentiate(g, z)
val hToZ = dlogGroup.exponentiate(h, z)
diff --git a/interpreter/shared/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala b/interpreter/shared/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala
index 3327d94ebf..2350c3f1d2 100644
--- a/interpreter/shared/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/basics/SigmaProtocolFunctions.scala
@@ -1,7 +1,14 @@
package sigmastate.basics
+import sigmastate.SigmaLeaf
+import sigmastate.basics.CryptoConstants.dlogGroup
+import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog}
+import sigmastate.basics.VerifierMessage.Challenge
+import special.collection.Coll
import supertagged.TaggedType
+import java.math.BigInteger
+
/*
Abstracting Sigma protocols
Functionality to get:
@@ -25,7 +32,7 @@ trait VerifierMessage extends TranscriptMessage
object VerifierMessage {
/** A challenge from the verifier (message `e` of `SigmaProtocol`)*/
- object Challenge extends TaggedType[Array[Byte]]
+ object Challenge extends TaggedType[Coll[Byte]]
type Challenge = Challenge.Type
}
@@ -50,11 +57,35 @@ trait SigmaProtocol[SP <: SigmaProtocol[SP]] {
}
-trait SigmaProtocolCommonInput[SP <: SigmaProtocol[SP]] {
+trait SigmaProtocolPrivateInput[CI <: SigmaLeaf] {
+ /** Public image generated from the secret.
+ * Represents proof of knowledge proposition.
+ */
+ def publicImage: CI
+
+ /** Secret random number known to the prover. */
+ def w: BigInteger
}
-trait SigmaProtocolPrivateInput[SP <: SigmaProtocol[SP], CI <: SigmaProtocolCommonInput[SP]] {
- def publicImage: CI
+trait SigmaProtocolProver {
+ /** Computes response for the challenge in non-interactive sigma protocol.
+ *
+ * @param privateInput private input of the prover (secret)
+ * @param rnd random number generated by the prover (secret random number used to
+ * compute commitment)
+ * @param challenge challenge from the verifier (also computed by the prover in non-interactive case)
+ * @return response computed by the prover
+ */
+ protected def responseToChallenge(
+ privateInput: SigmaProtocolPrivateInput[_ <: SigmaLeaf],
+ rnd: BigInteger,
+ challenge: Challenge): BigInteger = {
+ val q: BigInteger = dlogGroup.order
+ val e: BigInteger = new BigInteger(1, challenge.toArray)
+ val ew: BigInteger = e.multiply(privateInput.w).mod(q)
+ val z: BigInteger = rnd.add(ew).mod(q)
+ z
+ }
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/BigIntegers.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/BigIntegers.scala
index 7bab41df79..43bded8e09 100644
--- a/interpreter/shared/src/main/scala/sigmastate/crypto/BigIntegers.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/crypto/BigIntegers.scala
@@ -65,7 +65,7 @@ object BigIntegers {
if (min.bitLength > max.bitLength / 2)
return createRandomInRange(ZERO, max.subtract(min), random).add(min)
- for ( i <- 0 until MAX_ITERATIONS ) {
+ for ( _ <- 0 until MAX_ITERATIONS ) {
val x = createRandomBigInteger(max.bitLength, random)
if (x.compareTo(min) >= 0 && x.compareTo(max) <= 0) return x
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala b/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala
index dc0617ea29..344305a157 100644
--- a/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/crypto/GF2_192_Poly.scala
@@ -32,7 +32,6 @@ package sigmastate.crypto
import debox.cfor
import java.util
-import java.util.Arrays
class GF2_192_Poly {
final private var c: Array[GF2_192] = null // must be not null and of length at least 1
diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/CostingDataContext.scala b/interpreter/shared/src/main/scala/sigmastate/eval/CostingDataContext.scala
index f7679573d6..72cd34b725 100644
--- a/interpreter/shared/src/main/scala/sigmastate/eval/CostingDataContext.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/eval/CostingDataContext.scala
@@ -129,7 +129,7 @@ case class CSigmaProp(sigmaTree: SigmaBoolean) extends SigmaProp with WrapperOf[
override def propBytes: Coll[Byte] = {
// in order to have comparisons like `box.propositionBytes == pk.propBytes` we need to make sure
// the same serialization method is used in both cases
- // TODO v6.0: add `pk.propBytes(version)`
+ // TODO v6.0: add `pk.propBytes(version)` (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/903)
val root = sigmaTree.toSigmaProp
val ergoTree = new ErgoTree(ErgoTree.DefaultHeader, EmptyConstants, Right(root), 0, null, None)
val bytes = DefaultSerializer.serializeErgoTree(ergoTree)
diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/Evaluation.scala b/interpreter/shared/src/main/scala/sigmastate/eval/Evaluation.scala
index 818e604dd2..fa8b50a6c9 100644
--- a/interpreter/shared/src/main/scala/sigmastate/eval/Evaluation.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/eval/Evaluation.scala
@@ -141,9 +141,6 @@ object Evaluation {
case _: Short => ShortType
case _: Int => IntType
case _: Long => LongType
- case _: Char => CharType
- case _: Float => FloatType
- case _: Double => DoubleType
case _: String => StringType
case _: Unit => UnitType
diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala b/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala
index d765e01c41..b4efc58348 100644
--- a/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/eval/Extensions.scala
@@ -4,6 +4,7 @@ import debox.{cfor, Buffer => DBuffer}
import org.ergoplatform.ErgoBox
import org.ergoplatform.ErgoBox.TokenId
import scalan.{Nullable, RType}
+import scorex.util.encode.Base16
import sigmastate.SType.AnyOps
import sigmastate.Values.{Constant, ConstantNode}
import sigmastate.crypto.{CryptoFacade, Ecp}
@@ -41,6 +42,8 @@ object Extensions {
implicit class ArrayByteOps(val arr: Array[Byte]) extends AnyVal {
/** Wraps array into TokenId instance. The source array in not cloned. */
@inline def toTokenId: TokenId = Digest32Coll @@ Colls.fromArray(arr)
+ /** Encodes array into hex string */
+ @inline def toHex: String = Base16.encode(arr)
}
implicit class EvalIterableOps[T: RType](seq: Iterable[T]) {
diff --git a/interpreter/shared/src/main/scala/sigmastate/eval/Profiler.scala b/interpreter/shared/src/main/scala/sigmastate/eval/Profiler.scala
index 2f0e7f3e0c..74e187b809 100644
--- a/interpreter/shared/src/main/scala/sigmastate/eval/Profiler.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/eval/Profiler.scala
@@ -169,12 +169,6 @@ class Profiler {
/** Timings of method calls */
private val mcStat = new StatCollection[Int, Long]()
- /** Update time measurement stats for a given method. */
- @inline private final def addMcTime(typeId: Byte, methodId: Byte, time: Long) = {
- val key = typeId << 8 | methodId
- mcStat.addPoint(key, time)
- }
-
/** Wrapper class which implements special equality between CostItem instances,
* suitable for collecting of the statistics. */
class CostItemKey(val costItem: CostItem) {
diff --git a/interpreter/shared/src/main/scala/sigmastate/exceptions/CompilerExceptions.scala b/interpreter/shared/src/main/scala/sigmastate/exceptions/CompilerExceptions.scala
index b652a4091d..8c331766af 100644
--- a/interpreter/shared/src/main/scala/sigmastate/exceptions/CompilerExceptions.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/exceptions/CompilerExceptions.scala
@@ -42,8 +42,7 @@ class TyperException(message: String, source: Option[SourceContext] = None)
class BuilderException(message: String, source: Option[SourceContext] = None)
extends CompilerException(message, source)
-// TODO v5.x: remove this exception
-class CosterException(message: String, source: Option[SourceContext], cause: Option[Throwable] = None)
+class GraphBuildingException(message: String, source: Option[SourceContext], cause: Option[Throwable] = None)
extends CompilerException(message, source, cause)
diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala
index bdd59eb850..de6ad39789 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ErgoTreeEvaluator.scala
@@ -1,7 +1,6 @@
package sigmastate.interpreter
import org.ergoplatform.ErgoLikeContext
-import org.ergoplatform.SigmaConstants.ScriptCostLimit
import sigmastate.{PerItemCost, VersionContext, TypeBasedCost, FixedCost, SType, JitCost}
import sigmastate.Values._
import sigmastate.eval.Profiler
@@ -42,7 +41,12 @@ case class EvalSettings(
* The default value is None, which means the version is defined by ErgoTree.version
* and Context.activatedScriptVersion.
*/
- evaluationMode: Option[EvaluationMode] = None)
+ evaluationMode: Option[EvaluationMode] = None,
+ /** Maximum execution cost of a script used by profiler.
+ * @see ErgoTreeEvaluator
+ */
+ scriptCostLimitInEvaluator: Int = 1000000
+)
object EvalSettings {
/** Enumeration type of evaluation modes of [[Interpreter]].
@@ -123,7 +127,7 @@ class ErgoTreeEvaluator(
protected val coster: CostAccumulator,
val profiler: Profiler,
val settings: EvalSettings) {
-
+
/** Evaluates the given expression in the given data environment. */
def eval(env: DataEnv, exp: SValue): Any = {
VersionContext.checkVersions(context.activatedScriptVersion, context.currentErgoTreeVersion)
@@ -392,7 +396,7 @@ object ErgoTreeEvaluator {
def forProfiling(profiler: Profiler, evalSettings: EvalSettings): ErgoTreeEvaluator = {
val acc = new CostAccumulator(
initialCost = JitCost(0),
- costLimit = Some(JitCost.fromBlockCost(ScriptCostLimit.value)))
+ costLimit = Some(JitCost.fromBlockCost(evalSettings.scriptCostLimitInEvaluator)))
new ErgoTreeEvaluator(
context = null,
constants = ArraySeq.empty,
diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala
index 13570e846c..ab2530f653 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Hint.scala
@@ -1,8 +1,7 @@
package sigmastate.interpreter
import java.math.BigInteger
-
-import sigmastate.{NodePosition, UncheckedTree}
+import sigmastate.{NodePosition, SigmaLeaf, UncheckedTree}
import sigmastate.Values.SigmaBoolean
import sigmastate.basics.FirstProverMessage
import sigmastate.basics.VerifierMessage.Challenge
@@ -13,6 +12,10 @@ import sigmastate.basics.VerifierMessage.Challenge
* prover knows that pk2 is known to another party, the prover may prove the statement (with an empty proof for "pk2").
*/
trait Hint {
+ /**
+ * Public image of a secret
+ */
+ def image: SigmaLeaf
/**
* A hint is related to a subtree (or a leaf) of a tree. This field encodes a position in the tree.
@@ -25,12 +28,6 @@ trait Hint {
* A hint which is indicating that a secret associated with its public image "image" is already proven.
*/
abstract class SecretProven extends Hint {
-
- /**
- * Public image of a secret which is proven
- */
- def image: SigmaBoolean
-
/**
* Challenge used for a proof
*/
@@ -46,16 +43,16 @@ abstract class SecretProven extends Hint {
* A hint which contains a proof-of-knowledge for a secret associated with its public image "image",
* with also the mark that the proof is real.
*/
-case class RealSecretProof(image: SigmaBoolean,
+case class RealSecretProof(image: SigmaLeaf,
challenge: Challenge,
uncheckedTree: UncheckedTree,
override val position: NodePosition) extends SecretProven
/**
* A hint which contains a proof-of-knowledge for a secret associated with its public image "image",
- * with also the mark that the proof is real.
+ * with also the mark that the proof is simulated (not real).
*/
-case class SimulatedSecretProof(image: SigmaBoolean,
+case class SimulatedSecretProof(image: SigmaLeaf,
challenge: Challenge,
uncheckedTree: UncheckedTree,
override val position: NodePosition) extends SecretProven
@@ -66,7 +63,7 @@ case class SimulatedSecretProof(image: SigmaBoolean,
* to randomness ("a" in a sigma protocol).
*/
abstract class CommitmentHint extends Hint {
- def image: SigmaBoolean
+ /** Commitment to randomness (first message in a sigma protocol) */
def commitment: FirstProverMessage
}
@@ -78,7 +75,7 @@ abstract class CommitmentHint extends Hint {
* @param secretRandomness - randomness
* @param commitment - commitment to randomness used while proving knowledge of the secret
*/
-case class OwnCommitment(override val image: SigmaBoolean,
+case class OwnCommitment(override val image: SigmaLeaf,
secretRandomness: BigInteger,
commitment: FirstProverMessage,
override val position: NodePosition) extends CommitmentHint
@@ -89,7 +86,7 @@ case class OwnCommitment(override val image: SigmaBoolean,
* @param image - image of a secret
* @param commitment - commitment to randomness used while proving knowledge of the secret
*/
-case class RealCommitment(override val image: SigmaBoolean,
+case class RealCommitment(override val image: SigmaLeaf,
commitment: FirstProverMessage,
override val position: NodePosition) extends CommitmentHint
@@ -99,7 +96,7 @@ case class RealCommitment(override val image: SigmaBoolean,
* @param image - image of a secret
* @param commitment - commitment to randomness used while proving knowledge of the secret
*/
-case class SimulatedCommitment(override val image: SigmaBoolean,
+case class SimulatedCommitment(override val image: SigmaLeaf,
commitment: FirstProverMessage,
override val position: NodePosition) extends CommitmentHint
diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala
index 1559fcc775..e606e72525 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/Interpreter.scala
@@ -282,7 +282,7 @@ trait Interpreter {
* @throws InterpreterException when cannot proceed and no activation yet.
*/
protected def checkSoftForkCondition(ergoTree: ErgoTree, context: CTX): Option[VerificationResult] = {
- // TODO v6.0: the condition below should be revised if necessary
+ // TODO v6.0: the condition below should be revised if necessary (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/904)
// The following conditions define behavior which depend on the version of ergoTree
// This works in addition to more fine-grained soft-forkability mechanism implemented
// using ValidationRules (see trySoftForkable method call here and in reduceToCrypto).
@@ -383,7 +383,7 @@ trait Interpreter {
* (and, if applicable, the associated data). Reject otherwise.
*/
val expectedChallenge = CryptoFunctions.hashFn(bytes)
- util.Arrays.equals(newRoot.challenge, expectedChallenge)
+ util.Arrays.equals(newRoot.challenge.toArray, expectedChallenge)
}
/**
@@ -405,7 +405,7 @@ trait Interpreter {
implicit val E = ErgoTreeEvaluator.getCurrentEvaluator
fixedCostOp(ComputeCommitments_DHT) {
val (a, b) = DiffieHellmanTupleInteractiveProver.computeCommitment(dh.proposition, dh.challenge, dh.secondMessage)
- dh.copy(commitmentOpt = Some(FirstDiffieHellmanTupleProverMessage(a, b)))
+ dh.copy(commitmentOpt = Some(FirstDHTupleProverMessage(a, b)))
}
case _: UncheckedSigmaTree => ???
diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala
index 73e0a96370..937aa37fda 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/InterpreterContext.scala
@@ -9,6 +9,8 @@ import sigmastate.utils.{SigmaByteReader, SigmaByteWriter}
import special.sigma
import special.sigma.AnyValue
+import scala.collection.mutable
+
/**
* User-defined variables to be put into context.
* Each variable is identified by `id: Byte` and can be accessed from a script
@@ -20,7 +22,7 @@ import special.sigma.AnyValue
*
* @param values internal container of the key-value pairs
*/
-case class ContextExtension(values: Map[Byte, EvaluatedValue[_ <: SType]]) {
+case class ContextExtension(values: scala.collection.Map[Byte, EvaluatedValue[_ <: SType]]) {
def add(bindings: VarBinding*): ContextExtension =
ContextExtension(values ++ bindings)
}
@@ -49,8 +51,7 @@ object ContextExtension {
error(s"Negative amount of context extension values: $extSize")
val ext = (0 until extSize)
.map(_ => (r.getByte(), r.getValue().asInstanceOf[EvaluatedValue[_ <: SType]]))
- .toMap[Byte, EvaluatedValue[_ <: SType]]
- ContextExtension(ext)
+ ContextExtension(mutable.LinkedHashMap(ext:_*))
}
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala
index ff91db6285..e9dfa6b052 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverInterpreter.scala
@@ -11,8 +11,10 @@ import sigmastate.basics.DLogProtocol._
import sigmastate.basics.VerifierMessage.Challenge
import sigmastate.basics._
import sigmastate.crypto.{GF2_192, GF2_192_Poly}
+import sigmastate.eval.Extensions.ArrayOps
import sigmastate.exceptions.InterpreterException
import sigmastate.utils.Helpers
+import special.collection.Coll
import java.math.BigInteger
import scala.util.Try
@@ -29,7 +31,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
override type ProofT = UncheckedTree
/** All secrets available for this prover. */
- def secrets: Seq[SigmaProtocolPrivateInput[_, _]]
+ def secrets: Seq[SigmaProtocolPrivateInput[_]]
/**
* Public keys of prover's secrets. This operation can be costly if there are many
@@ -91,7 +93,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
// Prover Step 8: compute the challenge for the root of the tree as the Fiat-Shamir hash of propBytes
// and the message being signed.
- val rootChallenge = Challenge @@ CryptoFunctions.hashFn(Helpers.concatArrays(propBytes, message))
+ val rootChallenge = Challenge @@ CryptoFunctions.hashFn(Helpers.concatArrays(propBytes, message)).toColl
val step8 = step6.withChallenge(rootChallenge)
// Prover Step 9: complete the proof by computing challenges at real nodes and additionally responses at real leaves
@@ -181,7 +183,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
// is known to an external participant in multi-signing;
// else mark it "simulated"
val isReal = hintsBag.realImages.contains(ul.proposition) || secrets.exists {
- case in: SigmaProtocolPrivateInput[_, _] => in.publicImage == ul.proposition
+ case in: SigmaProtocolPrivateInput[_] => in.publicImage == ul.proposition
}
ul.withSimulated(!isReal)
case t: UnprovenTree =>
@@ -287,7 +289,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
// take challenge from previously done proof stored in the hints bag,
// or generate random challenge for simulated child
val newChallenge = hintsBag.proofs.find(_.position == c.position).map(_.challenge).getOrElse(
- Challenge @@ secureRandomBytes(CryptoFunctions.soundnessBytes)
+ Challenge @@ secureRandomBytes(CryptoFunctions.soundnessBytes).toColl
)
c.withChallenge(newChallenge)
}
@@ -314,8 +316,10 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
// the other children and e_0.
assert(or.challengeOpt.isDefined)
val unprovenChildren = or.children.cast[UnprovenTree]
- val t = unprovenChildren.tail.map(_.withChallenge(Challenge @@ secureRandomBytes(CryptoFunctions.soundnessBytes)))
- val toXor: Seq[Array[Byte]] = or.challengeOpt.get +: t.map(_.challengeOpt.get)
+ val t = unprovenChildren.tail.map(
+ _.withChallenge(Challenge @@ secureRandomBytes(CryptoFunctions.soundnessBytes).toColl)
+ )
+ val toXor: Seq[Coll[Byte]] = or.challengeOpt.get +: t.map(_.challengeOpt.get)
val xoredChallenge = Challenge @@ Helpers.xor(toXor: _*)
val h = unprovenChildren.head.withChallenge(xoredChallenge)
or.copy(children = h +: t)
@@ -329,11 +333,11 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
assert(t.challengeOpt.isDefined)
val n = t.children.length
val unprovenChildren = t.children.cast[UnprovenTree]
- val q = GF2_192_Poly.fromByteArray(t.challengeOpt.get, secureRandomBytes(CryptoFunctions.soundnessBytes * (n - t.k)))
+ val q = GF2_192_Poly.fromByteArray(t.challengeOpt.get.toArray, secureRandomBytes(CryptoFunctions.soundnessBytes * (n - t.k)))
val newChildren = unprovenChildren.foldLeft((Seq[UnprovenTree](), 1)) {
case ((childSeq, childIndex), child) =>
- (childSeq :+ child.withChallenge(Challenge @@ q.evaluate(childIndex.toByte).toByteArray), childIndex + 1)
+ (childSeq :+ child.withChallenge(Challenge @@ q.evaluate(childIndex.toByte).toByteArray.toColl), childIndex + 1)
}._1
t.withPolynomial(q).copy(children = newChildren)
@@ -395,7 +399,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
// Step 6 (real leaf -- compute the commitment a or take it from the hints bag)
hintsBag.commitments.find(_.position == dhu.position).map { cmtHint =>
- dhu.copy(commitmentOpt = Some(cmtHint.commitment.asInstanceOf[FirstDiffieHellmanTupleProverMessage]))
+ dhu.copy(commitmentOpt = Some(cmtHint.commitment.asInstanceOf[FirstDHTupleProverMessage]))
}.getOrElse {
if (dhu.simulated) {
// Step 5 (simulated leaf -- complete the simulation)
@@ -412,7 +416,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
case t: ProofTree => error(s"Don't know how to challengeSimulated($t)")
})
- private def extractChallenge(pt: ProofTree): Option[Array[Byte]] = pt match {
+ private def extractChallenge(pt: ProofTree): Option[Challenge] = pt match {
case upt: UnprovenTree => upt.challengeOpt
case sn: UncheckedSchnorr => Some(sn.challenge)
case dh: UncheckedDiffieHellmanTuple => Some(dh.challenge)
@@ -461,16 +465,16 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
// has a challenge. Other ways are more of a pain because the children can be of different types
val challengeOpt = extractChallenge(child)
if (challengeOpt.isEmpty) (p, v)
- else (p :+ count.toByte, v :+ new GF2_192(challengeOpt.get))
+ else (p :+ count.toByte, v :+ new GF2_192(challengeOpt.get.toArray))
}
(newPoints, newValues, count + 1)
}
- val q = GF2_192_Poly.interpolate(points, values, new GF2_192(t.challengeOpt.get))
+ val q = GF2_192_Poly.interpolate(points, values, new GF2_192(t.challengeOpt.get.toArray))
val newChildren = t.children.foldLeft(Seq[ProofTree](), 1) {
case ((s, count), child) =>
val newChild = child match {
- case r: UnprovenTree if r.real => r.withChallenge(Challenge @@ q.evaluate(count.toByte).toByteArray)
+ case r: UnprovenTree if r.real => r.withChallenge(Challenge @@ q.evaluate(count.toByte).toByteArray.toColl)
case p: ProofTree => p
}
(s :+ newChild, count + 1)
@@ -538,7 +542,7 @@ trait ProverInterpreter extends Interpreter with ProverUtils {
provenSchnorr.secondMessage
}.getOrElse {
val bs = secureRandomBytes(32)
- SecondDiffieHellmanTupleProverMessage(new BigInteger(1, bs).mod(CryptoConstants.groupOrder))
+ SecondDHTupleProverMessage(new BigInteger(1, bs).mod(CryptoConstants.groupOrder))
}
}
UncheckedDiffieHellmanTuple(dhu.proposition, None, dhu.challengeOpt.get, z)
diff --git a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala
index 7a3ce291db..e2b0fe8f5a 100644
--- a/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/interpreter/ProverUtils.scala
@@ -41,7 +41,7 @@ trait ProverUtils extends Interpreter {
sc.children.zipWithIndex.foldLeft(bag) { case (b, (child, idx)) =>
traverseNode(child, b, position.child(idx))
}
- case leaf: SigmaProofOfKnowledgeLeaf[_, _] =>
+ case leaf: SigmaLeaf =>
if (generateFor.contains(leaf)) {
val (r, a) = leaf match {
case _: ProveDlog =>
@@ -115,19 +115,19 @@ trait ProverUtils extends Interpreter {
inner.children.zipWithIndex.foldLeft(hintsBag) { case (hb, (c, idx)) =>
traverseNode(c, realPropositions, simulatedPropositions, hb, position.child(idx))
}
- case leaf: UncheckedLeaf[_] =>
+ case leaf: UncheckedLeaf =>
val realFound = realPropositions.contains(leaf.proposition)
val simulatedFound = simulatedPropositions.contains(leaf.proposition)
if (realFound || simulatedFound) {
val hints = if (realFound) {
Seq(
RealCommitment(leaf.proposition, leaf.commitmentOpt.get, position),
- RealSecretProof(leaf.proposition, Challenge @@ leaf.challenge, leaf, position)
+ RealSecretProof(leaf.proposition, leaf.challenge, leaf, position)
)
} else {
Seq(
SimulatedCommitment(leaf.proposition, leaf.commitmentOpt.get, position),
- SimulatedSecretProof(leaf.proposition, Challenge @@ leaf.challenge, leaf, position)
+ SimulatedSecretProof(leaf.proposition, leaf.challenge, leaf, position)
)
}
hintsBag.addHints(hints: _*)
diff --git a/interpreter/shared/src/main/scala/sigmastate/lang/SigmaBuilder.scala b/interpreter/shared/src/main/scala/sigmastate/lang/SigmaBuilder.scala
index 813664931d..55d1575ff9 100644
--- a/interpreter/shared/src/main/scala/sigmastate/lang/SigmaBuilder.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/lang/SigmaBuilder.scala
@@ -158,9 +158,6 @@ abstract class SigmaBuilder {
def mkTaggedVariable[T <: SType](varId: Byte, tpe: T): TaggedVariable[T]
- def mkSomeValue[T <: SType](x: Value[T]): Value[SOption[T]]
- def mkNoneValue[T <: SType](elemType: T): Value[SOption[T]]
-
def mkBlock(bindings: Seq[Val], result: Value[SType]): Value[SType]
def mkBlockValue(items: IndexedSeq[BlockItem], result: Value[SType]): Value[SType]
def mkValUse(valId: Int, tpe: SType): Value[SType]
@@ -527,11 +524,6 @@ class StdSigmaBuilder extends SigmaBuilder {
override def mkTaggedVariable[T <: SType](varId: Byte, tpe: T): TaggedVariable[T] =
TaggedVariableNode(varId, tpe).withSrcCtx(currentSrcCtx.value).asInstanceOf[TaggedVariable[T]]
- override def mkSomeValue[T <: SType](x: Value[T]): Value[SOption[T]] =
- SomeValue(x).withSrcCtx(currentSrcCtx.value)
- override def mkNoneValue[T <: SType](elemType: T): Value[SOption[T]] =
- NoneValue(elemType).withSrcCtx(currentSrcCtx.value)
-
override def mkBlock(bindings: Seq[Val], result: Value[SType]): Value[SType] =
Block(bindings, result).withSrcCtx(currentSrcCtx.value)
diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/DataSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/DataSerializer.scala
index d659dbf81b..f38fd97309 100644
--- a/interpreter/shared/src/main/scala/sigmastate/serialization/DataSerializer.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/serialization/DataSerializer.scala
@@ -19,7 +19,6 @@ import scala.collection.mutable
/** This works in tandem with ConstantSerializer, if you change one make sure to check the other.*/
object DataSerializer {
- // TODO v5.x: control maxTreeDepth same as in deserialize
/** Use type descriptor `tpe` to deconstruct type structure and recursively serialize subcomponents.
* Primitive types are leaves of the type tree, and they are served as basis of recursion.
* The data value `v` is expected to conform to the type described by `tpe`.
diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala
index b8d8d07093..765aaeb7b4 100644
--- a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/serialization/ModQArithOpSerializer.scala
@@ -6,7 +6,7 @@ import sigmastate.utils.SigmaByteWriter.DataInfo
import sigmastate.utils.{SigmaByteReader, SigmaByteWriter}
import sigmastate.{ModQArithOpCompanion, SType, ModQArithOp}
-// TODO v6.0 (2h): make sure it is covered with tests
+// TODO v6.0: make sure it is covered with tests (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/327)
case class ModQArithOpSerializer(override val opDesc: ModQArithOpCompanion, cons: (BigIntValue, BigIntValue) => BigIntValue)
extends ValueSerializer[ModQArithOp] {
val leftInfo: DataInfo[SValue] = opDesc.argInfos(0)
diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/ModQSerializer.scala
index f48a6e6388..335c12c1af 100644
--- a/interpreter/shared/src/main/scala/sigmastate/serialization/ModQSerializer.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/serialization/ModQSerializer.scala
@@ -5,7 +5,7 @@ import sigmastate.lang.Terms._
import sigmastate.utils.{SigmaByteReader, SigmaByteWriter}
import sigmastate.{ModQ, SType}
-// TODO v6.0 (2h): make sure it is covered with tests
+// TODO v6.0: make sure it is covered with tests (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/327)
object ModQSerializer extends ValueSerializer[ModQ] {
override def opDesc = ModQ
diff --git a/interpreter/shared/src/main/scala/sigmastate/serialization/ValueSerializer.scala b/interpreter/shared/src/main/scala/sigmastate/serialization/ValueSerializer.scala
index abb0cbe964..ca5a60926c 100644
--- a/interpreter/shared/src/main/scala/sigmastate/serialization/ValueSerializer.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/serialization/ValueSerializer.scala
@@ -21,7 +21,7 @@ import scala.collection.mutable.{HashMap, Map}
abstract class ValueSerializer[V <: Value[SType]] extends SigmaSerializer[Value[SType], V] {
import scala.language.implicitConversions
- val companion = ValueSerializer
+ private val companion = ValueSerializer
def getComplexity: Int = OpCodeComplexity.getOrElse(opCode, MinimalComplexity)
lazy val complexity: Int = getComplexity
@@ -49,7 +49,8 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
private val constantSerializer = ConstantSerializer(builder)
private val constantPlaceholderSerializer = ConstantPlaceholderSerializer(mkConstantPlaceholder)
- val serializers = SparseArrayContainer.buildForSerializers(Seq[ValueSerializer[_ <: Value[SType]]](
+ val serializers: SparseArrayContainer[ValueSerializer[_ <: Value[SType]]] =
+ SparseArrayContainer.buildForSerializers(Seq[ValueSerializer[_ <: Value[SType]]](
constantSerializer,
constantPlaceholderSerializer,
TupleSerializer(mkTuple),
@@ -144,7 +145,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
SigmaTransformerSerializer(SigmaOr, mkSigmaOr),
BoolToSigmaPropSerializer(mkBoolToSigmaProp),
- // TODO hard-fork: this ModQ serializers should be removed only as part of hard-fork
+ // NOTE: these ModQ serializers can be removed only as part of hard-fork
// because their removal may break deserialization of transaction, when for example
// ModQ operation happen to be in one of the outputs (i.e. script is not executed
// during validation, however deserializer is still used)
@@ -161,7 +162,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
))
private def serializable(v: Value[SType]): Value[SType] = v match {
- case upcast: Upcast[SType, _]@unchecked =>
+ case upcast: Upcast[SNumericType, _]@unchecked =>
upcast.input
case _ => v
}
@@ -171,10 +172,10 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
CheckValidOpCode(serializer, opCode)
serializer
}
- def addSerializer(opCode: OpCode, ser: ValueSerializer[_ <: Value[SType]]) = {
+ def addSerializer(opCode: OpCode, ser: ValueSerializer[_ <: Value[SType]]): Unit = {
serializers.add(opCode, ser)
}
- def removeSerializer(opCode: OpCode) = {
+ def removeSerializer(opCode: OpCode): Unit = {
serializers.remove(opCode)
}
@@ -184,13 +185,13 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
def parent: Scope
def children: ChildrenMap
def get(name: String): Option[Scope] = children.find(_._1 == name).map(_._2)
- def add(name: String, s: Scope) = {
+ def add(name: String, s: Scope): ChildrenMap = {
assert(get(name).isEmpty, s"Error while adding scope $s: name $name already exists in $this")
children += (name -> s)
}
def showInScope(v: String): String
- def provideScope(n: String, createNewScope: => Scope) = {
+ def provideScope(n: String, createNewScope: => Scope): Scope = {
val scope = get(n) match {
case Some(saved) => saved
case None =>
@@ -203,7 +204,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
}
case class SerScope(opCode: OpCode, children: ChildrenMap) extends Scope {
- def serializer = getSerializer(opCode)
+ private def serializer = getSerializer(opCode)
def name = s"Serializer of ${serializer.opDesc}"
override def parent: Scope = null
override def showInScope(v: String): String = name + "/" + v
@@ -211,8 +212,8 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
}
case class DataScope(parent: Scope, data: DataInfo[_]) extends Scope {
- def name = data.info.name
- override def children = mutable.ArrayBuffer.empty
+ override def name: String = data.info.name
+ override def children: ChildrenMap = mutable.ArrayBuffer.empty
override def showInScope(v: String): String = parent.showInScope(s"DataInfo($data)")
override def toString = s"DataScope($data)"
}
@@ -250,7 +251,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
}
val collectSerInfo: Boolean = false
- val serializerInfo: Map[OpCode, SerScope] = HashMap.empty
+ val serializerInfo: mutable.Map[OpCode, SerScope] = mutable.HashMap.empty
private var scopeStack: List[Scope] = Nil
def printSerInfo(): String = {
@@ -347,7 +348,7 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
scope.get(prop.info.name) match {
case None =>
scope.add(prop.info.name, DataScope(scope, prop))
- println(s"Added $prop to ${scope}")
+ println(s"Added $prop to $scope")
case Some(saved) => saved match {
case DataScope(_, data) =>
assert(data == prop, s"Saved property $data is different from being added $prop: scope $scope")
@@ -357,7 +358,6 @@ object ValueSerializer extends SigmaSerializerCompanion[Value[SType]] {
}
}
- // TODO v5.x: control maxTreeDepth same as in deserialize (see Reader.level property and SigmaSerializer.MaxTreeDepth)
override def serialize(v: Value[SType], w: SigmaByteWriter): Unit = serializable(v) match {
case c: Constant[SType] =>
w.constantExtractionStore match {
diff --git a/interpreter/shared/src/main/scala/sigmastate/trees.scala b/interpreter/shared/src/main/scala/sigmastate/trees.scala
index 0d60fa1b74..254aa7c317 100644
--- a/interpreter/shared/src/main/scala/sigmastate/trees.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/trees.scala
@@ -1,34 +1,31 @@
package sigmastate
+import debox.{cfor, Map => DMap}
import org.ergoplatform.SigmaConstants
import org.ergoplatform.validation.SigmaValidationSettings
-import scalan.{ExactIntegral, ExactNumeric, ExactOrdering, Nullable}
+import scalan.ExactIntegral._
+import scalan.ExactOrdering._
import scalan.OverloadHack.Overloaded1
+import scalan.{ExactIntegral, ExactOrdering}
import scorex.crypto.hash.{Blake2b256, CryptographicHash32, Sha256}
+import sigmastate.ArithOp.OperationImpl
import sigmastate.Operations._
import sigmastate.SCollection.{SByteArray, SIntArray}
import sigmastate.SOption.SIntOption
import sigmastate.Values._
-import sigmastate.basics.{SigmaProtocol, SigmaProtocolCommonInput, SigmaProtocolPrivateInput}
+import sigmastate.eval.Extensions.EvalCollOps
+import sigmastate.eval.NumericOps.{BigIntIsExactIntegral, BigIntIsExactOrdering}
+import sigmastate.eval.{Colls, SigmaDsl}
import sigmastate.interpreter.ErgoTreeEvaluator
import sigmastate.interpreter.ErgoTreeEvaluator.DataEnv
import sigmastate.serialization.OpCodes._
import sigmastate.serialization._
import sigmastate.utxo.{SimpleTransformerCompanion, Transformer}
-import debox.{Map => DMap}
-import scalan.ExactIntegral._
-import scalan.ExactOrdering._
-import sigmastate.ArithOp.OperationImpl
-import sigmastate.eval.NumericOps.{BigIntIsExactIntegral, BigIntIsExactOrdering}
-import sigmastate.eval.{Colls, SigmaDsl}
-import sigmastate.lang.TransformingSigmaBuilder
import special.collection.Coll
import special.sigma.{GroupElement, SigmaProp}
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
-import debox.cfor
-import sigmastate.eval.Extensions.EvalCollOps
/**
* Basic trait for inner nodes of crypto-trees, so AND/OR/THRESHOLD sigma-protocol connectives
@@ -38,10 +35,13 @@ trait SigmaConjecture extends SigmaBoolean {
}
/**
- * Basic trait for leafs of crypto-trees, such as ProveDlog and ProveDiffieHellman instances
+ * Basic trait for leafs of crypto-trees, such as
+ * [[sigmastate.basics.DLogProtocol.ProveDlog]] and [[sigmastate.basics.ProveDHTuple]]
+ * instances.
+ * It plays the same role as [[SigmaConjecture]]. It used in prover to distinguish leafs from
+ * other nodes and have logic common to leaves regardless of the concrete leaf type.
*/
-trait SigmaProofOfKnowledgeLeaf[SP <: SigmaProtocol[SP], S <: SigmaProtocolPrivateInput[SP, _]]
- extends SigmaBoolean with SigmaProtocolCommonInput[SP]
+trait SigmaLeaf extends SigmaBoolean
/**
@@ -204,7 +204,7 @@ object CreateProveDlog extends FixedCostValueCompanion {
val OpType = SFunc(SGroupElement, SSigmaProp)
}
-// TODO v6.0: implement `eval` method and add support in GraphBuilding
+// TODO v6.0: implement `eval` method and add support in GraphBuilding (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/907)
/** Construct a new authenticated dictionary with given parameters and tree root digest.*/
case class CreateAvlTree(operationFlags: ByteValue,
digest: Value[SByteArray],
@@ -1092,7 +1092,7 @@ object BitOp {
}
}
-// TODO v6.0 (24h): implement modular operations
+// TODO v6.0: implement modular operations (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/327)
case class ModQ(input: Value[SBigInt.type])
extends NotReadyValue[SBigInt.type] {
override def companion = ModQ
@@ -1122,12 +1122,10 @@ trait OpGroup[C <: ValueCompanion] {
object ModQArithOp extends OpGroup[ModQArithOpCompanion] {
import OpCodes._
object PlusModQ extends ModQArithOpCompanion(PlusModQCode, "PlusModQ") {
- // TODO soft-fork:
// override def argInfos: Seq[ArgInfo] = PlusModQInfo.argInfos
override def argInfos: Seq[ArgInfo] = Seq(ArgInfo("this", ""), ArgInfo("other", ""))
}
object MinusModQ extends ModQArithOpCompanion(MinusModQCode, "MinusModQ") {
- // TODO soft-fork:
// override def argInfos: Seq[ArgInfo] = MinusModQInfo.argInfos
override def argInfos: Seq[ArgInfo] = Seq(ArgInfo("this", ""), ArgInfo("other", ""))
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/types.scala b/interpreter/shared/src/main/scala/sigmastate/types.scala
index fe7630eb3e..e4fd826304 100644
--- a/interpreter/shared/src/main/scala/sigmastate/types.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/types.scala
@@ -188,7 +188,7 @@ object SType {
* should be changed and SGlobal.typeId should be preserved. The regression tests in
* `property("MethodCall Codes")` should pass.
*/
- // TODO v6.0 (h4): should contain all numeric types (including also SNumericType)
+ // TODO v6.0: should contain all numeric types (including also SNumericType)
// to support method calls like 10.toByte which encoded as MethodCall with typeId = 4, methodId = 1
// see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667
lazy val types: Map[Byte, STypeCompanion] = Seq(
@@ -793,7 +793,7 @@ object SNumericType extends STypeCompanion {
/** Array of all numeric types ordered by number of bytes in the representation. */
final val allNumericTypes = Array(SByte, SShort, SInt, SLong, SBigInt)
- // TODO v6.0 (4h): this typeId is now shadowed by SGlobal.typeId
+ // TODO v6.0: this typeId is now shadowed by SGlobal.typeId
// see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/667
override def typeId: TypeCode = 106: Byte
@@ -2326,7 +2326,7 @@ case object SAvlTree extends SProduct with SPredefType with SMonoType {
val insert = Insert(ADKey @@ key.toArray, ADValue @@ value.toArray)
val insertRes = bv.performOneOperation(insert)
// TODO v6.0: throwing exception is not consistent with update semantics
- // however it preserves v4.0 semantics
+ // however it preserves v4.0 semantics (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908)
if (insertRes.isFailure) {
Interpreter.error(s"Incorrect insert for $tree (key: $key, value: $value, digest: ${tree.digest}): ${insertRes.failed.get}}")
}
diff --git a/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala b/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala
index df775cbecf..1b16ef05f9 100644
--- a/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/utils/Helpers.scala
@@ -1,5 +1,6 @@
package sigmastate.utils
+import debox.cfor
import io.circe.Decoder
import org.ergoplatform.settings.ErgoAlgos
import scalan.{OverloadHack, RType}
@@ -37,6 +38,18 @@ object Helpers {
def xor(bas: Array[Byte]*): Array[Byte] =
bas.reduce({case (ba, ba1) => xor(ba, ba1)}: ((Array[Byte], Array[Byte]) => Array[Byte]))
+ def xor(bas: Coll[Byte]*): Coll[Byte] = {
+ require(bas.nonEmpty, "at least one argument is required")
+ if (bas.length == 1) bas(0)
+ else {
+ val res = bas(0).toArray.clone()
+ cfor(1)(_ < bas.length, _ + 1) { i =>
+ xorU(res, bas(i).toArray)
+ }
+ Colls.fromArray(res)
+ }
+ }
+
/** Same as `xor` but makes in-place update of the first argument (hence suffix `U`)
* This is boxing-free version.
* @return reference to the updated first argument to easy chaining of calls. */
diff --git a/interpreter/shared/src/main/scala/sigmastate/utxo/transformers.scala b/interpreter/shared/src/main/scala/sigmastate/utxo/transformers.scala
index f78552197e..8a434afc2b 100644
--- a/interpreter/shared/src/main/scala/sigmastate/utxo/transformers.scala
+++ b/interpreter/shared/src/main/scala/sigmastate/utxo/transformers.scala
@@ -412,7 +412,6 @@ object ExtractScriptBytes extends SimpleTransformerCompanion with FixedCostValue
val OpType = SFunc(SBox, SByteArray)
override def opCode: OpCode = OpCodes.ExtractScriptBytesCode
- // TODO v5.x: ensure the following is true
/** The cost is fixed and doesn't include serialization of ErgoTree because
* the ErgoTree is expected to be constructed with non-null propositionBytes.
* This is (and must be) guaranteed by ErgoTree deserializer.
@@ -437,7 +436,7 @@ object ExtractBytes extends SimpleTransformerCompanion {
override def opCode: OpCode = OpCodes.ExtractBytesCode
/** The cost is fixed and doesn't include serialization of ErgoBox because
* the ErgoBox is expected to be constructed with non-null `bytes`.
- * TODO v5.x: This is not currently, but must be guaranteed by lazy ErgoBox deserializer. */
+ */
override val costKind = FixedCost(JitCost(12))
override def argInfos: Seq[ArgInfo] = ExtractBytesInfo.argInfos
}
@@ -618,7 +617,7 @@ case class OptionGetOrElse[V <: SType](input: Value[SOption[V]], default: Value[
override def tpe: V = input.tpe.elemType
protected final override def eval(env: DataEnv)(implicit E: ErgoTreeEvaluator): Any = {
val inputV = input.evalTo[Option[V#WrappedType]](env)
- val dV = default.evalTo[V#WrappedType](env) // TODO v6.0: execute lazily
+ val dV = default.evalTo[V#WrappedType](env) // TODO v6.0: execute lazily (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/906)
Value.checkType(default, dV) // necessary because cast to V#WrappedType is erased
addCost(OptionGetOrElse.costKind)
inputV.getOrElse(dV)
diff --git a/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala
index 9d5a8c116f..49fa4534bd 100644
--- a/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/CryptoFacadeSpecification.scala
@@ -11,6 +11,8 @@ import java.math.BigInteger
class CryptoFacadeSpecification extends AnyPropSpec with Matchers with ScalaCheckPropertyChecks {
+ val G_hex = "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+
property("CryptoFacade.HashHmacSHA512") {
val cases = Table(
("string", "hash"),
@@ -54,7 +56,7 @@ class CryptoFacadeSpecification extends AnyPropSpec with Matchers with ScalaChec
}
}
- property("CryptoFacade.encodePoint") {
+ property("CryptoFacade.getASN1Encoding") {
val ctx = CryptoFacade.createCryptoContext()
val G = ctx.generator
val Q = ctx.order
@@ -62,8 +64,8 @@ class CryptoFacadeSpecification extends AnyPropSpec with Matchers with ScalaChec
("point", "expectedHex"),
(ctx.infinity(), "00"),
(CryptoFacade.exponentiatePoint(G, Q), "00"),
- (G, "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
- (CryptoFacade.exponentiatePoint(G, BigInteger.ONE), "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"),
+ (G, G_hex),
+ (CryptoFacade.exponentiatePoint(G, BigInteger.ONE), G_hex),
(CryptoFacade.exponentiatePoint(G, Q.subtract(BigInteger.ONE)), "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798")
)
forAll (vectors) { (point, expectedHex) =>
@@ -71,4 +73,20 @@ class CryptoFacadeSpecification extends AnyPropSpec with Matchers with ScalaChec
res shouldBe expectedHex
}
}
+
+ property("CryptoContext.decodePoint") {
+ val ctx = CryptoFacade.createCryptoContext()
+
+ val inf = ctx.decodePoint(Array[Byte](0))
+ CryptoFacade.isInfinityPoint(inf) shouldBe true
+
+ val G = ctx.generator
+ ctx.decodePoint(ErgoAlgos.decode(G_hex).get) shouldBe G
+
+ val Q = ctx.order
+ val Q_minus_1 = Q.subtract(BigInteger.ONE)
+ val maxExp = CryptoFacade.exponentiatePoint(G, Q_minus_1)
+ val maxExpBytes = ErgoAlgos.decode("0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798").get
+ ctx.decodePoint(maxExpBytes) shouldBe maxExp
+ }
}
\ No newline at end of file
diff --git a/interpreter/shared/src/test/scala/sigmastate/SigmaProtocolSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/SigmaProtocolSpecification.scala
index 96931f5e84..669cb09da5 100644
--- a/interpreter/shared/src/test/scala/sigmastate/SigmaProtocolSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/SigmaProtocolSpecification.scala
@@ -7,8 +7,8 @@ import special.sigma.SigmaTestingData
class SigmaProtocolSpecification extends SigmaTestingData {
property("CThresholdUncheckedNode equality") {
- val c1 = Challenge @@ Array[Byte](1)
- val c2 = Challenge @@ Array[Byte](2)
+ val c1 = Challenge @@ Coll[Byte](1)
+ val c2 = Challenge @@ Coll[Byte](2)
val n0 = CThresholdUncheckedNode(c1, Seq(), 0, None)
val n1 = CThresholdUncheckedNode(c1, Seq(), 0, None)
val n2 = CThresholdUncheckedNode(c2, Seq(), 0, None)
diff --git a/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala b/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala
index 2ac254da5b..4e40d91c23 100644
--- a/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/TestsBase.scala
@@ -31,4 +31,7 @@ trait TestsBase extends Matchers with VersionTesting {
/** Transform sigma proposition into [[ErgoTree]] using current ergoTreeHeaderInTests. */
def mkTestErgoTree(prop: SigmaBoolean): ErgoTree =
ErgoTree.fromSigmaBoolean(ergoTreeHeaderInTests, prop)
+
+ /** Max cost of script execution in tests. */
+ val scriptCostLimitInTests: Int = 1000000
}
diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala
index 2945192700..702d890147 100644
--- a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeContextTesting.scala
@@ -1,16 +1,16 @@
package sigmastate.helpers
-import org.ergoplatform.SigmaConstants.ScriptCostLimit
import org.ergoplatform.ErgoLikeContext.Height
import org.ergoplatform._
-import org.ergoplatform.validation.{ValidationRules, SigmaValidationSettings}
+import org.ergoplatform.validation.{SigmaValidationSettings, ValidationRules}
import sigmastate.AvlTreeData
import sigmastate.basics.CryptoConstants
import sigmastate.eval._
import sigmastate.interpreter.ContextExtension
-import sigmastate.serialization.{SigmaSerializer, GroupElementSerializer}
+import sigmastate.interpreter.ErgoTreeEvaluator.DefaultEvalSettings
+import sigmastate.serialization.{GroupElementSerializer, SigmaSerializer}
import special.collection.Coll
-import special.sigma.{Box, PreHeader, Header}
+import special.sigma.{Box, Header, PreHeader}
object ErgoLikeContextTesting {
/* NO HF PROOF:
@@ -46,7 +46,8 @@ object ErgoLikeContextTesting {
new ErgoLikeContext(
lastBlockUtxoRoot, noHeaders, dummyPreHeader(currentHeight, minerPubkey), noBoxes,
boxesToSpend, spendingTransaction, boxesToSpend.indexOf(self), extension, vs,
- ScriptCostLimit.value, initCost = 0L, activatedVersion)
+ DefaultEvalSettings.scriptCostLimitInEvaluator,
+ initCost = 0L, activatedVersion)
def apply(currentHeight: Height,
lastBlockUtxoRoot: AvlTreeData,
@@ -59,7 +60,7 @@ object ErgoLikeContextTesting {
new ErgoLikeContext(
lastBlockUtxoRoot, noHeaders, dummyPreHeader(currentHeight, minerPubkey),
dataBoxes, boxesToSpend, spendingTransaction, selfIndex, ContextExtension.empty,
- ValidationRules.currentSettings, ScriptCostLimit.value,
+ ValidationRules.currentSettings, DefaultEvalSettings.scriptCostLimitInEvaluator,
initCost = 0L, activatedVersion)
diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala
index aec7d4a723..2a3609a3fc 100644
--- a/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/helpers/ErgoLikeTestProvingInterpreter.scala
@@ -8,7 +8,7 @@ import sigmastate.interpreter.ProverInterpreter
class ErgoLikeTestProvingInterpreter
extends ErgoLikeTestInterpreter with ProverInterpreter {
- override lazy val secrets: Seq[SigmaProtocolPrivateInput[_, _]] = {
+ override lazy val secrets: Seq[SigmaProtocolPrivateInput[_]] = {
(1 to 4).map(_ => DLogProverInput.random()) ++
(1 to 4).map(_ => DiffieHellmanTupleProverInput.random())
}
diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/NegativeTesting.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/NegativeTesting.scala
index 158dc2b1bf..fb004c2302 100644
--- a/interpreter/shared/src/test/scala/sigmastate/helpers/NegativeTesting.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/helpers/NegativeTesting.scala
@@ -114,7 +114,7 @@ trait NegativeTesting extends Matchers {
def repeatAndReturnLast[A](nIters: Int)(block: => A): A = {
require(nIters > 0)
var res = block
- cfor(1)(_ < nIters, _ + 1) { i =>
+ cfor(1)(_ < nIters, _ + 1) { _ =>
res = block
}
res
diff --git a/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala b/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala
index 0ebac010ae..fa1b9f163c 100644
--- a/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/helpers/TestingHelpers.scala
@@ -1,20 +1,17 @@
package sigmastate.helpers
-import scorex.crypto.hash.Digest32
-import special.collection.{Coll, CollOverArray, PairOfCols}
-import scorex.util.ModifierId
-import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, ErgoLikeContext, ErgoLikeTransaction, ErgoLikeTransactionTemplate, Input, UnsignedInput}
-import sigmastate.Values.ErgoTree
import org.ergoplatform.ErgoBox.{AdditionalRegisters, Token, allZerosModifierId}
import org.ergoplatform.validation.SigmaValidationSettings
+import org.ergoplatform._
+import scorex.util.ModifierId
import sigmastate.AvlTreeData
-import sigmastate.eval.CostingSigmaDslBuilder
-import sigmastate.eval._
+import sigmastate.Values.ErgoTree
+import sigmastate.eval.{CostingSigmaDslBuilder, _}
import sigmastate.interpreter.ContextExtension
+import special.collection.{Coll, CollOverArray, PairOfCols}
import special.sigma.{Header, PreHeader}
import scala.collection.compat.immutable.ArraySeq
-import scala.collection.mutable.WrappedArray
// TODO refactor: unification is required between two hierarchies of tests
// and as part of it, more methods can be moved to TestingHelpers
diff --git a/interpreter/shared/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala b/interpreter/shared/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala
index 7015587e34..8fa3ad0304 100644
--- a/interpreter/shared/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/lang/SigmaBuilderTest.scala
@@ -135,7 +135,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val v = true
val c = BooleanConstant(v)
test[SBoolean.type](v, c)
- testArray[SBoolean.type](v, c) // TODO v6.0: arrays should not be liftable directly
+ testArray[SBoolean.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testColl[SBoolean.type](v, c)
}
@@ -144,7 +144,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val c = ByteConstant(v)
testNumeric[SByte.type](v, c)
testLiftingOfCAnyValue[SByte.type](v, c)
- testArray[SByte.type](v, c) // TODO v6.0: arrays should not be liftable directly
+ testArray[SByte.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testColl[SByte.type](v, c)
}
@@ -153,7 +153,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val c = ShortConstant(v)
testNumeric[SShort.type](v, c)
testLiftingOfCAnyValue[SShort.type](v, c)
- testArray[SShort.type](v, c) // TODO v6.0: arrays should not be liftable directly
+ testArray[SShort.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testColl[SShort.type](v, c)
}
@@ -161,7 +161,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val v = 1
val c = IntConstant(v)
test[SInt.type](v, c)
- testArray[SInt.type](v, c) // TODO v6.0: arrays should not be liftable directly
+ testArray[SInt.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testColl[SInt.type](v, c)
}
@@ -169,7 +169,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val v = 1L
val c = LongConstant(v)
test[SLong.type](v, c)
- testArray[SLong.type](v, c) // TODO v6.0: arrays should not be liftable directly
+ testArray[SLong.type](v, c) // TODO v6.0: arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testColl[SLong.type](v, c)
}
@@ -177,14 +177,14 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
val v = "abc"
val c = StringConstant(v)
test[SString.type](v, c)
- testArray[SString.type](v, c) // TODO v6.0: String should be liftable at all (not supported in ErgoTree)
+ testArray[SString.type](v, c) // TODO v6.0: String should be liftable at all (not supported in ErgoTree) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testColl[SString.type](v, c)
}
property("liftToConstant BigInteger") {
val v = BigInteger.valueOf(1L)
val c = BigIntConstant(v)
- testSuccess(v, c) // TODO v6.0: both BigInteger and arrays should not be liftable directly
+ testSuccess(v, c) // TODO v6.0: both BigInteger and arrays should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
val arr = Array.fill(10)(v)
testSuccess(arr, TransformingSigmaBuilder.mkCollectionConstant[SBigInt.type](arr.map(SigmaDsl.BigInt), c.tpe))
}
@@ -208,7 +208,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
property("liftToConstant ErgoBox") {
val v = TestData.b2.asInstanceOf[CostingBox].wrappedValue
val c = BoxConstant(TestData.b2)
- testSuccess(v, c) // TODO v6.0: ErgoBox should not be liftable directly
+ testSuccess(v, c) // TODO v6.0: ErgoBox should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testFailure(Array.fill(10)(v))
}
@@ -235,7 +235,7 @@ class SigmaBuilderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Ma
property("liftToConstant AvlTreeData") {
val v = TestData.t1.asInstanceOf[CAvlTree].wrappedValue
val c = AvlTreeConstant(SigmaDsl.avlTree(v))
- testSuccess(v, c) // TODO v6.0: AvlTreeData should not be liftable directly
+ testSuccess(v, c) // TODO v6.0: AvlTreeData should not be liftable directly (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/905)
testFailure(Array.fill(10)(v))
}
diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantSerializerSpecification.scala
index 8b261df880..42df295d53 100644
--- a/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantSerializerSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/serialization/ConstantSerializerSpecification.scala
@@ -1,12 +1,11 @@
package sigmastate.serialization
import java.math.BigInteger
-
import org.ergoplatform._
import org.scalacheck.Arbitrary._
import scalan.RType
import sigmastate.SCollection.SByteArray
-import sigmastate.Values.{LongConstant, FalseLeaf, Constant, SValue, TrueLeaf, BigIntConstant, GroupGenerator, ByteArrayConstant}
+import sigmastate.Values.{BigIntConstant, ByteArrayConstant, Constant, FalseLeaf, GroupGenerator, LongConstant, SValue, TrueLeaf}
import sigmastate.basics.CryptoConstants.EcPointType
import sigmastate._
import sigmastate.eval._
@@ -19,6 +18,8 @@ import scorex.util.encode.Base16
import sigmastate.exceptions.SerializerException
import sigmastate.lang.DeserializationSigmaBuilder
+import scala.annotation.nowarn
+
class ConstantSerializerSpecification extends TableSerializationSpecification {
private def testCollection[T <: SType](tpe: T) = {
@@ -47,8 +48,8 @@ class ConstantSerializerSpecification extends TableSerializationSpecification {
def testTuples[T <: SType](tpe: T) = {
implicit val wWrapped = wrappedTypeGen(tpe)
implicit val tT = Evaluation.stypeToRType(tpe)
- implicit val tag = tT.classTag
- implicit val tAny = RType.AnyType
+ @nowarn implicit val tag = tT.classTag
+ implicit val tAny: RType[Any] = RType.AnyType
forAll { in: (T#WrappedType, T#WrappedType) =>
val (x,y) = (in._1, in._2)
roundTripTest(Constant[SType]((x, y).asWrappedType, STuple(tpe, tpe)))
diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/DataSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/serialization/DataSerializerSpecification.scala
index 61f6fc85e3..d50f943d7a 100644
--- a/interpreter/shared/src/test/scala/sigmastate/serialization/DataSerializerSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/serialization/DataSerializerSpecification.scala
@@ -1,12 +1,11 @@
package sigmastate.serialization
import java.math.BigInteger
-
import org.ergoplatform.ErgoBox
import org.scalacheck.Arbitrary._
import scalan.RType
import sigmastate.SCollection.SByteArray
-import sigmastate.Values.{SigmaBoolean, ErgoTree}
+import sigmastate.Values.{ErgoTree, SigmaBoolean}
import sigmastate._
import sigmastate.eval.Evaluation
import sigmastate.eval._
@@ -14,12 +13,13 @@ import sigmastate.eval.Extensions._
import sigmastate.basics.CryptoConstants.EcPointType
import special.sigma.AvlTree
import SType.AnyOps
-import org.ergoplatform.SigmaConstants.ScriptCostLimit
import sigmastate.exceptions.SerializerException
import sigmastate.interpreter.{CostAccumulator, ErgoTreeEvaluator}
import sigmastate.interpreter.ErgoTreeEvaluator.DefaultProfiler
import sigmastate.utils.Helpers
+import scala.annotation.nowarn
+
class DataSerializerSpecification extends SerializationSpecification {
def roundtrip[T <: SType](obj: T#WrappedType, tpe: T) = {
@@ -30,13 +30,14 @@ class DataSerializerSpecification extends SerializationSpecification {
val res = DataSerializer.deserialize(tpe, r)
res shouldBe obj
+ val es = ErgoTreeEvaluator.DefaultEvalSettings
val accumulator = new CostAccumulator(
initialCost = JitCost(0),
- costLimit = Some(JitCost.fromBlockCost(ScriptCostLimit.value)))
+ costLimit = Some(JitCost.fromBlockCost(es.scriptCostLimitInEvaluator)))
val evaluator = new ErgoTreeEvaluator(
context = null,
constants = ErgoTree.EmptyConstants,
- coster = accumulator, DefaultProfiler, ErgoTreeEvaluator.DefaultEvalSettings)
+ coster = accumulator, DefaultProfiler, es)
val ok = DataValueComparer.equalDataValues(res, obj)(evaluator)
ok shouldBe true
@@ -76,8 +77,8 @@ class DataSerializerSpecification extends SerializationSpecification {
def testTuples[T <: SType](tpe: T) = {
implicit val wWrapped = wrappedTypeGen(tpe)
- implicit val tag = tpe.classTag[T#WrappedType]
- implicit val tAny = RType.AnyType
+ @nowarn implicit val tag = tpe.classTag[T#WrappedType]
+ implicit val tAny: RType[Any] = RType.AnyType
forAll { in: (T#WrappedType, T#WrappedType) =>
val (x,y) = (in._1, in._2)
roundtrip[SType]((x, y).asWrappedType, STuple(tpe, tpe))
diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala
index 6df3cc89d0..a66c56f2f2 100644
--- a/interpreter/shared/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/serialization/SigSerializerSpecification.scala
@@ -1,17 +1,17 @@
package sigmastate.serialization
import java.math.BigInteger
-import java.util
import org.ergoplatform.settings.ErgoAlgos
-import org.scalacheck.{Gen, Arbitrary}
+import org.scalacheck.{Arbitrary, Gen}
import org.scalatest.Assertion
import sigmastate.Values.SigmaBoolean
import sigmastate._
import sigmastate.basics.DLogProtocol.{ProveDlog, SecondDLogProverMessage}
import sigmastate.basics.VerifierMessage.Challenge
-import sigmastate.basics.{SecondDiffieHellmanTupleProverMessage, ProveDHTuple}
+import sigmastate.basics.{ProveDHTuple, SecondDHTupleProverMessage}
import sigmastate.crypto.GF2_192_Poly
-import sigmastate.helpers.{ErgoLikeTransactionTesting, ErgoLikeContextTesting, ContextEnrichingTestProvingInterpreter, TestingCommons}
+import sigmastate.eval.Extensions.ArrayOps
+import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTransactionTesting, TestingCommons}
import sigmastate.interpreter.Interpreter
import sigmastate.serialization.generators.ObjectGenerators
import sigmastate.utils.Helpers
@@ -54,7 +54,7 @@ class SigSerializerSpecification extends TestingCommons
// `firstMessageOpt` is not serialized
sch1.copy(commitmentOpt = None) == sch2
case (conj1: UncheckedConjecture, conj2: UncheckedConjecture) =>
- util.Arrays.equals(conj1.challenge, conj2.challenge) &&
+ conj1.challenge == conj2.challenge &&
conj1.children.zip(conj2.children).forall(t => isEquivalent(t._1, t._2))
case _ => false
}
@@ -147,7 +147,7 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("02e8e77123e300f8324e7b5c4cbe0f7ac616e0b78fc45f28f54fa6696231fc8ec3")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("c6429b70f4926a3ba1454f1aec116075f9e9fbe8a8f72114"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("c6429b70f4926a3ba1454f1aec116075f9e9fbe8a8f72114").toColl,
SecondDLogProverMessage(
BigInt("b277b8462a8b9098f5d4c934ab2876eb1b5707f3119e209bdbbad831e7cc4a41", 16)
)
@@ -172,8 +172,8 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("034132d4c7eb387f12ef40ba3ec03723bda0ee5707f7471185aafc316167e85137")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("9ec740b57353cb2f6035bb1a481b0066b2fdc0406a6fa67e"),
- SecondDiffieHellmanTupleProverMessage(
+ Challenge @@ ErgoAlgos.decodeUnsafe("9ec740b57353cb2f6035bb1a481b0066b2fdc0406a6fa67e").toColl,
+ SecondDHTupleProverMessage(
new BigInteger("bb2e6f44a38052b3f564fafcd477c4eb8cda1a8a553a4a5f38f1e1084d6a69f0", 16)
)
),
@@ -194,7 +194,7 @@ class SigSerializerSpecification extends TestingCommons
"a00b476899e583aefc18b237a7a70e73baace72aa533271a561d3432c347dcaec8975fdefb36389abe21656aadcfda0a0259681ce17bc47c9539ae1e7068292bb9646a9ffe4e11653495bd67588cfd6454d82cc455036e5b"
),
CAndUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("a00b476899e583aefc18b237a7a70e73baace72aa533271a"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("a00b476899e583aefc18b237a7a70e73baace72aa533271a").toColl,
List(
UncheckedSchnorr(
ProveDlog(
@@ -203,7 +203,7 @@ class SigSerializerSpecification extends TestingCommons
)
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("a00b476899e583aefc18b237a7a70e73baace72aa533271a"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("a00b476899e583aefc18b237a7a70e73baace72aa533271a").toColl,
SecondDLogProverMessage(
BigInt("561d3432c347dcaec8975fdefb36389abe21656aadcfda0a0259681ce17bc47c", 16)
)
@@ -215,7 +215,7 @@ class SigSerializerSpecification extends TestingCommons
)
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("a00b476899e583aefc18b237a7a70e73baace72aa533271a"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("a00b476899e583aefc18b237a7a70e73baace72aa533271a").toColl,
SecondDLogProverMessage(
BigInt("9539ae1e7068292bb9646a9ffe4e11653495bd67588cfd6454d82cc455036e5b", 16)
)
@@ -239,7 +239,7 @@ class SigSerializerSpecification extends TestingCommons
"c617e65a2ca62ac97bc33a33b76cb669622129ba0e094ad96287d97c2c6d6c8e48790d7c44961f7d958d59222ab4d7c814808a466a3e66e6f98e02d421757baa2842288b8d02787b5111db2e8924623790175e5bf27a2e4513e8eb196c22c8cf26a9d7b51cd7e386508db9c12b070d84"
),
COrUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("c617e65a2ca62ac97bc33a33b76cb669622129ba0e094ad9"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("c617e65a2ca62ac97bc33a33b76cb669622129ba0e094ad9").toColl,
List(
UncheckedSchnorr(
ProveDlog(
@@ -248,7 +248,7 @@ class SigSerializerSpecification extends TestingCommons
)
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("6287d97c2c6d6c8e48790d7c44961f7d958d59222ab4d7c8"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("6287d97c2c6d6c8e48790d7c44961f7d958d59222ab4d7c8").toColl,
SecondDLogProverMessage(
BigInt("14808a466a3e66e6f98e02d421757baa2842288b8d02787b5111db2e89246237", 16)
)
@@ -260,7 +260,7 @@ class SigSerializerSpecification extends TestingCommons
)
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("a4903f2600cb464733ba374ff3faa914f7ac709824bd9d11"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("a4903f2600cb464733ba374ff3faa914f7ac709824bd9d11").toColl,
SecondDLogProverMessage(
BigInt("90175e5bf27a2e4513e8eb196c22c8cf26a9d7b51cd7e386508db9c12b070d84", 16)
)
@@ -295,7 +295,7 @@ class SigSerializerSpecification extends TestingCommons
"96addfddcc197bdbacf5c0142fb16c39384b3699fa47da7dffd3149193b042fda134c0e208fefcb791379959ac6fc731adf47e32000fc75e2923dba482c843c7f6b684cbf2ceec5bfdf5fe6d13cabe5d15f8295ca4e8094fba3c4716bfdfc3c462417a79a61fcc487d6997a42739d533eebffa3b420a6e2e44616a1341e5baa1165c6c22e91a81addd97c3bd2fe40ecdbbda6f43bf71240da8dac878c044c16d42a4b34c536bbb1b"
),
COrUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("96addfddcc197bdbacf5c0142fb16c39384b3699fa47da7d"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("96addfddcc197bdbacf5c0142fb16c39384b3699fa47da7d").toColl,
List(
UncheckedDiffieHellmanTuple(
ProveDHTuple(
@@ -305,16 +305,16 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("03f17cefec3911966dc9952090325267a5cf7f9b0be76b02623021989d7f0007a2")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("ffd3149193b042fda134c0e208fefcb791379959ac6fc731"),
- SecondDiffieHellmanTupleProverMessage(new BigInteger("adf47e32000fc75e2923dba482c843c7f6b684cbf2ceec5bfdf5fe6d13cabe5d", 16))
+ Challenge @@ ErgoAlgos.decodeUnsafe("ffd3149193b042fda134c0e208fefcb791379959ac6fc731").toColl,
+ SecondDHTupleProverMessage(new BigInteger("adf47e32000fc75e2923dba482c843c7f6b684cbf2ceec5bfdf5fe6d13cabe5d", 16))
),
COrUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("697ecb4c5fa939260dc100f6274f908ea97cafc056281d4c"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("697ecb4c5fa939260dc100f6274f908ea97cafc056281d4c").toColl,
List(
UncheckedSchnorr(
ProveDlog(Helpers.decodeECPoint("03f997167c03aa234732e3a68126b371dffa1e409f62ca8fa18cea6acd1dbe54d5")),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("15f8295ca4e8094fba3c4716bfdfc3c462417a79a61fcc48"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("15f8295ca4e8094fba3c4716bfdfc3c462417a79a61fcc48").toColl,
SecondDLogProverMessage(BigInt("7d6997a42739d533eebffa3b420a6e2e44616a1341e5baa1165c6c22e91a81ad", 16))
),
UncheckedDiffieHellmanTuple(
@@ -325,8 +325,8 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("02fc58b939b105231da101540c87e56f5703460c179935aaee47137f3c367904f1")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("7c86e210fb413069b7fd47e09890534acb3dd5b9f037d104"),
- SecondDiffieHellmanTupleProverMessage(new BigInteger("dd97c3bd2fe40ecdbbda6f43bf71240da8dac878c044c16d42a4b34c536bbb1b", 16))
+ Challenge @@ ErgoAlgos.decodeUnsafe("7c86e210fb413069b7fd47e09890534acb3dd5b9f037d104").toColl,
+ SecondDHTupleProverMessage(new BigInteger("dd97c3bd2fe40ecdbbda6f43bf71240da8dac878c044c16d42a4b34c536bbb1b", 16))
)
)
)
@@ -370,15 +370,15 @@ class SigSerializerSpecification extends TestingCommons
"4fdc76711fd844de0831d8e90ebaf9c622117a062b2f8b63ff8b9c2a4eed345a11c697f6850cf3a38763d738539ad2d2e0a3e44384f23eee260931d88e1f5241a2600a7c98545ada675fd5e627e8e84f140fc95e28775cde52e71bb4d7b5ee2564553fac5b52202530fcbcdf205b7cca145202fb2a5bb181a890eb15536b08b747ea163f6b5d32a116fa9e1eb6b348fd82d3ebc11c125e5bc3f09c499aa0a8db14dc1780b4181f9bae5ed0f743f71b82b18784380814507d810cbef61ebc0b30e7f324083e2d3d08"
),
COrUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("4fdc76711fd844de0831d8e90ebaf9c622117a062b2f8b63"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("4fdc76711fd844de0831d8e90ebaf9c622117a062b2f8b63").toColl,
List(
CAndUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("ff8b9c2a4eed345a11c697f6850cf3a38763d738539ad2d2"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("ff8b9c2a4eed345a11c697f6850cf3a38763d738539ad2d2").toColl,
List(
UncheckedSchnorr(
ProveDlog(Helpers.decodeECPoint("0368c0d88d9eb2972bbfc23c961de6307f6a944352cbfe316f262401feabdaa87d")),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("ff8b9c2a4eed345a11c697f6850cf3a38763d738539ad2d2"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("ff8b9c2a4eed345a11c697f6850cf3a38763d738539ad2d2").toColl,
SecondDLogProverMessage(BigInt("e0a3e44384f23eee260931d88e1f5241a2600a7c98545ada675fd5e627e8e84f", 16))
),
UncheckedDiffieHellmanTuple(
@@ -389,13 +389,13 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("029d4ec275379f9212a53e15994aef203dcec43a177c0b1f40afcf592e5753ce67")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("ff8b9c2a4eed345a11c697f6850cf3a38763d738539ad2d2"),
- SecondDiffieHellmanTupleProverMessage(new BigInteger("140fc95e28775cde52e71bb4d7b5ee2564553fac5b52202530fcbcdf205b7cca", 16))
+ Challenge @@ ErgoAlgos.decodeUnsafe("ff8b9c2a4eed345a11c697f6850cf3a38763d738539ad2d2").toColl,
+ SecondDHTupleProverMessage(new BigInteger("140fc95e28775cde52e71bb4d7b5ee2564553fac5b52202530fcbcdf205b7cca", 16))
)
)
),
COrUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("b057ea5b5135708419f74f1f8bb60a65a572ad3e78b559b1"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("b057ea5b5135708419f74f1f8bb60a65a572ad3e78b559b1").toColl,
List(
UncheckedDiffieHellmanTuple(
ProveDHTuple(
@@ -405,8 +405,8 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("0315d84dba1b29074f766e57bb11843687da899180cf2487ccecd0a3ec5f05365a")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("145202fb2a5bb181a890eb15536b08b747ea163f6b5d32a1"),
- SecondDiffieHellmanTupleProverMessage(new BigInteger("16fa9e1eb6b348fd82d3ebc11c125e5bc3f09c499aa0a8db14dc1780b4181f9b", 16))
+ Challenge @@ ErgoAlgos.decodeUnsafe("145202fb2a5bb181a890eb15536b08b747ea163f6b5d32a1").toColl,
+ SecondDHTupleProverMessage(new BigInteger("16fa9e1eb6b348fd82d3ebc11c125e5bc3f09c499aa0a8db14dc1780b4181f9b", 16))
),
UncheckedDiffieHellmanTuple(
ProveDHTuple(
@@ -416,8 +416,8 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("0315d84dba1b29074f766e57bb11843687da899180cf2487ccecd0a3ec5f05365a")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("a405e8a07b6ec105b167a40ad8dd02d2e298bb0113e86b10"),
- SecondDiffieHellmanTupleProverMessage(new BigInteger("ae5ed0f743f71b82b18784380814507d810cbef61ebc0b30e7f324083e2d3d08", 16))
+ Challenge @@ ErgoAlgos.decodeUnsafe("a405e8a07b6ec105b167a40ad8dd02d2e298bb0113e86b10").toColl,
+ SecondDHTupleProverMessage(new BigInteger("ae5ed0f743f71b82b18784380814507d810cbef61ebc0b30e7f324083e2d3d08", 16))
)
)
)
@@ -448,12 +448,12 @@ class SigSerializerSpecification extends TestingCommons
"c94696c3e3089d9fd1174c18e6dd22f1be8003bbea08011fcf39310e7c9049c1c9966198b8d63a2f19e98843b81b74399f662dba4e764cd548406dd180453dd1bc0e24562f0184d189ca25a41ca8b54ada857dd649d3228a8c359ac499d430ecada3f92d5206cddeffb16248068c1003477d717e04afbf206c87a59ce5263ee7cc4020b5772d91b1df00bd72b15347fd"
),
CThresholdUncheckedNode(
- Challenge @@ ErgoAlgos.decodeUnsafe("c94696c3e3089d9fd1174c18e6dd22f1be8003bbea08011f"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("c94696c3e3089d9fd1174c18e6dd22f1be8003bbea08011f").toColl,
List(
UncheckedSchnorr(
ProveDlog(Helpers.decodeECPoint("03a5a5234701fff48be4ed1b3e1fab446657eeddb52e2573c52b9c4021f2403866")),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("067fa7cd9f98d45e18812d805e0b18dea7698bf852137526"),
+ Challenge @@ ErgoAlgos.decodeUnsafe("067fa7cd9f98d45e18812d805e0b18dea7698bf852137526").toColl,
SecondDLogProverMessage(BigInt("9f662dba4e764cd548406dd180453dd1bc0e24562f0184d189ca25a41ca8b54a", 16))
),
UncheckedDiffieHellmanTuple(
@@ -464,8 +464,8 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("02730455ebb8c01a89dced09c5253c9bfa4b1471d1068ba30ab226104a6551c461")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("5735f4df1b280e1d423a8f28977057af8c52123c9a3fe96d"),
- SecondDiffieHellmanTupleProverMessage(new BigInteger("da857dd649d3228a8c359ac499d430ecada3f92d5206cddeffb16248068c1003", 16))
+ Challenge @@ ErgoAlgos.decodeUnsafe("5735f4df1b280e1d423a8f28977057af8c52123c9a3fe96d").toColl,
+ SecondDHTupleProverMessage(new BigInteger("da857dd649d3228a8c359ac499d430ecada3f92d5206cddeffb16248068c1003", 16))
),
UncheckedDiffieHellmanTuple(
ProveDHTuple(
@@ -475,8 +475,8 @@ class SigSerializerSpecification extends TestingCommons
Helpers.decodeECPoint("03cefefa1511430ca2a873759107085f269f6fbcd4e836db7760749f52b7f7923a")
),
None,
- Challenge @@ ErgoAlgos.decodeUnsafe("980cc5d167b847dc8baceeb02fa66d8095bb9a7f22249d54"),
- SecondDiffieHellmanTupleProverMessage(new BigInteger("477d717e04afbf206c87a59ce5263ee7cc4020b5772d91b1df00bd72b15347fd", 16))
+ Challenge @@ ErgoAlgos.decodeUnsafe("980cc5d167b847dc8baceeb02fa66d8095bb9a7f22249d54").toColl,
+ SecondDHTupleProverMessage(new BigInteger("477d717e04afbf206c87a59ce5263ee7cc4020b5772d91b1df00bd72b15347fd", 16))
)
),
2,
@@ -491,7 +491,7 @@ class SigSerializerSpecification extends TestingCommons
)
)
- cases.zipWithIndex.foreach { case (c, iCase) =>
+ cases.zipWithIndex.foreach { case (c, _) =>
val sigBytes = SigSerializer.toProofBytes(c.uncheckedTree)
sigBytes shouldBe c.proof
val uncheckedTree = SigSerializer.parseAndComputeChallenges(c.prop, c.proof)(null)
@@ -525,7 +525,9 @@ class SigSerializerSpecification extends TestingCommons
r.position = 0
var reported = false
- val res = SigSerializer.readBytesChecked(r, nRequested, msg => reported = true)
+ val res = SigSerializer.readBytesChecked(r,
+ numRequestedBytes = nRequested,
+ onError = _ => reported = true)
res shouldBe bytes
reported shouldBe true
}
diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala b/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala
index 0e15cd525e..d566e416ba 100644
--- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/serialization/generators/ObjectGenerators.scala
@@ -27,6 +27,7 @@ import special.sigma._
import java.math.BigInteger
import scala.collection.compat.immutable.ArraySeq
+import scala.collection.mutable
import scala.collection.mutable.ListBuffer
import scala.reflect.ClassTag
@@ -209,7 +210,7 @@ trait ObjectGenerators extends TypeGenerators
lazy val evaluatedValueGen: Gen[EvaluatedValue[SType]] =
Gen.oneOf(booleanConstGen.asInstanceOf[Gen[EvaluatedValue[SType]]], byteArrayConstGen, longConstGen)
- def additionalRegistersGen(cnt: Byte): Seq[Gen[(NonMandatoryRegisterId, EvaluatedValue[SType])]] = {
+ def additionalRegistersGen(cnt: Byte): Seq[Gen[(NonMandatoryRegisterId, EvaluatedValue[_ <: SType])]] = {
scala.util.Random.shuffle((0 until cnt).toList)
.map(_ + ErgoBox.startingNonMandatoryIndex)
.map(rI => ErgoBox.registerByIndex(rI).asInstanceOf[NonMandatoryRegisterId])
@@ -237,8 +238,8 @@ trait ObjectGenerators extends TypeGenerators
val unsignedShortGen: Gen[Short] = Gen.chooseNum(0, Short.MaxValue).map(_.toShort)
lazy val contextExtensionGen: Gen[ContextExtension] = for {
- values <- Gen.sequence(contextExtensionValuesGen(0, 5))(Buildable.buildableSeq)
- } yield ContextExtension(values.toMap)
+ values: collection.Seq[(Byte, EvaluatedValue[SType])] <- Gen.sequence(contextExtensionValuesGen(0, 5))(Buildable.buildableSeq)
+ } yield ContextExtension(mutable.LinkedHashMap[Byte, EvaluatedValue[SType]](values.sortBy(_._1).toSeq:_*))
lazy val serializedProverResultGen: Gen[ProverResult] = for {
bytes <- arrayOfRange(1, 100, arbByte.arbitrary)
@@ -347,7 +348,8 @@ trait ObjectGenerators extends TypeGenerators
lazy val additionalRegistersGen: Gen[AdditionalRegisters] = for {
regNum <- Gen.chooseNum[Byte](0, ErgoBox.nonMandatoryRegistersCount)
regs <- Gen.sequence(additionalRegistersGen(regNum))(Buildable.buildableSeq)
- } yield regs.toMap
+ } yield
+ Map(regs.toIndexedSeq:_*)
def ergoBoxTokens(availableTokens: Seq[TokenId]): Gen[Coll[Token]] = for {
tokens <-
diff --git a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala b/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala
index c4c964cc74..2582d1305c 100644
--- a/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/serialization/generators/TypeGenerators.scala
@@ -5,24 +5,24 @@ import org.scalacheck.Arbitrary.arbString
import sigmastate._
trait TypeGenerators {
- implicit val booleanTypeGen = Gen.const(SBoolean)
- implicit val byteTypeGen = Gen.const(SByte)
- implicit val shortTypeGen = Gen.const(SShort)
- implicit val intTypeGen = Gen.const(SInt)
- implicit val longTypeGen = Gen.const(SLong)
- implicit val bigIntTypeGen = Gen.const(SBigInt)
- implicit val groupElementTypeGen = Gen.const(SGroupElement)
- implicit val sigmaPropTypeGen = Gen.const(SSigmaProp)
- implicit val boxTypeGen = Gen.const(SBox)
- implicit val avlTreeTypeGen = Gen.const(SAvlTree)
- implicit val optionSigmaPropTypeGen = Gen.const(SOption(SSigmaProp))
+ implicit val booleanTypeGen: Gen[SBoolean.type] = Gen.const(SBoolean)
+ implicit val byteTypeGen: Gen[SByte.type] = Gen.const(SByte)
+ implicit val shortTypeGen: Gen[SShort.type] = Gen.const(SShort)
+ implicit val intTypeGen: Gen[SInt.type] = Gen.const(SInt)
+ implicit val longTypeGen: Gen[SLong.type] = Gen.const(SLong)
+ implicit val bigIntTypeGen: Gen[SBigInt.type] = Gen.const(SBigInt)
+ implicit val groupElementTypeGen: Gen[SGroupElement.type] = Gen.const(SGroupElement)
+ implicit val sigmaPropTypeGen: Gen[SSigmaProp.type] = Gen.const(SSigmaProp)
+ implicit val boxTypeGen: Gen[SBox.type] = Gen.const(SBox)
+ implicit val avlTreeTypeGen: Gen[SAvlTree.type] = Gen.const(SAvlTree)
+ implicit val optionSigmaPropTypeGen: Gen[SOption[SSigmaProp.type]] = Gen.const(SOption(SSigmaProp))
implicit val primTypeGen: Gen[SPrimType] =
Gen.oneOf[SPrimType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnit)
- implicit val arbPrimType = Arbitrary(primTypeGen)
+ implicit val arbPrimType: Arbitrary[SPrimType] = Arbitrary(primTypeGen)
implicit val predefTypeGen: Gen[SPredefType] =
Gen.oneOf[SPredefType](SBoolean, SByte, SShort, SInt, SLong, SBigInt, SGroupElement, SSigmaProp, SUnit, SBox, SAvlTree)
- implicit val arbPredefType = Arbitrary(predefTypeGen)
+ implicit val arbPredefType: Arbitrary[SPredefType] = Arbitrary(predefTypeGen)
implicit def genToArbitrary[T: Gen]: Arbitrary[T] = Arbitrary(implicitly[Gen[T]])
diff --git a/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala b/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala
index c1accad043..3325035bbd 100644
--- a/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/utils/HelpersTests.scala
@@ -5,12 +5,16 @@ import Helpers._
import org.scalatest.matchers.should.Matchers
import org.scalatest.propspec.AnyPropSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
+import sigmastate.eval.Extensions.ArrayOps
class HelpersTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matchers with ObjectGenerators {
property("xorU") {
forAll(arrayGen[Byte]) { arr =>
val x = xor(arr, arr)
+ val xColl = xor(arr.toColl, arr.toColl)
+ x shouldBe xColl.toArray
+
val cloned = arr.clone()
xorU(cloned, arr)
cloned shouldBe x
@@ -18,7 +22,13 @@ class HelpersTests extends AnyPropSpec with ScalaCheckPropertyChecks with Matche
val arr1 = x
val arr2 = cloned
val arr3 = xor(arr1, arr2)
+ val arr3Coll = xor(arr1.toColl, arr2.toColl)
+ arr3 shouldBe arr3Coll.toArray
+
val res1 = xor(cloned, arr1, arr2, arr3)
+ val res1Coll = xor(cloned.toColl, arr1.toColl, arr2.toColl, arr3.toColl)
+ res1 shouldBe res1Coll.toArray
+
val res2 = cloned
xorU(res2, Seq(arr1, arr2, arr3))
diff --git a/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala b/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala
index 1ca5bb96a2..360754db6f 100644
--- a/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala
+++ b/interpreter/shared/src/test/scala/sigmastate/utxo/ProverSpecification.scala
@@ -5,7 +5,7 @@ import scorex.crypto.hash.Blake2b256
import sigmastate.Values.SigmaBoolean
import sigmastate._
import sigmastate.basics.DLogProtocol.FirstDLogProverMessage
-import sigmastate.basics.{FirstDiffieHellmanTupleProverMessage, SecP256K1Group}
+import sigmastate.basics.{FirstDHTupleProverMessage, SecP256K1Group}
import sigmastate.exceptions.InterpreterException
import sigmastate.helpers.{ErgoLikeTestProvingInterpreter, TestingCommons}
import sigmastate.interpreter.{HintsBag, ProverInterpreter}
@@ -52,7 +52,7 @@ class ProverSpecification extends TestingCommons {
h3.realCommitments.head.commitment shouldBe h3.ownCommitments.head.commitment
- h3.realCommitments.head.commitment.isInstanceOf[FirstDiffieHellmanTupleProverMessage] shouldBe true
+ h3.realCommitments.head.commitment.isInstanceOf[FirstDHTupleProverMessage] shouldBe true
}
property("setPositions - and") {
diff --git a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala
index 4c073d3dc9..a544361d98 100644
--- a/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala
+++ b/interpreter/shared/src/test/scala/special/sigma/ContractsTestkit.scala
@@ -7,7 +7,9 @@ import sigmastate.{AvlTreeData, Values}
import sigmastate.eval._
import sigmastate.eval.Extensions._
import sigmastate.helpers.TestingHelpers._
-import scalan._ // imports implicit ClassTag
+import scalan._
+
+import scala.annotation.nowarn // imports implicit ClassTag
trait ContractsTestkit {
val R0 = 0.toByte;
@@ -59,7 +61,7 @@ trait ContractsTestkit {
val AliceId = Array[Byte](1) // 0x0001
- def newAliceBox(id: Byte, value: Long): Box = {
+ def newAliceBox(@nowarn id: Byte, value: Long): Box = {
val ergoBox = testBox(value,
ErgoTree.fromProposition(Values.TrueSigmaProp),
creationHeight = 0, additionalTokens = Seq(), additionalRegisters = Map())
diff --git a/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala b/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala
index 3838ac87d2..d2b2fa819e 100644
--- a/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala
+++ b/interpreter/shared/src/test/scala/special/sigma/SigmaTestingData.scala
@@ -7,7 +7,7 @@ import org.scalacheck.Gen.containerOfN
import org.scalacheck.util.Buildable
import org.scalacheck.{Arbitrary, Gen}
import scalan.RType
-import scorex.crypto.authds.{ADDigest, ADKey, ADValue}
+import scorex.crypto.authds.{ADKey, ADValue}
import scorex.crypto.hash.{Blake2b256, Digest32}
import scorex.util.ModifierId
import sigmastate.Values.{ByteArrayConstant, ConcreteCollection, ConstantPlaceholder, ErgoTree, FalseLeaf, IntConstant, LongConstant, SigmaPropConstant, TrueLeaf}
@@ -43,11 +43,11 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators {
len <- Gen.choose(0, 100)
arr <- containerOfN[Array, Byte](len, Arbitrary.arbByte.arbitrary)
} yield arr
- val bytesCollGen = bytesGen.map(Colls.fromArray(_))
- val intsCollGen = arrayGen[Int].map(Colls.fromArray(_))
- implicit val arbBytes = Arbitrary(bytesCollGen)
- implicit val arbInts = Arbitrary(intsCollGen)
- val keyCollGen = collOfN[Byte](32, arbitrary[Byte])
+ val bytesCollGen: Gen[Coll[Byte]] = bytesGen.map(Colls.fromArray(_))
+ val intsCollGen: Gen[Coll[Int]] = arrayGen[Int].map(Colls.fromArray(_))
+ implicit val arbBytes: Arbitrary[Coll[Byte]] = Arbitrary(bytesCollGen)
+ implicit val arbInts: Arbitrary[Coll[Int]] = Arbitrary(intsCollGen)
+ val keyCollGen: Gen[Coll[Byte]] = collOfN[Byte](32, arbitrary[Byte])
import org.ergoplatform.dsl.AvlTreeHelpers._
def createAvlTreeAndProver(entries: (Coll[Byte], Coll[Byte])*) = {
@@ -132,7 +132,7 @@ trait SigmaTestingData extends TestingCommons with ObjectGenerators {
def createBigIntMaxValue(): BigInt = BigIntMaxValue_instances.getNext
- // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact)
+ // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
val BigIntMinValue = CBigInt(new BigInteger("-7F" + "ff" * 31, 16))
val BigIntMaxValue = createBigIntMaxValue()
diff --git a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala
index 8a45d4d66b..e4869e4190 100644
--- a/parsers/shared/src/main/scala/sigmastate/lang/Types.scala
+++ b/parsers/shared/src/main/scala/sigmastate/lang/Types.scala
@@ -3,7 +3,6 @@ package sigmastate.lang
import fastparse._
import ScalaWhitespace._
import sigmastate._
-import sigmastate.SCollection.SByteArray
import Values._
import sigmastate.lang.Terms.Ident
import sigmastate.lang.syntax.Core
diff --git a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala
index e28cd71473..42d02fefb5 100644
--- a/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala
+++ b/parsers/shared/src/test/scala/sigmastate/lang/SigmaParserTest.scala
@@ -1,7 +1,8 @@
package sigmastate.lang
import fastparse.Parsed
-import org.ergoplatform.{ErgoAddressEncoder, ErgoBox}
+import fastparse.Parsed.Failure
+import org.ergoplatform.ErgoBox
import org.scalatest.matchers.should.Matchers
import org.scalatest.propspec.AnyPropSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
@@ -25,9 +26,9 @@ class SigmaParserTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat
v.sourceContext.isDefined shouldBe true
assertSrcCtxForAllNodes(v)
v
- case f@Parsed.Failure(_, _, extra) =>
- val traced = extra.traced
- println(s"\nTRACE: ${traced.trace}")
+ case f: Failure =>
+ val traced = f.extra.trace()
+ println(s"\nTRACE: ${traced.msg}")
f.get // force show error diagnostics
}
}
diff --git a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala
index 085ad468f9..a992dbf0d7 100644
--- a/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala
+++ b/sc/shared/src/main/scala/sigmastate/eval/GraphBuilding.scala
@@ -1,7 +1,6 @@
package sigmastate.eval
import org.ergoplatform._
-import org.ergoplatform.validation.ValidationRules.CheckTupleType
import scalan.ExactIntegral.{ByteIsExactIntegral, IntIsExactIntegral, LongIsExactIntegral, ShortIsExactIntegral}
import scalan.ExactOrdering.{ByteIsExactOrdering, IntIsExactOrdering, LongIsExactOrdering, ShortIsExactOrdering}
import scalan.util.Extensions.ByteOps
@@ -15,7 +14,7 @@ import sigmastate.serialization.OpCodes
import sigmastate.utxo._
import sigmastate._
import sigmastate.basics.CryptoConstants.EcPointType
-import sigmastate.exceptions.{SigmaException, CosterException}
+import sigmastate.exceptions.{SigmaException, GraphBuildingException}
import scala.collection.mutable.ArrayBuffer
/** Perform translation of typed expression given by [[Value]] to a graph in IRContext.
@@ -396,8 +395,8 @@ trait GraphBuilding extends SigmaLibrary { IR: IRContext =>
protected implicit def groupElementToECPoint(g: special.sigma.GroupElement): EcPointType = CostingSigmaDslBuilder.toECPoint(g).asInstanceOf[EcPointType]
- def error(msg: String) = throw new CosterException(msg, None)
- def error(msg: String, srcCtx: Option[SourceContext]) = throw new CosterException(msg, srcCtx)
+ def error(msg: String) = throw new GraphBuildingException(msg, None)
+ def error(msg: String, srcCtx: Option[SourceContext]) = throw new GraphBuildingException(msg, srcCtx)
/** Translates the given typed expression to IR graph representing a function from
* Context to some type T.
diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala b/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala
index 07616af0c4..f30b25b44c 100644
--- a/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala
+++ b/sc/shared/src/main/scala/sigmastate/lang/SigmaBinder.scala
@@ -48,7 +48,6 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder,
case "SELF" => Some(Self)
case "CONTEXT" => Some(Context)
case "Global" => Some(Global)
- case "None" => Some(mkNoneValue(NoType))
case _ => None
}
}
@@ -62,12 +61,6 @@ class SigmaBinder(env: ScriptEnv, builder: SigmaBuilder,
val tpe = if (args.isEmpty) NoType else args(0).tpe
Some(mkConcreteCollection(args, tpe))
- // Rule: Some(x) -->
- case Apply(i @ Ident("Some", _), args) => args match {
- case Seq(arg) => Some(mkSomeValue(arg))
- case _ => error(s"Invalid arguments of Some: expected one argument but found $args", i.sourceContext)
- }
-
// Rule: min(x, y) -->
case Apply(i @ Ident("min", _), args) => args match {
case Seq(l: SValue, r: SValue) =>
diff --git a/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala b/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala
index ad4dddcf51..ccc0784abe 100644
--- a/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala
+++ b/sc/shared/src/main/scala/sigmastate/lang/SigmaTyper.scala
@@ -497,9 +497,6 @@ class SigmaTyper(val builder: SigmaBuilder,
case Negation(i) => unmap[SNumericType](env, "-", i.asNumValue)(mkNegation)(tT)
case BitInversion(i) => unmap[SNumericType](env, "~", i.asNumValue)(mkBitInversion)(tT)
- case SomeValue(x) => SomeValue(assignType(env, x))
- case v: NoneValue[_] => v
-
case Global => Global
case Context => Context
case Height => Height
diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala
index 6b3c219aaa..22359c1606 100644
--- a/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala
+++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoAddressSpecification.scala
@@ -1,7 +1,6 @@
package org.ergoplatform
import org.ergoplatform.ErgoAddressEncoder.{MainnetNetworkPrefix, TestnetNetworkPrefix, hash256}
-import org.ergoplatform.SigmaConstants.ScriptCostLimit
import org.ergoplatform.validation.{ValidationException, ValidationRules}
import org.scalatest.{Assertion, TryValues}
import scorex.crypto.hash.Blake2b256
@@ -260,7 +259,7 @@ class ErgoAddressSpecification extends SigmaDslTesting
property("negative cases: deserialized script + costing exceptions") {
implicit lazy val IR = new TestingIRContext
- def testPay2SHAddress(address: Pay2SHAddress, script: VarBinding, costLimit: Int = ScriptCostLimit.value): CostedProverResult = {
+ def testPay2SHAddress(address: Pay2SHAddress, script: VarBinding, costLimit: Int = scriptCostLimitInTests): CostedProverResult = {
val boxToSpend = testBox(10, address.script, creationHeight = 5)
val ctx = copyContext(ErgoLikeContextTesting.dummy(boxToSpend, activatedVersionInTests)
.withExtension(ContextExtension(Seq(
diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala
index c0c926c9a2..25c01af43d 100644
--- a/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala
+++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoLikeTransactionSpec.scala
@@ -100,6 +100,7 @@ class ErgoLikeTransactionSpec extends SigmaDslTesting {
).map(identity).toConstant
// TODO v6.0 (16h): fix collections equality and remove map(identity)
// (PairOfColl should be equal CollOverArray but now it is not)
+ // see (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/909)
res shouldBe exp
}
diff --git a/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala
index 36c5e78067..89247a968a 100644
--- a/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala
+++ b/sc/shared/src/test/scala/org/ergoplatform/ErgoTreePredefSpec.scala
@@ -226,7 +226,7 @@ class ErgoTreePredefSpec extends CompilerTestingCommons with CompilerCrossVersio
boxesToSpend = inputBoxes,
spendingTransaction,
self = inputBoxes.head,
- activatedVersionInTests).withCostLimit(SigmaConstants.ScriptCostLimit.value * 10)
+ activatedVersionInTests).withCostLimit(scriptCostLimitInTests * 10)
val pr = prover.prove(emptyEnv + (ScriptNameProp -> "tokenThresholdScript_prove"), prop, ctx, fakeMessage).getOrThrow
verifier.verify(emptyEnv + (ScriptNameProp -> "tokenThresholdScript_verify"), prop, ctx, pr, fakeMessage).getOrThrow._1 shouldBe true
diff --git a/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala
index 711827fe1e..9909f485b5 100644
--- a/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala
+++ b/sc/shared/src/test/scala/org/ergoplatform/validation/RuleStatusSerializerSpec.scala
@@ -1,13 +1,12 @@
package org.ergoplatform.validation
-import org.scalatest.Assertion
import sigmastate.helpers.CompilerTestingCommons
import sigmastate.serialization.{SigmaSerializer, SerializationSpecification}
class RuleStatusSerializerSpec extends SerializationSpecification with CompilerTestingCommons {
- private def roundtrip(status: RuleStatus): Assertion = {
- implicit val ser = RuleStatusSerializer
+ private def roundtrip(status: RuleStatus) = {
+ implicit val ser: RuleStatusSerializer.type = RuleStatusSerializer
roundTripTest(status)
roundTripTestWithPos(status)
}
diff --git a/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala b/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala
index b74d4014f2..c4162103c6 100644
--- a/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala
+++ b/sc/shared/src/test/scala/org/ergoplatform/validation/SigmaValidationSettingsSerializerSpec.scala
@@ -1,26 +1,34 @@
package org.ergoplatform.validation
+import org.ergoplatform.validation.ValidationRules.{FirstRuleId, currentSettings}
import org.scalatest.Assertion
import sigmastate.helpers.CompilerTestingCommons
import sigmastate.serialization.SerializationSpecification
class SigmaValidationSettingsSerializerSpec extends SerializationSpecification with CompilerTestingCommons {
- private def roundtrip(settings: SigmaValidationSettings): Assertion = {
- implicit val set = SigmaValidationSettingsSerializer
+ private def roundtrip(settings: SigmaValidationSettings) = {
+ implicit val set: SigmaValidationSettingsSerializer.type = SigmaValidationSettingsSerializer
roundTripTest(settings)
roundTripTestWithPos(settings)
}
property("ValidationRules.currentSettings round trip") {
- roundtrip(ValidationRules.currentSettings)
+ roundtrip(currentSettings)
}
property("SigmaValidationSettings round trip") {
forAll(ruleIdGen, statusGen, MinSuccessful(100)) { (ruleId, status) =>
- val vs = ValidationRules.currentSettings.updated(ruleId, status)
+ val vs = currentSettings.updated(ruleId, status)
roundtrip(vs)
}
}
+ property("SigmaValidationSettings equality") {
+ val vs = currentSettings
+ val vs_copy = currentSettings.updated(FirstRuleId, currentSettings.getStatus(FirstRuleId).get)
+ val vs2 = currentSettings.updated(FirstRuleId, DisabledRule)
+ vs.equals(vs2) shouldBe false
+ vs.equals(vs_copy) shouldBe true
+ }
}
\ No newline at end of file
diff --git a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
index 20aabb1b00..7bffa895f8 100644
--- a/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/ErgoTreeSpecification.scala
@@ -256,7 +256,7 @@ class ErgoTreeSpecification extends SigmaDslTesting with ContractsTestkit {
{ import SSigmaProp._
(SSigmaProp.typeId, Seq(
MInfo(1, PropBytesMethod),
- MInfo(2, IsProvenMethod) // TODO v5.x (3h): this method must be removed
+ MInfo(2, IsProvenMethod) // TODO v5.x (3h): this method must be removed (see https://github.com/ScorexFoundation/sigmastate-interpreter/pull/800)
), true)
},
{ import SBox._
diff --git a/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala b/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala
index 8981ace5df..588dfe4f96 100644
--- a/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala
+++ b/sc/shared/src/test/scala/sigmastate/helpers/CompilerTestingCommons.scala
@@ -1,6 +1,5 @@
package sigmastate.helpers
-import org.ergoplatform.SigmaConstants.ScriptCostLimit
import org.ergoplatform._
import org.ergoplatform.validation.ValidationRules.CheckSerializableTypeCode
import org.ergoplatform.validation.{ValidationException, ValidationSpecification}
@@ -137,7 +136,7 @@ trait CompilerTestingCommons extends TestingCommons
val sigmaCtx = createContexts(in, bindings)
val accumulator = new CostAccumulator(
initialCost = JitCost(0),
- costLimit = Some(JitCost.fromBlockCost(ScriptCostLimit.value)))
+ costLimit = Some(JitCost.fromBlockCost(evalSettings.scriptCostLimitInEvaluator)))
val evaluator = new ErgoTreeEvaluator(
context = sigmaCtx,
constants = ErgoTree.EmptyConstants,
@@ -175,22 +174,26 @@ trait CompilerTestingCommons extends TestingCommons
funcJitFromExpr(funcScript, compiledTree, bindings:_*)
}
- protected def roundTripTest[T](v: T)(implicit serializer: SigmaSerializer[T, T]): Assertion = {
+ protected def roundTripTest[T](v: T)(implicit serializer: SigmaSerializer[T, T]): T = {
// using default sigma reader/writer
val bytes = serializer.toBytes(v)
bytes.nonEmpty shouldBe true
val r = SigmaSerializer.startReader(bytes)
val positionLimitBefore = r.positionLimit
- serializer.parse(r) shouldBe v
+ val parsed = serializer.parse(r)
+ parsed shouldBe v
r.positionLimit shouldBe positionLimitBefore
+ parsed
}
- protected def roundTripTestWithPos[T](v: T)(implicit serializer: SigmaSerializer[T, T]): Assertion = {
+ protected def roundTripTestWithPos[T](v: T)(implicit serializer: SigmaSerializer[T, T]): T = {
val randomBytesCount = Gen.chooseNum(1, 20).sample.get
val randomBytes = Gen.listOfN(randomBytesCount, arbByte.arbitrary).sample.get.toArray
val bytes = serializer.toBytes(v)
- serializer.parse(SigmaSerializer.startReader(bytes)) shouldBe v
+ val parsed = serializer.parse(SigmaSerializer.startReader(bytes))
+ parsed shouldBe v
serializer.parse(SigmaSerializer.startReader(randomBytes ++ bytes, randomBytesCount)) shouldBe v
+ parsed
}
def testReduce(I: Interpreter)(ctx: I.CTX, prop: SigmaPropValue): SigmaBoolean = {
diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala
index b8aefc1041..397c5c5e5d 100644
--- a/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala
+++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaBinderTest.scala
@@ -143,19 +143,6 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat
If(EQ(IntConstant(10), IntConstant(11)), IntConstant(2), IntConstant(3)))
}
- // TODO v6.0 (4h): SomeValue and NoneValue are not used in ErgoTree and can be
- // either removed or implemented in v4.x
- property("Option constructors") {
- bind(env, "None") shouldBe NoneValue(NoType)
- bind(env, "Some(None)") shouldBe SomeValue(NoneValue(NoType))
- bind(env, "Some(10)") shouldBe SomeValue(IntConstant(10))
- bind(env, "Some(X)") shouldBe SomeValue(Ident("X"))
- bind(env, "Some(Some(X - 1))") shouldBe
- SomeValue(SomeValue(mkMinus(Ident("X").asValue[SInt.type], IntConstant(1))))
- bind(env, "Some(Some(X + 1))") shouldBe
- SomeValue(SomeValue(plus(Ident("X").asValue[SInt.type], IntConstant(1))))
- }
-
property("lambdas") {
bind(env, "{ (a: Int) => a - 1 }") shouldBe
Lambda(IndexedSeq("a" -> SInt), NoType, mkMinus(IntIdent("a"), 1))
@@ -214,8 +201,4 @@ class SigmaBinderTest extends AnyPropSpec with ScalaCheckPropertyChecks with Mat
e.source shouldBe Some(SourceContext(2, 5, "val x = 10"))
}
- property("fail Some (invalid arguments)") {
- fail(env, "Some(1, 2)", 1, 1)
- fail(env, "Some()", 1, 1)
- }
}
diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala
index 96a430a51d..f6266cf285 100644
--- a/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala
+++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaCompilerTest.scala
@@ -8,7 +8,7 @@ import sigmastate._
import sigmastate.helpers.CompilerTestingCommons
import sigmastate.interpreter.Interpreter.ScriptEnv
import sigmastate.lang.Terms.{Apply, Ident, Lambda, MethodCall, ZKProofBlock}
-import sigmastate.exceptions.{CosterException, InvalidArguments, TyperException}
+import sigmastate.exceptions.{GraphBuildingException, InvalidArguments, TyperException}
import sigmastate.serialization.ValueSerializer
import sigmastate.serialization.generators.ObjectGenerators
import sigmastate.utxo.{ByIndex, ExtractAmount, GetVar, SelectField}
@@ -23,15 +23,15 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec
private def comp(x: String): Value[SType] = compile(env, x)
private def testMissingCosting(script: String, expected: SValue): Unit = {
- an [CosterException] should be thrownBy comp(env, script)
+ an [GraphBuildingException] should be thrownBy comp(env, script)
}
private def testMissingCostingWOSerialization(script: String, expected: SValue): Unit = {
- an [CosterException] should be thrownBy comp(env, script)
+ an [GraphBuildingException] should be thrownBy comp(env, script)
}
private def costerFail(env: ScriptEnv, x: String, expectedLine: Int, expectedCol: Int): Unit = {
- val exception = the[CosterException] thrownBy comp(env, x)
+ val exception = the[GraphBuildingException] thrownBy comp(env, x)
withClue(s"Exception: $exception, is missing source context:") { exception.source shouldBe defined }
val sourceContext = exception.source.get
sourceContext.line shouldBe expectedLine
@@ -320,11 +320,6 @@ class SigmaCompilerTest extends CompilerTestingCommons with LangTests with Objec
)
}
- property("failed option constructors (not supported)") {
- costerFail("None", 1, 1)
- costerFail("Some(10)", 1, 1)
- }
-
property("byteArrayToLong") {
comp("byteArrayToLong(longToByteArray(1L))") shouldBe ByteArrayToLong(LongToByteArray(LongConstant(1)))
}
diff --git a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala
index 22c2a1fd7a..85476c3c6d 100644
--- a/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala
+++ b/sc/shared/src/test/scala/sigmastate/lang/SigmaTyperTest.scala
@@ -214,13 +214,6 @@ class SigmaTyperTest extends AnyPropSpec
typefail(env, "Coll(1, false)", 1, 1)
}
- property("Option constructors") {
- typecheck(env, "Some(10)") shouldBe SOption(SInt)
- typecheck(env, "Some(x)") shouldBe SOption(SInt)
- typecheck(env, "Some(x + 1)") shouldBe SOption(SInt)
- typecheck(env, "Some(Some(10))") shouldBe SOption(SOption(SInt))
- }
-
property("methods returning Option") {
typecheck(env, "getVar[Int](10)") shouldBe SOption(SInt)
typecheck(env, "{ val v = getVar[Int](1); v.get }") shouldBe SInt
diff --git a/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala b/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala
index 54ee4f4857..bfba524baf 100644
--- a/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala
+++ b/sc/shared/src/test/scala/sigmastate/utxo/SerializationRoundTripSpec.scala
@@ -55,8 +55,14 @@ class SerializationRoundTripSpec extends AnyPropSpec
}
property("ErgoBox: Serializer round trip") {
- forAll { t: ErgoBox => roundTripTest(t)(ErgoBox.sigmaSerializer) }
- forAll { t: ErgoBox => roundTripTestWithPos(t)(ErgoBox.sigmaSerializer) }
+ forAll { t: ErgoBox =>
+ val parsed = roundTripTest(t)(ErgoBox.sigmaSerializer)
+ parsed.bytes shouldBe t.bytes
+ }
+ forAll { t: ErgoBox =>
+ val parsed = roundTripTestWithPos(t)(ErgoBox.sigmaSerializer)
+ parsed.bytes shouldBe t.bytes
+ }
}
property("ContextExtension: Serializer round trip") {
diff --git a/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala b/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala
index f2f797a832..ce442ecb50 100644
--- a/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala
+++ b/sc/shared/src/test/scala/sigmastate/utxo/ThresholdSpecification.scala
@@ -4,7 +4,7 @@ import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog}
import sigmastate.Values.{ConcreteCollection, FalseLeaf, IntConstant, SigmaPropConstant, SigmaPropValue, TrueLeaf}
import sigmastate._
import sigmastate.helpers.{ContextEnrichingTestProvingInterpreter, ErgoLikeContextTesting, ErgoLikeTestInterpreter, ErgoLikeTransactionTesting, CompilerTestingCommons}
-import sigmastate.exceptions.CosterException
+import sigmastate.exceptions.GraphBuildingException
class ThresholdSpecification extends CompilerTestingCommons
with CompilerCrossVersionProps {
@@ -407,8 +407,8 @@ class ThresholdSpecification extends CompilerTestingCommons
val keyName = "pubkeyA"
val env = Map(keyName -> pubkeyA)
val pubKeysStrExceeding = Array.fill[String](AtLeast.MaxChildrenCount + 1)(keyName).mkString(",")
- an[CosterException] should be thrownBy compile(env, s"""atLeast(2, Coll($pubKeysStrExceeding))""")
- an[CosterException] should be thrownBy
+ an[GraphBuildingException] should be thrownBy compile(env, s"""atLeast(2, Coll($pubKeysStrExceeding))""")
+ an[GraphBuildingException] should be thrownBy
compile(env, s"""{ val arr = Coll($pubKeysStrExceeding); atLeast(2, arr) }""")
// max children should work fine
diff --git a/sc/shared/src/test/scala/special/sigma/DataValueComparerSpecification.scala b/sc/shared/src/test/scala/special/sigma/DataValueComparerSpecification.scala
index b264d52144..eaca756182 100644
--- a/sc/shared/src/test/scala/special/sigma/DataValueComparerSpecification.scala
+++ b/sc/shared/src/test/scala/special/sigma/DataValueComparerSpecification.scala
@@ -1,6 +1,5 @@
package special.sigma
-import org.ergoplatform.SigmaConstants.ScriptCostLimit
import org.scalatest.BeforeAndAfterAll
import scalan.RType
import scalan.util.BenchmarkUtil
@@ -23,7 +22,8 @@ class DataValueComparerSpecification extends SigmaDslTesting
isMeasureOperationTime = true,
isMeasureScriptTime = true,
isLogEnabled = false, // don't commit the `true` value (CI log is too high)
- costTracingEnabled = true // should always be enabled in tests (and false by default)
+ costTracingEnabled = true, // should always be enabled in tests (and false by default)
+ scriptCostLimitInEvaluator = scriptCostLimitInTests
)
override val nBenchmarkIters = 10
@@ -36,7 +36,7 @@ class DataValueComparerSpecification extends SigmaDslTesting
def createEvaluator(settings: EvalSettings, profiler: Profiler): ErgoTreeEvaluator = {
val accumulator = new CostAccumulator(
initialCost = JitCost(0),
- costLimit = Some(JitCost.fromBlockCost(ScriptCostLimit.value)))
+ costLimit = Some(JitCost.fromBlockCost(settings.scriptCostLimitInEvaluator)))
val evaluator = new ErgoTreeEvaluator(
context = null,
constants = ErgoTree.EmptyConstants,
diff --git a/sc/shared/src/test/scala/special/sigma/SigmaDslSpecification.scala b/sc/shared/src/test/scala/special/sigma/SigmaDslSpecification.scala
index 07984e81f4..ead1af1873 100644
--- a/sc/shared/src/test/scala/special/sigma/SigmaDslSpecification.scala
+++ b/sc/shared/src/test/scala/special/sigma/SigmaDslSpecification.scala
@@ -39,7 +39,7 @@ import sigmastate.basics.ProveDHTuple
import sigmastate.interpreter._
import org.scalactic.source.Position
import sigmastate.helpers.SigmaPPrint
-import sigmastate.exceptions.CosterException
+import sigmastate.exceptions.GraphBuildingException
import scala.collection.compat.immutable.ArraySeq
/** This suite tests every method of every SigmaDsl type to be equivalent to
@@ -2202,7 +2202,7 @@ class SigmaDslSpecification extends SigmaDslTesting
(BigIntMaxValue, BigIntMinValue) -> expect(false),
(BigIntMaxValue, -47.toBigInt) -> expect(false),
(BigIntMaxValue, BigIntMaxValue) -> expect(false),
- (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases
+ (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
(BigIntOverlimit, BigIntOverlimit) -> expect(false)
)
@@ -2219,7 +2219,7 @@ class SigmaDslSpecification extends SigmaDslTesting
property("BigInt LE, GE") {
val o = NumericOps.BigIntIsExactOrdering
- // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact)
+ // TODO v6.0: this values have bitCount == 255 (see to256BitValueExact) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
val BigIntMinValue = CBigInt(new BigInteger("-7F" + "ff" * 31, 16))
val BigIntMaxValue = CBigInt(new BigInteger("7F" + "ff" * 31, 16))
val BigIntOverlimit = CBigInt(new BigInteger("7F" + "ff" * 33, 16))
@@ -2262,7 +2262,7 @@ class SigmaDslSpecification extends SigmaDslTesting
(BigIntMaxValue, BigIntMinValue) -> expect(false),
(BigIntMaxValue, -47.toBigInt) -> expect(false),
(BigIntMaxValue, BigIntMaxValue) -> expect(true),
- (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases
+ (BigIntMaxValue, BigIntOverlimit) -> expect(true), // TODO v6.0: reject this overlimit cases (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/554)
(BigIntOverlimit, BigIntOverlimit) -> expect(true)
)
@@ -2274,7 +2274,7 @@ class SigmaDslSpecification extends SigmaDslTesting
}
property("BigInt methods equivalence (new features)") {
- // TODO v6.0 (2h): the behavior of `upcast` for BigInt is different from all other Numeric types
+ // TODO v6.0: the behavior of `upcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877)
// The `Upcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree.
// It makes sense to fix this inconsistency as part of upcoming forks
assertExceptionThrown(
@@ -2282,7 +2282,7 @@ class SigmaDslSpecification extends SigmaDslTesting
_.getMessage.contains("Cannot upcast value")
)
- // TODO v6.0 (2h): the behavior of `downcast` for BigInt is different from all other Numeric types
+ // TODO v6.0: the behavior of `downcast` for BigInt is different from all other Numeric types (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/877)
// The `Downcast(bigInt, SBigInt)` node is never produced by ErgoScript compiler, but is still valid ErgoTree.
// It makes sense to fix this inconsistency as part of HF
assertExceptionThrown(
@@ -2919,12 +2919,6 @@ class SigmaDslSpecification extends SigmaDslTesting
))
}
- // TODO v6.0 (3h): related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479
- // property("GroupElement.isIdentity equivalence") {
- // // val isIdentity = existingFeature({ (x: GroupElement) => x.isIdentity },
- // // "{ (x: GroupElement) => x.isIdentity }")
- // }
-
property("AvlTree properties equivalence") {
def expectedExprFor(propName: String) = {
FuncValue(
@@ -3555,7 +3549,7 @@ class SigmaDslSpecification extends SigmaDslTesting
val invalidKvs = Colls.fromItems((invalidKey -> value)) // NOTE, insertProof is based on `key`
val input = (tree, (invalidKvs, insertProof))
val (res, _) = insert.checkEquality(input).getOrThrow
- res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug)
+ res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908)
insert.checkExpected(input, Expected(Success(res), 1796, costDetails2, 1796))
}
@@ -3717,7 +3711,7 @@ class SigmaDslSpecification extends SigmaDslTesting
val invalidKvs = Colls.fromItems((key -> invalidValue))
val input = (tree, (invalidKvs, updateProof))
val (res, _) = update.checkEquality(input).getOrThrow
- res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug)
+ res.isDefined shouldBe true // TODO v6.0: should it really be true? (looks like a bug) (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/908)
update.checkExpected(input, Expected(Success(res), 1805, costDetails2, 1805))
}
@@ -4044,7 +4038,7 @@ class SigmaDslSpecification extends SigmaDslTesting
"{ (x: Box) => x.creationInfo }",
FuncValue(Vector((1, SBox)), ExtractCreationInfo(ValUse(1, SBox)))))
- // TODO v6.0 (2h): fix collections equality and remove map(identity)
+ // TODO v6.0: fix collections equality and remove map(identity)(see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/909)
// (PairOfColl should be equal CollOverArray)
verifyCases(
Seq(
@@ -4068,7 +4062,7 @@ class SigmaDslSpecification extends SigmaDslTesting
}
property("Box properties equivalence (new features)") {
- // TODO v6.0 (4h): related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416
+ // TODO v6.0: related to https://github.com/ScorexFoundation/sigmastate-interpreter/issues/416
val getReg = newFeature((x: Box) => x.getReg[Int](1).get,
"{ (x: Box) => x.getReg[Int](1).get }")
@@ -4344,7 +4338,7 @@ class SigmaDslSpecification extends SigmaDslTesting
val box3 = SigmaDsl.Box(testBox(20, TrueTree, 0, Seq(), Map(
ErgoBox.R4 -> Constant((10, 20L).asInstanceOf[SType#WrappedType], STuple(SInt, SLong))
- // TODO v6.0 (1h): uncomment after DataSerializer support of Option type
+ // TODO v6.0: uncomment after DataSerializer support of Option type (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659)
// ErgoBox.R5 -> Constant((10, Some(20L)).asInstanceOf[SType#WrappedType], STuple(SInt, SOption(SLong)))
// ErgoBox.R6 -> Constant[SOption[SInt.type]](Option(10), SOption(SInt)),
)))
@@ -4448,7 +4442,7 @@ class SigmaDslSpecification extends SigmaDslTesting
)))
- // TODO v6.0 (1h): uncomment after DataSerializer support of Option type
+ // TODO v6.0: uncomment after DataSerializer support of Option type (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659)
// verifyCases(
// Seq(
// (box3, Expected(Success(10), cost = 36468))//, expCostDetails, 1790))
@@ -5164,7 +5158,7 @@ class SigmaDslSpecification extends SigmaDslTesting
existingPropTest("minerPubKey", { (x: Context) => x.minerPubKey }),
preGeneratedSamples = Some(samples))
-// TODO v6.0 (2h): implement support of Option[T] in DataSerializer
+// TODO v6.0: implement support of Option[T] in DataSerializer (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/659)
// this will allow passing optional values in registers and also in constants
// testCases2(
// Seq(
@@ -9095,7 +9089,7 @@ class SigmaDslSpecification extends SigmaDslTesting
) ))
}
- // TODO v6.0 (3h): implement Option.fold
+ // TODO v6.0: implement Option.fold (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/479)
property("Option new methods") {
val n = ExactNumeric.LongIsExactNumeric
val fold = newFeature({ (x: Option[Long]) => x.fold(5.toLong)( (v: Long) => n.plus(v, 1) ) },
@@ -10032,7 +10026,7 @@ class SigmaDslSpecification extends SigmaDslTesting
|""".stripMargin
)
- // TODO v6.0: Add support of SFunc in TypeSerializer
+ // TODO v6.0: Add support of SFunc in TypeSerializer (see https://github.com/ScorexFoundation/sigmastate-interpreter/issues/847)
assertExceptionThrown(
f.verifyCase(Coll[Int](), Expected(Success(Coll[Int]()), 0)),
exceptionLike[MatchError]("(SInt$) => SInt$ (of class sigmastate.SFunc)")
diff --git a/sc/shared/src/test/scala/special/sigma/SigmaDslTesting.scala b/sc/shared/src/test/scala/special/sigma/SigmaDslTesting.scala
index e7781a289c..1630e7f8a9 100644
--- a/sc/shared/src/test/scala/special/sigma/SigmaDslTesting.scala
+++ b/sc/shared/src/test/scala/special/sigma/SigmaDslTesting.scala
@@ -1,6 +1,6 @@
package special.sigma
-import org.ergoplatform.SigmaConstants.ScriptCostLimit
+import debox.cfor
import org.ergoplatform._
import org.ergoplatform.dsl.{ContractSpec, SigmaContractSyntax, TestContractSpec}
import org.ergoplatform.validation.ValidationRules.CheckSerializableTypeCode
@@ -9,6 +9,10 @@ import org.scalacheck.Arbitrary._
import org.scalacheck.Gen.frequency
import org.scalacheck.{Arbitrary, Gen}
import org.scalatest.exceptions.TestFailedException
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.propspec.AnyPropSpec
+import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
+import scalan.Platform.threadSleepOrNoOp
import scalan.RType
import scalan.RType._
import scalan.util.BenchmarkUtil
@@ -18,7 +22,7 @@ import scalan.util.StringUtil.StringUtilExtensions
import sigmastate.SType.AnyOps
import sigmastate.Values.{ByteArrayConstant, Constant, ConstantNode, ErgoTree, IntConstant, SValue}
import sigmastate.basics.DLogProtocol.{DLogProverInput, ProveDlog}
-import sigmastate.basics.{SigmaProtocol, SigmaProtocolCommonInput, SigmaProtocolPrivateInput}
+import sigmastate.basics.SigmaProtocolPrivateInput
import sigmastate.eval.Extensions._
import sigmastate.eval.{CompiletimeIRContext, CostingBox, CostingDataContext, Evaluation, IRContext, SigmaDsl}
import sigmastate.helpers.TestingHelpers._
@@ -30,13 +34,8 @@ import sigmastate.serialization.ValueSerializer
import sigmastate.serialization.generators.ObjectGenerators
import sigmastate.utils.Helpers._
import sigmastate.utxo.{DeserializeContext, DeserializeRegister, GetVar, OptionGet}
-import sigmastate.{SOption, SSigmaProp, SType, VersionContext, eval}
+import sigmastate.{SOption, SSigmaProp, SType, SigmaLeaf, VersionContext, eval}
import special.collection.{Coll, CollType}
-import debox.cfor
-import org.scalatest.matchers.should.Matchers
-import org.scalatest.propspec.AnyPropSpec
-import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
-import scalan.Platform.threadSleepOrNoOp
import java.util
import scala.collection.mutable
@@ -85,7 +84,7 @@ class SigmaDslTesting extends AnyPropSpec
val sk2: DLogProverInput = decodeSecretInput("34648336872573478681093104997365775365807654884817677358848426648354905397359")
val sk3: DLogProverInput = decodeSecretInput("50415569076448343263191022044468203756975150511337537963383000142821297891310")
- val secrets: Seq[SigmaProtocolPrivateInput[_ <: SigmaProtocol[_], _ <: SigmaProtocolCommonInput[_]]] = {
+ val secrets: Seq[SigmaProtocolPrivateInput[_ <: SigmaLeaf]] = {
// Note, not all secrets are used, which is required by checkVerify
// This is to make AtLeast to be unproved and thus the verify is successfull
// because of the other condition in SigmaOr (see checkVerify)
@@ -366,7 +365,7 @@ class SigmaDslTesting extends AnyPropSpec
createErgoLikeContext(
newCtx,
ValidationRules.currentSettings,
- ScriptCostLimit.value,
+ evalSettings.scriptCostLimitInEvaluator,
initCost = initialCostInTests.value
)
@@ -374,7 +373,10 @@ class SigmaDslTesting extends AnyPropSpec
val box = createBox(0, compiledTree, additionalRegisters = newRegisters)
// make sure we are doing tests with the box with is actually serializable
- try roundTripTest(box)(ErgoBox.sigmaSerializer)
+ try {
+ val parsed = roundTripTest(box)(ErgoBox.sigmaSerializer)
+ parsed.bytes shouldBe box.bytes
+ }
catch {
case ValidationException(_, r: CheckSerializableTypeCode.type, Seq(SOption.OptionTypeCode), _) =>
// ignore the problem with Option serialization, but test all the other cases
@@ -845,7 +847,7 @@ class SigmaDslTesting extends AnyPropSpec
/** in v5.x the old and the new interpreters are the same */
val oldImpl = () => funcJit[A, B](script)
- val newImpl = oldImpl // funcJit[A, B](script) // TODO v6.0 (16h): use actual new implementation here
+ val newImpl = oldImpl // funcJit[A, B](script) // TODO v6.0: use actual new implementation here (https://github.com/ScorexFoundation/sigmastate-interpreter/issues/910)
/** In v5.x this method just checks the old implementations fails on the new feature. */
override def checkEquality(input: A, logInputOutput: Boolean = false): Try[(B, CostDetails)] = {
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala
index 4f4cc87010..def671c2d4 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainParameters.scala
@@ -1,10 +1,29 @@
package org.ergoplatform.sdk.js
-import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters
+import org.ergoplatform.sdk
import scala.scalajs.js.UndefOr
import scala.scalajs.js.annotation.JSExportTopLevel
+import scala.scalajs.js
+
+/** JS exported version of the [[sdk.BlockchainParameters]] class with the same fields.
+ * Blockchain parameters re-adjustable via miners voting and voting-related data.
+ * All these fields are included into extension section of a first block of a voting epoch.
+ *
+ * @param storageFeeFactor cost of storing 1 byte in UTXO for four years, in nanoErgs
+ * @param minValuePerByte cost of a transaction output, in computation unit
+ * @param maxBlockSize max block size, in bytes
+ * @param tokenAccessCost cost of a token contained in a transaction, in computation unit
+ * @param inputCost cost of a transaction input, in computation unit
+ * @param dataInputCost cost of a transaction data input, in computation unit
+ * @param outputCost cost of a transaction output, in computation unit
+ * @param maxBlockCost computation units limit per block
+ * @param softForkStartingHeight height when voting for a soft-fork had been started
+ * @param softForkVotesCollected votes for soft-fork collected in previous epochs
+ * @param blockVersion Protocol version activated on the network
+ * @see sdk.BlockchainParameters
+ */
@JSExportTopLevel("BlockchainParameters")
class BlockchainParameters(
val storageFeeFactor: Int,
@@ -15,20 +34,7 @@ class BlockchainParameters(
val dataInputCost: Int,
val outputCost: Int,
val maxBlockCost: Int,
- val _softForkStartingHeight: UndefOr[Int],
- val _softForkVotesCollected: UndefOr[Int],
+ val softForkStartingHeight: UndefOr[Int],
+ val softForkVotesCollected: UndefOr[Int],
val blockVersion: Byte
-) extends ErgoLikeParameters {
- import org.ergoplatform.sdk.Iso._
- /**
- * @return height when voting for a soft-fork had been started
- */
- override def softForkStartingHeight: Option[Int] =
- Isos.isoUndefOr[Int, Int](identityIso).to(_softForkStartingHeight)
-
- /**
- * @return votes for soft-fork collected in previous epochs
- */
- override def softForkVotesCollected: Option[Int] =
- Isos.isoUndefOr[Int, Int](identityIso).to(_softForkVotesCollected)
-}
+) extends js.Object
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala
index 0b91cbae57..425d4c7e88 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/BlockchainStateContext.scala
@@ -3,10 +3,14 @@ package org.ergoplatform.sdk.js
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
-/** Equivalent of [[org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeStateContext]] available from JS. */
+/** Equivalent of [[org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext]] available from JS.
+ * @param sigmaLastHeaders fixed number (10 in Ergo) of last block headers
+ * @param previousStateDigest hex of UTXO set digest from a last header (of sigmaLastHeaders)
+ * @param sigmaPreHeader returns pre-header (header without certain fields) of the current block
+ */
@JSExportTopLevel("BlockchainStateContext")
class BlockchainStateContext(
val sigmaLastHeaders: js.Array[Header],
val previousStateDigest: String,
val sigmaPreHeader: PreHeader
-)
+) extends js.Object
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ErgoTree.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ErgoTree.scala
index 810cc1ea81..8469545d95 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ErgoTree.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ErgoTree.scala
@@ -1,8 +1,6 @@
package org.ergoplatform.sdk.js
-import scorex.util.encode.Base16
import sigmastate.Values
-import sigmastate.serialization.ErgoTreeSerializer
import scala.scalajs.js
import scala.scalajs.js.JSConverters.JSRichIterableOnce
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/GroupElement.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/GroupElement.scala
new file mode 100644
index 0000000000..cc20e704bb
--- /dev/null
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/GroupElement.scala
@@ -0,0 +1,29 @@
+package org.ergoplatform.sdk.js
+
+import sigmastate.crypto.{CryptoFacade, CryptoFacadeJs, Ecp, Platform}
+import sigmastate.eval.Extensions.ArrayByteOps
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSExportTopLevel
+
+/** Equivalent of [[special.sigma.GroupElement]] available from JS. */
+@JSExportTopLevel("GroupElement")
+class GroupElement(val point: Ecp) extends js.Object {
+ /** Returns the point encoded as hex string (ASN.1 encoding).
+ * @see CryptoFacade.getASN1Encoding
+ */
+ def toPointHex(): String = {
+ CryptoFacade.getASN1Encoding(point, true).toHex
+ }
+}
+
+@JSExportTopLevel("GroupElementObj")
+object GroupElement extends js.Object {
+ /** Creates a new [[GroupElement]] from the given hex string (ASN.1 encoding)
+ * representation of the underlying [[sigmastate.crypto.Platform.Point]].
+ */
+ def fromPointHex(pointHex: String): GroupElement = {
+ val point = CryptoFacadeJs.createCryptoContext().decodePoint(pointHex)
+ new GroupElement(new Platform.Ecp(point))
+ }
+}
\ No newline at end of file
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala
index fecd4ef67c..f95b8eb0e1 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala
@@ -3,22 +3,48 @@ package org.ergoplatform.sdk.js
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
-/** Equivalent of [[special.sigma.Header]] available from JS. */
+/** Equivalent of [[special.sigma.Header]] available from JS.
+ * Represents data of the block header available in Sigma propositions.
+ */
@JSExportTopLevel("Header")
class Header(
+ /** Hex representation of ModifierId of this Header */
val id: String,
+ /** Block version, to be increased on every soft and hardfork. */
val version: Byte,
+ /** Hex representation of ModifierId of the parent block */
val parentId: String,
+ /** Hex hash of ADProofs for transactions in a block */
val ADProofsRoot: String,
+ /** AvlTree of a state after block application */
val stateRoot: AvlTree,
+ /** Hex of root hash (for a Merkle tree) of transactions in a block. */
val transactionsRoot: String,
+ /** Block timestamp (in milliseconds since beginning of Unix Epoch) */
val timestamp: js.BigInt,
+ /** Current difficulty in a compressed view.
+ * NOTE: actually it is unsigned Int */
val nBits: js.BigInt,
+ /** Block height */
val height: Int,
+ /** Hex of root hash of extension section */
val extensionRoot: String,
- val minerPk: String,
- val powOnetimePk: String,
+
+ /** Miner public key (hex of EC Point). Should be used to collect block rewards.
+ * Part of Autolykos solution.
+ */
+ val minerPk: GroupElement,
+
+ /** One-time public key (hex of EC Point). Prevents revealing of miners secret. */
+ val powOnetimePk: GroupElement,
+
+ /** Hex of nonce bytes */
val powNonce: String,
+
+ /** Distance between pseudo-random number, corresponding to nonce `powNonce` and a secret,
+ * corresponding to `minerPk`. The lower `powDistance` is, the harder it was to find this solution. */
val powDistance: js.BigInt,
+
+ /** Miner votes for changing system parameters. */
val votes: String
) extends js.Object
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala
index c00f72bdff..a317e3ab29 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Isos.scala
@@ -1,30 +1,29 @@
package org.ergoplatform.sdk.js
import org.ergoplatform.ErgoBox._
-import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, UnsignedErgoLikeTransaction, UnsignedInput}
-import org.ergoplatform.sdk.{ExtendedInputBox, Iso}
import org.ergoplatform.sdk.JavaHelpers.UniversalConverter
-import org.ergoplatform.sdk.wallet.protocol.context.{CErgoLikeStateContext, ErgoLikeStateContext}
+import org.ergoplatform.sdk.{Iso, ExtendedInputBox}
+import org.ergoplatform.sdk.wallet.protocol.context
+import org.ergoplatform._
import scalan.RType
-import scorex.crypto.authds.{ADDigest, ADKey}
+import scorex.crypto.authds.ADKey
import scorex.util.ModifierId
import scorex.util.encode.Base16
-import sigmastate.{AvlTreeData, AvlTreeFlags, SType}
import sigmastate.Values.{Constant, GroupElementConstant}
import sigmastate.eval.Extensions.ArrayOps
-import sigmastate.eval.{CAvlTree, CBigInt, CHeader, CPreHeader, Colls, Digest32Coll, Evaluation}
-import sigmastate.fleetSdkCommon.{distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod}
-import sigmastate.interpreter.ContextExtension
-import sigmastate.serialization.{ErgoTreeSerializer, ValueSerializer}
-import special.collection.Coll
-import special.collection.Extensions.CollBytesOps
-import special.sigma
-import special.sigma.GroupElement
+import sigmastate.eval.{CBigInt, Digest32Coll, Evaluation, CAvlTree, Colls, CGroupElement, CPreHeader, CHeader}
import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box
import sigmastate.fleetSdkCommon.distEsmTypesCommonMod.HexString
import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters
import sigmastate.fleetSdkCommon.distEsmTypesTokenMod.TokenAmount
-import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.UnsignedTransaction
+import sigmastate.fleetSdkCommon.distEsmTypesTransactionsMod.{UnsignedTransaction, SignedTransaction}
+import sigmastate.fleetSdkCommon.{distEsmTypesProverResultMod => proverResultMod, distEsmTypesContextExtensionMod => contextExtensionMod, distEsmTypesInputsMod => inputsMod, distEsmTypesBoxesMod => boxesMod, distEsmTypesCommonMod => commonMod, distEsmTypesRegistersMod => registersMod, distEsmTypesTokenMod => tokenMod}
+import sigmastate.interpreter.{ContextExtension, ProverResult}
+import sigmastate.serialization.{ErgoTreeSerializer, ValueSerializer}
+import sigmastate.{AvlTreeData, SType, AvlTreeFlags}
+import special.collection.Coll
+import special.collection.Extensions.CollBytesOps
+import special.sigma
import java.math.BigInteger
import scala.collection.immutable.ListMap
@@ -57,17 +56,26 @@ object Isos {
override def from(x: Coll[Byte]): String = x.toHex
}
- val isoStringToGroupElement: Iso[String, GroupElement] = new Iso[String, GroupElement] {
- override def to(x: String): GroupElement = {
+ val isoStringToGroupElement: Iso[String, sigma.GroupElement] = new Iso[String, sigma.GroupElement] {
+ override def to(x: String): sigma.GroupElement = {
val bytes = Base16.decode(x).get
ValueSerializer.deserialize(bytes).asInstanceOf[GroupElementConstant].value
}
- override def from(x: GroupElement): String = {
+ override def from(x: sigma.GroupElement): String = {
val bytes = ValueSerializer.serialize(GroupElementConstant(x))
Base16.encode(bytes)
}
}
+ val isoGroupElement: Iso[GroupElement, special.sigma.GroupElement] = new Iso[GroupElement, special.sigma.GroupElement] {
+ override def to(x: GroupElement): sigma.GroupElement = {
+ CGroupElement(x.point)
+ }
+ override def from(x: sigma.GroupElement): GroupElement = {
+ new GroupElement(x.asInstanceOf[CGroupElement].wrappedValue)
+ }
+ }
+
implicit val isoBoxId: Iso[boxesMod.BoxId, ErgoBox.BoxId] = new Iso[boxesMod.BoxId, ErgoBox.BoxId] {
override def to(x: boxesMod.BoxId): ErgoBox.BoxId = ADKey @@@ isoStringToArray.to(x)
@@ -124,8 +132,8 @@ object Isos {
nBits = isoBigIntToLong.to(a.nBits),
height = a.height,
extensionRoot = isoStringToColl.to(a.extensionRoot),
- minerPk = isoStringToGroupElement.to(a.minerPk),
- powOnetimePk = isoStringToGroupElement.to(a.powOnetimePk),
+ minerPk = isoGroupElement.to(a.minerPk),
+ powOnetimePk = isoGroupElement.to(a.powOnetimePk),
powNonce = isoStringToColl.to(a.powNonce),
powDistance = isoBigInt.to(a.powDistance),
votes = isoStringToColl.to(a.votes)
@@ -144,8 +152,8 @@ object Isos {
nBits = isoBigIntToLong.from(header.nBits),
height = header.height,
extensionRoot = isoStringToColl.from(header.extensionRoot),
- minerPk = isoStringToGroupElement.from(header.minerPk),
- powOnetimePk = isoStringToGroupElement.from(header.powOnetimePk),
+ minerPk = isoGroupElement.from(header.minerPk),
+ powOnetimePk = isoGroupElement.from(header.powOnetimePk),
powNonce = isoStringToColl.from(header.powNonce),
powDistance = isoBigInt.from(header.powDistance),
votes = isoStringToColl.from(header.votes)
@@ -161,7 +169,7 @@ object Isos {
timestamp = isoBigIntToLong.to(a.timestamp),
nBits = isoBigIntToLong.to(a.nBits),
height = a.height,
- minerPk = isoStringToGroupElement.to(a.minerPk),
+ minerPk = isoGroupElement.to(a.minerPk),
votes = isoStringToColl.to(a.votes)
)
}
@@ -173,22 +181,55 @@ object Isos {
timestamp = isoBigIntToLong.from(header.timestamp),
nBits = isoBigIntToLong.from(header.nBits),
height = header.height,
- minerPk = isoStringToGroupElement.from(header.minerPk),
+ minerPk = isoGroupElement.from(header.minerPk),
votes = isoStringToColl.from(header.votes)
)
}
}
- implicit val isoBlockchainStateContext: Iso[BlockchainStateContext, ErgoLikeStateContext] = new Iso[BlockchainStateContext, ErgoLikeStateContext] {
- override def to(a: BlockchainStateContext): ErgoLikeStateContext = {
- CErgoLikeStateContext(
+ val isoBlockchainParameters: Iso[BlockchainParameters, sdk.BlockchainParameters] = new Iso[BlockchainParameters, sdk.BlockchainParameters] {
+ override def to(a: BlockchainParameters): sdk.BlockchainParameters = {
+ sdk.BlockchainParameters(
+ storageFeeFactor = a.storageFeeFactor,
+ minValuePerByte = a.minValuePerByte,
+ maxBlockSize = a.maxBlockSize,
+ tokenAccessCost = a.tokenAccessCost,
+ inputCost = a.inputCost,
+ dataInputCost = a.dataInputCost,
+ outputCost = a.outputCost,
+ maxBlockCost = a.maxBlockCost,
+ softForkStartingHeight = Isos.isoUndefOr[Int, Int](Iso.identityIso).to(a.softForkStartingHeight),
+ softForkVotesCollected = Isos.isoUndefOr[Int, Int](Iso.identityIso).to(a.softForkVotesCollected),
+ blockVersion = a.blockVersion
+ )
+ }
+ override def from(b: sdk.BlockchainParameters): BlockchainParameters = {
+ new BlockchainParameters(
+ storageFeeFactor = b.storageFeeFactor,
+ minValuePerByte = b.minValuePerByte,
+ maxBlockSize = b.maxBlockSize,
+ tokenAccessCost = b.tokenAccessCost,
+ inputCost = b.inputCost,
+ dataInputCost = b.dataInputCost,
+ outputCost = b.outputCost,
+ maxBlockCost = b.maxBlockCost,
+ softForkStartingHeight = Isos.isoUndefOr[Int, Int](Iso.identityIso).from(b.softForkStartingHeight),
+ softForkVotesCollected = Isos.isoUndefOr[Int, Int](Iso.identityIso).from(b.softForkVotesCollected),
+ blockVersion = b.blockVersion
+ )
+ }
+ }
+
+ implicit val isoBlockchainStateContext: Iso[BlockchainStateContext, context.BlockchainStateContext] = new Iso[BlockchainStateContext, context.BlockchainStateContext] {
+ override def to(a: BlockchainStateContext): context.BlockchainStateContext = {
+ context.BlockchainStateContext(
sigmaLastHeaders = isoArrayToColl(isoHeader).to(a.sigmaLastHeaders),
previousStateDigest = isoStringToColl.to(a.previousStateDigest),
sigmaPreHeader = isoPreHeader.to(a.sigmaPreHeader)
)
}
- override def from(b: ErgoLikeStateContext): BlockchainStateContext = {
+ override def from(b: context.BlockchainStateContext): BlockchainStateContext = {
new BlockchainStateContext(
sigmaLastHeaders = isoArrayToColl(isoHeader).from(b.sigmaLastHeaders),
previousStateDigest = isoStringToColl.from(b.previousStateDigest),
@@ -200,7 +241,7 @@ object Isos {
implicit val isoContextExtension: Iso[contextExtensionMod.ContextExtension, ContextExtension] = new Iso[contextExtensionMod.ContextExtension, ContextExtension] {
override def to(x: contextExtensionMod.ContextExtension): ContextExtension = {
var map = new ListMap[Byte, Constant[SType]]()
- val keys = js.Object.keys(x)
+ val keys = js.Object.keys(x).sorted
for ( k <- keys ) {
val id = k.toInt.toByte
val c = isoHexStringToConstant.to(x.apply(id).get.get)
@@ -227,6 +268,28 @@ object Isos {
inputsMod.UnsignedInput(x.boxId.convertTo[boxesMod.BoxId], isoContextExtension.from(x.extension))
}
+ implicit val isoProverResult: Iso[proverResultMod.ProverResult, ProverResult] = new Iso[proverResultMod.ProverResult, ProverResult] {
+ override def to(a: proverResultMod.ProverResult): ProverResult = {
+ ProverResult(
+ proof = isoStringToArray.to(a.proofBytes),
+ extension = isoContextExtension.to(a.extension)
+ )
+ }
+ override def from(b: ProverResult): proverResultMod.ProverResult = {
+ proverResultMod.ProverResult(
+ isoContextExtension.from(b.extension),
+ isoStringToArray.from(b.proof)
+ )
+ }
+ }
+
+ implicit val isoSignedInput: Iso[inputsMod.SignedInput, Input] = new Iso[inputsMod.SignedInput, Input] {
+ override def to(x: inputsMod.SignedInput): Input =
+ Input(x.boxId.convertTo[ErgoBox.BoxId], isoProverResult.to(x.spendingProof))
+ override def from(x: Input): inputsMod.SignedInput =
+ inputsMod.SignedInput(x.boxId.convertTo[boxesMod.BoxId], isoProverResult.from(x.spendingProof))
+ }
+
implicit val isoDataInput: Iso[inputsMod.DataInput, DataInput] = new Iso[inputsMod.DataInput, DataInput] {
override def to(x: inputsMod.DataInput): DataInput = DataInput(x.boxId.convertTo[ErgoBox.BoxId])
@@ -321,8 +384,8 @@ object Isos {
}
}
- implicit val isoBoxCandidate: Iso[boxesMod.BoxCandidate[commonMod.Amount], ErgoBoxCandidate] = new Iso[boxesMod.BoxCandidate[commonMod.Amount], ErgoBoxCandidate] {
- override def to(x: boxesMod.BoxCandidate[commonMod.Amount]): ErgoBoxCandidate = {
+ implicit val isoBoxCandidate: Iso[boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters], ErgoBoxCandidate] = new Iso[boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters], ErgoBoxCandidate] {
+ override def to(x: boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters]): ErgoBoxCandidate = {
val ergoBoxCandidate = new ErgoBoxCandidate(
value = isoAmount.to(x.value),
ergoTree = {
@@ -336,11 +399,11 @@ object Isos {
ergoBoxCandidate
}
- override def from(x: ErgoBoxCandidate): boxesMod.BoxCandidate[commonMod.Amount] = {
+ override def from(x: ErgoBoxCandidate): boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters] = {
val ergoTree = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(x.ergoTree)
val ergoTreeStr = Base16.encode(ergoTree)
val assets = isoTokenArray.from(x.additionalTokens)
- boxesMod.BoxCandidate[commonMod.Amount](
+ boxesMod.BoxCandidate[commonMod.Amount, NonMandatoryRegisters](
ergoTree = ergoTreeStr,
value = isoAmount.from(x.value),
assets = assets,
@@ -350,27 +413,8 @@ object Isos {
}
}
- // Implements Iso between UnsignedTransaction and UnsignedErgoLikeTransaction
- val isoUnsignedTransaction: Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] =
- new Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] {
- override def to(a: UnsignedTransaction): UnsignedErgoLikeTransaction = {
- new UnsignedErgoLikeTransaction(
- inputs = isoArrayToIndexed(isoUnsignedInput).to(a.inputs),
- dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs),
- outputCandidates = isoArrayToIndexed(isoBoxCandidate).to(a.outputs),
- )
- }
- override def from(b: UnsignedErgoLikeTransaction): UnsignedTransaction = {
- UnsignedTransaction(
- inputs = isoArrayToIndexed(isoUnsignedInput).from(b.inputs),
- dataInputs = isoArrayToIndexed(isoDataInput).from(b.dataInputs),
- outputs = isoArrayToIndexed(isoBoxCandidate).from(b.outputCandidates)
- )
- }
- }
-
- val isoBox: Iso[Box[commonMod.Amount], ErgoBox] = new Iso[Box[commonMod.Amount], ErgoBox] {
- override def to(x: Box[commonMod.Amount]): ErgoBox = {
+ val isoBox: Iso[Box[commonMod.Amount, NonMandatoryRegisters], ErgoBox] = new Iso[Box[commonMod.Amount, NonMandatoryRegisters], ErgoBox] {
+ override def to(x: Box[commonMod.Amount, NonMandatoryRegisters]): ErgoBox = {
val ergoBox = new ErgoBox(
value = isoAmount.to(x.value),
ergoTree = {
@@ -386,11 +430,11 @@ object Isos {
ergoBox
}
- override def from(x: ErgoBox): Box[commonMod.Amount] = {
+ override def from(x: ErgoBox): Box[commonMod.Amount, NonMandatoryRegisters] = {
val ergoTree = ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(x.ergoTree)
val ergoTreeStr = Base16.encode(ergoTree)
val assets = isoTokenArray.from(x.additionalTokens)
- Box[commonMod.Amount](
+ Box[commonMod.Amount, NonMandatoryRegisters](
boxId = Base16.encode(x.id),
ergoTree = ergoTreeStr,
value = isoAmount.from(x.value),
@@ -406,7 +450,7 @@ object Isos {
val isoEIP12UnsignedInput: Iso[inputsMod.EIP12UnsignedInput, ExtendedInputBox] =
new Iso[inputsMod.EIP12UnsignedInput, ExtendedInputBox] {
override def to(x: inputsMod.EIP12UnsignedInput): ExtendedInputBox = {
- val box = Box[commonMod.Amount](
+ val box = Box[commonMod.Amount, NonMandatoryRegisters](
boxId = x.boxId,
ergoTree = x.ergoTree,
value = x.value,
@@ -436,4 +480,42 @@ object Isos {
)
}
}
+
+ val isoUnsignedTransaction: Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] =
+ new Iso[UnsignedTransaction, UnsignedErgoLikeTransaction] {
+ override def to(a: UnsignedTransaction): UnsignedErgoLikeTransaction = {
+ new UnsignedErgoLikeTransaction(
+ inputs = isoArrayToIndexed(isoUnsignedInput).to(a.inputs),
+ dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs),
+ outputCandidates = isoArrayToIndexed(isoBoxCandidate).to(a.outputs),
+ )
+ }
+
+ override def from(b: UnsignedErgoLikeTransaction): UnsignedTransaction = {
+ UnsignedTransaction(
+ inputs = isoArrayToIndexed(isoUnsignedInput).from(b.inputs),
+ dataInputs = isoArrayToIndexed(isoDataInput).from(b.dataInputs),
+ outputs = isoArrayToIndexed(isoBoxCandidate).from(b.outputCandidates)
+ )
+ }
+ }
+
+ val isoSignedTransaction: Iso[SignedTransaction, ErgoLikeTransaction] =
+ new Iso[SignedTransaction, ErgoLikeTransaction] {
+ override def to(a: SignedTransaction): ErgoLikeTransaction = {
+ new ErgoLikeTransaction(
+ inputs = isoArrayToIndexed(isoSignedInput).to(a.inputs),
+ dataInputs = isoArrayToIndexed(isoDataInput).to(a.dataInputs),
+ outputCandidates = isoArrayToIndexed(isoBox).to(a.outputs),
+ )
+ }
+
+ override def from(tx: ErgoLikeTransaction): SignedTransaction = {
+ val inputs = isoArrayToIndexed(isoSignedInput).from(tx.inputs)
+ val dataInputs = isoArrayToIndexed(isoDataInput).from(tx.dataInputs)
+ val outputs = isoArrayToIndexed(isoBox).from(tx.outputs)
+ SignedTransaction(dataInputs, tx.id, inputs, outputs)
+ }
+ }
+
}
\ No newline at end of file
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala
index 7e38032446..ae75b67ece 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/PreHeader.scala
@@ -3,14 +3,25 @@ package org.ergoplatform.sdk.js
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
-/** Equivalent of [[special.sigma.PreHeader]] available from JS. */
+/** Equivalent of [[special.sigma.PreHeader]] available from JS.
+ * Only header fields that can be predicted by a miner.
+ */
@JSExportTopLevel("PreHeader")
class PreHeader(
+ /** Block version, to be increased on every soft and hardfork. */
val version: Byte,
+ /** Hex of id of parent block */
val parentId: String,
+ /** Block timestamp (in milliseconds since beginning of Unix Epoch) */
val timestamp: js.BigInt,
+
+ /** Current difficulty in a compressed view.
+ * NOTE: actually it is unsigned integer */
val nBits: js.BigInt,
+ /** Block height */
val height: Int,
- val minerPk: String,
+ /** Miner public key (hex of EC Point). Should be used to collect block rewards. */
+ val minerPk: GroupElement,
+ /** Hex of miner votes bytes for changing system parameters. */
val votes: String
) extends js.Object
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala
index 4dfce27d44..8d3b3cd2e3 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ProverBuilder.scala
@@ -1,7 +1,5 @@
package org.ergoplatform.sdk.js
-import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
-import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters
import org.ergoplatform.sdk
import org.ergoplatform.sdk.SecretString
@@ -10,10 +8,16 @@ import scala.scalajs.js.annotation.JSExportTopLevel
import Isos._
import sigmastate.eval.SigmaDsl
-/** Equivalent of [[sdk.ProverBuilder]] available from JS. */
+/** Equivalent of [[sdk.ProverBuilder]] available from JS.
+ *
+ * @param parameters Blockchain parameters re-adjustable via miners voting and
+ * voting-related data. All of them are included into extension
+ * section of a first block of a voting epoch.
+ * @param network Network prefix to use for addresses.
+ */
@JSExportTopLevel("ProverBuilder")
-class ProverBuilder(parameters: ErgoLikeParameters, networkPrefix: NetworkPrefix) extends js.Object {
- val _builder = new sdk.ProverBuilder(parameters, networkPrefix)
+class ProverBuilder(parameters: BlockchainParameters, network: Byte) extends js.Object {
+ val _builder = new sdk.ProverBuilder(Isos.isoBlockchainParameters.to(parameters), network)
/** Configure this builder to use the given seed when building a new prover.
*
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala
new file mode 100644
index 0000000000..66c5ce7f38
--- /dev/null
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/ReducedTransaction.scala
@@ -0,0 +1,22 @@
+package org.ergoplatform.sdk.js
+
+import org.ergoplatform.sdk
+
+import scala.scalajs.js
+import scala.scalajs.js.annotation.JSExportTopLevel
+
+/** Equivalent of [[sdk.ReducedTransaction]] available from JS. */
+@JSExportTopLevel("ReducedTransaction")
+class ReducedTransaction(val _tx: sdk.ReducedTransaction) extends js.Object {
+ /** Serialized bytes of this transaction in hex format. */
+ def toHex: String = _tx.toHex
+}
+
+@JSExportTopLevel("ReducedTransactionObj")
+object ReducedTransaction extends js.Object {
+ /** Creates a [[ReducedTransaction]] from serialized bytes in hex format. */
+ def fromHex(hex: String): ReducedTransaction = {
+ val tx = sdk.ReducedTransaction.fromHex(hex)
+ new ReducedTransaction(tx)
+ }
+}
\ No newline at end of file
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProp.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProp.scala
index 0722b68e5a..bc2da23561 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProp.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProp.scala
@@ -1,9 +1,24 @@
package org.ergoplatform.sdk.js
import sigmastate.Values.SigmaBoolean
+import sigmastate.basics.DLogProtocol.ProveDlog
+
import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
/** Equivalent of [[special.sigma.SigmaProp]] available from JS. */
@JSExportTopLevel("SigmaProp")
-class SigmaProp(val sigmaBoolean: SigmaBoolean) extends js.Object
+class SigmaProp(val sigmaBoolean: SigmaBoolean) extends js.Object {
+}
+
+@JSExportTopLevel("SigmaPropObj")
+object SigmaProp extends js.Object {
+ /** Creates a new [[SigmaProp]] from the given hex string of public key.
+ * @param pointHex hex representation of elliptic curve point (ASN.1 encoded)
+ * @see CryptoFacade.getASN1Encoding, GroupElement.fromPointHex, Point
+ */
+ def fromPointHex(pointHex: String): SigmaProp = {
+ val point = GroupElement.fromPointHex(pointHex).point
+ new SigmaProp(ProveDlog(point))
+ }
+}
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala
index 41bf634473..b7ca33b1de 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/SigmaProver.scala
@@ -2,6 +2,7 @@ package org.ergoplatform.sdk.js
import org.ergoplatform.sdk
import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box
+import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters
import sigmastate.fleetSdkCommon.{distEsmTypesCommonMod => commonMod, distEsmTypesInputsMod => inputsMod, distEsmTypesTokenMod => tokenMod, distEsmTypesTransactionsMod => transactionsMod}
import scala.scalajs.js
@@ -12,47 +13,58 @@ import scala.scalajs.js.annotation.JSExportTopLevel
class SigmaProver(_prover: sdk.SigmaProver) extends js.Object {
import Isos._
- //TODO finish implementation
+ /** Returns the Pay-to-Public-Key (P2PK) address associated with the prover's public key.
+ * The returned address corresponds to the master secret derived from the mnemonic
+ * phrase configured in the [[ProverBuilder]].
+ */
+ def getP2PKAddress: String = {
+ val addr = _prover.getP2PKAddress
+ addr.toString
+ }
+
+ /** Returns the prover's secret key. */
+ def getSecretKey: js.BigInt =
+ isoBigInt.from(_prover.getSecretKey)
+
+ /** Returns a sequence of EIP-3 addresses associated with the prover's secret keys. */
+ def getEip3Addresses: js.Array[String] = {
+ val addresses = _prover.getEip3Addresses
+ js.Array(addresses.map(_.toString): _*)
+ }
+
+ /** Reduces the transaction to the reduced form, which is ready to be signed.
+ * @param stateCtx blockchain state context
+ * @param unsignedTx unsigned transaction to be reduced (created by Fleet builders)
+ * @param boxesToSpend boxes to be spent by the transaction
+ * @param dataInputs data inputs to be used by the transaction
+ * @param tokensToBurn tokens to be burned by the transaction
+ * @param baseCost base cost of the transaction
+ * @return reduced transaction
+ */
def reduce(
stateCtx: BlockchainStateContext,
unsignedTx: transactionsMod.UnsignedTransaction,
boxesToSpend: js.Array[inputsMod.EIP12UnsignedInput],
+ dataInputs: js.Array[Box[commonMod.Amount, NonMandatoryRegisters]],
+ tokensToBurn: js.Array[tokenMod.TokenAmount[commonMod.Amount]],
baseCost: Int): ReducedTransaction = {
- val tx = sdk.UnreducedTransaction(
+ val unreducedTx = sdk.UnreducedTransaction(
unsignedTx = isoUnsignedTransaction.to(unsignedTx),
boxesToSpend = isoArrayToIndexed(isoEIP12UnsignedInput).to(boxesToSpend),
- dataInputs = IndexedSeq.empty,
- tokensToBurn = IndexedSeq.empty
- )
- _prover.reduce(
- isoBlockchainStateContext.to(stateCtx),
- tx,
- baseCost
+ dataInputs = isoArrayToIndexed(isoBox).to(dataInputs),
+ tokensToBurn = isoArrayToIndexed(isoToken.andThen(sdk.Iso.isoErgoTokenToPair.inverse)).to(tokensToBurn)
)
- new ReducedTransaction
+ val ctx = isoBlockchainStateContext.to(stateCtx)
+ val reducedTx = _prover.reduce(ctx, unreducedTx, baseCost)
+ new ReducedTransaction(reducedTx)
}
- def reduceTransaction(
- unsignedTx: transactionsMod.UnsignedTransaction,
- boxesToSpend: js.Array[inputsMod.EIP12UnsignedInput],
- dataBoxes: js.Array[Box[commonMod.Amount]],
- stateDigest: String,
- baseCost: Int,
- tokensToBurn: js.Array[tokenMod.TokenAmount[commonMod.Amount]]
- ): (ReducedTransaction, Int) = {
- val tx = Isos.isoUnsignedTransaction.to(unsignedTx)
-// val inputs: = boxesToSpend.map(isoEIP12UnsignedInput.to).toArray
-
- (new ReducedTransaction, 0)
+ /** Signs the reduced transaction.
+ * @param reducedTx reduced transaction to be signed
+ * @return signed transaction containting all the required proofs (signatures)
+ */
+ def signReduced(reducedTx: ReducedTransaction): transactionsMod.SignedTransaction = {
+ val signed = _prover.signReduced(reducedTx._tx)
+ isoSignedTransaction.from(signed.ergoTx)
}
-
-}
-
-//TODO finish implementation
-@JSExportTopLevel("ReducedTransaction")
-class ReducedTransaction
-
-//TODO finish implementation
-@JSExportTopLevel("SigmaProverObj")
-object SigmaProver {
}
diff --git a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Value.scala b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Value.scala
index b926cc54aa..0c30216dbc 100644
--- a/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Value.scala
+++ b/sdk/js/src/main/scala/org/ergoplatform/sdk/js/Value.scala
@@ -10,6 +10,7 @@ import sigmastate.crypto.Platform
import sigmastate.eval.{CAvlTree, CGroupElement, CSigmaProp, Colls, CostingBox, Evaluation, SigmaDsl}
import sigmastate.fleetSdkCommon.distEsmTypesBoxesMod.Box
import sigmastate.fleetSdkCommon.distEsmTypesCommonMod
+import sigmastate.fleetSdkCommon.distEsmTypesRegistersMod.NonMandatoryRegisters
import sigmastate.lang.DeserializationSigmaBuilder
import sigmastate.serialization.{ConstantSerializer, DataSerializer, SigmaSerializer}
import special.collection.{Coll, CollType}
@@ -88,8 +89,8 @@ object Value extends js.Object {
val v = data.asInstanceOf[js.BigInt]
SigmaDsl.BigInt(new BigInteger(v.toString(16), 16))
case special.sigma.GroupElementRType =>
- val point = data.asInstanceOf[Platform.Point]
- SigmaDsl.GroupElement(new Platform.Ecp(point))
+ val ge = data.asInstanceOf[GroupElement]
+ SigmaDsl.GroupElement(ge.point)
case special.sigma.SigmaPropRType =>
val p = data.asInstanceOf[SigmaProp]
SigmaDsl.SigmaProp(p.sigmaBoolean)
@@ -97,7 +98,7 @@ object Value extends js.Object {
val t = data.asInstanceOf[AvlTree]
Isos.isoAvlTree.to(t)
case special.sigma.BoxRType =>
- val t = data.asInstanceOf[Box[distEsmTypesCommonMod.Amount]]
+ val t = data.asInstanceOf[Box[distEsmTypesCommonMod.Amount, NonMandatoryRegisters]]
SigmaDsl.Box(Isos.isoBox.to(t))
case ct: CollType[a] =>
val xs = data.asInstanceOf[js.Array[Any]]
@@ -128,8 +129,8 @@ object Value extends js.Object {
val hex = SigmaDsl.toBigInteger(value.asInstanceOf[special.sigma.BigInt]).toString(10)
js.BigInt(hex)
case special.sigma.GroupElementRType =>
- val point: Platform.Point = value.asInstanceOf[CGroupElement].wrappedValue.asInstanceOf[Platform.Ecp].point
- point
+ val point = value.asInstanceOf[CGroupElement].wrappedValue.asInstanceOf[Platform.Ecp]
+ new GroupElement(point)
case special.sigma.SigmaPropRType =>
new SigmaProp(value.asInstanceOf[CSigmaProp].wrappedValue)
case special.sigma.AvlTreeRType =>
@@ -163,6 +164,10 @@ object Value extends js.Object {
n
case special.sigma.BigIntRType =>
data.asInstanceOf[js.BigInt]
+ case special.sigma.GroupElementRType =>
+ data.asInstanceOf[GroupElement]
+ case special.sigma.SigmaPropRType =>
+ data.asInstanceOf[SigmaProp]
case PairType(l, r) => data match {
case arr: js.Array[Any @unchecked] =>
checkJsData(arr(0), l)
@@ -212,6 +217,22 @@ object Value extends js.Object {
new Value(n, Type.BigInt)
}
+ /** Creates a Value of GroupElement type from [[sigmastate.crypto.Platform.Point]] hex.
+ * @param pointHex hex of ASN representation of [[sigmastate.crypto.Platform.Point]]
+ */
+ def ofGroupElement(pointHex: String): Value = {
+ val ge = GroupElement.fromPointHex(pointHex)
+ new Value(ge, Type.GroupElement)
+ }
+
+ /** Creates a Value of SigmaProp type from [[sigmastate.crypto.Platform.Point]] hex.
+ * @param pointHex hex of ASN representation of [[sigmastate.crypto.Platform.Point]]
+ */
+ def ofSigmaProp(pointHex: String): Value = {
+ val sp = SigmaProp.fromPointHex(pointHex)
+ new Value(sp, Type.SigmaProp)
+ }
+
/** Create Pair value from two values. */
def pairOf(l: Value, r: Value): Value = {
val data = js.Array(l.data, r.data) // the l and r data have been validated
@@ -237,7 +258,9 @@ object Value extends js.Object {
* descriptor.
* @param hex the string is obtained as hex encoding of serialized ConstantNode.
* (The bytes obtained by ConstantSerializer in sigma)
- * @return new deserialized ErgoValue instance
+ * @return new deserialized Value instance containing:
+ * - suitable JS value in its `data` field
+ * - and [[Type]] descriptor in its `tpe` field
*/
def fromHex(hex: String): Value = {
val bytes = Base16.decode(hex).fold(t => throw t, identity)
diff --git a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala
index f8e7962055..47eedf2706 100644
--- a/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala
+++ b/sdk/js/src/test/scala/org/ergoplatform/sdk/js/IsosSpec.scala
@@ -1,18 +1,17 @@
package org.ergoplatform.sdk.js
import org.ergoplatform.ErgoBox.{AdditionalRegisters, BoxId, TokenId}
-import org.ergoplatform.sdk.{ExtendedInputBox, Iso}
import org.ergoplatform._
-import org.ergoplatform.sdk.wallet.protocol.context.{CErgoLikeStateContext, ErgoLikeStateContext}
+import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext
+import org.ergoplatform.sdk.{ExtendedInputBox, Iso}
import org.scalacheck.{Arbitrary, Gen}
import org.scalatest.matchers.should.Matchers
import org.scalatest.propspec.AnyPropSpec
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
-import scorex.crypto.authds.ADDigest
import sigmastate.SType
import sigmastate.Values.Constant
import sigmastate.eval.Colls
-import sigmastate.interpreter.ContextExtension
+import sigmastate.interpreter.{ContextExtension, ProverResult}
import sigmastate.serialization.generators.ObjectGenerators
import special.collection.Coll
import special.sigma
@@ -27,19 +26,18 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
extension <- contextExtensionGen
} yield ExtendedInputBox(box, extension)
- lazy val ergoLikeStateContextGen: Gen[ErgoLikeStateContext] = for {
+ lazy val blockchainStateContextGen: Gen[BlockchainStateContext] = for {
stateRoot <- avlTreeGen
headers <- headersGen(stateRoot)
preHeader <- preHeaderGen(headers.headOption.map(_.id).getOrElse(modifierIdBytesGen.sample.get))
- } yield CErgoLikeStateContext(
+ } yield BlockchainStateContext(
sigmaLastHeaders = Colls.fromItems(headers:_*),
previousStateDigest = stateRoot.digest,
sigmaPreHeader = preHeader
)
def roundtrip[A,B](iso: Iso[A,B])(b: B): Unit = {
- val invIso = iso.inverse
- invIso.from(invIso.to(b)) shouldBe b
+ iso.to(iso.from(b)) shouldBe b
}
override implicit val generatorDrivenConfig: PropertyCheckConfiguration = PropertyCheckConfiguration(minSuccessful = 30)
@@ -93,7 +91,7 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}
property("Iso.isoBlockchainStateContext") {
- forAll(ergoLikeStateContextGen) { (c: ErgoLikeStateContext) =>
+ forAll(blockchainStateContextGen) { (c: BlockchainStateContext) =>
roundtrip(Isos.isoBlockchainStateContext)(c)
}
}
@@ -104,12 +102,24 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}
}
+ property("Iso.isoProverResult") {
+ forAll { (c: ProverResult) =>
+ roundtrip(Isos.isoProverResult)(c)
+ }
+ }
+
property("Iso.isoUnsignedInput") {
forAll { (c: UnsignedInput) =>
roundtrip(Isos.isoUnsignedInput)(c)
}
}
+ property("Iso.isoSignedInput") {
+ forAll { (c: Input) =>
+ roundtrip(Isos.isoSignedInput)(c)
+ }
+ }
+
property("Iso.isoDataInput") {
forAll { (c: DataInput) =>
roundtrip(Isos.isoDataInput)(c)
@@ -165,12 +175,6 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}
}
- ignore("Iso.isoUnsignedTransaction") {
- forAll { (tx: UnsignedErgoLikeTransaction) =>
- roundtrip(Isos.isoUnsignedTransaction)(tx)
- }
- }
-
property("Iso.isoBox") {
forAll { (b: ErgoBox) =>
roundtrip(Isos.isoBox)(b)
@@ -183,4 +187,15 @@ class IsosSpec extends AnyPropSpec with Matchers with ObjectGenerators with Sca
}
}
+ property("Iso.isoUnsignedTransaction") {
+ forAll { (tx: UnsignedErgoLikeTransaction) =>
+ roundtrip(Isos.isoUnsignedTransaction)(tx)
+ }
+ }
+
+ property("Iso.isoSignedTransaction") {
+ forAll { (tx: ErgoLikeTransaction) =>
+ roundtrip(Isos.isoSignedTransaction)(tx)
+ }
+ }
}
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala
index 91e684d9ac..dc289ba4e6 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/AppkitProvingInterpreter.scala
@@ -5,7 +5,7 @@ import org.ergoplatform._
import org.ergoplatform.sdk.Extensions.{CollOps, PairCollOps}
import org.ergoplatform.sdk.JavaHelpers.{TokenColl, UniversalConverter}
import org.ergoplatform.sdk.utils.ArithUtils
-import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext, TransactionContext}
+import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, TransactionContext}
import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey
import org.ergoplatform.validation.ValidationRules
import scalan.util.Extensions.LongOps
@@ -35,7 +35,7 @@ class AppkitProvingInterpreter(
val secretKeys: IndexedSeq[ExtendedSecretKey],
val dLogInputs: IndexedSeq[DLogProverInput],
val dhtInputs: IndexedSeq[DiffieHellmanTupleProverInput],
- params: ErgoLikeParameters)
+ params: BlockchainParameters)
extends ReducingInterpreter(params) with ProverInterpreter {
override type CTX = ErgoLikeContext
@@ -44,7 +44,7 @@ class AppkitProvingInterpreter(
/** All secrets available to this interpreter including [[ExtendedSecretKey]], dlog and
* dht secrets.
*/
- override val secrets: Seq[SigmaProtocolPrivateInput[_, _]] = {
+ override val secrets: Seq[SigmaProtocolPrivateInput[_]] = {
val dlogs: IndexedSeq[DLogProverInput] = secretKeys.map(_.privateInput)
dlogs ++ dLogInputs ++ dhtInputs
}
@@ -79,7 +79,7 @@ class AppkitProvingInterpreter(
* The returned cost doesn't include `baseCost`.
*/
def sign(unreducedTx: UnreducedTransaction,
- stateContext: ErgoLikeStateContext,
+ stateContext: BlockchainStateContext,
baseCost: Int): Try[SignedTransaction] = Try {
val maxCost = params.maxBlockCost
var currentCost: Long = baseCost
@@ -112,7 +112,7 @@ class AppkitProvingInterpreter(
unsignedTx: UnsignedErgoLikeTransaction,
boxesToSpend: IndexedSeq[ExtendedInputBox],
dataBoxes: IndexedSeq[ErgoBox],
- stateContext: ErgoLikeStateContext,
+ stateContext: BlockchainStateContext,
baseCost: Int,
tokensToBurn: IndexedSeq[ErgoToken]): ReducedErgoLikeTransaction = {
if (unsignedTx.inputs.length != boxesToSpend.length) throw new Exception("Not enough boxes to spend")
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/BlockchainContext.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/BlockchainContext.scala
new file mode 100644
index 0000000000..d5b6edaf69
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/BlockchainContext.scala
@@ -0,0 +1,22 @@
+package org.ergoplatform.sdk
+
+import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext
+import special.collection.Coll
+import special.sigma.Header
+
+/** Represents a specific context of blockchain for execution
+ * of transaction building scenario.
+ * It contains methods for accessing blockchain data, current blockchain state,
+ * node information etc.
+ * An instance of this class can also be used to create new builders
+ * for creating new transactions and provers (used for transaction signing).
+ */
+case class BlockchainContext(
+ networkType: NetworkType,
+ parameters: BlockchainParameters,
+ stateContext: BlockchainStateContext
+) {
+ def headers: Coll[Header] = stateContext.sigmaLastHeaders
+
+ def height: Int = headers(0).height
+}
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/BlockchainParameters.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/BlockchainParameters.scala
new file mode 100644
index 0000000000..4a1014b112
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/BlockchainParameters.scala
@@ -0,0 +1,52 @@
+package org.ergoplatform.sdk
+
+/** Blockchain parameters re-adjustable via miners voting and voting-related data.
+ * All these fields are included into extension section of a first block of a voting epoch.
+ *
+ * @param storageFeeFactor cost of storing 1 byte in UTXO for four years, in nanoErgs
+ * @param minValuePerByte cost of a transaction output, in computation unit
+ * @param maxBlockSize max block size, in bytes
+ * @param tokenAccessCost cost of a token contained in a transaction, in computation unit
+ * @param inputCost cost of a transaction input, in computation unit
+ * @param dataInputCost cost of a transaction data input, in computation unit
+ * @param outputCost cost of a transaction output, in computation unit
+ * @param maxBlockCost computation units limit per block
+ * @param softForkStartingHeight height when voting for a soft-fork had been started
+ * @param softForkVotesCollected votes for soft-fork collected in previous epochs
+ * @param blockVersion Protocol version activated on the network
+ */
+case class BlockchainParameters(
+ storageFeeFactor: Int,
+ minValuePerByte: Int,
+ maxBlockSize: Int,
+ tokenAccessCost: Int,
+ inputCost: Int,
+ dataInputCost: Int,
+ outputCost: Int,
+ maxBlockCost: Int,
+ softForkStartingHeight: Option[Int],
+ softForkVotesCollected: Option[Int],
+ blockVersion: Byte
+)
+
+/** Global parameters used by SDK */
+object BlockchainParameters {
+ /** A number of blocks a miner should wait before he/she can spend block reward.
+ * This is part of Ergo protocol and cannot be changed.
+ */
+ val MinerRewardDelay_Mainnet = 720
+
+ val MinerRewardDelay_Testnet = 720
+
+ /** One Erg is 10^9 NanoErg */
+ val OneErg: Long = 1000 * 1000 * 1000
+
+ /** Minimum transaction fee in NanoErgs as it is defined in Ergo protocol. */
+ val MinFee: Long = 1000 * 1000
+
+ /** Minimum value for a change. It can be used to compute change output value.
+ * If computed change is less than this value, it is added to the fee
+ * and `change` output in not added to the transaction.
+ */
+ val MinChangeValue: Long = 1000 * 1000
+}
\ No newline at end of file
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/BoxSelectionResult.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/BoxSelectionResult.scala
new file mode 100644
index 0000000000..d63ae82a44
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/BoxSelectionResult.scala
@@ -0,0 +1,15 @@
+package org.ergoplatform.sdk
+
+import org.ergoplatform.ErgoBoxAssets
+
+/**
+ * Containter for box selector output
+ *
+ * @param inputBoxes - transaction inputs chosen by a selector
+ * @param changeBoxes - change outputs
+ * @param payToReemissionBox - pay-to-reemission output mde according to EIP-27, if needed
+ */
+class BoxSelectionResult[T <: ErgoBoxAssets](
+ val inputBoxes: Seq[T],
+ val changeBoxes: Seq[ErgoBoxAssets],
+ val payToReemissionBox: Option[ErgoBoxAssets])
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala
index 311475c134..05cd83daea 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ExtendedInputBox.scala
@@ -1,6 +1,7 @@
package org.ergoplatform.sdk
-import org.ergoplatform.{ErgoBox, UnsignedInput}
+import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, UnsignedInput}
+import scorex.util.ModifierId
import sigmastate.interpreter.ContextExtension
/** Input ErgoBox paired with context variables (aka ContextExtensions).
@@ -16,4 +17,25 @@ case class ExtendedInputBox(
extension: ContextExtension
) {
def toUnsignedInput: UnsignedInput = new UnsignedInput(box.id, extension)
+ def value: Long = box.value
}
+
+case class OutBox(candidate: ErgoBoxCandidate) {
+ /**
+ * Converts this box candidate into a new instance of {@link ExtendedInputBox} by
+ * associating it with the given transaction and output position.
+ * This method can be used to create input boxed from scratch, without
+ * retrieving them from the UTXOs. Thus created boxes can be indistinguishable from those
+ * loaded from blockchain node, and as result can be used to create new transactions.
+ * This method can also be used to create chains of transactions in advance
+ *
+ * @param txId the id of the transaction of which created the box which will be returned
+ * @param outputIndex zero-based position (index) of the box in the outputs of the transaction.
+ * @return a new {@link ExtendedInputBox} representing UTXO box as an input of a next transaction.
+ */
+ def convertToInputWith(txId: String, boxIndex: Short): ExtendedInputBox = {
+ val box = candidate.toBox(ModifierId @@ txId, boxIndex)
+ ExtendedInputBox(box, ContextExtension.empty)
+ }
+}
+
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala
index 840ec21576..426090b125 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Extensions.scala
@@ -2,8 +2,10 @@ package org.ergoplatform.sdk
import debox.cfor
import scalan.RType
-import scalan.rtypeToClassTag // actually required
+import scalan.rtypeToClassTag // actually used
+import sigmastate.eval.CPreHeader
import special.collection.{Coll, CollBuilder, PairColl}
+import special.sigma.{Header, PreHeader}
import scala.collection.compat.BuildFrom
import scala.collection.{GenIterable, immutable}
@@ -196,4 +198,14 @@ object Extensions {
builder.pairCollFromArrays(ks, vs)
}
}
+
+ implicit class HeaderOps(val h: Header) extends AnyVal {
+ def toPreHeader: PreHeader = {
+ CPreHeader(h.version, h.parentId, h.timestamp, h.nBits, h.height, h.minerPk, h.votes)
+ }
+ }
+
+ implicit class DoubleOps(val i: Double) extends AnyVal {
+ def erg: Long = (i * 1000000000L).toLong
+ }
}
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/InputBoxesValidator.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/InputBoxesValidator.scala
new file mode 100644
index 0000000000..7e3e24a8b7
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/InputBoxesValidator.scala
@@ -0,0 +1,127 @@
+package org.ergoplatform.sdk
+
+import org.ergoplatform.SigmaConstants.MaxBoxSize
+import org.ergoplatform.sdk.wallet.Constants.MaxAssetsPerBox
+import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap}
+import org.ergoplatform.{ErgoBoxAssets, ErgoBoxAssetsHolder}
+import scorex.util.ModifierId
+
+import scala.collection.mutable
+
+object BoxSelection {
+ // from https://github.com/ergoplatform/ergo/blob/2ce78a0380977b8ca354518edca93a5269ac9f53/src/main/scala/org/ergoplatform/settings/Parameters.scala#L258-L258
+ private val MinValuePerByteDefault = 30 * 12
+
+ val MinBoxValue: Long = (MaxBoxSize.value / 2L) * MinValuePerByteDefault
+
+ trait Error {
+ def message: String
+ }
+
+ final case class NotEnoughErgsError(
+ message: String,
+ balanceFound: Long) extends Error
+
+ final case class NotEnoughTokensError(
+ message: String,
+ tokensFound: Map[ModifierId, Long]) extends Error
+
+ final case class NotEnoughCoinsForChangeBoxesError(message: String) extends Error
+
+ /**
+ * Pass through implementation of the box selector. Unlike DefaultBoxSelector from ergo-wallet,
+ * it does not select input boxes. We do this in SDK ourselves and do not need the selector
+ * to interfere with how we built our transaction. Instead, this selector performs validation
+ * and calculates the necessary change box
+ */
+ class InputBoxesValidator {
+
+ def select[T <: ErgoBoxAssets](inputBoxes: Iterator[T],
+ externalFilter: T => Boolean,
+ targetBalance: Long,
+ targetAssets: TokensMap): Either[Error, BoxSelectionResult[T]] = {
+ //mutable structures to collect results
+ val res = mutable.Buffer[T]()
+ var currentBalance = 0L
+ val currentAssets = mutable.Map[ModifierId, Long]()
+
+ // select all input boxes - we only validate here
+ inputBoxes.foreach { box: T =>
+ currentBalance = currentBalance + box.value
+ AssetUtils.mergeAssetsMut(currentAssets, box.tokens)
+ res += box
+ }
+
+ if (currentBalance - targetBalance >= 0) {
+ //now check if we found all tokens
+ if (targetAssets.forall {
+ case (id, targetAmt) => currentAssets.getOrElse(id, 0L) >= targetAmt
+ }) {
+ formChangeBoxes(currentBalance, targetBalance, currentAssets, targetAssets) match {
+ case Right(changeBoxes) => Right(new BoxSelectionResult(res.toSeq, changeBoxes, None))
+ case Left(error) => Left(error)
+ }
+ } else {
+ Left(NotEnoughTokensError(
+ s"Not enough tokens in input boxes to send $targetAssets (found only $currentAssets)", currentAssets.toMap)
+ )
+ }
+ } else {
+ Left(NotEnoughErgsError(
+ s"not enough boxes to meet ERG needs $targetBalance (found only $currentBalance)", currentBalance)
+ )
+ }
+ }
+
+ /**
+ * Helper method to construct change outputs
+ *
+ * @param foundBalance - ERG balance of boxes collected
+ * (spendable only, so after possibly deducting re-emission tokens)
+ * @param targetBalance - ERG amount to be transferred to recipients
+ * @param foundBoxAssets - assets balances of boxes
+ * @param targetBoxAssets - assets amounts to be transferred to recipients
+ * @return
+ */
+ def formChangeBoxes(foundBalance: Long,
+ targetBalance: Long,
+ foundBoxAssets: mutable.Map[ModifierId, Long],
+ targetBoxAssets: TokensMap): Either[Error, Seq[ErgoBoxAssets]] = {
+ AssetUtils.subtractAssetsMut(foundBoxAssets, targetBoxAssets)
+ val changeBoxesAssets: Seq[mutable.Map[ModifierId, Long]] = foundBoxAssets.grouped(MaxAssetsPerBox).toSeq
+ val changeBalance = foundBalance - targetBalance
+ //at least a minimum amount of ERG should be assigned per a created box
+ if (changeBoxesAssets.size * MinBoxValue > changeBalance) {
+ Left(NotEnoughCoinsForChangeBoxesError(
+ s"Not enough nanoERGs ($changeBalance nanoERG) to create ${changeBoxesAssets.size} change boxes, \nfor $changeBoxesAssets"
+ ))
+ } else {
+ val changeBoxes = if (changeBoxesAssets.nonEmpty) {
+ val baseChangeBalance = changeBalance / changeBoxesAssets.size
+
+ val changeBoxesNoBalanceAdjusted = changeBoxesAssets.map { a =>
+ ErgoBoxAssetsHolder(baseChangeBalance, a.toMap)
+ }
+
+ val modifiedBoxOpt = changeBoxesNoBalanceAdjusted.headOption.map { firstBox =>
+ ErgoBoxAssetsHolder(
+ changeBalance - baseChangeBalance * (changeBoxesAssets.size - 1),
+ firstBox.tokens
+ )
+ }
+
+ modifiedBoxOpt.toSeq ++ changeBoxesNoBalanceAdjusted.tail
+ } else if (changeBalance > 0) {
+ Seq(ErgoBoxAssetsHolder(changeBalance))
+ } else {
+ Seq.empty
+ }
+
+ Right(changeBoxes)
+ }
+ }
+
+ }
+
+}
+
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala
index 4a49e23283..52c653e9db 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JavaHelpers.scala
@@ -109,21 +109,25 @@ object Iso extends LowPriorityIsos {
override def from(t: Token): ErgoToken = new ErgoToken(t._1.toArray, t._2)
}
- implicit val isoJListErgoTokenToMapPair: Iso[JList[ErgoToken], mutable.LinkedHashMap[ModifierId, Long]] =
- new Iso[JList[ErgoToken], mutable.LinkedHashMap[ModifierId, Long]] {
- override def to(a: JList[ErgoToken]): mutable.LinkedHashMap[ModifierId, Long] = {
- import JavaHelpers._
+ implicit val isoErgoTokenSeqToLinkedMap: Iso[IndexedSeq[ErgoToken], mutable.LinkedHashMap[ModifierId, Long]] =
+ new Iso[IndexedSeq[ErgoToken], mutable.LinkedHashMap[ModifierId, Long]] {
+ override def to(a: IndexedSeq[ErgoToken]): mutable.LinkedHashMap[ModifierId, Long] = {
val lhm = new mutable.LinkedHashMap[ModifierId, Long]()
- a.convertTo[IndexedSeq[Token]]
- .map(t => bytesToId(t._1.toArray) -> t._2)
- .foldLeft(lhm)(_ += _)
+ a.foreach { et =>
+ val t = isoErgoTokenToPair.to(et)
+ lhm += bytesToId(t._1.toArray) -> t._2
+ }
+ lhm
}
- override def from(t: mutable.LinkedHashMap[ModifierId, Long]): JList[ErgoToken] = {
- import JavaHelpers._
- val pairs: IndexedSeq[Token] = t.toIndexedSeq
- .map(t => (Digest32Coll @@ Colls.fromArray(idToBytes(t._1))) -> t._2)
- pairs.convertTo[JList[ErgoToken]]
+ override def from(t: mutable.LinkedHashMap[ModifierId, Long]): IndexedSeq[ErgoToken] = {
+ val pairs = t.toIndexedSeq
+ .map { t =>
+ val id = Digest32Coll @@ Colls.fromArray(idToBytes(t._1))
+ val value = t._2
+ isoErgoTokenToPair.from((id, value))
+ }
+ pairs
}
}
@@ -294,9 +298,6 @@ object JavaHelpers {
ErgoAlgos.encode(ErgoAlgos.hash(s))
}
- def toPreHeader(h: Header): special.sigma.PreHeader = {
- CPreHeader(h.version, h.parentId, h.timestamp, h.nBits, h.height, h.minerPk, h.votes)
- }
def toSigmaBoolean(ergoTree: ErgoTree): SigmaBoolean = {
val prop = ergoTree.toProposition(ergoTree.isConstantSegregation)
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala
index 42d25e09d4..52e1565ca3 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/JsonCodecs.scala
@@ -1,7 +1,6 @@
package org.ergoplatform.sdk
import java.math.BigInteger
-
import cats.syntax.either._
import io.circe._
import io.circe.syntax._
@@ -19,6 +18,7 @@ import sigmastate.interpreter.{ContextExtension, ProverResult}
import sigmastate.{AvlTreeData, AvlTreeFlags, SType}
import special.collection.Coll
import special.sigma.{AnyValue, Header, PreHeader}
+
import scala.util.Try
import sigmastate.utils.Helpers._ // required for Scala 2.11
import org.ergoplatform.ErgoBox
@@ -33,6 +33,8 @@ import org.ergoplatform.ErgoLikeTransactionTemplate
import org.ergoplatform.ErgoBoxCandidate
import org.ergoplatform.ErgoLikeContext
+import scala.collection.mutable
+
trait JsonCodecs {
def fromTry[T](tryResult: Try[T])(implicit cursor: ACursor): Either[DecodingFailure, T] = {
@@ -237,14 +239,14 @@ trait JsonCodecs {
})
implicit val contextExtensionEncoder: Encoder[ContextExtension] = Encoder.instance({ extension =>
- extension.values.map { case (key, value) =>
- key -> evaluatedValueEncoder(value)
- }.asJson
+ Json.obj(extension.values.toSeq.map { case (key, value) =>
+ key.toString -> evaluatedValueEncoder(value)
+ }: _*)
})
implicit val contextExtensionDecoder: Decoder[ContextExtension] = Decoder.instance({ cursor =>
for {
- values <- cursor.as[Map[Byte, EvaluatedValue[SType]]]
+ values <- cursor.as[mutable.LinkedHashMap[Byte, EvaluatedValue[SType]]]
} yield ContextExtension(values)
})
@@ -258,8 +260,8 @@ trait JsonCodecs {
implicit val proverResultDecoder: Decoder[ProverResult] = Decoder.instance({ cursor =>
for {
proofBytes <- cursor.downField("proofBytes").as[Array[Byte]]
- extMap <- cursor.downField("extension").as[Map[Byte, EvaluatedValue[SType]]]
- } yield ProverResult(proofBytes, ContextExtension(extMap))
+ ext <- cursor.downField("extension").as[ContextExtension]
+ } yield ProverResult(proofBytes, ext)
})
@@ -295,13 +297,17 @@ trait JsonCodecs {
decodeErgoTree(_.asInstanceOf[ErgoTree])
}
- implicit def registersEncoder[T <: EvaluatedValue[_ <: SType]]: Encoder[Map[NonMandatoryRegisterId, T]] = Encoder.instance({ m =>
+ implicit def registersEncoder[T <: EvaluatedValue[_ <: SType]]: Encoder[scala.collection.Map[NonMandatoryRegisterId, T]] = Encoder.instance({ m =>
Json.obj(
m.toSeq
.sortBy(_._1.number)
.map { case (k, v) => registerIdEncoder(k) -> evaluatedValueEncoder(v) }: _*)
})
+ implicit def registersDecoder[T <: EvaluatedValue[_ <: SType]]: Decoder[scala.collection.Map[NonMandatoryRegisterId, T]] = Decoder.instance({ implicit m =>
+ m.as[mutable.LinkedHashMap[NonMandatoryRegisterId, EvaluatedValue[SType]]].asInstanceOf[Decoder.Result[scala.collection.Map[NonMandatoryRegisterId, T]]]
+ })
+
implicit val ergoBoxEncoder: Encoder[ErgoBox] = Encoder.instance({ box =>
Json.obj(
"boxId" -> box.id.asJson,
@@ -321,7 +327,7 @@ trait JsonCodecs {
ergoTreeBytes <- cursor.downField("ergoTree").as[Array[Byte]]
additionalTokens <- cursor.downField("assets").as(Decoder.decodeSeq(assetDecoder))
creationHeight <- cursor.downField("creationHeight").as[Int]
- additionalRegisters <- cursor.downField("additionalRegisters").as[Map[NonMandatoryRegisterId, EvaluatedValue[SType]]]
+ additionalRegisters <- cursor.downField("additionalRegisters").as(registersDecoder)
transactionId <- cursor.downField("transactionId").as[ModifierId]
index <- cursor.downField("index").as[Short]
} yield new ErgoBox(
@@ -335,62 +341,86 @@ trait JsonCodecs {
)
})
+ implicit val ergoBoxCandidateEncoder: Encoder[ErgoBoxCandidate] = Encoder.instance({ box =>
+ Json.obj(
+ "value" -> box.value.asJson,
+ "ergoTree" -> ErgoTreeSerializer.DefaultSerializer.serializeErgoTree(box.ergoTree).asJson,
+ "assets" -> box.additionalTokens.toArray.toSeq.asJson,
+ "creationHeight" -> box.creationHeight.asJson,
+ "additionalRegisters" -> box.additionalRegisters.asJson
+ )
+ })
+
+ implicit val ergoBoxCandidateDecoder: Decoder[ErgoBoxCandidate] = Decoder.instance({ cursor =>
+ for {
+ value <- cursor.downField("value").as[Long]
+ ergoTreeBytes <- cursor.downField("ergoTree").as[Array[Byte]]
+ additionalTokens <- cursor.downField("assets").as(Decoder.decodeSeq(assetDecoder))
+ creationHeight <- cursor.downField("creationHeight").as[Int]
+ additionalRegisters <- cursor.downField("additionalRegisters").as(registersDecoder)
+ } yield new ErgoBoxCandidate(
+ value = value,
+ ergoTree = ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(ergoTreeBytes),
+ creationHeight = creationHeight,
+ additionalTokens = additionalTokens.toColl,
+ additionalRegisters = additionalRegisters
+ )
+ })
+
implicit val ergoLikeTransactionEncoder: Encoder[ErgoLikeTransaction] = Encoder.instance({ tx =>
Json.obj(
+ "type" -> "ELT".asJson, // ErgoLikeTransaction
"id" -> tx.id.asJson,
"inputs" -> tx.inputs.asJson,
"dataInputs" -> tx.dataInputs.asJson,
- "outputs" -> tx.outputs.asJson
+ "outputs" -> tx.outputCandidates.asJson
)
})
+ implicit val ergoLikeTransactionDecoder: Decoder[ErgoLikeTransaction] = Decoder.instance({ implicit cursor =>
+ for {
+ t <- cursor.downField("type").as[String]
+ inputs <- {require(t == "ELT"); cursor.downField("inputs").as[IndexedSeq[Input]] }
+ dataInputs <- cursor.downField("dataInputs").as[IndexedSeq[DataInput]]
+ outputs <- cursor.downField("outputs").as[IndexedSeq[ErgoBoxCandidate]]
+ } yield new ErgoLikeTransaction(inputs, dataInputs, outputs)
+ })
+
implicit val unsignedErgoLikeTransactionEncoder: Encoder[UnsignedErgoLikeTransaction] = Encoder.instance({ tx =>
Json.obj(
+ "type" -> "UELT".asJson, // UnsignedErgoLikeTransaction
"id" -> tx.id.asJson,
"inputs" -> tx.inputs.asJson,
"dataInputs" -> tx.dataInputs.asJson,
- "outputs" -> tx.outputs.asJson
+ "outputs" -> tx.outputCandidates.asJson
)
})
+ implicit val unsignedErgoLikeTransactionDecoder: Decoder[UnsignedErgoLikeTransaction] = Decoder.instance({ implicit cursor =>
+ for {
+ t <- cursor.downField("type").as[String]
+ inputs <- {require(t == "UELT"); cursor.downField("inputs").as[IndexedSeq[UnsignedInput]] }
+ dataInputs <- cursor.downField("dataInputs").as[IndexedSeq[DataInput]]
+ outputs <- cursor.downField("outputs").as[IndexedSeq[ErgoBoxCandidate]]
+ } yield new UnsignedErgoLikeTransaction(inputs, dataInputs, outputs)
+ })
+
implicit def ergoLikeTransactionTemplateEncoder[T <: UnsignedInput]: Encoder[ErgoLikeTransactionTemplate[T]] = Encoder.instance({
case transaction: ErgoLikeTransaction => ergoLikeTransactionEncoder(transaction)
case transaction: UnsignedErgoLikeTransaction => unsignedErgoLikeTransactionEncoder(transaction)
case t => throw new SigmaException(s"Don't know how to encode transaction $t")
})
- implicit val transactionOutputsDecoder: Decoder[(ErgoBoxCandidate, Option[BoxId])] = Decoder.instance({ cursor =>
- for {
- maybeId <- cursor.downField("boxId").as[Option[BoxId]]
- value <- cursor.downField("value").as[Long]
- creationHeight <- cursor.downField("creationHeight").as[Int]
- ergoTree <- cursor.downField("ergoTree").as[ErgoTree]
- assets <- cursor.downField("assets").as[Seq[(ErgoBox.TokenId, Long)]] // TODO optimize: encode directly into Coll avoiding allocation of Tuple2 for each element
- registers <- cursor.downField("additionalRegisters").as[Map[NonMandatoryRegisterId, EvaluatedValue[SType]]]
- } yield (new ErgoBoxCandidate(value, ergoTree, creationHeight, assets.toColl, registers), maybeId)
- })
-
- implicit val ergoLikeTransactionDecoder: Decoder[ErgoLikeTransaction] = Decoder.instance({ implicit cursor =>
- for {
- inputs <- cursor.downField("inputs").as[IndexedSeq[Input]]
- dataInputs <- cursor.downField("dataInputs").as[IndexedSeq[DataInput]]
- outputsWithIndex <- cursor.downField("outputs").as[IndexedSeq[(ErgoBoxCandidate, Option[BoxId])]]
- } yield new ErgoLikeTransaction(inputs, dataInputs, outputsWithIndex.map(_._1))
- })
-
- implicit val unsignedErgoLikeTransactionDecoder: Decoder[UnsignedErgoLikeTransaction] = Decoder.instance({ implicit cursor =>
+ implicit val ergoLikeTransactionTemplateDecoder: Decoder[ErgoLikeTransactionTemplate[_ <: UnsignedInput]] = Decoder.instance({ implicit cursor =>
for {
- inputs <- cursor.downField("inputs").as[IndexedSeq[UnsignedInput]]
- dataInputs <- cursor.downField("dataInputs").as[IndexedSeq[DataInput]]
- outputsWithIndex <- cursor.downField("outputs").as[IndexedSeq[(ErgoBoxCandidate, Option[BoxId])]]
- } yield new UnsignedErgoLikeTransaction(inputs, dataInputs, outputsWithIndex.map(_._1))
+ t <- cursor.downField("type").as[String]
+ tx <- t match {
+ case "ELT" => ergoLikeTransactionDecoder(cursor)
+ case "UELT" => unsignedErgoLikeTransactionDecoder(cursor)
+ }
+ } yield tx
})
- implicit val ergoLikeTransactionTemplateDecoder: Decoder[ErgoLikeTransactionTemplate[_ <: UnsignedInput]] = {
- ergoLikeTransactionDecoder.asInstanceOf[Decoder[ErgoLikeTransactionTemplate[_ <: UnsignedInput]]] or
- unsignedErgoLikeTransactionDecoder.asInstanceOf[Decoder[ErgoLikeTransactionTemplate[_ <: UnsignedInput]]]
- }
-
implicit val sigmaValidationSettingsEncoder: Encoder[SigmaValidationSettings] = Encoder.instance({ v =>
SigmaValidationSettingsSerializer.toBytes(v).asJson
})
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/NetworkType.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/NetworkType.scala
new file mode 100644
index 0000000000..806ce6b014
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/NetworkType.scala
@@ -0,0 +1,49 @@
+package org.ergoplatform.sdk
+
+import org.ergoplatform.ErgoAddressEncoder
+import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
+
+/**
+ * Enumeration of network types as they are defined by Ergo specification of {@link ErgoAddress}.
+ */
+abstract class NetworkType {
+
+ /**
+ * The network prefix code used in Ergo addresses
+ */
+ val networkPrefix: NetworkPrefix
+
+ /**
+ * verbose name for network type as reported by Node API
+ */
+ val verboseName: String
+}
+
+object NetworkType {
+ /** Mainnet network type.
+ *
+ * @see ErgoAddressEncoder#MainnetNetworkPrefix()
+ */
+ case object Mainnet extends NetworkType {
+ override val networkPrefix: NetworkPrefix = ErgoAddressEncoder.MainnetNetworkPrefix
+ override val verboseName = "mainnet"
+ }
+
+ /** Testnet network type.
+ *
+ * @see ErgoAddressEncoder#TestnetNetworkPrefix()
+ */
+ case object Testnet extends NetworkType {
+ override val networkPrefix: NetworkPrefix = ErgoAddressEncoder.TestnetNetworkPrefix
+ override val verboseName = "testnet"
+ }
+
+ /** @return network type for given verbose name */
+ def fromName(name: String): Option[NetworkType] = name match {
+ case "mainnet" => Some(Mainnet)
+ case "testnet" => Some(Testnet)
+ case _ => None
+ }
+
+}
+
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/OutBoxBuilder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/OutBoxBuilder.scala
new file mode 100644
index 0000000000..5c2f531588
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/OutBoxBuilder.scala
@@ -0,0 +1,80 @@
+package org.ergoplatform.sdk
+
+import org.ergoplatform.ErgoBox.TokenId
+import org.ergoplatform.sdk.JavaHelpers.collRType
+import org.ergoplatform.{ErgoBox, ErgoBoxCandidate, SigmaConstants}
+import scalan.RType
+import sigmastate.SType
+import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue}
+import sigmastate.eval.Colls
+
+import scala.collection.mutable.ArrayBuffer
+
+class OutBoxBuilder(val _txB: UnsignedTransactionBuilder) {
+ private val _ctx = _txB.ctx
+ private var _value: Long = 0
+ private var _contract: ErgoTree = _
+ private val _tokens = ArrayBuffer.empty[ErgoToken]
+ private val _registers = ArrayBuffer.empty[Constant[_]]
+ private var _creationHeightOpt: Option[Int] = None
+
+ def value(value: Long): this.type = {
+ _value = value
+ this
+ }
+
+ def contract(contract: ErgoTree): this.type = {
+ _contract = contract
+ this
+ }
+
+ def tokens(tokens: ErgoToken*): this.type = {
+ require(tokens.nonEmpty, "At least one token should be specified")
+ val maxTokens = SigmaConstants.MaxTokens.value
+ require(tokens.size <= maxTokens, SigmaConstants.MaxTokens.description + s": $maxTokens")
+ _tokens ++= tokens
+ this
+ }
+
+ def registers(registers: Constant[_]*): this.type = {
+ require(registers.nonEmpty, "At least one register should be specified")
+ _registers.clear()
+ _registers ++= registers
+ this
+ }
+
+ def creationHeight(height: Int): OutBoxBuilder = {
+ _creationHeightOpt = Some(height)
+ this
+ }
+
+ def build(): OutBox = {
+ require(_contract != null, "Contract is not defined")
+ val ergoBoxCandidate = OutBoxBuilder.createBoxCandidate(
+ _value, _contract, _tokens.toSeq, _registers.toSeq,
+ creationHeight = _creationHeightOpt.getOrElse(_txB.ctx.height))
+ OutBox(ergoBoxCandidate)
+ }
+}
+
+object OutBoxBuilder {
+ def apply(txB: UnsignedTransactionBuilder): OutBoxBuilder = new OutBoxBuilder(txB)
+
+ private[sdk] def createBoxCandidate(
+ value: Long, tree: ErgoTree,
+ tokens: Seq[ErgoToken],
+ registers: Seq[Constant[_]], creationHeight: Int): ErgoBoxCandidate = {
+ import org.ergoplatform.ErgoBox.nonMandatoryRegisters
+ val nRegs = registers.length
+ require(nRegs <= nonMandatoryRegisters.length,
+ s"Too many additional registers $nRegs. Max allowed ${nonMandatoryRegisters.length}")
+ implicit val TokenIdRType: RType[TokenId] = collRType(RType.ByteType).asInstanceOf[RType[TokenId]]
+ val ts = Colls.fromItems(tokens.map(Iso.isoErgoTokenToPair.to(_)): _*)
+ val rs = registers.zipWithIndex.map { case (c, i) =>
+ val id = ErgoBox.nonMandatoryRegisters(i)
+ id -> c.asInstanceOf[EvaluatedValue[_ <: SType]]
+ }.toMap
+ new ErgoBoxCandidate(value, tree, creationHeight, ts, rs)
+ }
+}
+
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ProverBuilder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ProverBuilder.scala
index 2bdfe48fb1..0704cbe8d6 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ProverBuilder.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ProverBuilder.scala
@@ -1,7 +1,6 @@
package org.ergoplatform.sdk
-import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
-import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeParameters
+import org.ergoplatform.ErgoAddressEncoder.{MainnetNetworkPrefix, NetworkPrefix}
import org.ergoplatform.sdk.wallet.secrets.ExtendedSecretKey
import sigmastate.basics.DLogProtocol.DLogProverInput
import sigmastate.basics.{DLogProtocol, DiffieHellmanTupleProverInput}
@@ -10,8 +9,14 @@ import special.sigma.GroupElement
import java.math.BigInteger
import scala.collection.mutable.ArrayBuffer
-/** A builder class for constructing a `Prover` with specified secrets. */
-class ProverBuilder(parameters: ErgoLikeParameters, networkPrefix: NetworkPrefix) {
+/** A builder class for constructing a `Prover` with specified secrets.
+ *
+ * @param parameters Blockchain parameters re-adjustable via miners voting and
+ * voting-related data. All of them are included into extension
+ * section of a first block of a voting epoch.
+ * @param networkPrefix Network prefix to use for addresses.
+ */
+class ProverBuilder(parameters: BlockchainParameters, networkPrefix: NetworkPrefix) {
private var _masterKey: Option[ExtendedSecretKey] = None
/** Generated EIP-3 secret keys paired with their derivation path index. */
@@ -27,7 +32,7 @@ class ProverBuilder(parameters: ErgoLikeParameters, networkPrefix: NetworkPrefix
def withMnemonic(
mnemonicPhrase: SecretString,
mnemonicPass: SecretString,
- usePre1627KeyDerivation: Boolean
+ usePre1627KeyDerivation: Boolean = false
): ProverBuilder = {
_masterKey = Some(JavaHelpers.seedToMasterKey(mnemonicPhrase, mnemonicPass, usePre1627KeyDerivation))
this
@@ -89,3 +94,8 @@ class ProverBuilder(parameters: ErgoLikeParameters, networkPrefix: NetworkPrefix
}
}
+object ProverBuilder {
+ def forMainnet(parameters: BlockchainParameters): ProverBuilder =
+ new ProverBuilder(parameters, MainnetNetworkPrefix)
+}
+
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala
index b7d8547e32..f06c37fd39 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/ReducingInterpreter.scala
@@ -3,7 +3,7 @@ package org.ergoplatform.sdk
import org.ergoplatform.sdk.Extensions.{CollOps, PairCollOps}
import org.ergoplatform.sdk.JavaHelpers.UniversalConverter
import org.ergoplatform.sdk.utils.ArithUtils
-import org.ergoplatform.sdk.wallet.protocol.context.{ErgoLikeParameters, ErgoLikeStateContext, TransactionContext}
+import org.ergoplatform.sdk.wallet.protocol.context.{BlockchainStateContext, TransactionContext}
import org.ergoplatform.validation.ValidationRules
import org.ergoplatform.{ErgoLikeContext, ErgoLikeInterpreter}
import scalan.util.Extensions.LongOps
@@ -20,7 +20,7 @@ import java.util.{Objects, List => JList}
import scala.collection.mutable
/** Interpreter that can reduce transactions with given chain parameters. */
-class ReducingInterpreter(params: ErgoLikeParameters) extends ErgoLikeInterpreter {
+class ReducingInterpreter(params: BlockchainParameters) extends ErgoLikeInterpreter {
override type CTX = ErgoLikeContext
import org.ergoplatform.sdk.Iso._
@@ -56,7 +56,7 @@ class ReducingInterpreter(params: ErgoLikeParameters) extends ErgoLikeInterprete
*/
def reduceTransaction(
unreducedTx: UnreducedTransaction,
- stateContext: ErgoLikeStateContext,
+ stateContext: BlockchainStateContext,
baseCost: Int
): ReducedTransaction = {
val unsignedTx = unreducedTx.unsignedTx
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala
index 255534e14b..8090627083 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/SigmaProver.scala
@@ -2,7 +2,7 @@ package org.ergoplatform.sdk
import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
import org.ergoplatform._
-import org.ergoplatform.sdk.wallet.protocol.context.ErgoLikeStateContext
+import org.ergoplatform.sdk.wallet.protocol.context.BlockchainStateContext
import sigmastate.eval.{CostingSigmaDslBuilder, SigmaDsl}
import sigmastate.interpreter.HintsBag
import sigmastate.utils.Helpers.TryOps
@@ -16,7 +16,10 @@ import special.sigma.{BigInt, SigmaProp}
class SigmaProver(_prover: AppkitProvingInterpreter, networkPrefix: NetworkPrefix) {
implicit val ergoAddressEncoder: ErgoAddressEncoder = ErgoAddressEncoder(networkPrefix)
- /** Returns the Pay-to-Public-Key (P2PK) address associated with the prover's public key. */
+ /** Returns the Pay-to-Public-Key (P2PK) address associated with the prover's public key.
+ * The returned address corresponds to the master secret derived from the mnemonic
+ * phrase configured in the [[ProverBuilder]].
+ */
def getP2PKAddress: P2PKAddress = {
val pk = _prover.pubKeys(0)
P2PKAddress(pk)
@@ -37,16 +40,16 @@ class SigmaProver(_prover: AppkitProvingInterpreter, networkPrefix: NetworkPrefi
addresses
}
- /** Signs a given `UnreducedTransaction` using the prover's secret keys and the provided `ErgoLikeStateContext`.
+ /** Signs a given `UnreducedTransaction` using the prover's secret keys and the provided [[BlockchainStateContext]].
* Uses baseCost == 0.
*/
- def sign(stateCtx: ErgoLikeStateContext, tx: UnreducedTransaction): SignedTransaction =
+ def sign(stateCtx: BlockchainStateContext, tx: UnreducedTransaction): SignedTransaction =
sign(stateCtx, tx, baseCost = 0)
- /** Signs a given `UnreducedTransaction` using the prover's secret keys and the provided `ErgoLikeStateContext`.
+ /** Signs a given `UnreducedTransaction` using the prover's secret keys and the provided [[BlockchainStateContext]].
* Uses the given baseCost.
*/
- def sign(stateCtx: ErgoLikeStateContext, tx: UnreducedTransaction, baseCost: Int): SignedTransaction = {
+ def sign(stateCtx: BlockchainStateContext, tx: UnreducedTransaction, baseCost: Int): SignedTransaction = {
val signed = _prover
.sign(tx, stateContext = stateCtx, baseCost = baseCost)
.getOrThrow
@@ -65,9 +68,9 @@ class SigmaProver(_prover: AppkitProvingInterpreter, networkPrefix: NetworkPrefi
}
/** Reduces a given `UnreducedTransaction` using the prover's secret keys and the
- * provided `ErgoLikeStateContext` with a base cost.
+ * provided [[BlockchainStateContext]] with a base cost.
*/
- def reduce(stateCtx: ErgoLikeStateContext, tx: UnreducedTransaction, baseCost: Int): ReducedTransaction = {
+ def reduce(stateCtx: BlockchainStateContext, tx: UnreducedTransaction, baseCost: Int): ReducedTransaction = {
val reduced = _prover.reduceTransaction(
unreducedTx = tx, stateContext = stateCtx, baseCost = baseCost)
reduced
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala
index 8073b77c9e..275814a983 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/Transactions.scala
@@ -1,6 +1,9 @@
package org.ergoplatform.sdk
+import org.ergoplatform.sdk.JavaHelpers.StringExtensions
import org.ergoplatform.{ErgoBox, ErgoLikeTransaction, UnsignedErgoLikeTransaction}
+import sigmastate.eval.Extensions.ArrayByteOps
+import sigmastate.serialization.SigmaSerializer
import java.util
@@ -45,7 +48,23 @@ case class UnreducedTransaction(
}
/** Represents results for transaction reduction by [[ReducingInterpreter]]. */
-case class ReducedTransaction(ergoTx: ReducedErgoLikeTransaction)
+case class ReducedTransaction(ergoTx: ReducedErgoLikeTransaction) {
+ /** Serialized bytes of this transaction in hex format. */
+ def toHex: String = {
+ val w = SigmaSerializer.startWriter()
+ ReducedErgoLikeTransactionSerializer.serialize(ergoTx, w)
+ w.toBytes.toHex
+ }
+}
+
+object ReducedTransaction {
+ /** Creates a [[ReducedTransaction]] from serialized bytes in hex format. */
+ def fromHex(hex: String): ReducedTransaction = {
+ val r = SigmaSerializer.startReader(hex.toBytes)
+ val tx = ReducedErgoLikeTransactionSerializer.parse(r)
+ ReducedTransaction(tx)
+ }
+}
/** Represents results for transaction signing by a prover like [[SigmaProver]]. */
case class SignedTransaction(ergoTx: ErgoLikeTransaction, cost: Int)
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/UnsignedTransactionBuilder.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/UnsignedTransactionBuilder.scala
new file mode 100644
index 0000000000..e48f26fd6f
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/UnsignedTransactionBuilder.scala
@@ -0,0 +1,240 @@
+package org.ergoplatform.sdk
+
+import org.ergoplatform.ErgoBox.TokenId
+import org.ergoplatform._
+import org.ergoplatform.sdk.BlockchainParameters.MinChangeValue
+import org.ergoplatform.sdk.BoxSelection.InputBoxesValidator
+import org.ergoplatform.sdk.Extensions.HeaderOps
+import org.ergoplatform.sdk.wallet.{AssetUtils, TokensMap}
+import scorex.util.{ModifierId, bytesToId}
+import sigmastate.eval.Extensions.ArrayOps
+import sigmastate.utils.Extensions.ModifierIdOps
+import special.collection.Coll
+import special.collection.Extensions.CollBytesOps
+import special.sigma.PreHeader
+
+import scala.collection.mutable.ArrayBuffer
+import scala.util.Try
+
+class UnsignedTransactionBuilder(val ctx: BlockchainContext) {
+ private[sdk] val _inputs: ArrayBuffer[ExtendedInputBox] = ArrayBuffer.empty[ExtendedInputBox]
+ private[sdk] val _outputs: ArrayBuffer[OutBox] = ArrayBuffer.empty[OutBox]
+ private[sdk] val _dataInputs: ArrayBuffer[ErgoBox] = ArrayBuffer.empty[ErgoBox]
+
+ private var _tokensToBurn: Option[ArrayBuffer[ErgoToken]] = None
+ private var _feeAmount: Option[Long] = None
+ private var _changeAddress: Option[ErgoAddress] = None
+ private var _ph: Option[PreHeader] = None
+
+ def preHeader(ph: PreHeader): this.type = {
+ require(_ph.isEmpty, "PreHeader is already specified")
+ _ph = Some(ph)
+ this
+ }
+
+ def addInputs(boxes: ExtendedInputBox*): this.type = {
+ _inputs ++= boxes
+ this
+ }
+
+ def addDataInputs(boxes: ErgoBox*): this.type = {
+ _dataInputs ++= boxes
+ this
+ }
+
+ def addOutputs(outBoxes: OutBox*): this.type = {
+ _outputs ++= outBoxes
+ this
+ }
+
+ def fee(feeAmount: Long): this.type = {
+ require(_feeAmount.isEmpty, "Fee already defined")
+ _feeAmount = Some(feeAmount)
+ this
+ }
+
+ def addTokensToBurn(tokens: ErgoToken*): this.type = {
+ if (_tokensToBurn.isEmpty)
+ _tokensToBurn = Some(ArrayBuffer.empty[ErgoToken])
+
+ _tokensToBurn.get ++= tokens
+ this
+ }
+
+ def sendChangeTo(changeAddress: ErgoAddress): this.type = {
+ require(_changeAddress.isEmpty, "Change address is already specified")
+ _changeAddress = Some(changeAddress)
+ this
+ }
+
+ private def getDefined[T](opt: Option[T], msg: => String): T = {
+ opt match {
+ case Some(x) => x
+ case _ =>
+ throw new IllegalArgumentException("requirement failed: " + msg)
+ }
+ }
+
+ def build(): UnreducedTransaction = {
+ val boxesToSpend = _inputs.toIndexedSeq
+ val outputCandidates = _outputs.map(c => c.candidate).toIndexedSeq
+ require(!outputCandidates.isEmpty, "Output boxes are not specified")
+
+ val dataInputBoxes = _dataInputs.toIndexedSeq
+ val dataInputs = _dataInputs.map(box => DataInput(box.id)).toIndexedSeq
+ require(_feeAmount.isEmpty || _feeAmount.get >= BlockchainParameters.MinFee,
+ s"When fee amount is defined it should be >= ${BlockchainParameters.MinFee}, got ${_feeAmount.get}")
+ val changeAddress = getDefined(_changeAddress, "Change address is not defined")
+ val inputBoxesSeq = boxesToSpend.map(eb => eb.box)
+ val requestedToBurn = _tokensToBurn.fold(IndexedSeq.empty[ErgoToken])(_.toIndexedSeq)
+ val burnTokens = Iso.isoErgoTokenSeqToLinkedMap.to(requestedToBurn).toMap
+ val rewardDelay = ctx.networkType match {
+ case NetworkType.Mainnet => BlockchainParameters.MinerRewardDelay_Mainnet
+ case NetworkType.Testnet => BlockchainParameters.MinerRewardDelay_Testnet
+ }
+ val tx = UnsignedTransactionBuilder.buildUnsignedTx(
+ inputs = inputBoxesSeq, dataInputs = dataInputs, outputCandidates = outputCandidates,
+ currentHeight = ctx.height, createFeeOutput = _feeAmount,
+ changeAddress = changeAddress, minChangeValue = MinChangeValue,
+ minerRewardDelay = rewardDelay,
+ burnTokens = burnTokens).get
+
+ // the method above don't accept ContextExtension along with inputs, thus, after the
+ // transaction has been built we need to zip with the extensions that have been
+ // attached to the inputBoxes
+ val txWithExtensions = new UnsignedErgoLikeTransaction(
+ inputs = boxesToSpend.map(_.toUnsignedInput),
+ tx.dataInputs, tx.outputCandidates
+ )
+ UnreducedTransaction(txWithExtensions, boxesToSpend, dataInputBoxes, requestedToBurn)
+ }
+
+ def preHeader: PreHeader = _ph.getOrElse(ctx.headers(0).toPreHeader)
+
+ def outBoxBuilder: OutBoxBuilder = OutBoxBuilder(this)
+
+ def networkType: NetworkType = ctx.networkType
+
+ def inputBoxes: IndexedSeq[ExtendedInputBox] = _inputs.toIndexedSeq
+
+ def outputBoxes: IndexedSeq[OutBox] = _outputs.toIndexedSeq
+}
+
+object UnsignedTransactionBuilder {
+ def apply(ctx: BlockchainContext): UnsignedTransactionBuilder = new UnsignedTransactionBuilder(ctx)
+
+ private def validateStatelessChecks(
+ inputs: IndexedSeq[ErgoBox], dataInputs: IndexedSeq[DataInput],
+ outputCandidates: Seq[ErgoBoxCandidate]): Unit = {
+ // checks from ErgoTransaction.validateStateless
+ require(inputs.nonEmpty, "inputs cannot be empty")
+ require(outputCandidates.nonEmpty, "outputCandidates cannot be empty")
+ require(inputs.size <= Short.MaxValue, s"too many inputs - ${inputs.size} (max ${Short.MaxValue})")
+ require(dataInputs.size <= Short.MaxValue, s"too many dataInputs - ${dataInputs.size} (max ${Short.MaxValue})")
+ require(outputCandidates.size <= Short.MaxValue,
+ s"too many outputCandidates - ${outputCandidates.size} (max ${Short.MaxValue})")
+ require(outputCandidates.forall(_.value >= 0), s"outputCandidate.value must be >= 0")
+ val outputSumTry = Try(outputCandidates.map(_.value).reduce(java7.compat.Math.addExact(_, _)))
+ require(outputSumTry.isSuccess, s"Sum of transaction output values should not exceed ${Long.MaxValue}")
+ require(inputs.distinct.size == inputs.size, s"There should be no duplicate inputs")
+ }
+
+ def collectOutputTokens(outputCandidates: Seq[ErgoBoxCandidate]): TokensMap = {
+ AssetUtils.mergeAssets(
+ initialMap = Map.empty[ModifierId, Long],
+ maps = outputCandidates.map(b => collTokensToMap(b.additionalTokens)): _*)
+ }
+
+ def collTokensToMap(tokens: Coll[(TokenId, Long)]): TokensMap =
+ tokens.toArray.map(t => t._1.toModifierId -> t._2).toMap
+
+ def tokensMapToColl(tokens: TokensMap): Coll[(TokenId, Long)] =
+ tokens.toArray.map { t => t._1.toTokenId -> t._2 }.toColl
+
+ /** Creates unsigned transaction from given inputs and outputs adding outputs with miner's fee and change
+ * Runs required checks ensuring that resulted transaction will be successfully validated by a node.
+ *
+ * @param inputs - input boxes
+ * @param dataInputs - data inputs
+ * @param outputCandidates - output candidate boxes
+ * @param currentHeight - current height (used in miner's fee box and change box)
+ * @param createFeeOutput - optional fee amount to put in a new miner's fee box, which will be
+ * created by this method. If None, then feeOut is not created.
+ * @param changeAddress - address where to send change from the input boxes
+ * @param minChangeValue - minimum change value to send, otherwise add to miner's fee
+ * @param minerRewardDelay - reward delay to encode in miner's fee box
+ * @return unsigned transaction
+ */
+ def buildUnsignedTx(
+ inputs: IndexedSeq[ErgoBox],
+ dataInputs: IndexedSeq[DataInput],
+ outputCandidates: Seq[ErgoBoxCandidate],
+ currentHeight: Int,
+ createFeeOutput: Option[Long],
+ changeAddress: ErgoAddress,
+ minChangeValue: Long,
+ minerRewardDelay: Int,
+ burnTokens: TokensMap = Map.empty
+ ): Try[UnsignedErgoLikeTransaction] = Try {
+ validateStatelessChecks(inputs, dataInputs, outputCandidates)
+
+ // TODO: implement all appropriate checks from ErgoTransaction.validateStatefull
+ val feeAmount = createFeeOutput.getOrElse(0L)
+ require(createFeeOutput.fold(true)(_ > 0), s"expected fee amount > 0, got $feeAmount")
+ val inputTotal = inputs.map(_.value).sum
+ val outputSum = outputCandidates.map(_.value).sum
+ val outputTotal = outputSum + feeAmount
+ val changeAmt = inputTotal - outputTotal
+ require(changeAmt >= 0, s"total inputs $inputTotal is less then total outputs $outputTotal")
+ val firstInputBoxId = bytesToId(inputs(0).id)
+ val tokensOut = collectOutputTokens(outputCandidates)
+ // remove minted tokens if any
+ val tokensOutNoMinted = tokensOut.filterKeys(_ != firstInputBoxId)
+ val mintedTokensNum = tokensOut.size - tokensOutNoMinted.size
+ require(mintedTokensNum <= 1, s"Only one token can be minted, but found $mintedTokensNum")
+ require(burnTokens.values.forall(_ > 0),
+ s"Incorrect burnTokens specification, positive values are expected: $burnTokens")
+ // add burnTokens to target assets so that they are excluded from the change outputs
+ // thus total outputs assets will be reduced which is interpreted as _token burning_
+ val tokensOutWithBurned = AssetUtils.mergeAssets(tokensOutNoMinted.toMap, burnTokens)
+ val boxSelector = new InputBoxesValidator
+ val selection = boxSelector.select[ErgoBox](inputs.iterator, _ => true, outputTotal, tokensOutWithBurned) match {
+ case Left(err) => throw new IllegalArgumentException(
+ s"failed to calculate change for outputTotal: $outputTotal, \ntokens: $tokensOut, \nburnTokens: $burnTokens, \ninputs: $inputs, \nreason: $err")
+ case Right(v) => v
+ }
+ // although we're only interested in change boxes, make sure selection contains exact inputs
+ assert(selection.inputBoxes == inputs, s"unexpected selected boxes, expected: $inputs, got ${selection.inputBoxes}")
+ val changeBoxes = selection.changeBoxes
+ val changeBoxesHaveTokens = changeBoxes.exists(_.tokens.nonEmpty)
+ val changeGoesToFee = changeAmt < minChangeValue && !changeBoxesHaveTokens
+ require(!changeGoesToFee || (changeAmt == 0 || createFeeOutput.isDefined),
+ s"""When change=$changeAmt < minChangeValue=$minChangeValue it is added to miner's fee,
+ |in this case createFeeOutput should be defined
+ |""".stripMargin)
+ val feeOutOpt = createFeeOutput.map { fee =>
+ // if computed changeAmt is too small give it to miner as tips
+ val actualFee = if (changeGoesToFee) fee + changeAmt else fee
+ new ErgoBoxCandidate(
+ actualFee,
+ ErgoTreePredef.feeProposition(minerRewardDelay),
+ currentHeight
+ )
+ }
+ val addedChangeOut = if (!changeGoesToFee) {
+ val script = changeAddress.script
+ changeBoxes.map { cb =>
+ new ErgoBoxCandidate(cb.value, script, currentHeight, tokensMapToColl(cb.tokens))
+ }
+ } else {
+ Seq()
+ }
+ val finalOutputCandidates = outputCandidates ++ feeOutOpt ++ addedChangeOut
+ new UnsignedErgoLikeTransaction(
+ inputs.map(b => new UnsignedInput(b.id)),
+ dataInputs,
+ finalOutputCandidates.toIndexedSeq
+ )
+ }
+}
+
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/BlockchainStateContext.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/BlockchainStateContext.scala
new file mode 100644
index 0000000000..5c8d97df4f
--- /dev/null
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/BlockchainStateContext.scala
@@ -0,0 +1,15 @@
+package org.ergoplatform.sdk.wallet.protocol.context
+
+import special.collection.Coll
+
+/** Blockchain context used in tx signing.
+ *
+ * @param sigmaLastHeaders fixed number (10 in Ergo) of last block headers
+ * @param previousStateDigest UTXO set digest from a last header (of sigmaLastHeaders)
+ * @param sigmaPreHeader returns pre-header (header without certain fields) of the current block
+ */
+case class BlockchainStateContext(
+ sigmaLastHeaders: Coll[special.sigma.Header],
+ previousStateDigest: Coll[Byte],
+ sigmaPreHeader: special.sigma.PreHeader
+)
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/ErgoLikeParameters.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/ErgoLikeParameters.scala
deleted file mode 100644
index b84387e2b3..0000000000
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/ErgoLikeParameters.scala
+++ /dev/null
@@ -1,63 +0,0 @@
-package org.ergoplatform.sdk.wallet.protocol.context
-
-/**
- * Blockchain parameters readjustable via miners voting and voting-related data.
- * All these fields are included into extension section of a first block of a voting epoch.
- */
-trait ErgoLikeParameters {
-
- /**
- * @return cost of storing 1 byte in UTXO for four years, in nanoErgs
- */
- def storageFeeFactor: Int
-
- /**
- * @return cost of a transaction output, in computation unit
- */
- def minValuePerByte: Int
-
- /**
- * @return max block size, in bytes
- */
- def maxBlockSize: Int
-
- /**
- * @return cost of a token contained in a transaction, in computation unit
- */
- def tokenAccessCost: Int
-
- /**
- * @return cost of a transaction input, in computation unit
- */
- def inputCost: Int
-
- /**
- * @return cost of a transaction data input, in computation unit
- */
- def dataInputCost: Int
-
- /**
- * @return cost of a transaction output, in computation unit
- */
- def outputCost: Int
-
- /**
- * @return computation units limit per block
- */
- def maxBlockCost: Int
-
- /**
- * @return height when voting for a soft-fork had been started
- */
- def softForkStartingHeight: Option[Int]
-
- /**
- * @return votes for soft-fork collected in previous epochs
- */
- def softForkVotesCollected: Option[Int]
-
- /**
- * @return Protocol version
- */
- def blockVersion: Byte
-}
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/ErgoLikeStateContext.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/ErgoLikeStateContext.scala
deleted file mode 100644
index b150c83218..0000000000
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/protocol/context/ErgoLikeStateContext.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-package org.ergoplatform.sdk.wallet.protocol.context
-
-import scorex.crypto.authds.ADDigest
-import special.collection.Coll
-
-import java.util
-
-/**
- * Blockchain context used in transaction validation.
- */
-trait ErgoLikeStateContext {
-
- /**
- * @return fixed number (10 in Ergo) of last block headers
- */
- def sigmaLastHeaders: Coll[special.sigma.Header]
-
- // todo remove from ErgoLikeContext and from ErgoStateContext
- /**
- * @return UTXO set digest from a last header (of sigmaLastHeaders)
- */
- def previousStateDigest: Coll[Byte]
-
- /**
- * @return returns pre-header (header without certain fields) of the current block
- */
- def sigmaPreHeader: special.sigma.PreHeader
-}
-
-/** Representis the Ergo-like state context for tx signing.
- *
- * @param sigmaLastHeaders the last headers of the Sigma blockchain
- * @param previousStateDigest the bytes representing the previous state digest
- * @param sigmaPreHeader the pre-header object
- */
-case class CErgoLikeStateContext(
- sigmaLastHeaders: Coll[special.sigma.Header],
- previousStateDigest: Coll[Byte],
- sigmaPreHeader: special.sigma.PreHeader
-) extends ErgoLikeStateContext {
-}
diff --git a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/SecretKey.scala b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/SecretKey.scala
index 0d6d54d719..73ce7d39ea 100644
--- a/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/SecretKey.scala
+++ b/sdk/shared/src/main/scala/org/ergoplatform/sdk/wallet/secrets/SecretKey.scala
@@ -10,7 +10,7 @@ trait SecretKey {
/**
* Private (secret) input of a sigma protocol
*/
- def privateInput: SigmaProtocolPrivateInput[_, _]
+ def privateInput: SigmaProtocolPrivateInput[_]
}
/**
@@ -19,7 +19,7 @@ trait SecretKey {
sealed trait PrimitiveSecretKey extends SecretKey
object PrimitiveSecretKey {
- def apply(sigmaPrivateInput: SigmaProtocolPrivateInput[_, _]): PrimitiveSecretKey = sigmaPrivateInput match {
+ def apply(sigmaPrivateInput: SigmaProtocolPrivateInput[_]): PrimitiveSecretKey = sigmaPrivateInput match {
case dls: DLogProverInput => DlogSecretKey(dls)
case dhts: DiffieHellmanTupleProverInput => DhtSecretKey(dhts)
}
diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala
index 3dfb5c4157..72c75ace73 100644
--- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala
+++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/ExtensionsSpec.scala
@@ -91,7 +91,7 @@ class ExtensionsSpec extends AnyPropSpec with ScalaCheckPropertyChecks with Matc
val left = builder.pairColl(leftKeys, leftValues)
val right = builder.pairColl(rightKeys, rightValues)
val res = builder.outerJoin(left, right)(l => l._2 - 2, r => r._2 - 3, i => i._2._1 + 5)
- val (ks, vs) = builder.unzip(res)
+ val (_, vs) = builder.unzip(res)
vs.sum shouldBe (col.sum * 2 + col.map(_ + 5).sum)
}
// test(builder.fromItems(0))
diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala
index e651a542b8..e85757985e 100644
--- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala
+++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/JsonSerializationSpec.scala
@@ -16,17 +16,12 @@ import sigmastate.basics.DLogProtocol.ProveDlog
import sigmastate.eval.Digest32Coll
import sigmastate.interpreter.{ContextExtension, ProverResult}
import sigmastate.serialization.SerializationSpecification
-import sigmastate.utils.Helpers._
+import sigmastate.utils.Helpers.DecoderResultOps // required for Scala 2.11 (extension method toTry)
import special.collection.Coll
import special.sigma.{Header, PreHeader}
-import org.ergoplatform.ErgoLikeContext
-import org.ergoplatform.DataInput
-import org.ergoplatform.Input
-import org.ergoplatform.UnsignedInput
-import org.ergoplatform.ErgoBox
-import org.ergoplatform.ErgoLikeTransaction
-import org.ergoplatform.UnsignedErgoLikeTransaction
-import org.ergoplatform.ErgoLikeTransactionTemplate
+import org.ergoplatform.{DataInput, ErgoBox, ErgoBoxCandidate, ErgoLikeContext, ErgoLikeTransaction, ErgoLikeTransactionTemplate, Input, UnsignedErgoLikeTransaction, UnsignedInput}
+
+import scala.collection.mutable
class JsonSerializationSpec extends SerializationSpecification with JsonCodecs {
@@ -36,7 +31,7 @@ class JsonSerializationSpec extends SerializationSpecification with JsonCodecs {
}
property("ErgoLikeContext should be encoded into JSON and decoded back correctly") {
- forAll(ergoLikeContextGen) { v: ErgoLikeContext => jsonRoundTrip(v) }
+ forAll(ergoLikeContextGen, MinSuccessful(50)) { v: ErgoLikeContext => jsonRoundTrip(v) }
}
property("sigma.BigInt should be encoded into JSON and decoded back correctly") {
@@ -84,43 +79,49 @@ class JsonSerializationSpec extends SerializationSpecification with JsonCodecs {
}
property("Input should be encoded into JSON and decoded back correctly") {
- forAll(inputGen) { v: Input => jsonRoundTrip(v) }
+ forAll(inputGen, MinSuccessful(50)) { v: Input => jsonRoundTrip(v) }
}
property("UnsignedInput should be encoded into JSON and decoded back correctly") {
- forAll(unsignedInputGen) { v: UnsignedInput => jsonRoundTrip(v) }
+ forAll(unsignedInputGen, MinSuccessful(50)) { v: UnsignedInput => jsonRoundTrip(v) }
}
property("ContextExtension should be encoded into JSON and decoded back correctly") {
- forAll(contextExtensionGen) { v: ContextExtension => jsonRoundTrip(v) }
+ forAll(contextExtensionGen, MinSuccessful(500)) { v: ContextExtension => jsonRoundTrip(v) }
+ }
+
+ property("AdditionalRegisters should be encoded into JSON and decoded back correctly") {
+ forAll(additionalRegistersGen, MinSuccessful(500)) { regs =>
+ jsonRoundTrip(regs)(registersEncoder, registersDecoder)
+ }
}
property("ProverResult should be encoded into JSON and decoded back correctly") {
- forAll(serializedProverResultGen) { v: ProverResult => jsonRoundTrip(v) }
+ forAll(serializedProverResultGen, MinSuccessful(500)) { v: ProverResult => jsonRoundTrip(v) }
}
property("AvlTreeData should be encoded into JSON and decoded back correctly") {
- forAll(avlTreeDataGen) { v: AvlTreeData => jsonRoundTrip(v) }
+ forAll(avlTreeDataGen, MinSuccessful(500)) { v: AvlTreeData => jsonRoundTrip(v) }
}
property("ErgoTree should be encoded into JSON and decoded back correctly") {
- forAll(ergoTreeGen) { v: ErgoTree => jsonRoundTrip(v) }
+ forAll(ergoTreeGen, MinSuccessful(500)) { v: ErgoTree => jsonRoundTrip(v) }
}
property("ErgoBox should be encoded into JSON and decoded back correctly") {
- forAll(ergoBoxGen) { v: ErgoBox => jsonRoundTrip(v) }
+ forAll(ergoBoxGen, MinSuccessful(500)) { v: ErgoBox => jsonRoundTrip(v) }
}
property("ErgoLikeTransaction should be encoded into JSON and decoded back correctly") {
- forAll(ergoLikeTransactionGen) { v: ErgoLikeTransaction => jsonRoundTrip(v) }
+ forAll(ergoLikeTransactionGen, MinSuccessful(50)) { v: ErgoLikeTransaction => jsonRoundTrip(v) }
}
property("UnsignedErgoLikeTransaction should be encoded into JSON and decoded back correctly") {
- forAll(unsignedErgoLikeTransactionGen) { v: UnsignedErgoLikeTransaction => jsonRoundTrip(v) }
+ forAll(unsignedErgoLikeTransactionGen, MinSuccessful(50)) { v: UnsignedErgoLikeTransaction => jsonRoundTrip(v) }
}
property("ErgoLikeTransactionTemplate should be encoded into JSON and decoded back correctly") {
- forAll(ergoLikeTransactionTemplateGen) { v: ErgoLikeTransactionTemplate[_ <: UnsignedInput] =>
+ forAll(ergoLikeTransactionTemplateGen, MinSuccessful(50)) { v: ErgoLikeTransactionTemplate[_ <: UnsignedInput] =>
v.asJson.as(ergoLikeTransactionTemplateDecoder).toTry.get shouldEqual v
}
}
@@ -136,7 +137,7 @@ class JsonSerializationSpec extends SerializationSpecification with JsonCodecs {
CryptoConstants.dlogGroup.ctx.decodePoint(point).asInstanceOf[CryptoConstants.EcPointType]
)
}.get
- val regs = Map(
+ val regs = scala.collection.Map(
R7 -> LongArrayConstant(Array(1L, 2L, 1234123L)),
R4 -> ByteConstant(1),
R6 -> IntConstant(10),
diff --git a/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala b/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala
index e0672d4a3d..e4cc09c4a2 100644
--- a/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala
+++ b/sdk/shared/src/test/scala/org/ergoplatform/sdk/wallet/utils/Generators.scala
@@ -17,7 +17,6 @@ import sigmastate.eval.Extensions._
import scorex.util.{ModifierId, bytesToId}
import sigmastate.eval._
import sigmastate.helpers.TestingHelpers._
-import scorex.crypto.hash.Digest32
import sigmastate.crypto.CryptoFacade
trait Generators {
diff --git a/sigma-js/package-lock.json b/sigma-js/package-lock.json
index 48475c6a2a..153254dd3e 100644
--- a/sigma-js/package-lock.json
+++ b/sigma-js/package-lock.json
@@ -9,9 +9,9 @@
"version": "0.2.2",
"license": "MIT",
"dependencies": {
- "@fleet-sdk/common": "0.1.0-alpha.14",
+ "@fleet-sdk/common": "0.1.3",
"@noble/hashes": "1.1.4",
- "sigmajs-crypto-facade": "0.0.6"
+ "sigmajs-crypto-facade": "0.0.7"
},
"devDependencies": {
"jest": "^29.0.3",
@@ -592,11 +592,11 @@
"dev": true
},
"node_modules/@fleet-sdk/common": {
- "version": "0.1.0-alpha.14",
- "resolved": "https://registry.npmjs.org/@fleet-sdk/common/-/common-0.1.0-alpha.14.tgz",
- "integrity": "sha512-w6AMHe77FaSb759e3EwcOVRQ/lEsCdr1pXq376B+T80do5pUcWjrlrIEKKNrEJPCyqW8nNClxIqBVqkFuPbbMw==",
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@fleet-sdk/common/-/common-0.1.3.tgz",
+ "integrity": "sha512-gYEkHhgGpgIcmCL3nCw8E9zHkT2WLmR+mPdxFlUE6fwcwISURbJrP6W9mF7D5Y0ShAP5Is2w3edh7AyIc7ctIQ==",
"engines": {
- "node": ">=10"
+ "node": ">=14"
}
},
"node_modules/@istanbuljs/load-nyc-config": {
@@ -3163,9 +3163,9 @@
}
},
"node_modules/sigmajs-crypto-facade": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/sigmajs-crypto-facade/-/sigmajs-crypto-facade-0.0.6.tgz",
- "integrity": "sha512-Nbz+CZ0rgMvDN76C3bQjrFHO30qSHE9Fti+Co4WvNzL9UTaxSOlfSFJy89u0QX+IkVNvXd35RqiyiekLpTUSsA==",
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/sigmajs-crypto-facade/-/sigmajs-crypto-facade-0.0.7.tgz",
+ "integrity": "sha512-4XK8ZS9NKAbo8aGnU6o5GkBW6Upl8+OK8A1KreVDMAamfvZ0iq4LoVH8rHaeEPf9moVtaC4QZY5RYI+0OwiydA==",
"dependencies": {
"@noble/hashes": "^1.1.4"
},
@@ -3994,9 +3994,9 @@
"dev": true
},
"@fleet-sdk/common": {
- "version": "0.1.0-alpha.14",
- "resolved": "https://registry.npmjs.org/@fleet-sdk/common/-/common-0.1.0-alpha.14.tgz",
- "integrity": "sha512-w6AMHe77FaSb759e3EwcOVRQ/lEsCdr1pXq376B+T80do5pUcWjrlrIEKKNrEJPCyqW8nNClxIqBVqkFuPbbMw=="
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@fleet-sdk/common/-/common-0.1.3.tgz",
+ "integrity": "sha512-gYEkHhgGpgIcmCL3nCw8E9zHkT2WLmR+mPdxFlUE6fwcwISURbJrP6W9mF7D5Y0ShAP5Is2w3edh7AyIc7ctIQ=="
},
"@istanbuljs/load-nyc-config": {
"version": "1.1.0",
@@ -5935,9 +5935,9 @@
}
},
"sigmajs-crypto-facade": {
- "version": "0.0.6",
- "resolved": "https://registry.npmjs.org/sigmajs-crypto-facade/-/sigmajs-crypto-facade-0.0.6.tgz",
- "integrity": "sha512-Nbz+CZ0rgMvDN76C3bQjrFHO30qSHE9Fti+Co4WvNzL9UTaxSOlfSFJy89u0QX+IkVNvXd35RqiyiekLpTUSsA==",
+ "version": "0.0.7",
+ "resolved": "https://registry.npmjs.org/sigmajs-crypto-facade/-/sigmajs-crypto-facade-0.0.7.tgz",
+ "integrity": "sha512-4XK8ZS9NKAbo8aGnU6o5GkBW6Upl8+OK8A1KreVDMAamfvZ0iq4LoVH8rHaeEPf9moVtaC4QZY5RYI+0OwiydA==",
"requires": {
"@noble/hashes": "^1.1.4"
}
diff --git a/sigma-js/package.json b/sigma-js/package.json
index 4263896038..57d5b171cf 100644
--- a/sigma-js/package.json
+++ b/sigma-js/package.json
@@ -36,8 +36,8 @@
},
"dependencies": {
"@noble/hashes": "1.1.4",
- "@fleet-sdk/common": "0.1.0-alpha.14",
- "sigmajs-crypto-facade": "0.0.6"
+ "@fleet-sdk/common": "0.1.3",
+ "sigmajs-crypto-facade": "0.0.7"
},
"devDependencies": {
"jest": "^29.0.3",
diff --git a/sigma-js/sigmastate-js.d.ts b/sigma-js/sigmastate-js.d.ts
index 04bc1c7f49..34eef4df12 100644
--- a/sigma-js/sigmastate-js.d.ts
+++ b/sigma-js/sigmastate-js.d.ts
@@ -20,6 +20,21 @@ declare module "sigmastate-js/main" {
static fromHex(value: HexString): ErgoTree;
}
+ export declare class GroupElement {
+ toPointHex(): HexString;
+ }
+
+ export declare class GroupElementObj {
+ static fromPointHex(value: HexString): GroupElement;
+ }
+
+ export declare class SigmaProp {
+ }
+
+ export declare class SigmaPropObj {
+ static fromPointHex(value: HexString): SigmaProp;
+ }
+
export declare class Type {
name: string;
toString(): string;
@@ -55,8 +70,10 @@ declare module "sigmastate-js/main" {
static ofInt(value: number): Value;
static ofLong(value: bigint): Value;
static ofBigInt(value: bigint): Value;
+ static ofGroupElement(pointHex: string): Value;
+ static ofSigmaProp(pointHex: string): Value;
static pairOf(left: Value, right: Value): Value<[R, L]>;
- static collOf(items: T[], type: Type): Value;
+ static collOf(items: T[], elemType: Type): Value;
static fromHex(hex: HexString): Value;
}
diff --git a/sigma-js/tests/js/GroupElement.spec.js b/sigma-js/tests/js/GroupElement.spec.js
new file mode 100644
index 0000000000..6d860691be
--- /dev/null
+++ b/sigma-js/tests/js/GroupElement.spec.js
@@ -0,0 +1,13 @@
+const { GroupElementObj, ValueObj } = require("sigmastate-js/main");
+
+let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5";
+
+describe("GroupElement", () => {
+ it("should implement toPointHex/fromPointHex", () => {
+ let ge = GroupElementObj.fromPointHex(pointAsn1Hex)
+ expect(ge.toPointHex()).toEqual(pointAsn1Hex)
+
+ let v = ValueObj.ofGroupElement(pointAsn1Hex)
+ expect(v.toHex()).toEqual("07"/* GroupElement type id */ + pointAsn1Hex)
+ });
+});
\ No newline at end of file
diff --git a/sigma-js/tests/js/SigmaProp.spec.js b/sigma-js/tests/js/SigmaProp.spec.js
new file mode 100644
index 0000000000..89568120c7
--- /dev/null
+++ b/sigma-js/tests/js/SigmaProp.spec.js
@@ -0,0 +1,14 @@
+const { SigmaPropObj, ValueObj } = require("sigmastate-js/main");
+
+let pointAsn1Hex = "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5";
+
+describe("SigmaProp", () => {
+ it("should implement fromPointHex", () => {
+ let ge = SigmaPropObj.fromPointHex(pointAsn1Hex)
+ expect(ge).not.toBeUndefined()
+
+ let v = ValueObj.ofSigmaProp(pointAsn1Hex)
+ expect(v.toHex())
+ .toEqual("08"/* SigmaProp type id */ + "cd"/* ProveDlog.opCode */ + pointAsn1Hex)
+ });
+});
\ No newline at end of file
diff --git a/sigma-js/tests/js/Value.spec.js b/sigma-js/tests/js/Value.spec.js
index bac3251d4c..e687f8aecb 100644
--- a/sigma-js/tests/js/Value.spec.js
+++ b/sigma-js/tests/js/Value.spec.js
@@ -1,4 +1,4 @@
-const { TypeObj, ValueObj } = require("sigmastate-js/main");
+const { TypeObj, ValueObj, SigmaPropObj, SigmaProp} = require("sigmastate-js/main");
function testRange(factory, min, max) {
expect(factory(max).data).toEqual(max);
@@ -105,6 +105,14 @@ describe("Smoke tests for Values", () => {
expect(collV.toHex()).toEqual(collHex)
});
+ it("Value of type Coll[SigmaProp]", () => {
+ let sp1 = SigmaPropObj.fromPointHex(groupElementHex.substring(2))
+ let sp2 = SigmaPropObj.fromPointHex(sigmaPropHex.substring(4))
+ let collV = ValueObj.collOf([sp1, sp2], TypeObj.SigmaProp)
+
+ expect(collV.tpe.name).toEqual("Coll[SigmaProp]");
+ });
+
it("Pair Value.toHex", () => {
let fst = ValueObj.ofByte(10)
let snd = ValueObj.ofLong(20)