From d3be7350a93355918d066304eb3df65cd935bfe9 Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Tue, 28 Nov 2023 15:07:49 +0300 Subject: [PATCH] NODE-2520 Corrected fail/reject on transfer action balance error (#3897) --- .../diffs/invoke/InvokeDiffsCommon.scala | 22 +++---- .../ci/InvokeTransferBalanceErrorTest.scala | 59 +++++++++++++++++++ .../state/diffs/ci/RideV5FailRejectTest.scala | 29 +-------- 3 files changed, 72 insertions(+), 38 deletions(-) create mode 100644 node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeTransferBalanceErrorTest.scala diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala index b00d78891e..4201c4a3e9 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/invoke/InvokeDiffsCommon.scala @@ -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.* @@ -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( @@ -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, @@ -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)) ) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeTransferBalanceErrorTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeTransferBalanceErrorTest.scala new file mode 100644 index 0000000000..359f5a8f37 --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeTransferBalanceErrorTest.scala @@ -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") + } + } +} diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala index 2bb47c91b4..77c5111484 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/RideV5FailRejectTest.scala @@ -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(" || ")} @@ -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))