Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NODE-2597 Blinking tests #3899

Merged
merged 8 commits into from
Nov 19, 2023
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -121,31 +121,6 @@ class FailedTransactionGrpcSuite extends GrpcBaseTransactionSuite with FailedTra
sender.setScript(contract, Right(Some(script)), setScriptFee, waitForTx = true)
}

test("InvokeScriptTransaction: insufficient action fees propagates failed transaction") {
val invokeFee = 0.005.waves
val setAssetScriptMinFee = setAssetScriptFee + smartFee * 2
val priorityFee = setAssetScriptMinFee + invokeFee

updateAssetScript(result = true, smartAsset, contract, setAssetScriptMinFee)

for (typeName <- Seq("transfer", "issue", "reissue", "burn")) {
updateTikTok("unknown", setAssetScriptMinFee)

overflowBlock()
sendTxsAndThenPriorityTx(
_ =>
sender
.broadcastInvokeScript(
caller,
Recipient().withPublicKeyHash(contractAddr),
Some(FUNCTION_CALL(FunctionHeader.User("tikTok"), List.empty)),
fee = invokeFee
),
() => updateTikTok(typeName, priorityFee, waitForTx = false)
)((txs, _) => assertFailedTxs(txs))
}
}

test("InvokeScriptTransaction: invoke script error in payment asset propagates failed transaction") {
val invokeFee = 0.005.waves + smartFee
val setAssetScriptMinFee = setAssetScriptFee + smartFee
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,12 @@ 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.state.ByteStr
import com.wavesplatform.common.utils.EitherExt2
import com.wavesplatform.it.api.SyncHttpApi.*
import com.wavesplatform.it.api.{StateChanges, TransactionStatus}
import com.wavesplatform.it.sync.*
import com.wavesplatform.it.transactions.BaseTransactionSuite
import com.wavesplatform.lang.v1.compiler.Terms
import com.wavesplatform.lang.v1.estimator.v3.ScriptEstimatorV3
import com.wavesplatform.state.{BooleanDataEntry, StringDataEntry}
import com.wavesplatform.state.StringDataEntry
import com.wavesplatform.test.*
import com.wavesplatform.transaction.assets.exchange.AssetPair
import com.wavesplatform.transaction.smart.script.ScriptCompiler
Expand Down Expand Up @@ -118,52 +115,6 @@ class FailedTransactionSuite extends BaseTransactionSuite with CancelAfterFailur
sender.setScript(contract, Some(script), setScriptFee, waitForTx = true).id
}

test("InvokeScriptTransaction: insufficient action fees propagates failed transaction") {
val invokeFee = 0.005.waves
val setAssetScriptMinFee = setAssetScriptFee + smartFee
val priorityFee = setAssetScriptMinFee + invokeFee

updateAssetScript(result = true, smartAsset, contract, setAssetScriptMinFee)

for (typeName <- Seq("transfer", "issue", "reissue", "burn")) {
updateTikTok("unknown", setAssetScriptMinFee)

val prevBalance = sender.balance(caller.toAddress.toString).balance
val prevAssetBalance = sender.assetBalance(contractAddress, smartAsset)
val prevAssets = sender.assetsBalance(contractAddress)

overflowBlock()
sendTxsAndThenPriorityTx(
_ => sender.invokeScript(caller, contractAddress, Some("tikTok"), fee = invokeFee)._1.id,
() => updateTikTok(typeName, priorityFee, waitForTx = false)
) { (txs, priorityTx) =>
logPriorityTx(priorityTx)

val failed = assertFailedTxs(txs)

sender.balance(caller.toAddress.toString).balance shouldBe prevBalance - txs.size * invokeFee
sender.assetBalance(contractAddress, smartAsset) shouldBe prevAssetBalance
sender.assetsBalance(contractAddress).balances should contain theSameElementsAs prevAssets.balances

val (scriptInvokedInfo, issuedInfo) =
if (typeName == "issue")
("", " with 1 assets issued")
else
(" with 1 total scripts invoked", "")

val minFee = if (typeName == "issue") invokeFee + issueFee else invokeFee + smartFee
val text = s"Fee in WAVES for InvokeScriptTransaction ($invokeFee in WAVES)" +
s"$scriptInvokedInfo$issuedInfo does not exceed minimal value of $minFee WAVES."

failed.foreach { s =>
checkStateChange(sender.stateChanges(s.id), 2, text)
}

failed
}
}
}

test("InvokeScriptTransaction: reject transactions if account script failed") {
val invokeFee = 0.005.waves
val setAssetScriptMinFee = setAssetScriptFee + smartFee
Expand Down Expand Up @@ -212,50 +163,6 @@ class FailedTransactionSuite extends BaseTransactionSuite with CancelAfterFailur
}
}

