com.wavesplatform.state.diffs.AssetTransactionsDiffs.scala Maven / Gradle / Ivy
The newest version!
package com.wavesplatform.state.diffs
import cats.implicits.catsSyntaxEitherId
import cats.syntax.ior.*
import com.google.common.base.Utf8
import com.google.protobuf.ByteString
import com.wavesplatform.features.BlockchainFeatures.*
import com.wavesplatform.features.EstimatorProvider.*
import com.wavesplatform.lang.ValidationError
import com.wavesplatform.lang.script.Script
import com.wavesplatform.lang.v1.ContractLimits.MaxExprSizeInBytes
import com.wavesplatform.lang.v1.traits.domain.*
import com.wavesplatform.state.*
import com.wavesplatform.state.diffs.DiffsCommon.*
import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves}
import com.wavesplatform.transaction.ERC20Address
import com.wavesplatform.transaction.TxValidationError.GenericError
import com.wavesplatform.transaction.assets.*
import scala.collection.immutable.VectorMap
import scala.util.Either.cond
object AssetTransactionsDiffs {
def updateInfo(blockchain: Blockchain)(tx: UpdateAssetInfoTransaction): Either[ValidationError, StateSnapshot] =
for {
_ <- validateAsset(blockchain, tx.assetId, tx.sender.toAddress, issuerOnly = true)
lastUpdateHeight <- blockchain
.assetDescription(tx.assetId)
.map(_.lastUpdatedAt)
.toRight(GenericError("Asset doesn't exist"))
minUpdateInfoInterval = blockchain.settings.functionalitySettings.minAssetInfoUpdateInterval
updateAllowedHeight = lastUpdateHeight + minUpdateInfoInterval
updatedInfo = AssetInfo(tx.name, tx.description, Height @@ blockchain.height)
_ <- cond(
blockchain.height >= updateAllowedHeight,
(),
GenericError(
s"Can't update info of asset with id=${tx.assetId.id} before $updateAllowedHeight block, " +
s"current height=${blockchain.height}, minUpdateInfoInterval=$minUpdateInfoInterval"
)
)
feePortfolio <- tx.feeAsset match {
case Waves =>
Right(Portfolio(-tx.feeAmount.value))
case IssuedAsset(asset) if blockchain.isFeatureActivated(RideV6) =>
Left(GenericError(s"Invalid fee asset: $asset, only Waves can be used to pay fees for UpdateAssetInfoTransaction"))
case asset @ IssuedAsset(_) =>
Right(Portfolio.build(asset -> -tx.feeAmount.value))
}
snapshot <- StateSnapshot.build(
blockchain,
portfolios = Map(tx.sender.toAddress -> feePortfolio),
updatedAssets = Map(tx.assetId -> updatedInfo.leftIor)
)
} yield snapshot
def sponsor(b: Blockchain)(tx: SponsorFeeTransaction): Either[ValidationError, StateSnapshot] =
processSponsor(b, tx.sender.toAddress, tx.fee.value, SponsorFee(tx.asset.id, tx.minSponsoredAssetFee.map(_.value)))
def burn(b: Blockchain)(tx: BurnTransaction): Either[ValidationError, StateSnapshot] =
processBurn(b, tx.sender.toAddress, tx.fee.value, Burn(tx.asset.id, tx.quantity.value))
def reissue(b: Blockchain, blockTime: Long)(tx: ReissueTransaction): Either[ValidationError, StateSnapshot] =
processReissue(b, tx.sender.toAddress, blockTime, tx.fee.value, Reissue(tx.asset.id, tx.reissuable, tx.quantity.value))
private def checkEstimationOverflow(b: Blockchain, script: Option[(Script, Long)]): Either[GenericError, Unit] =
if (b.checkEstimationOverflow && script.exists(_._2 < 0))
Left(GenericError(s"Unexpected negative complexity"))
else
Right(())
def setAssetScript(blockchain: Blockchain)(tx: SetAssetScriptTransaction): Either[ValidationError, StateSnapshot] =
for {
_ <- validateAsset(blockchain, tx.asset, tx.sender.toAddress, issuerOnly = true)
script <- countVerifierComplexity(tx.script, blockchain, isAsset = true)
_ <-
if (!blockchain.hasAssetScript(tx.asset)) Left(GenericError("Cannot set script on an asset issued without a script"))
else checkEstimationOverflow(blockchain, script)
_ <- checkScriptSize(blockchain, tx.script)
snapshot <- StateSnapshot.build(
blockchain,
assetScripts = script.fold(Map[IssuedAsset, AssetScriptInfo]()) { case (script, complexity) =>
Map(tx.asset -> AssetScriptInfo(script, complexity))
},
portfolios = Map(tx.sender.toAddress -> Portfolio(-tx.fee.value))
)
} yield snapshot
private def checkScriptSize(b: Blockchain, scriptOpt: Option[Script]): Either[GenericError, Unit] =
scriptOpt.fold(().asRight[GenericError]) { script =>
cond(
!b.isFeatureActivated(BlockRewardDistribution) || script.bytes().size <= MaxExprSizeInBytes,
(),
GenericError(s"Script is too large: ${script.bytes().size} bytes > $MaxExprSizeInBytes bytes")
)
}
def issue(blockchain: Blockchain)(tx: IssueTransaction): Either[ValidationError, StateSnapshot] = { // TODO: unify with InvokeScript action diff?
// First 20 bytes of id should be unique
def requireUnique(): Boolean =
blockchain.resolveERC20Address(ERC20Address(tx.asset)).isEmpty
def requireValidUtf(): Boolean = {
def isValid(str: ByteString): Boolean = {
val wellFormed = Utf8.isWellFormed(str.toByteArray)
val convertible = str == ByteString.copyFromUtf8(str.toStringUtf8)
wellFormed && convertible
}
(isValid(tx.name) && isValid(tx.description)) || !blockchain.isFeatureActivated(BlockV5)
}
val assetInfo = AssetInfo(tx.name, tx.description, Height @@ blockchain.height)
val assetVolume = AssetVolumeInfo(tx.reissuable, BigInt(tx.quantity.value))
val assetStatic = AssetStaticInfo(TransactionId @@ tx.id(), TransactionId @@ tx.id(), tx.sender, tx.decimals.value, blockchain.isNFT(tx))
val asset = IssuedAsset(tx.id())
for {
_ <- cond(requireUnique(), (), GenericError(s"Asset ${tx.asset} is already issued"))
_ <- cond(requireValidUtf(), (), GenericError("Valid UTF-8 strings required"))
_ <- checkScriptSize(blockchain, tx.script)
script <- countVerifierComplexity(tx.script, blockchain, isAsset = true)
_ <- checkEstimationOverflow(blockchain, script)
snapshot <- StateSnapshot.build(
blockchain,
assetScripts = script.fold(Map[IssuedAsset, AssetScriptInfo]()) { case (script, complexity) =>
Map(asset -> AssetScriptInfo(script, complexity))
},
issuedAssets = Seq(asset -> NewAssetInfo(assetStatic, assetInfo, assetVolume)),
portfolios = VectorMap(tx.sender.toAddress -> Portfolio.build(-tx.fee.value, asset, tx.quantity.value))
)
} yield snapshot
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy