Skip to content

Commit

Permalink
core-serializers: Value moved to core.js
Browse files Browse the repository at this point in the history
  • Loading branch information
aslesarenko committed Sep 13, 2023
1 parent 5695687 commit 4fcd2a7
Show file tree
Hide file tree
Showing 24 changed files with 197 additions and 164 deletions.
50 changes: 50 additions & 0 deletions core/js/src/main/scala/sigma/js/AvlTree.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package sigma.js

import sigma.Extensions.ArrayOps
import sigma.data.Iso.{isoStringToArray, isoStringToColl}
import sigma.data.{AvlTreeData, AvlTreeFlags, CAvlTree, Iso}

import scala.scalajs.js
import scala.scalajs.js.UndefOr
import scala.scalajs.js.annotation.JSExportTopLevel

/** Equivalent of [[sigma.AvlTree]] available from JS. */
@JSExportTopLevel("AvlTree")
class AvlTree(
val digest: String,
val insertAllowed: Boolean,
val updateAllowed: Boolean,
val removeAllowed: Boolean,
val keyLength: Int,
val valueLengthOpt: UndefOr[Int]
) extends js.Object

object AvlTree {

implicit val isoAvlTree: Iso[AvlTree, sigma.AvlTree] = new Iso[AvlTree, sigma.AvlTree] {
override def to(x: AvlTree): sigma.AvlTree = {
CAvlTree(
AvlTreeData(
digest = isoStringToArray.to(x.digest).toColl,
treeFlags = AvlTreeFlags(x.insertAllowed, x.updateAllowed, x.removeAllowed),
x.keyLength,
valueLengthOpt = sigma.js.Isos.isoUndefOr(Iso.identityIso[Int]).to(x.valueLengthOpt),
),
)
}

override def from(x: sigma.AvlTree): AvlTree = {
val tree = x.asInstanceOf[CAvlTree]
val data = tree.treeData
new AvlTree(
digest = isoStringToColl.from(tree.digest),
insertAllowed = data.treeFlags.insertAllowed,
updateAllowed = data.treeFlags.updateAllowed,
removeAllowed = data.treeFlags.removeAllowed,
keyLength = data.keyLength,
valueLengthOpt = sigma.js.Isos.isoUndefOr(Iso.identityIso[Int]).from(data.valueLengthOpt),
)
}
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.ergoplatform.sdk.js
package sigma.js

import sigma.Extensions.CoreArrayByteOps
import sigma.crypto.{CryptoFacade, CryptoFacadeJs, Ecp, Platform}
import sigmastate.eval.Extensions.ArrayByteOps

import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
Expand Down
29 changes: 29 additions & 0 deletions core/js/src/main/scala/sigma/js/Isos.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package sigma.js

import sigma.{Coll, Colls}
import sigma.data.{Iso, RType}

import scala.reflect.ClassTag
import scala.scalajs.js
import scala.scalajs.js.JSConverters.JSRichOption

object Isos {

implicit def isoUndefOr[A, B](implicit iso: Iso[A, B]): Iso[js.UndefOr[A], Option[B]] = new Iso[js.UndefOr[A], Option[B]] {
override def to(x: js.UndefOr[A]): Option[B] = x.toOption.map(iso.to)
override def from(x: Option[B]): js.UndefOr[A] = x.map(iso.from).orUndefined
}

implicit def isoArrayToColl[A, B](iso: Iso[A, B])
(implicit ctA: ClassTag[A], tB: RType[B]): Iso[js.Array[A], Coll[B]] = new Iso[js.Array[A], Coll[B]] {
override def to(x: js.Array[A]): Coll[B] = Colls.fromArray(x.map(iso.to).toArray(tB.classTag))
override def from(x: Coll[B]): js.Array[A] = js.Array(x.toArray.map(iso.from): _*)
}

implicit def isoArrayToIndexed[A, B](iso: Iso[A, B])
(implicit cB: ClassTag[B]): Iso[js.Array[A], IndexedSeq[B]] = new Iso[js.Array[A], IndexedSeq[B]] {
override def to(x: js.Array[A]): IndexedSeq[B] = x.map(iso.to).toArray(cB).toIndexedSeq
override def from(x: IndexedSeq[B]): js.Array[A] = js.Array(x.map(iso.from): _*)
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ergoplatform.sdk.js
package sigma.js

import sigma.data.{ProveDlog, SigmaBoolean}

import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel

Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
package org.ergoplatform.sdk.js
package sigma
package js

import org.ergoplatform.sdk.js.Value.toRuntimeData
import sigma.data.{CAvlTree, CGroupElement, CSigmaProp, CollType, PairType, RType}
import scorex.util.Extensions.{IntOps, LongOps}
import scorex.util.encode.Base16
import sigma.ast.SType
import sigma.crypto.Platform
import sigma.js.Type
import sigmastate.eval.{CostingBox, 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 sigma.data._
import sigma.js.Value.toRuntimeData
import sigma.serialization.{CoreDataSerializer, CoreSerializer}
import sigma.util.Extensions.BigIntOps
import sigma.{Coll, Colls, Evaluation}

import java.math.BigInteger
Expand Down Expand Up @@ -46,7 +42,7 @@ class Value(val data: Any, val tpe: Type) extends js.Object {
/** Get Sigma runtime value which can be passed to interpreter, saved in register and
* [[sigmastate.Values.Constant]] nodes.
*/
final private[js] def runtimeData: Any = toRuntimeData(data, tpe.rtype)
final def runtimeData: Any = toRuntimeData(data, tpe.rtype)

/**
* Encode this value as Base16 hex string.
Expand All @@ -57,15 +53,11 @@ class Value(val data: Any, val tpe: Type) extends js.Object {
* @return hex string of serialized bytes
*/
def toHex(): String = {
// this can be implemented using ConstantSerializer and isoValueToConstant, but this
// will add dependence on Constant and Values, which we want to avoid facilitate
// module splitting
// TODO simplify if module splitting fails
val stype = Evaluation.rtypeToSType(tpe.rtype)
val value = runtimeData.asInstanceOf[SType#WrappedType]
val w = SigmaSerializer.startWriter()
val w = CoreSerializer.startWriter()
w.putType(stype)
DataSerializer.serialize(value, stype, w)
CoreDataSerializer.serialize(value, stype, w)
Base16.encode(w.toBytes)
}
}
Expand All @@ -87,19 +79,16 @@ object Value extends js.Object {
case sigma.LongType => java.lang.Long.parseLong(data.asInstanceOf[js.BigInt].toString(10))
case sigma.BigIntRType =>
val v = data.asInstanceOf[js.BigInt]
SigmaDsl.BigInt(new BigInteger(v.toString(16), 16))
CBigInt(new BigInteger(v.toString(16), 16))
case sigma.GroupElementRType =>
val ge = data.asInstanceOf[GroupElement]
SigmaDsl.GroupElement(ge.point)
CGroupElement(ge.point)
case sigma.SigmaPropRType =>
val p = data.asInstanceOf[SigmaProp]
SigmaDsl.SigmaProp(p.sigmaBoolean)
CSigmaProp(p.sigmaBoolean)
case sigma.AvlTreeRType =>
val t = data.asInstanceOf[AvlTree]
Isos.isoAvlTree.to(t)
case sigma.BoxRType =>
val t = data.asInstanceOf[Box[distEsmTypesCommonMod.Amount, NonMandatoryRegisters]]
SigmaDsl.Box(Isos.isoBox.to(t))
AvlTree.isoAvlTree.to(t)
case ct: CollType[a] =>
val xs = data.asInstanceOf[js.Array[Any]]
implicit val cT = ct.tItem.classTag
Expand All @@ -121,22 +110,20 @@ object Value extends js.Object {
* @param value runtime value of type given by `rtype`
* @param rtype type descriptor of Sigma runtime value
*/
final private[js] def fromRuntimeData(value: Any, rtype: RType[_]): Any = rtype match {
final def fromRuntimeData(value: Any, rtype: RType[_]): Any = rtype match {
case sigma.BooleanType => value
case sigma.ByteType | sigma.ShortType | sigma.IntType => value
case sigma.LongType => js.BigInt(value.asInstanceOf[Long].toString)
case sigma.BigIntRType =>
val hex = SigmaDsl.toBigInteger(value.asInstanceOf[sigma.BigInt]).toString(10)
val hex = value.asInstanceOf[sigma.BigInt].toBigInteger.toString(10)
js.BigInt(hex)
case sigma.GroupElementRType =>
val point = value.asInstanceOf[CGroupElement].wrappedValue.asInstanceOf[Platform.Ecp]
new GroupElement(point)
case sigma.SigmaPropRType =>
new SigmaProp(value.asInstanceOf[CSigmaProp].wrappedValue)
case sigma.AvlTreeRType =>
Isos.isoAvlTree.from(value.asInstanceOf[CAvlTree])
case sigma.BoxRType =>
Isos.isoBox.from(value.asInstanceOf[CostingBox].wrappedValue)
AvlTree.isoAvlTree.from(value.asInstanceOf[CAvlTree])
case ct: CollType[a] =>
val arr = value.asInstanceOf[Coll[a]].toArray
js.Array(arr.map(x => fromRuntimeData(x, ct.tItem)):_*)
Expand Down Expand Up @@ -263,9 +250,12 @@ object Value extends js.Object {
* - and [[Type]] descriptor in its `tpe` field
*/
def fromHex(hex: String): Value = {
val bytes = Base16.decode(hex).fold(t => throw t, identity)
val S = ConstantSerializer(DeserializationSigmaBuilder)
val c = S.deserialize(SigmaSerializer.startReader(bytes))
Isos.isoValueToConstant.from(c)
val bytes = Base16.decode(hex).fold(t => throw t, identity)
val r = CoreSerializer.startReader(bytes)
val stype = r.getType()
val value = CoreDataSerializer.deserialize(stype, r)
val rtype = Evaluation.stypeToRType(stype)
val jsvalue = Value.fromRuntimeData(value, rtype)
new Value(jsvalue, new Type(rtype))
}
}
6 changes: 6 additions & 0 deletions core/shared/src/main/scala/sigma/Extensions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ import scorex.util.{ModifierId, bytesToId}
import sigma.data.RType

object Extensions {
/** Extension methods for `Array[Byte]` not available for generic `Array[T]`. */
implicit class CoreArrayByteOps(val arr: Array[Byte]) extends AnyVal {
/** Encodes array into hex string */
@inline def toHex: String = Base16.encode(arr)
}

implicit class ArrayOps[T: RType](arr: Array[T]) {
/** Wraps array into Coll instance. The source array in not cloned. */
@inline def toColl: Coll[T] = Colls.fromArray(arr)
Expand Down
23 changes: 23 additions & 0 deletions core/shared/src/main/scala/sigma/data/Iso.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package sigma.data

import scorex.util.encode.Base16
import sigma.Extensions.CollBytesOps
import sigma.{Coll, Colls}

/** Type-class of isomorphisms between types.
* Isomorphism between two types `A` and `B` essentially say that both types
* represents the same information (entity) but in a different way.
Expand Down Expand Up @@ -27,3 +31,22 @@ final case class ComposeIso[A, B, C](iso2: Iso[B, C], iso1: Iso[A, B]) extends I
def to(a: A): C = iso2.to(iso1.to(a))
}

object Iso {
implicit def identityIso[A]: Iso[A, A] = new Iso[A, A] {
override def to(a: A): A = a
override def from(b: A): A = b
}

implicit def inverseIso[A, B](implicit iso: Iso[A, B]): Iso[B, A] = InverseIso[A, B](iso)

val isoStringToArray: Iso[String, Array[Byte]] = new Iso[String, Array[Byte]] {
override def to(x: String): Array[Byte] = Base16.decode(x).get
override def from(x: Array[Byte]): String = Base16.encode(x)
}

val isoStringToColl: Iso[String, Coll[Byte]] = new Iso[String, Coll[Byte]] {
override def to(x: String): Coll[Byte] = Colls.fromArray(Base16.decode(x).get)
override def from(x: Coll[Byte]): String = x.toHex
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ object Extensions {
@inline def toBigInt: BigInt = CostingSigmaDslBuilder.BigInt(BigInteger.valueOf(x))
}

/** Extension methods for `Coll[Byte]` not available for generic `Array[T]`. */
/** Extension methods for `Array[Byte]` not available for generic `Array[T]`. */
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)
Expand Down
3 changes: 2 additions & 1 deletion sc/js/src/main/scala/sigmastate/lang/js/SigmaCompiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import org.scalablytyped.runtime.StringDictionary

import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel
import org.ergoplatform.sdk.js.{ErgoTree, Value}
import org.ergoplatform.sdk.js.ErgoTree
import sigma.js.Value
import sigmastate.Values
import sigmastate.eval.CompiletimeIRContext
import sigmastate.lang.Terms.ValueOps
Expand Down
16 changes: 0 additions & 16 deletions sdk/js/src/main/scala/org/ergoplatform/sdk/js/AvlTree.scala

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.ergoplatform.sdk.js

import sigma.js.Value
import sigmastate.Values

import scala.scalajs.js
Expand Down
2 changes: 2 additions & 0 deletions sdk/js/src/main/scala/org/ergoplatform/sdk/js/Header.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.ergoplatform.sdk.js

import sigma.js.{AvlTree, GroupElement}

import scala.scalajs.js
import scala.scalajs.js.annotation.JSExportTopLevel

Expand Down
Loading

0 comments on commit 4fcd2a7

Please sign in to comment.