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

com.wavesplatform.transaction.smart.script.trace.TraceStep.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform.transaction.smart.script.trace

import cats.Id
import com.wavesplatform.account.{Address, AddressOrAlias}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.lang.{CommonError, ValidationError}
import com.wavesplatform.lang.v1.compiler.Terms.FUNCTION_CALL
import com.wavesplatform.lang.v1.evaluator.EvaluatorV2.LogKeys
import com.wavesplatform.lang.v1.evaluator.{Log, ScriptResult}
import com.wavesplatform.serialization.ScriptValuesJson
import com.wavesplatform.state.InvokeScriptResult
import com.wavesplatform.transaction.Asset.IssuedAsset
import com.wavesplatform.transaction.TransactionBase
import com.wavesplatform.transaction.TxValidationError.{FailedTransactionError, ScriptExecutionError, TransactionNotAllowedByScript}
import com.wavesplatform.transaction.assets.*
import com.wavesplatform.transaction.assets.exchange.ExchangeTransaction
import com.wavesplatform.transaction.smart.InvokeScriptTransaction
import com.wavesplatform.transaction.transfer.{MassTransferTransaction, TransferTransaction}
import play.api.libs.json.Json.JsValueWrapper
import play.api.libs.json.*

sealed abstract class TraceStep {
  def json: JsObject // TODO: Is this format necessary?
  def loggedJson: JsObject = json
}

case class AccountVerifierTrace(
    address: Address,
    errorOpt: Option[ValidationError]
) extends TraceStep {

  override lazy val json: JsObject = Json
    .obj(
      "type" -> "verifier",
      "id"   -> address.toString
    ) ++ TraceStep.maybeErrorJson(errorOpt)
}

object AssetVerifierTrace {
  type AssetContext = AssetContext.Value
  object AssetContext extends Enumeration {
    val Unknown, OrderAmount, OrderPrice, MatcherFee, Payment, Reissue, Burn, Sponsor, Transfer, UpdateInfo = Value

    def fromTxAndAsset(tx: TransactionBase, asset: IssuedAsset): AssetContext = tx match {
      case i: InvokeScriptTransaction if i.payments.exists(_.assetId == asset) => AssetContext.Payment

      case e: ExchangeTransaction if e.order1.assetPair.amountAsset == asset                            => AssetContext.OrderAmount
      case e: ExchangeTransaction if e.order1.assetPair.priceAsset == asset                             => AssetContext.OrderPrice
      case e: ExchangeTransaction if Set(e.order1.matcherFeeAssetId, e.order2.matcherFeeAssetId)(asset) => AssetContext.OrderPrice

      case r: ReissueTransaction if r.asset == asset           => AssetContext.Reissue
      case r: BurnTransaction if r.asset == asset              => AssetContext.Burn
      case s: SponsorFeeTransaction if s.asset == asset        => AssetContext.Sponsor
      case u: UpdateAssetInfoTransaction if u.assetId == asset => AssetContext.UpdateInfo
      case u: SetAssetScriptTransaction if u.asset == asset    => AssetContext.UpdateInfo

      case t: TransferTransaction if t.assetId == asset       => AssetContext.Transfer
      case mt: MassTransferTransaction if mt.assetId == asset => AssetContext.Transfer

      case _ => AssetContext.Unknown
    }
  }
}

case class AssetVerifierTrace(
    id: ByteStr,
    errorOpt: Option[ValidationError],
    context: AssetVerifierTrace.AssetContext = AssetVerifierTrace.AssetContext.Unknown
) extends TraceStep {
  override lazy val json: JsObject = Json.obj(
    "type"    -> "asset",
    "context" -> context.toString.updated(0, context.toString.charAt(0).toLower),
    "id"      -> id.toString
  ) ++ TraceStep.maybeErrorJson(errorOpt)
}

case class InvokeScriptTrace(
    invokeId: ByteStr,
    dAppAddressOrAlias: AddressOrAlias,
    functionCall: FUNCTION_CALL,
    resultE: Either[ValidationError, ScriptResult],
    log: Log[Id],
    invocations: Seq[InvokeScriptTrace]
) extends TraceStep {
  override lazy val json: JsObject       = maybeLoggedJson(false)
  override lazy val loggedJson: JsObject = maybeLoggedJson(true)

  def maybeLoggedJson(logged: Boolean)(implicit invokeResultWrites: OWrites[InvokeScriptResult] = InvokeScriptResult.jsonFormat): JsObject = {
    Json.obj(
      "type"        -> "dApp",
      "id"          -> dAppAddressOrAlias.toString,
      "function"    -> functionCall.function.funcName,
      "args"        -> functionCall.args.map(_.toString),
      "invocations" -> invocations.map(_.maybeLoggedJson(logged)(invokeResultWrites))
    ) ++ (resultE match {
      case Right(value) => TraceStep.maybeErrorJson(None) ++ Json.obj("result" -> TraceStep.scriptResultJson(invokeId, value))
      case Left(e)      => TraceStep.maybeErrorJson(Some(e))
    }) ++ (if (logged) Json.obj(TraceStep.logJson(log)) else JsObject.empty)
  }
}

object TraceStep {
  private[trace] def scriptResultJson(invokeId: ByteStr, v: ScriptResult)(implicit invokeResultWrites: OWrites[InvokeScriptResult]): JsObject =
    Json.toJsObject(InvokeScriptResult.fromLangResult(invokeId, v))

  private[trace] def maybeErrorJson(errorOpt: Option[ValidationError]): JsObject =
    errorOpt match {
      case Some(e) => Json.obj("result" -> "failure") ++ TraceStep.errorJson(e)
      case None    => Json.obj("result" -> "success", "error" -> JsNull)
    }

  private def errorJson(e: ValidationError): JsObject = e match {
    case see: ScriptExecutionError          => Json.obj(logJson(see.log), "error" -> see.message)
    case tne: TransactionNotAllowedByScript => Json.obj(logJson(tne.log), "error" -> JsNull)
    case fte: FailedTransactionError        => Json.obj(logJson(fte.log), "error" -> fte.error.map(JsString.apply))
    case a                                  => Json.obj("error" -> a.toString)
  }

  def logJson(l: Log[Id]): (String, JsValueWrapper) =
    "vars" -> l.collect {
      case (k, Right(v)) if !LogKeys.TraceExcluded.exists(k.contains)   => Json.obj("name" -> k) ++ ScriptValuesJson.serializeValue(v, intAsString = false)
      case (k, Left(CommonError(_, Some(fte: FailedTransactionError)))) => Json.obj("name" -> k, "error" -> fte.error)
      case (k, Left(err))                                               => Json.obj("name" -> k, "error" -> err.message)
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy