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

com.wavesplatform.transaction.serialization.impl.InvokeScriptTxSerializer.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform.transaction.serialization.impl

import java.nio.ByteBuffer
import com.google.common.primitives.{Bytes, Longs}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.*
import com.wavesplatform.lang.v1.compiler.Terms
import com.wavesplatform.lang.v1.compiler.Terms.EXPR
import com.wavesplatform.lang.v1.serialization.SerdeV1
import com.wavesplatform.serialization.*
import com.wavesplatform.transaction.smart.InvokeScriptTransaction
import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment
import com.wavesplatform.transaction.{Asset, PBSince, TxPositiveAmount, TxVersion}
import play.api.libs.json.{JsArray, JsObject, JsString, Json}

import scala.util.Try

object InvokeScriptTxSerializer {
  def functionCallToJson(fc: Terms.FUNCTION_CALL): JsObject = {
    Json.obj(
      "function" -> JsString(fc.function.funcName),
      "args" -> JsArray(
        fc.args.map {
          case Terms.ARR(elements) => Json.obj("type" -> "list", "value" -> elements.map(mapSingleArg))
          case other               => mapSingleArg(other)
        }
      )
    )
  }

  private def mapSingleArg(arg: EXPR): JsObject =
    arg match {
      case Terms.CONST_LONG(num)      => Json.obj("type" -> "integer", "value" -> num)
      case Terms.CONST_BOOLEAN(bool)  => Json.obj("type" -> "boolean", "value" -> bool)
      case Terms.CONST_BYTESTR(bytes) => Json.obj("type" -> "binary", "value" -> bytes.base64)
      case Terms.CONST_STRING(str)    => Json.obj("type" -> "string", "value" -> str)
      case Terms.ARR(_) =>
        Json.obj(
          "type"  -> "list",
          "value" -> "unsupported"
        ) // should not be shown on normal cases, added only to avoid NotImplementedError while constructing error for illegal callable argument type
      case arg => throw new NotImplementedError(s"Not supported: $arg")
    }

  def toJson(tx: InvokeScriptTransaction): JsObject = BaseTxJson.toJson(tx) ++ Json.obj(
    "dApp"    -> tx.dApp.toString,
    "payment" -> tx.payments
  ) ++ Json.obj("call" -> InvokeScriptTxSerializer.functionCallToJson(tx.funcCall))

  def bodyBytes(tx: InvokeScriptTransaction): Array[Byte] = {
    import tx.*
    version match {
      case TxVersion.V1 =>
        Bytes.concat(
          Array(tpe.id.toByte, version, chainId),
          sender.arr,
          dApp.bytes,
          Deser.serializeOption(funcCallOpt)(SerdeV1.serialize(_)),
          Deser.serializeArrays(payments.map(pmt => Longs.toByteArray(pmt.amount) ++ pmt.assetId.byteRepr)),
          Longs.toByteArray(fee.value),
          feeAssetId.byteRepr,
          Longs.toByteArray(timestamp)
        )

      case _ =>
        PBTransactionSerializer.bodyBytes(tx)
    }
  }

  def toBytes(tx: InvokeScriptTransaction): Array[Byte] =
    if (PBSince.affects(tx)) PBTransactionSerializer.bytes(tx)
    else Bytes.concat(Array(0: Byte), this.bodyBytes(tx), tx.proofs.bytes())

  def parseBytes(bytes: Array[Byte]): Try[InvokeScriptTransaction] = Try {
    def parsePayment(arr: Array[Byte]): Payment = {
      val amt               = Longs.fromByteArray(arr.take(8))
      val (maybeAssetId, _) = Deser.parseOption(arr, 8, 32)(ByteStr.apply)
      val asset             = Asset.fromCompatId(maybeAssetId)
      Payment(amt, asset)
    }

    val buf = ByteBuffer.wrap(bytes)
    require(buf.getByte == 0 && buf.getByte == InvokeScriptTransaction.typeId && buf.getByte == 1, "transaction type mismatch")
    val chainId = buf.getByte

    val sender       = buf.getPublicKey
    val dApp         = buf.getAddressOrAlias(Some(chainId))
    val functionCall = Deser.parseOption(buf)(SerdeV1.deserializeFunctionCall(_).explicitGet())
    val payments     = Deser.parseArrays(buf).map(parsePayment)
    val fee          = TxPositiveAmount.unsafeFrom(buf.getLong)
    val feeAssetId   = buf.getAsset
    val timestamp    = buf.getLong
    InvokeScriptTransaction(TxVersion.V1, sender, dApp, functionCall, payments, fee, feeAssetId, timestamp, buf.getProofs, chainId)
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy