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

com.wavesplatform.api.http.utils.UtilsEvaluationRequest.scala Maven / Gradle / Ivy

The newest version!
package com.wavesplatform.api.http.utils

import cats.implicits.toTraverseOps
import cats.syntax.either.*
import com.wavesplatform.account.{Address, PublicKey}
import com.wavesplatform.api.http.requests.InvokeScriptRequest
import com.wavesplatform.api.http.requests.InvokeScriptRequest.FunctionCallPart
import com.wavesplatform.api.http.utils.UtilsApiRoute.DefaultPublicKey
import com.wavesplatform.api.http.utils.UtilsEvaluator.{ConflictingRequestStructure, ParseJsonError}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.lang.directives.values.{Expression, StdLibVersion, V6}
import com.wavesplatform.lang.v1.compiler.Terms.EXPR
import com.wavesplatform.lang.v1.compiler.{ExpressionCompiler, Terms}
import com.wavesplatform.lang.v1.evaluator.ContractEvaluator.Invocation
import com.wavesplatform.lang.v1.parser.Parser.LibrariesOffset.NoLibraries
import com.wavesplatform.lang.v1.traits.domain.Recipient.Address as RideAddress
import com.wavesplatform.lang.{ValidationError, utils}
import com.wavesplatform.state.diffs.FeeValidation.{FeeConstants, FeeUnit}
import com.wavesplatform.state.{Blockchain, BlockchainOverrides, SnapshotBlockchain, StateSnapshot}
import com.wavesplatform.transaction.TxValidationError.GenericError
import com.wavesplatform.transaction.smart.AttachedPaymentExtractor
import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment
import com.wavesplatform.transaction.{Asset, TransactionType, smart}
import play.api.libs.json.*

import scala.collection.immutable.VectorMap

sealed trait UtilsEvaluationRequest {
  def state: Option[BlockchainOverrides]
  def mkBlockchain(underlying: Blockchain): Blockchain = {

    state.fold(underlying) { ovs =>
      SnapshotBlockchain(
        underlying,
        StateSnapshot(balances = VectorMap.from[(Address, Asset), Long](for {
          (addr, ov)    <- ovs.accounts
          (id, balance) <- ov.assetBalances
        } yield ((addr, id), balance.value)) ++ VectorMap.from(ovs.accounts.flatMap { case (addr, acc) =>
          acc.regularBalance.map(v => ((addr, Asset.Waves), v.value))
        }))
      )
    }
  }
}

object UtilsEvaluationRequest {
  // This should be Reads, but we will broke REST API
  def parse(request: JsObject): Either[ValidationError, UtilsEvaluationRequest] = {
    def withoutCommonFields = request - "state"
    (request.validate[UtilsExprRequest], request.validate[UtilsInvocationRequest]) match {
      case (JsSuccess(_, _), JsSuccess(_, _)) if withoutCommonFields.fields.size > 1 =>
        ConflictingRequestStructure.asLeft
      case (JsSuccess(exprRequest, _), _)       => exprRequest.asRight
      case (_, JsSuccess(invocationRequest, _)) => invocationRequest.asRight
      case (_, e: JsError)                      => ParseJsonError(e).asLeft
    }
  }
}

case class UtilsExprRequest(
    expr: String,
    state: Option[BlockchainOverrides] = None
) extends UtilsEvaluationRequest {
  def parseCall(version: StdLibVersion): Either[GenericError, Terms.EXPR] = compile(version, expr)

  private def compile(version: StdLibVersion, str: String): Either[GenericError, EXPR] =
    ExpressionCompiler
      .compileUntyped(str, NoLibraries, utils.compilerContext(version, Expression, isAssetScript = false).copy(arbitraryDeclarations = true), version)
      .leftMap(GenericError(_))
}

object UtilsExprRequest {
  implicit val utilsExprRequestReads: Reads[UtilsExprRequest] = Json.using[Json.WithDefaultValues].reads[UtilsExprRequest]
}

case class UtilsInvocationRequest(
    call: FunctionCallPart = FunctionCallPart("default", Nil),
    id: String = ByteStr(empty32Bytes).toString,
    fee: Long = FeeConstants(TransactionType.InvokeScript) * FeeUnit,
    feeAssetId: Option[String] = None,
    sender: Option[String] = None,
    senderPublicKey: String = DefaultPublicKey.toString,
    payment: Seq[Payment] = Nil,
    state: Option[BlockchainOverrides] = None
) extends UtilsEvaluationRequest {
  def toInvocation: Either[ValidationError, Invocation] =
    for {
      senderPK <- PublicKey.fromBase58String(senderPublicKey)
      id       <- decodeBase58(id)
      functionCall = InvokeScriptRequest.buildFunctionCall(call)
      feeAssetId <- feeAssetId.traverse(decodeBase58)
      senderAddress <- sender
        .fold(RideAddress(ByteStr(senderPK.toAddress.bytes)).asRight[ValidationError]) { s =>
          Address.fromString(s).map(a => RideAddress(ByteStr(a.bytes)))
        }
      payments <- AttachedPaymentExtractor
        .extractPayments(payment, V6, blockchainAllowsMultiPayment = true, smart.DApp)
        .leftMap(GenericError(_))
    } yield Invocation(functionCall, senderAddress, senderPK, senderAddress, senderPK, payments, id, fee, feeAssetId)

  private def decodeBase58(base58: String): Either[ValidationError, ByteStr] =
    ByteStr.decodeBase58(base58).toEither.leftMap(e => GenericError(String.valueOf(e.getMessage)))
}

object UtilsInvocationRequest {
  implicit val reads: Reads[UtilsInvocationRequest] = Json.using[Json.WithDefaultValues].reads[UtilsInvocationRequest]
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy