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

com.wavesplatform.transaction.smart.script.ScriptRunner.scala Maven / Gradle / Ivy

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

import cats.Id
import cats.syntax.either.*
import com.wavesplatform.account.AddressScheme
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.features.BlockchainFeatures.*
import com.wavesplatform.features.EstimatorProvider.*
import com.wavesplatform.features.EvaluatorFixProvider.*
import com.wavesplatform.lang.*
import com.wavesplatform.lang.contract.DApp
import com.wavesplatform.lang.directives.DirectiveSet
import com.wavesplatform.lang.directives.values.*
import com.wavesplatform.lang.script.v1.ExprScript
import com.wavesplatform.lang.script.{ContractScript, Script}
import com.wavesplatform.lang.v1.ContractLimits
import com.wavesplatform.lang.v1.compiler.Terms.{EVALUATED, EXPR, TRUE}
import com.wavesplatform.lang.v1.evaluator.*
import com.wavesplatform.lang.v1.evaluator.ContractEvaluator.LogExtraInfo
import com.wavesplatform.lang.v1.evaluator.ctx.EvaluationContext
import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.Bindings
import com.wavesplatform.lang.v1.traits.Environment
import com.wavesplatform.state.*
import com.wavesplatform.transaction.smart.{DApp as DAppTarget, *}
import com.wavesplatform.transaction.{Authorized, Proven}
import monix.eval.Coeval

object ScriptRunner {
  type TxOrd = BlockchainContext.In

  def apply(
      in: TxOrd,
      blockchain: Blockchain,
      script: Script,
      isAssetScript: Boolean,
      scriptContainerAddress: Environment.Tthis,
      enableExecutionLog: Boolean,
      complexityLimit: Int = Int.MaxValue,
      default: EVALUATED = TRUE
  ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) =
    applyGeneric(
      in,
      blockchain,
      script,
      isAssetScript,
      scriptContainerAddress,
      complexityLimit,
      default,
      blockchain.isFeatureActivated(SynchronousCalls),
      blockchain.isFeatureActivated(SynchronousCalls),
      blockchain.isFeatureActivated(SynchronousCalls) &&
        blockchain.height > blockchain.settings.functionalitySettings.enforceTransferValidationAfter,
      blockchain.checkEstimatorSumOverflow,
      blockchain.newEvaluatorMode,
      blockchain.isFeatureActivated(RideV6),
      enableExecutionLog,
      blockchain.isFeatureActivated(ConsensusImprovements),
      blockchain.isFeatureActivated(LightNode)
    )

  def applyGeneric(
      in: TxOrd,
      blockchain: Blockchain,
      script: Script,
      isAssetScript: Boolean,
      scriptContainerAddress: Environment.Tthis,
      defaultLimit: Int,
      default: EVALUATED,
      useCorrectScriptVersion: Boolean,
      fixUnicodeFunctions: Boolean,
      useNewPowPrecision: Boolean,
      checkEstimatorSumOverflow: Boolean,
      newEvaluatorMode: Boolean,
      checkWeakPk: Boolean,
      enableExecutionLog: Boolean,
      fixBigScriptField: Boolean,
      fixedThrownError: Boolean
  ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = {

    def evalVerifier(
        isContract: Boolean,
        partialEvaluate: (DirectiveSet, EvaluationContext[Environment, Id]) => (Log[Id], Int, Either[ExecutionError, EVALUATED])
    ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = {
      val txId = in.eliminate(_.id(), _ => ByteStr.empty)
      val ctxE =
        for {
          ds <- DirectiveSet(script.stdLibVersion, if (isAssetScript) Asset else Account, Expression)
          mi <- buildThisValue(in, blockchain, ds, scriptContainerAddress)
          ctx <- BlockchainContext
            .build(
              script.stdLibVersion,
              AddressScheme.current.chainId,
              Coeval.evalOnce(mi),
              Coeval.evalOnce(blockchain.height),
              blockchain,
              isAssetScript,
              isContract,
              scriptContainerAddress,
              txId,
              fixUnicodeFunctions,
              useNewPowPrecision,
              fixBigScriptField
            )
        } yield (ds, ctx)

      ctxE.fold(e => (Nil, 0, Left(e)), { case (ds, ctx) => partialEvaluate(ds, ctx) })
    }

    def evaluate(
        ctx: EvaluationContext[Environment, Id],
        expr: EXPR,
        logExtraInfo: LogExtraInfo,
        version: StdLibVersion
    ): (Log[Id], Int, Either[ExecutionError, EVALUATED]) = {
      val correctedLimit =
        if (isAssetScript)
          ContractLimits.MaxComplexityByVersion(version)
        else
          ContractLimits.MaxAccountVerifierComplexityByVersion(version)

      val (limit, onExceed) =
        if (defaultLimit == Int.MaxValue)
          if (checkEstimatorSumOverflow)
            (correctedLimit, (_: EXPR) => Left(CommonError(s"Verifier complexity limit = $correctedLimit is exceeded")))
          else
            (defaultLimit, (_: EXPR) => Left(CommonError(s"Verifier complexity limit = $defaultLimit is exceeded")))
        else
          (defaultLimit, (_: EXPR) => Right(default))

      val (log, unusedComplexity, result) =
        EvaluatorV2.applyOrDefault(
          ctx,
          expr,
          logExtraInfo,
          script.stdLibVersion,
          limit,
          correctFunctionCallScope = checkEstimatorSumOverflow,
          newMode = newEvaluatorMode,
          onExceed,
          enableExecutionLog,
          fixedThrownError
        )

      (log, limit - unusedComplexity, result)
    }

    script match {
      case s: ExprScript =>
        evalVerifier(isContract = false, (_, ctx) => evaluate(ctx, s.expr, LogExtraInfo(), s.stdLibVersion))

      case ContractScript.ContractScriptImpl(v, DApp(_, decls, _, Some(vf))) =>
        val partialEvaluate: (DirectiveSet, EvaluationContext[Environment, Id]) => (Log[Id], Int, Either[ExecutionError, EVALUATED]) = {
          (directives, ctx) =>
            val verify          = ContractEvaluator.verify(decls, vf, evaluate(ctx, _, _, v), _)
            val bindingsVersion = if (useCorrectScriptVersion) directives.stdLibVersion else V3
            in.eliminate(
              t =>
                RealTransactionWrapper(t, blockchain, directives.stdLibVersion, DAppTarget)
                  .fold(
                    e => (Nil, 0, Left(e)),
                    tx => verify(Bindings.transactionObject(tx, proofsEnabled = true, bindingsVersion, fixBigScriptField))
                  ),
              _.eliminate(
                t => verify(Bindings.orderObject(RealTransactionWrapper.ord(t), proofsEnabled = true, bindingsVersion)),
                _ => ???
              )
            )
        }
        evalVerifier(isContract = true, partialEvaluate)

      case ContractScript.ContractScriptImpl(_, DApp(_, _, _, None)) =>
        val proven: Proven & Authorized =
          in.eliminate(
            _.asInstanceOf[Proven & Authorized],
            _.eliminate(
              _.asInstanceOf[Proven & Authorized],
              _ => ???
            )
          )
        (Nil, 0, Verifier.verifyAsEllipticCurveSignature(proven, checkWeakPk).bimap(_.err, _ => TRUE))

      case other =>
        (Nil, 0, Left(s"$other: Unsupported script version"))
    }
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy