From 1fa151305cfb9707e443532b5ecdccb2246cb8e1 Mon Sep 17 00:00:00 2001 From: Evgeny Nichegovskiy Date: Fri, 27 Sep 2024 12:35:57 +0300 Subject: [PATCH] Moved integration test to unit test (#3968) --- .github/workflows/check-pr.yaml | 12 +- .../estimator/ScriptEstimatorV2Test.scala | 8 +- .../grpc/FailedTransactionGrpcSuite.scala | 274 ------------------ .../transactions/FailedTransactionSuite.scala | 262 ----------------- .../tests/src/test/resources/application.conf | 6 + .../wavesplatform/http/UtilsRouteSpec.scala | 4 +- .../diffs/ci/RideGeneratingBalanceSpec.scala | 3 +- .../wavesplatform/utx/UtxFailedTxsSpec.scala | 62 +++- 8 files changed, 80 insertions(+), 551 deletions(-) delete mode 100644 node-it/src/test/scala/com/wavesplatform/it/sync/grpc/FailedTransactionGrpcSuite.scala delete mode 100644 node-it/src/test/scala/com/wavesplatform/it/sync/transactions/FailedTransactionSuite.scala diff --git a/.github/workflows/check-pr.yaml b/.github/workflows/check-pr.yaml index aaa330c848b..9cd4f4abf6b 100644 --- a/.github/workflows/check-pr.yaml +++ b/.github/workflows/check-pr.yaml @@ -16,7 +16,7 @@ jobs: gpg-private-key: ${{ secrets.OSSRH_GPG_KEY }} gpg-passphrase: ${{ secrets.OSSRH_GPG_PASSPHRASE }} - name: Check PR - run: sbt --mem 10240 --batch ";checkPR;completeQaseRun" + run: sbt -J-Xlog:gc*=debug:file=./gc.log --mem 8096 --batch ";checkPR;completeQaseRun" env: QASE_ENABLE: true QASE_RUN_NAME: checkPR @@ -56,3 +56,13 @@ jobs: sbt_version=$(cut -d\" -f2 version.sbt) pr_number=${{ github.event.number }} sbt -Dproject.version=$sbt_version-$pr_number-SNAPSHOT --mem 4096 --batch publishSigned + - name: Save debug data + uses: actions/upload-artifact@v4 + if: always() + with: + if-no-files-found: ignore + name: debug-data + path: | + gc.log + hs*.log + core* diff --git a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorV2Test.scala b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorV2Test.scala index a5e71db79b4..09bfe8172e2 100644 --- a/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorV2Test.scala +++ b/lang/tests/src/test/scala/com/wavesplatform/lang/estimator/ScriptEstimatorV2Test.scala @@ -5,6 +5,8 @@ import com.wavesplatform.lang.directives.values.V3 import com.wavesplatform.lang.utils.* import com.wavesplatform.lang.v1.estimator.v2.ScriptEstimatorV2 +import java.util.concurrent.Semaphore + class ScriptEstimatorV2Test extends ScriptEstimatorTestBase(ScriptEstimatorV2) { property("transitive ref usage") { def estimateRefUsage(ref: String): Long = { @@ -62,16 +64,18 @@ class ScriptEstimatorV2Test extends ScriptEstimatorTestBase(ScriptEstimatorV2) { """.stripMargin @volatile var r: Either[String, Long] = Right(0) + val s = new Semaphore(0) val run: Runnable = { () => + s.release() r = estimate(functionCosts(V3), compile(hangingScript)) } val t = new Thread(run) t.setDaemon(true) t.start() - Thread.sleep(5000) + s.acquire() t.interrupt() - Thread.sleep(500) + t.join() r shouldBe Left("Script estimation was interrupted") t.getState shouldBe Thread.State.TERMINATED diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/grpc/FailedTransactionGrpcSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/grpc/FailedTransactionGrpcSuite.scala deleted file mode 100644 index cc611ee78b0..00000000000 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/grpc/FailedTransactionGrpcSuite.scala +++ /dev/null @@ -1,274 +0,0 @@ -package com.wavesplatform.it.sync.grpc - -import com.google.protobuf.ByteString -import com.typesafe.config.Config -import com.wavesplatform.account.{Address, KeyPair} -import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.common.utils.{Base58, EitherExt2} -import com.wavesplatform.it.api.SyncGrpcApi.* -import com.wavesplatform.it.sync.* -import com.wavesplatform.it.sync.transactions.FailedTransactionSuiteLike -import com.wavesplatform.lang.v1.FunctionHeader -import com.wavesplatform.lang.v1.compiler.Terms.FUNCTION_CALL -import com.wavesplatform.lang.v1.estimator.v3.ScriptEstimatorV3 -import com.wavesplatform.protobuf.Amount -import com.wavesplatform.protobuf.transaction.DataEntry -import com.wavesplatform.protobuf.transaction.{PBRecipients, PBSignedTransaction, PBTransactions, Recipient} -import com.wavesplatform.state.{BinaryDataEntry, StringDataEntry} -import com.wavesplatform.test.* -import com.wavesplatform.transaction.smart.script.ScriptCompiler - -class FailedTransactionGrpcSuite extends GrpcBaseTransactionSuite with FailedTransactionSuiteLike[PBSignedTransaction] { - import FailedTransactionSuiteLike.* - import grpcApi.* - - private val contract = KeyPair("thirdContract".getBytes("UTF-8")) - private val contractAddr = PBRecipients.create(Address.fromPublicKey(contract.publicKey)).getPublicKeyHash - private val caller = thirdAcc - private val callerAddr = PBRecipients.create(Address.fromPublicKey(thirdAcc.publicKey)).getPublicKeyHash - - private val assetAmount = 1000000000L - private var smartAsset = "" - private var sponsoredAsset = "" - - protected override def beforeAll(): Unit = { - super.beforeAll() - - sender.broadcastTransfer(sender.keyPair, Recipient().withPublicKeyHash(contractAddr), 100.waves, minFee, waitForTx = true) - - smartAsset = PBTransactions - .vanillaUnsafe( - sender - .broadcastIssue( - contract, - "Asset", - assetAmount, - 8, - reissuable = true, - issueFee, - description = "Description", - script = Right(ScriptCompiler.compile("true", ScriptEstimatorV3.latest).toOption.map(_._1)), - waitForTx = true - ) - ) - .id() - .toString - - sponsoredAsset = PBTransactions - .vanillaUnsafe( - sender - .broadcastIssue( - contract, - "Sponsored Asset", - assetAmount, - 8, - reissuable = true, - issueFee, - "Description", - script = Right(None), - waitForTx = true - ) - ) - .id() - .toString - - val scriptTextV4 = - s""" - |{-# STDLIB_VERSION 4 #-} - |{-# CONTENT_TYPE DAPP #-} - | - |let asset = base58'$smartAsset' - | - |@Callable(inv) - |func tikTok() = { - | let action = valueOrElse(getString(this, "tikTok"), "unknown") - | let check = ${"sigVerify(base58'', base58'', base58'') ||" * 16} false - | if (check) then [] - | else if (action == "transfer") then [ScriptTransfer(inv.caller, 15, asset)] - | else if (action == "issue") then [Issue("new asset", "", 100, 8, true, unit, 0)] - | else if (action == "reissue") then [Reissue(asset, 15, true)] - | else if (action == "burn") then [Burn(asset, 15)] - | else [] - |} - | - |@Callable(inv) - |func transferAndWrite(x: Int) = { - | let check = ${"sigVerify(base58'', base58'', base58'') ||" * 16} false - | if (check) then [] - | else if (x % 4 == 0) then [ScriptTransfer(inv.caller, 15, asset), IntegerEntry("n", x)] - | else if (x % 4 == 1) then [ScriptTransfer(inv.caller, 15, asset), BooleanEntry("b", x % 2 == 0)] - | else if (x % 4 == 2) then [ScriptTransfer(inv.caller, 15, asset), BinaryEntry("bn", toBytes(x))] - | else if (x % 4 == 3) then [ScriptTransfer(inv.caller, 15, asset), StringEntry("s", toString(x))] - | else [] - |} - | - |@Callable(inv) - |func canThrow() = { - | let action = valueOrElse(getString(this, "crash"), "no") - | let check = ${"sigVerify(base58'', base58'', base58'') ||" * 16} true - | - | if (action == "yes") - | then { - | if (check) - | then throw("Crashed by dApp") - | else throw("Crashed by dApp") - | } - | else [] - |} - | - """.stripMargin - val script = ScriptCompiler.compile(scriptTextV4, ScriptEstimatorV3.latest).explicitGet()._1 - sender.setScript(contract, Right(Some(script)), setScriptFee, waitForTx = true) - } - - test("InvokeScriptTransaction: invoke script error in payment asset propagates failed transaction") { - val invokeFee = 0.005.waves + smartFee - val setAssetScriptMinFee = setAssetScriptFee + smartFee - val priorityFee = setAssetScriptMinFee + invokeFee - - val paymentAsset = PBTransactions - .vanillaUnsafe( - sender - .broadcastIssue( - caller, - "paymentAsset", - assetAmount, - 8, - reissuable = true, - script = Right(ScriptCompiler.compile("true", ScriptEstimatorV3.latest).toOption.map(_._1)), - fee = issueFee + smartFee, - waitForTx = true - ) - ) - .id() - - updateAssetScript(result = true, smartAsset, contract, setAssetScriptMinFee) - updateTikTok("unknown", setAssetScriptMinFee) - - overflowBlock() - sendTxsAndThenPriorityTx( - _ => - sender - .broadcastInvokeScript( - caller, - Recipient().withPublicKeyHash(contractAddr), - Some(FUNCTION_CALL(FunctionHeader.User("tikTok"), List.empty)), - payments = Seq(Amount(ByteString.copyFrom(paymentAsset.arr), 15)), - fee = invokeFee - ), - () => updateAssetScript(result = false, paymentAsset.toString, caller, priorityFee, waitForTx = false) - )((txs, _) => assertFailedTxs(txs)) - } - - test("InvokeScriptTransaction: sponsored fee on failed transaction should be charged correctly") { - val invokeFee = 0.005.waves + smartFee - val invokeFeeInAsset = invokeFee / 100000 // assetFee = feeInWaves / feeUnit * sponsorship - val setAssetScriptMinFee = setAssetScriptFee + smartFee * 2 - val priorityFee = setAssetScriptMinFee + invokeFee - - updateAssetScript(result = true, smartAsset, contract, setAssetScriptMinFee) - updateTikTok("reissue", setAssetScriptMinFee) - - sender.broadcastSponsorFee( - contract, - Some(Amount.of(ByteString.copyFrom(Base58.decode(sponsoredAsset)), 1)), - sponsorFee + smartFee, - waitForTx = true - ) - sender.broadcastTransfer( - contract, - Recipient().withPublicKeyHash(callerAddr), - assetAmount, - smartMinFee, - assetId = sponsoredAsset, - waitForTx = true - ) - val prevBalance = sender.wavesBalance(contractAddr).regular - - sendTxsAndThenPriorityTx( - _ => - sender.broadcastInvokeScript( - caller, - Recipient().withPublicKeyHash(contractAddr), - Some(FUNCTION_CALL(FunctionHeader.User("tikTok"), List.empty)), - fee = invokeFeeInAsset, - feeAssetId = ByteString.copyFrom(Base58.decode(sponsoredAsset)) - ), - () => updateAssetScript(result = false, smartAsset, contract, priorityFee) - ) { (txs, _) => - sender.wavesBalance(contractAddr).regular shouldBe prevBalance - invokeFee * txs.size - priorityFee - assertFailedTxs(txs) - } - } - - test("InvokeScriptTransaction: reject transactions if account script failed") { - val invokeFee = 0.005.waves - val setAssetScriptMinFee = setAssetScriptFee + smartFee * 2 - val priorityFee = setAssetScriptMinFee + invokeFee - - updateTikTok("unknown", setAssetScriptMinFee) - updateAssetScript(result = true, smartAsset, contract, setAssetScriptMinFee) - - overflowBlock() - val prevBalance = sender.wavesBalance(callerAddr).regular - sendTxsAndThenPriorityTx( - _ => - sender.broadcastInvokeScript( - caller, - Recipient().withPublicKeyHash(contractAddr), - Some(FUNCTION_CALL(FunctionHeader.User("tikTok"), List.empty)), - fee = invokeFee - ), - () => - sender.setScript( - caller, - Right( - ScriptCompiler - .compile( - s""" - |{-# STDLIB_VERSION 3 #-} - |{-# CONTENT_TYPE EXPRESSION #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - | - |match (tx) { - |case _: InvokeScriptTransaction => false - |case _ => true - |} - |""".stripMargin, - ScriptEstimatorV3.latest - ) - .toOption - .map(_._1) - ), - fee = priorityFee - ) - ) { (txs, _) => - val invalid = assertInvalidTxs(txs) - sender.wavesBalance(callerAddr).regular shouldBe prevBalance - (txs.size - invalid.size) * invokeFee - priorityFee - invalid - } - } - - def overflowBlock(): Unit = { - val entries = List.tabulate(4)(n => PBTransactions.toPBDataEntry(BinaryDataEntry("test" + n, ByteStr(Array.fill(32767)(n.toByte))))) - val fee = calcDataFee(entries) - waitForEmptyUtx() - waitForHeightArise() - for (_ <- 1 to 8) sender.putData(sender.keyPair, entries, fee) - waitForEmptyUtx() - } - - private def calcDataFee(data: List[DataEntry]): Long = { - val dataSize = data.map(_.toByteArray.length).sum + 128 - if (dataSize > 1024) { - minFee * (dataSize / 1024 + 1) - } else minFee - } - - private def updateTikTok(result: String, fee: Long, waitForTx: Boolean = true): PBSignedTransaction = - sender.putData(contract, List(StringDataEntry("tikTok", result)).map(PBTransactions.toPBDataEntry), fee = fee, waitForTx = waitForTx) - - override protected def waitForHeightArise(): Unit = sender.waitForHeightArise() - - override protected def nodeConfigs: Seq[Config] = Configs -} diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/FailedTransactionSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/FailedTransactionSuite.scala deleted file mode 100644 index e7de960af51..00000000000 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/FailedTransactionSuite.scala +++ /dev/null @@ -1,262 +0,0 @@ -package com.wavesplatform.it.sync.transactions - -import com.typesafe.config.Config -import com.wavesplatform.api.http.ApiError.TransactionNotAllowedByAssetScript -import com.wavesplatform.api.http.DebugMessage -import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.it.api.SyncHttpApi.* -import com.wavesplatform.it.sync.* -import com.wavesplatform.it.transactions.BaseTransactionSuite -import com.wavesplatform.lang.v1.estimator.v3.ScriptEstimatorV3 -import com.wavesplatform.state.StringDataEntry -import com.wavesplatform.test.* -import com.wavesplatform.transaction.assets.exchange.AssetPair -import com.wavesplatform.transaction.smart.script.ScriptCompiler -import org.scalatest.CancelAfterFailure - -import scala.concurrent.duration.* - -class FailedTransactionSuite extends BaseTransactionSuite with CancelAfterFailure with FailedTransactionSuiteLike[String] with OverflowBlock { - import FailedTransactionSuite.* - import FailedTransactionSuiteLike.* - import restApi.* - - private lazy val contract = sender.createKeyPair() - private def caller = thirdKeyPair - - private val assetAmount = 1000000000L - private var smartAsset = "" - - private def seller = firstKeyPair - private def buyer = secondKeyPair - private def matcher = thirdKeyPair - - private def sellerAddress = firstKeyPair - private def buyerAddress = secondKeyPair - private def matcherAddress = thirdKeyPair - - private lazy val contractAddress: String = contract.toAddress.toString - - protected override def beforeAll(): Unit = { - super.beforeAll() - - sender.transfer(sender.keyPair, contractAddress, 100.waves, minFee, waitForTx = true) - - smartAsset = sender - .issue( - contract, - "Asset", - "Description", - assetAmount, - 8, - script = Some(ScriptCompiler.compile("true", ScriptEstimatorV3.latest).explicitGet()._1.bytes().base64), - waitForTx = true - ) - .id - - val scriptTextV4 = - s""" - |{-# STDLIB_VERSION 4 #-} - |{-# CONTENT_TYPE DAPP #-} - | - |let asset = base58'$smartAsset' - | - |@Callable(inv) - |func tikTok() = { - | let check = ${"sigVerify(base58'', base58'', base58'') ||" * 16} false - | let action = valueOrElse(getString(this, "tikTok"), "unknown") - | if (check) then [] - | else if (action == "transfer") then [ScriptTransfer(inv.caller, 15, asset)] - | else if (action == "issue") then [Issue("new asset", "", 100, 8, true, unit, 0)] - | else if (action == "reissue") then [Reissue(asset, 15, true)] - | else if (action == "burn") then [Burn(asset, 15)] - | else [] - |} - | - |@Callable(inv) - |func transferAndWrite(x: Int) = { - | if (x % 4 == 0) then [ScriptTransfer(inv.caller, 15, asset), IntegerEntry("n", x)] - | else if (x % 4 == 1) then [ScriptTransfer(inv.caller, 15, asset), BooleanEntry("b", x % 2 == 0)] - | else if (x % 4 == 2) then [ScriptTransfer(inv.caller, 15, asset), BinaryEntry("bn", toBytes(x))] - | else if (x % 4 == 3) then [ScriptTransfer(inv.caller, 15, asset), StringEntry("s", toString(x))] - | else [] - |} - | - |@Callable(inv) - |func canThrow() = { - | let action = valueOrElse(getString(this, "crash"), "no") - | let check = ${"sigVerify(base58'', base58'', base58'') ||" * 16} true - | - | if (action == "yes") - | then { - | if (check) - | then throw("Crashed by dApp") - | else throw("Crashed by dApp") - | } - | else [] - |} - | - |@Callable(inv) - |func defineTxHeight(id: ByteVector) = [BooleanEntry(toBase58String(id), transactionHeightById(id).isDefined())] - | - |@Callable(inv) - |func failAfterFirstCallHeight() = { - | strict c = ${"sigVerify(base58'', base58'', base58'') ||" * 16} true - | let heightEntry = match this.getInteger("heightEntry") { - | case _: Unit => height - | case h => if (h == height) then h else throw("height differs") - | } - | [IntegerEntry("heightEntry", heightEntry)] - |} - | - """.stripMargin - - val script = ScriptCompiler.compile(scriptTextV4, ScriptEstimatorV3.latest).explicitGet()._1.bytes().base64 - sender.setScript(contract, Some(script), setScriptFee, waitForTx = true).id - } - - test("InvokeScriptTransaction: reject transactions if account script failed") { - val invokeFee = 0.005.waves - val setAssetScriptMinFee = setAssetScriptFee + smartFee - val priorityFee = setAssetScriptMinFee + invokeFee - - updateTikTok("unknown", setAssetScriptMinFee) - updateAssetScript(result = true, smartAsset, contract, setAssetScriptMinFee) - - val prevBalance = sender.balance(caller.toAddress.toString).balance - - overflowBlock() - sendTxsAndThenPriorityTx( - _ => sender.invokeScript(caller, contractAddress, Some("tikTok"), fee = invokeFee)._1.id, - () => - sender - .setScript( - caller, - Some( - ScriptCompiler - .compile( - s""" - |{-# STDLIB_VERSION 3 #-} - |{-# CONTENT_TYPE EXPRESSION #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - | - |match (tx) { - | case _: InvokeScriptTransaction => false - | case _ => true - |} - |""".stripMargin, - ScriptEstimatorV3.latest - ) - .explicitGet() - ._1 - .bytes() - .base64 - ), - fee = priorityFee - ) - .id - ) { (txs, priorityTx) => - logPriorityTx(priorityTx) - val invalid = assertInvalidTxs(txs) - sender.balance(caller.toAddress.toString).balance shouldBe prevBalance - (txs.size - invalid.size) * invokeFee - priorityFee - invalid - } - } - - test("ExchangeTransaction: invalid exchange tx when asset script fails on broadcast") { - val init = Seq( - sender.setScript(firstKeyPair, None, setScriptFee + smartFee).id, - sender.setScript(secondKeyPair, None, setScriptFee + smartFee).id, - sender.setScript(thirdKeyPair, None, setScriptFee + smartFee).id - ) - waitForTxs(init) - - val Precondition(amountAsset, priceAsset, buyFeeAsset, sellFeeAsset) = - exchangePreconditions( - Some(ScriptCompiler.compile("true", ScriptEstimatorV3.latest).explicitGet()._1.bytes().base64) - ) - - val assetPair = AssetPair.createAssetPair(amountAsset, priceAsset).get - val fee = 0.003.waves + 4 * smartFee - val sellMatcherFee = fee / 100000L - val buyMatcherFee = fee / 100000L - - val allCases = - Seq((amountAsset, sellerAddress), (priceAsset, buyerAddress), (sellFeeAsset, matcherAddress), (buyFeeAsset, matcherAddress)) - - for ((invalidScriptAsset, owner) <- allCases) { - updateAssetScript(result = false, invalidScriptAsset, owner, setAssetScriptFee + smartFee) - val tx = mkExchange(buyer, seller, matcher, assetPair, fee, buyFeeAsset, sellFeeAsset, buyMatcherFee, sellMatcherFee) - assertApiError( - sender.signedBroadcast(tx.json()), - AssertiveApiError(TransactionNotAllowedByAssetScript.Id, "Transaction is not allowed by token-script") - ) - assertInvalidTxs(Seq(tx.id().toString)) - updateAssetScript(result = true, invalidScriptAsset, owner, setAssetScriptFee + smartFee) - } - } - - test("InvokeScriptTransaction: revalidate transactions returned to UTXPool because of `min-micro-block-age`") { - docker.restartNode(dockerNodes().head, configForMinMicroblockAge) - - val caller = sender.createKeyPair() - sender.transfer(sender.keyPair, caller.toAddress.toString, 100.waves, minFee, waitForTx = true) - - val txs = (1 to 9).map { _ => sender.invokeScript(caller, contractAddress, Some("failAfterFirstCallHeight"), fee = invokeFee) } - - val failHeight = txs.map(tx => sender.waitForTransaction(tx._1.id)).map(_.height).max - val failedTxs = sender.blockAt(failHeight).transactions.map(_.id) - - assertFailedTxs(failedTxs) - } - - def updateTikTok(result: String, fee: Long, waitForTx: Boolean = true): String = - sender.broadcastData(contract, List(StringDataEntry("tikTok", result)), fee = fee, waitForTx = waitForTx).id - - private def waitForTxs(txs: Seq[String]): Unit = - nodes.waitFor("preconditions", 500.millis)(_.transactionStatus(txs).forall(_.status == "confirmed"))(_.forall(identity)) - - private def exchangePreconditions(initScript: Option[String]): Precondition = { - val transfers = Seq( - sender.transfer(sender.keyPair, sellerAddress.toAddress.toString, 100.waves).id, - sender.transfer(sender.keyPair, buyerAddress.toAddress.toString, 100.waves).id, - sender.transfer(sender.keyPair, matcherAddress.toAddress.toString, 100.waves).id - ) - - val amountAsset = sender.issue(sellerAddress, "Amount asset", script = initScript, decimals = 8).id - val priceAsset = sender.issue(buyerAddress, "Price asset", script = initScript, decimals = 8).id - val sellFeeAsset = sender.issue(matcherAddress, "Seller fee asset", script = initScript, decimals = 8).id - val buyFeeAsset = sender.issue(matcherAddress, "Buyer fee asset", script = initScript, decimals = 8).id - - val preconditions = transfers ++ Seq( - amountAsset, - priceAsset, - sellFeeAsset, - buyFeeAsset - ) - - waitForTxs(preconditions) - - val transferToSeller = - sender.transfer(matcherAddress, sellerAddress.toAddress.toString, 1000000000, fee = minFee + smartFee, assetId = Some(sellFeeAsset)).id - val transferToBuyer = - sender.transfer(matcherAddress, buyerAddress.toAddress.toString, 1000000000, fee = minFee + smartFee, assetId = Some(buyFeeAsset)).id - - waitForTxs(Seq(transferToSeller, transferToBuyer)) - - Precondition(amountAsset, priceAsset, buyFeeAsset, sellFeeAsset) - } - - private def logPriorityTx(tx: String): Unit = { - log.debug(s"Priority transaction: $tx") - sender.printDebugMessage(DebugMessage(s"Priority transaction: $tx")) - } - - override protected def waitForHeightArise(): Unit = nodes.waitForHeightArise() - - override protected def nodeConfigs: Seq[Config] = Configs -} - -object FailedTransactionSuite { - case class Precondition(amountAsset: String, priceAsset: String, buyFeeAsset: String, sellFeeAsset: String) -} diff --git a/node/tests/src/test/resources/application.conf b/node/tests/src/test/resources/application.conf index 98841a94ec5..04c4959d8c7 100644 --- a/node/tests/src/test/resources/application.conf +++ b/node/tests/src/test/resources/application.conf @@ -14,3 +14,9 @@ waves { } } } + +akka { + stdout-loglevel = "OFF" + loglevel = "OFF" + loggers = [] +} diff --git a/node/tests/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala b/node/tests/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala index a1169464478..9941601904b 100644 --- a/node/tests/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala +++ b/node/tests/src/test/scala/com/wavesplatform/http/UtilsRouteSpec.scala @@ -284,8 +284,8 @@ class UtilsRouteSpec extends RouteSpec("/utils") with RestAPISettingsHelper with |{-# CONTENT_TYPE DAPP #-} |{-# SCRIPT_TYPE ACCOUNT #-} | - |let looooooooooooooooooooooooongName = base58'${"a" * 3602}' - |${(1 to 18).map(i => s"let a$i = base58'${"a" * 12200}'").mkString("\n")} + |let looooooooooooooooooooooooongName = base64'${"A" * 3518}' # 2640 bytes + |${(1 to 18).map(i => s"let a$i = base64'${"A" * 11912}'").mkString("\n")} # 18 * 8934 bytes |func test() = looooooooooooooooooooooooongName == looooooooooooooooooooooooongName """.stripMargin diff --git a/node/tests/src/test/scala/com/wavesplatform/state/diffs/ci/RideGeneratingBalanceSpec.scala b/node/tests/src/test/scala/com/wavesplatform/state/diffs/ci/RideGeneratingBalanceSpec.scala index 548c51e57ba..96fefe2977f 100644 --- a/node/tests/src/test/scala/com/wavesplatform/state/diffs/ci/RideGeneratingBalanceSpec.scala +++ b/node/tests/src/test/scala/com/wavesplatform/state/diffs/ci/RideGeneratingBalanceSpec.scala @@ -10,8 +10,9 @@ import com.wavesplatform.lang.v1.compiler.Terms.CONST_LONG import com.wavesplatform.test.{FreeSpec, NumericExt} import com.wavesplatform.transaction.TxHelpers import com.wavesplatform.transaction.smart.InvokeScriptTransaction +import org.scalatest.ParallelTestExecution -class RideGeneratingBalanceSpec extends FreeSpec with WithDomain { +class RideGeneratingBalanceSpec extends FreeSpec with WithDomain with ParallelTestExecution { def testGBAffectedByTxInCurrentBlock(preset: WavesSettings, stdLibVersion: StdLibVersion): Block = { val dAppAccount = TxHelpers.signer(999) diff --git a/node/tests/src/test/scala/com/wavesplatform/utx/UtxFailedTxsSpec.scala b/node/tests/src/test/scala/com/wavesplatform/utx/UtxFailedTxsSpec.scala index c34dc6aaa3c..24f436a08df 100644 --- a/node/tests/src/test/scala/com/wavesplatform/utx/UtxFailedTxsSpec.scala +++ b/node/tests/src/test/scala/com/wavesplatform/utx/UtxFailedTxsSpec.scala @@ -193,7 +193,7 @@ class UtxFailedTxsSpec extends FlatSpec with WithDomain with Eventually { utx.packUnconfirmed(MultiDimensionalMiningConstraint.Unlimited, None)._1 shouldBe Some(Seq(tx)) } - it should "cleanup transaction when script result changes" in utxTest { (d, utx) => + it should s"cleanup transaction when script result changes and complexity < ${ContractLimits.FailFreeInvokeComplexity}" in utxTest { (d, utx) => val (script, _) = ScriptCompiler .compile( """ @@ -206,13 +206,8 @@ class UtxFailedTxsSpec extends FlatSpec with WithDomain with Eventually { | if (height % 2 == 0) then | [] | else - | if (!sigVerify(base58'', base58'', base58'') - | && !sigVerify(base58'', base58'', base58'') - | && !sigVerify(base58'', base58'', base58'') - | && !sigVerify(base58'', base58'', base58'')) then - | throw("height is odd") - | else [IntegerEntry("h", height)] - | } + | throw("height is odd") + |} | """.stripMargin, ScriptEstimatorV3.latest ) @@ -229,12 +224,61 @@ class UtxFailedTxsSpec extends FlatSpec with WithDomain with Eventually { utx.size shouldBe 100 d.appendBlock() // Height is odd utx.addAndScheduleCleanup(Nil) - eventually(timeout(10 seconds), interval(500 millis)) { + eventually(timeout(5 seconds), interval(500 millis)) { utx.size shouldBe 0 utx.all shouldBe Nil } } + it should s"not cleanup transaction when script result changes and complexity > ${ContractLimits.FailFreeInvokeComplexity}" in withFS( + TestFunctionalitySettings + .withFeatures( + BlockchainFeatures.NG, + BlockchainFeatures.BlockV5, + BlockchainFeatures.Ride4DApps, + BlockchainFeatures.SynchronousCalls, + BlockchainFeatures.SmartAccounts + ) + )(utxTest { (d, utx) => + val (script, _) = ScriptCompiler + .compile( + s"""{-# STDLIB_VERSION 5 #-} + |{-# CONTENT_TYPE DAPP #-} + |{-# SCRIPT_TYPE ACCOUNT #-} + | + |@Callable(i) + |func test1000() = { + | if (height % 2 == 0) then + | [] + | else + | if (${"sigVerify(base58'', base58'', base58'') ||" * 8} true) then + | throw("height is odd") + | else throw("smth wrong") + |} + |""".stripMargin, + ScriptEstimatorV3.latest + ) + .explicitGet() + + d.appendBlock(TxHelpers.setScript(dApp, script)) + assert(d.blockchainUpdater.height % 2 == 0) + val invoke = TxHelpers.invoke(dApp.toAddress, Some("test1000")) + utx.putIfNew(invoke, forceValidate = true).resultE.explicitGet() + + utx.size shouldBe 1 + d.appendKeyBlock() // Height is odd + utx.addAndScheduleCleanup(Nil) + eventually(timeout(3 seconds), interval(500 millis)) { + utx.size shouldBe 1 + utx.all shouldBe Seq(invoke) + } + + utx.packUnconfirmed(MultiDimensionalMiningConstraint.Unlimited, None)._1 shouldBe Some(Seq(invoke)) + d.appendMicroBlock(invoke) + + d.blockchain.transactionMeta(invoke.id()) shouldBe Some(TxMeta(Height(3), Status.Failed, 1614)) + }) + private[this] def genExpr(targetComplexity: Int, result: Boolean): String = { s""" |if ($result) then