test("InvokeScriptTransaction: transactionHeightById returns only succeed transactions") {
val invokeFee = 0.005.waves + smartFee
val setAssetScriptMinFee = setAssetScriptFee + smartFee
val priorityFee = setAssetScriptMinFee + invokeFee

updateAccountScript(None, caller, setScriptFee + smartFee)
updateTikTok("reissue", setAssetScriptMinFee)
updateAssetScript(result = true, smartAsset, contract, setAssetScriptMinFee)
waitForEmptyUtx()
overflowBlock()

val failedTxs = sendTxsAndThenPriorityTx(
_ => sender.invokeScript(caller, contractAddress, Some("tikTok"), fee = invokeFee)._1.id,
() => updateAssetScript(result = false, smartAsset, contract, priorityFee)
) { (txs, priorityTx) =>
logPriorityTx(priorityTx)
assertFailedTxs(txs)
}

checkTransactionHeightById(failedTxs)
}

test("ExchangeTransaction: transaction validates as failed when asset script fails") {
val Precondition(amountAsset, priceAsset, buyFeeAsset, sellFeeAsset) =
exchangePreconditions(
Some(ScriptCompiler.compile("true", ScriptEstimatorV3(fixOverflow = true, overhead = false)).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 (assetScript, _) =
ScriptCompiler.compile("if true then throw(\"error\") else false", ScriptEstimatorV3(fixOverflow = true, overhead = false)).explicitGet()
val scriptTx = sender.setAssetScript(priceAsset, buyerAddress, script = Some(assetScript.bytes().base64))
nodes.waitForHeightAriseAndTxPresent(scriptTx.id)

val tx = mkExchange(buyer, seller, matcher, assetPair, fee, buyFeeAsset, sellFeeAsset, buyMatcherFee, sellMatcherFee)
val result = sender.signedValidate(tx.json())
(result \ "valid").as[Boolean] shouldBe false
(result \ "error").as[String] should include("not allowed by script of the asset")
}

test("ExchangeTransaction: invalid exchange tx when asset script fails on broadcast") {
val init = Seq(
sender.setScript(firstKeyPair, None, setScriptFee + smartFee).id,
Expand Down Expand Up @@ -309,38 +216,6 @@ class FailedTransactionSuite extends BaseTransactionSuite with CancelAfterFailur
private def waitForTxs(txs: Seq[String]): Unit =
nodes.waitFor("preconditions", 500.millis)(_.transactionStatus(txs).forall(_.status == "confirmed"))(_.forall(identity))

private def checkStateChange(info: StateChanges, code: Int, text: String, strict: Boolean = false): Unit = {
info.stateChanges shouldBe defined
info.stateChanges.get.issues.size shouldBe 0
info.stateChanges.get.reissues.size shouldBe 0
info.stateChanges.get.burns.size shouldBe 0
info.stateChanges.get.error shouldBe defined
info.stateChanges.get.error.get.code shouldBe code
if (strict)
info.stateChanges.get.error.get.text shouldBe text
else
info.stateChanges.get.error.get.text should include(text)
}

private def checkTransactionHeightById(failedTxs: Seq[TransactionStatus]): Unit = {
val defineTxs = failedTxs.map { status =>
sender
.invokeScript(
caller,
contractAddress,
Some("defineTxHeight"),
List(Terms.CONST_BYTESTR(ByteStr.decodeBase58(status.id).get).explicitGet()),
fee = invokeFee
)
._1
.id
}

waitForTxs(defineTxs)

failedTxs.foreach(status => sender.getDataByKey(contractAddress, status.id) shouldBe BooleanDataEntry(status.id, value = false))
}

private def exchangePreconditions(initScript: Option[String]): Precondition = {
val transfers = Seq(
sender.transfer(sender.keyPair, sellerAddress.toAddress.toString, 100.waves).id,
Expand Down
4 changes: 2 additions & 2 deletions node/src/test/scala/com/wavesplatform/RxScheduler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ trait RxScheduler extends BeforeAndAfterAll { _: Suite =>

def test[A](f: => Future[A]): A = Await.result(f, 10.seconds)

def send[A](p: Observer[A])(a: A): Future[Ack] =
def send[A](p: Observer[A], timeout: Int = 500)(a: A): Future[Ack] =
p.onNext(a)
.map(ack => {
Thread.sleep(500)
Thread.sleep(timeout)
ack
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class RxExtensionLoaderSpec extends FreeSpec with RxScheduler with BlockGen {
"should blacklist GetSignatures timeout" in withExtensionLoader(Seq.tabulate(100)(byteStr), 1.millis) { (_, _, _, ccsw, _) =>
val ch = new EmbeddedChannel()
test(for {
_ <- send(ccsw)(ChannelClosedAndSyncWith(None, Some(BestChannel(ch, 1: BigInt))))
_ <- send(ccsw, timeout = 1000)(ChannelClosedAndSyncWith(None, Some(BestChannel(ch, 1: BigInt))))
} yield {
ch.isOpen shouldBe false
})
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import com.wavesplatform.lang.v1.compiler.TestCompiler
import com.wavesplatform.state.diffs.FeeValidation.{FeeConstants, FeeUnit}
import com.wavesplatform.test.*
import com.wavesplatform.transaction.Asset.IssuedAsset
import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment
import com.wavesplatform.transaction.TxHelpers.*
import com.wavesplatform.transaction.smart.InvokeScriptTransaction
import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment
import com.wavesplatform.transaction.{Transaction, TransactionType, TxHelpers}
import org.scalatest.{EitherValues, Inside}

Expand All @@ -26,10 +27,7 @@ class InvokeActionsFeeTest extends PropSpec with Inside with WithState with DBCa

private val verifier: Script =
TestCompiler(V4).compileExpression(
s""" {-# STDLIB_VERSION 4 #-}
| {-# SCRIPT_TYPE ASSET #-}
| {-# CONTENT_TYPE EXPRESSION #-}
|
s"""
| !(sigVerify_32Kb(base58'', base58'', base58'') ||
| sigVerify_32Kb(base58'', base58'', base58'') ||
| sigVerify_32Kb(base58'', base58'', base58''))
Expand All @@ -38,13 +36,13 @@ class InvokeActionsFeeTest extends PropSpec with Inside with WithState with DBCa

private def dApp(asset: IssuedAsset): Script =
TestCompiler(V4).compileContract(s"""
| @Callable(i)
| func default() =
| [
| ScriptTransfer(i.caller, 1, base58'$asset'),
| Burn(base58'$asset', 1),
| Reissue(base58'$asset', 1, false)
| ]
| @Callable(i)
| func default() =
| [
| ScriptTransfer(i.caller, 1, base58'$asset'),
| Burn(base58'$asset', 1),
| Reissue(base58'$asset', 1, false)
| ]
""".stripMargin)

private val paymentPreconditions: (Seq[AddrWithBalance], List[Transaction], () => InvokeScriptTransaction, () => InvokeScriptTransaction) = {
Expand All @@ -64,6 +62,33 @@ class InvokeActionsFeeTest extends PropSpec with Inside with WithState with DBCa
(balances, List(issue, transfer1, transfer2, setVerifier, setDApp), invokeFromScripted, invokeFromNonScripted)
}

property("insufficient action fees propagates failed transaction before RIDE V5 activation") {
withDomain(RideV4, AddrWithBalance.enoughBalances(secondSigner)) { d =>
val issueTx = issue(script = Some(TestCompiler(V4).compileExpression("true")))
val asset = IssuedAsset(issueTx.id())
val dApp = TestCompiler(V4).compileContract(
s"""
| @Callable(i)
| func transfer() = [ScriptTransfer(i.caller, 1, base58'$asset')]
|
| @Callable(i)
| func reissue() = [Reissue(base58'$asset', 1, true)]
|
| @Callable(i)
| func burn() = [Burn(base58'$asset', 1)]
|
| @Callable(i)
| func issue() = [Issue("name", "", 1000, 4, true, unit, 0)]
""".stripMargin
)
d.appendBlock(issueTx, setScript(secondSigner, dApp))
d.appendAndAssertFailed(invoke(func = Some("transfer")), "with 1 total scripts invoked does not exceed minimal value of 900000 WAVES")
d.appendAndAssertFailed(invoke(func = Some("reissue")), "with 1 total scripts invoked does not exceed minimal value of 900000 WAVES")
d.appendAndAssertFailed(invoke(func = Some("burn")), "with 1 total scripts invoked does not exceed minimal value of 900000 WAVES")
d.appendAndAssertFailed(invoke(func = Some("issue")), "with 1 assets issued does not exceed minimal value of 100500000 WAVES")
}
}

property(s"fee for asset scripts is not required after activation ${BlockchainFeatures.SynchronousCalls}") {
val (balances, preparingTxs, invokeFromScripted, invokeFromNonScripted) = paymentPreconditions
withDomain(fsWithV5, balances) { d =>
Expand Down