All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ergoplatform.appkit.JavaHelpers.scala Maven / Gradle / Ivy

package org.ergoplatform.appkit

import org.ergoplatform.ErgoAddressEncoder.NetworkPrefix
import org.ergoplatform.ErgoBox.TokenId
import org.ergoplatform.ErgoScriptPredef.compileWithCosting
import org.ergoplatform._
import org.ergoplatform.sdk.Iso._
import org.ergoplatform.sdk.JavaHelpers.{UniversalConverter, collRType}
import org.ergoplatform.sdk.{ErgoToken, Iso, LowPriorityIsos}
import sigma.Colls
import sigma.data.RType
import sigmastate.SType
import sigmastate.Values.{Constant, ErgoTree, EvaluatedValue}
import sigmastate.eval.CostingSigmaDslBuilder.validationSettings
import sigmastate.eval.{CompiletimeIRContext, Evaluation}
import sigmastate.interpreter.ContextExtension
import sigmastate.lang.Terms.ValueOps
import sigmastate.serialization.ErgoTreeSerializer

import java.util
import java.util.{List => JList}
import scala.collection.JavaConverters
import scala.collection.compat.immutable.ArraySeq

object AppkitIso extends LowPriorityIsos {
  import org.ergoplatform.sdk.Iso._
  implicit val isoErgoTypeToSType: Iso[ErgoType[_], SType] = new Iso[ErgoType[_], SType] {
    override def to(et: ErgoType[_]): SType = Evaluation.rtypeToSType(et.getRType)
    override def from(st: SType): ErgoType[_] = new ErgoType(Evaluation.stypeToRType(st))
  }

  implicit val isoErgoValueToSValue: Iso[ErgoValue[_], EvaluatedValue[SType]] = new Iso[ErgoValue[_], EvaluatedValue[SType]] {
    override def to(x: ErgoValue[_]): EvaluatedValue[SType] =
      Constant(x.getValue.asInstanceOf[SType#WrappedType], Evaluation.rtypeToSType(x.getType.getRType))
      
    override def from(x: EvaluatedValue[SType]): ErgoValue[_] = {
      new ErgoValue(x.value, new ErgoType(Evaluation.stypeToRType(x.tpe)))
    }
  }

  implicit val isoContextVarsToContextExtension: Iso[JList[ContextVar], ContextExtension] = new Iso[JList[ContextVar], ContextExtension] {
    import org.ergoplatform.sdk.JavaHelpers._
    override def to(vars: JList[ContextVar]): ContextExtension = {
      var values: Map[Byte, EvaluatedValue[SType]] = Map.empty
      vars.convertTo[IndexedSeq[ContextVar]].foreach { v =>
        val id = v.getId
        val value = v.getValue
        if (values.contains(id)) sys.error(s"Duplicate variable id: ($id -> $value")
        values += (v.getId() -> isoErgoValueToSValue.to(v.getValue))
      }
      ContextExtension(values)
    }
    override def from(b: ContextExtension): JList[ContextVar] = {
      val iso = JListToIndexedSeq[ContextVar, ContextVar]
      val vars = iso.from(b.values
        .map { case (id, v) => new ContextVar(id, isoErgoValueToSValue.from(v)) }
        .toIndexedSeq)
      vars
    }
  }

}

object AppkitHelpers {
  implicit class ListOps[A](val xs: JList[A]) extends AnyVal {
    def map[B](f: A => B): JList[B] = {
      xs.convertTo[IndexedSeq[A]].map(f).convertTo[JList[B]]
    }
  }

  /** This value must be lazy to prevent early access to uninitialized unitType value. */
  lazy val UnitErgoVal = new ErgoValue[Unit]((), ErgoType.unitType)

  /** Transforms serialized bytes of ErgoTree with segregated constants by
    * replacing constants at given positions with new values. This operation
    * allow to use serialized scripts as pre-defined templates.
    * See [[sigmastate.SubstConstants]] for details.
    *
    * @param ergoTreeBytes serialized ErgoTree with ConstantSegregationFlag set to 1.
    * @param positions     zero based indexes in ErgoTree.constants array which
    *                      should be replaced with new values
    * @param newValues     new values to be injected into the corresponding
    *                      positions in ErgoTree.constants array
    * @return a new ErgoTree such that only specified constants
    *         are replaced and all other remain exactly the same
    */
  def substituteErgoTreeConstants(ergoTreeBytes: Array[Byte], positions: Array[Int], newValues: Array[ErgoValue[_]]): ErgoTree = {
    val (newBytes, _) = ErgoTreeSerializer.DefaultSerializer.substituteConstants(
      ergoTreeBytes, positions, newValues.map(v => AppkitIso.isoErgoValueToSValue.to(v).asInstanceOf[Constant[SType]]))
    ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(newBytes)
  }

  def toJavaList[T](xs: Seq[T]): util.List[T] = {
    import JavaConverters._
    xs.asJava
  }

  /** Wraps an array into sequence.
    * This method in necessary because ArraySeq is not available from Java (for all Scala versions).
    */
  def arraySeq[T](arr: Array[T]): Seq[T] =
    ArraySeq.unsafeWrapArray(arr)

  def compile(constants: util.Map[String, Object], contractText: String, networkPrefix: NetworkPrefix): ErgoTree = {
    import JavaConverters._
    val env = constants.asScala.toMap
    implicit val IR = new CompiletimeIRContext
    val prop = compileWithCosting(env, contractText, networkPrefix).asSigmaProp
    ErgoTree.fromProposition(prop)
  }

  /** Extracts registers as a list of ErgoValue instances (containing type descriptors). */
  def getBoxRegisters(ergoBox: ErgoBoxCandidate): JList[ErgoValue[_]] = {
    val size = ergoBox.additionalRegisters.size
    val res = new util.ArrayList[ErgoValue[_]](size)
    for ((r, v: EvaluatedValue[_]) <- ergoBox.additionalRegisters.toIndexedSeq.sortBy(_._1.number)) {
      val i = r.number - ErgoBox.mandatoryRegistersCount
      require(i == res.size(), s"registers are not densely packed in a box: ${ergoBox.additionalRegisters}")
      res.add(AppkitIso.isoErgoValueToSValue.from(v))
    }
    res
  }

  //TODO refactor: when similar method is avaialble in Sigma
  def createBoxCandidate(
        value: Long, tree: ErgoTree,
        tokens: Seq[ErgoToken],
        registers: Seq[ErgoValue[_]], creationHeight: Int): ErgoBoxCandidate = {
    import 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(sigma.ByteType).asInstanceOf[RType[TokenId]]
    val ts = Colls.fromItems(tokens.map(isoErgoTokenToPair.to(_)):_*)
    val rs = registers.zipWithIndex.map { case (ergoValue, i) =>
      val id = ErgoBox.nonMandatoryRegisters(i)
      val value = AppkitIso.isoErgoValueToSValue.to(ergoValue)
      id -> value
    }.toMap

    new ErgoBoxCandidate(value, tree, creationHeight, ts, rs)
  }

  def secretStringToOption(secretString: org.ergoplatform.wallet.interface4j.SecretString): Option[org.ergoplatform.wallet.interface4j.SecretString] = {
    if (secretString == null || secretString.isEmpty) None else Some(secretString)
  }

}







© 2015 - 2025 Weber Informatics LLC | Privacy Policy