Skip to content

Commit

Permalink
NODE-2520 Corrected fail/reject on transfer action balance error (#3897)
Browse files Browse the repository at this point in the history
  • Loading branch information
xrtm000 authored Nov 28, 2023
1 parent bb322af commit d3be735
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import com.wavesplatform.account.{Address, AddressOrAlias, PublicKey}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.EitherExt2
import com.wavesplatform.features.BlockchainFeatures
import com.wavesplatform.features.BlockchainFeatures.{BlockRewardDistribution, BlockV5, RideV6, SynchronousCalls}
import com.wavesplatform.features.BlockchainFeatures.*
import com.wavesplatform.features.EstimatorProvider.*
import com.wavesplatform.features.InvokeScriptSelfPaymentPolicyProvider.*
import com.wavesplatform.features.ScriptTransferValidationProvider.*
Expand Down Expand Up @@ -514,17 +514,17 @@ object InvokeDiffsCommon {
e
)
)
).flatMap(nextDiff =>
).flatMap(portfolioSnapshot =>
blockchain
.assetScript(a)
.fold {
val r = checkAsset(blockchain, id)
.map(_ => nextDiff)
.map(_ => portfolioSnapshot)
.leftMap(FailedTransactionError.dAppExecution(_, 0): ValidationError)
TracedResult(r)
} { case AssetScriptInfo(script, complexity) =>
val assetVerifierSnapshot =
if (blockchain.disallowSelfPayment) nextDiff
if (blockchain.disallowSelfPayment) portfolioSnapshot
else
StateSnapshot
.build(
Expand All @@ -549,9 +549,11 @@ object InvokeDiffsCommon {
tx.timestamp,
tx.txId
)
val assetValidationDiff = for {
_ <- BalanceDiffValidation.cond(blockchain, _.isFeatureActivated(BlockchainFeatures.RideV6))(assetVerifierSnapshot)
assetValidationDiff <- validatePseudoTxWithSmartAssetScript(blockchain, tx)(
val assetValidationSnapshot = for {
_ <- BalanceDiffValidation
.cond(blockchain, _.isFeatureActivated(RideV6))(assetVerifierSnapshot)
.leftMap(e => if (blockchain.isFeatureActivated(LightNode)) FailedTransactionError.asFailedScriptError(e) else e)
snapshot <- validatePseudoTxWithSmartAssetScript(blockchain, tx)(
pseudoTx,
a.id,
assetVerifierSnapshot,
Expand All @@ -560,10 +562,10 @@ object InvokeDiffsCommon {
complexityLimit,
enableExecutionLog
)
} yield assetValidationDiff
val errorOpt = assetValidationDiff.fold(Some(_), _ => None)
} yield snapshot
val errorOpt = assetValidationSnapshot.fold(Some(_), _ => None)
TracedResult(
assetValidationDiff.map(d => nextDiff.setScriptsComplexity(d.scriptsComplexity)),
assetValidationSnapshot.map(d => portfolioSnapshot.setScriptsComplexity(d.scriptsComplexity)),
List(AssetVerifierTrace(id, errorOpt, AssetContext.Transfer))
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.wavesplatform.state.diffs.ci

import com.wavesplatform.db.WithDomain
import com.wavesplatform.db.WithState.AddrWithBalance
import com.wavesplatform.features.BlockchainFeatures.{LightNode, RideV6}
import com.wavesplatform.lang.directives.values.V5
import com.wavesplatform.lang.v1.compiler.TestCompiler
import com.wavesplatform.test.DomainPresets.{RideV5, WavesSettingsOps}
import com.wavesplatform.test.{PropSpec, produce}
import com.wavesplatform.transaction.Asset.IssuedAsset
import com.wavesplatform.transaction.TxHelpers.*

class InvokeTransferBalanceErrorTest extends PropSpec with WithDomain {
private val assetFailScript = TestCompiler(V5).compileExpression(
s"""
| strict c = ${(1 to 5).map(_ => "sigVerify(base58'', base58'', base58'')").mkString(" || ")}
| if (true) then throw() else true
""".stripMargin
)

property("invoke is always rejected with a lack of funds without execution of ScriptTransfer script after RideV6, corrected after LightNode") {
val issueTx = issue(signer(10), script = Some(assetFailScript))
val asset = IssuedAsset(issueTx.id())
val dApp = TestCompiler(V5).compileContract(
s"""
| @Callable(i)
| func default() = {
| [ScriptTransfer(i.caller, 1, base58'$asset')]
| }
|
| @Callable(i)
| func complex() = {
| strict c = ${(1 to 6).map(_ => "sigVerify(base58'', base58'', base58'')").mkString(" || ")}
| [ScriptTransfer(i.caller, 1, base58'$asset')]
| }
""".stripMargin
)
withDomain(
RideV5.setFeaturesHeight(RideV6 -> 6, LightNode -> 7),
AddrWithBalance.enoughBalances(secondSigner, signer(10))
) { d =>
d.appendBlock(issueTx)
d.appendBlock(setScript(secondSigner, dApp))

// RideV5 — always failed
d.appendAndAssertFailed(invoke(), "Transaction is not allowed by script of the asset")
d.appendAndAssertFailed(invoke(func = Some("complex")), "Transaction is not allowed by script of the asset")

// RideV6 — always rejected
d.appendBlockE(invoke()) should produce("negative asset balance")
d.appendBlockE(invoke(func = Some("complex"))) should produce("negative asset balance")

// LightNode — rejected or failed
d.appendBlock()
d.appendBlockE(invoke()) should produce("negative asset balance")
d.appendAndAssertFailed(invoke(func = Some("complex")), "negative asset balance")
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,13 @@ import com.wavesplatform.state.StringDataEntry
import com.wavesplatform.state.TxMeta.Status
import com.wavesplatform.state.diffs.FeeValidation.{FeeConstants, FeeUnit}
import com.wavesplatform.test.*
import com.wavesplatform.test.DomainPresets.RideV5
import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves}
import com.wavesplatform.transaction.TxHelpers.*
import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment
import com.wavesplatform.transaction.{TransactionType, TxHelpers}

class RideV5FailRejectTest extends PropSpec with WithDomain {
import DomainPresets.*

private val assetFailScript = TestCompiler(V5).compileExpression(
s"""
| strict c = ${(1 to 5).map(_ => "sigVerify(base58'', base58'', base58'')").mkString(" || ")}
Expand Down Expand Up @@ -107,32 +106,6 @@ class RideV5FailRejectTest extends PropSpec with WithDomain {
}
}

property("invoke is rejected with a lack of funds without execution of ScriptTransfer script only after RideV6") {
val issueTx = issue(signer(10), script = Some(assetFailScript))
val asset = IssuedAsset(issueTx.id())
val dApp = TestCompiler(V5).compileContract(
s"""
| @Callable(i)
| func default() = [
| ScriptTransfer(i.caller, 1, base58'$asset')
| ]
""".stripMargin
)
val invokeTx = invoke()
withDomain(RideV5, AddrWithBalance.enoughBalances(secondSigner, signer(10))) { d =>
d.appendBlock(issueTx)
d.appendBlock(setScript(secondSigner, dApp))
d.appendAndAssertFailed(invokeTx, "Transaction is not allowed by script of the asset")
}

// TODO: move test after bug fix NODE-2520
withDomain(RideV6, AddrWithBalance.enoughBalances(secondSigner, signer(10))) { d =>
d.appendBlock(issueTx)
d.appendBlock(setScript(secondSigner, dApp))
d.appendBlockE(invokeTx) should produce("negative asset balance")
}
}

property("failed invoke doesn't affect state") {
withDomain(RideV5, AddrWithBalance.enoughBalances(secondSigner, signer(10))) { d =>
val failAssetIssue = issue(script = Some(assetFailScript))
Expand Down

0 comments on commit d3be735

Please sign in to comment.