From 15bb32e031c3a60c4cd6b910eababfe5ba5ba33f Mon Sep 17 00:00:00 2001 From: Artyom Sayadyan Date: Fri, 6 Oct 2023 15:13:36 +0300 Subject: [PATCH] NODE-2611 Forbid RIDE V1-V3 (#3880) --- .../lang/v1/compiler/TestCompiler.scala | 2 +- .../state/diffs/CommonValidation.scala | 9 + .../ci/InvokeScriptTransactionDiffTest.scala | 806 +++++++++--------- .../state/diffs/ci/OverdraftTest.scala | 76 +- .../diffs/smart/eth/EthereumInvokeTest.scala | 9 +- .../smart/predef/BrokenUnicodeTest.scala | 40 +- .../smart/predef/ContextFunctionsTest.scala | 131 ++- .../predef/RideVersionDeactivationTest.scala | 49 ++ project/Dependencies.scala | 2 +- 9 files changed, 594 insertions(+), 530 deletions(-) create mode 100644 node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideVersionDeactivationTest.scala diff --git a/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala b/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala index 50b34e6d105..deebef973c6 100644 --- a/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala +++ b/lang/testkit/src/main/scala/com/wavesplatform/lang/v1/compiler/TestCompiler.scala @@ -27,7 +27,7 @@ class TestCompiler(version: StdLibVersion) { (baseCompilerContext |+| WavesContext.build(Global, DirectiveSet(version, Account, DAppType).explicitGet(), fixBigScriptField = true)).compilerContext - lazy val expressionContext: CTX[Environment] = + private lazy val expressionContext: CTX[Environment] = WavesContext.build(Global, DirectiveSet(version, Account, Expression).explicitGet(), fixBigScriptField = true) private lazy val expressionCompilerContext = diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala b/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala index 205e19c787a..5e59323091c 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/CommonValidation.scala @@ -3,6 +3,7 @@ package com.wavesplatform.state.diffs import cats.implicits.toBifunctorOps import com.wavesplatform.account.{Address, AddressScheme} import com.wavesplatform.database.patch.DisableHijackedAliases +import com.wavesplatform.features.BlockchainFeatures.TransactionStateSnapshot import com.wavesplatform.features.OverdraftValidationProvider.* import com.wavesplatform.features.{BlockchainFeature, BlockchainFeatures, RideVersionProvider} import com.wavesplatform.lang.ValidationError @@ -144,6 +145,13 @@ object CommonValidation { case v => barrierByVersion(v) } + def oldScriptVersionDeactivation(sc: Script): Either[ActivationError, Unit] = sc.stdLibVersion match { + case V1 | V2 | V3 if blockchain.isFeatureActivated(TransactionStateSnapshot) => + Left(ActivationError(s"Script version below V4 is not allowed after ${TransactionStateSnapshot.description} feature activation")) + case _ => + Right(()) + } + def scriptTypeActivation(sc: Script): Either[ActivationError, T] = (sc: @unchecked) match { case _: ExprScript => Right(tx) case _: ContractScript.ContractScriptImpl => barrierByVersion(V3) @@ -151,6 +159,7 @@ object CommonValidation { for { _ <- scriptVersionActivation(sc) + _ <- oldScriptVersionDeactivation(sc) _ <- scriptTypeActivation(sc) } yield tx diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala index debd24b85f0..9199d7286d1 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/InvokeScriptTransactionDiffTest.scala @@ -1,6 +1,5 @@ package com.wavesplatform.state.diffs.ci -import scala.collection.immutable import com.google.protobuf.ByteString import com.wavesplatform.TestValues import com.wavesplatform.TestValues.invokeFee @@ -8,6 +7,7 @@ import com.wavesplatform.account.* import com.wavesplatform.block.{Block, BlockHeader, SignedBlockHeader} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 +import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.db.{DBCacheSettings, WithDomain} import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.history.Domain @@ -21,14 +21,15 @@ import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.script.ContractScript.ContractScriptImpl import com.wavesplatform.lang.script.Script import com.wavesplatform.lang.v1.ContractLimits +import com.wavesplatform.lang.v1.ContractLimits.{MaxCallableActionsAmountBeforeV6, MaxWriteSetSize} import com.wavesplatform.lang.v1.FunctionHeader.{Native, User} import com.wavesplatform.lang.v1.compiler.Terms.* import com.wavesplatform.lang.v1.compiler.{Terms, TestCompiler} -import com.wavesplatform.lang.v1.estimator.v3.ScriptEstimatorV3 import com.wavesplatform.lang.v1.evaluator.FunctionIds.CREATE_LIST -import com.wavesplatform.lang.v1.evaluator.ScriptResultV3 import com.wavesplatform.lang.v1.evaluator.ctx.impl.GlobalValNames import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.FieldNames +import com.wavesplatform.lang.v1.evaluator.ScriptResultV3 +import com.wavesplatform.lang.v1.traits.domain.AssetTransfer import com.wavesplatform.protobuf.dapp.DAppMeta import com.wavesplatform.settings.TestSettings import com.wavesplatform.state.* @@ -42,13 +43,13 @@ import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.* import com.wavesplatform.transaction.assets.* import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment -import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.transaction.smart.script.trace.{AssetVerifierTrace, InvokeScriptTrace, TracedResult} import com.wavesplatform.transaction.smart.{InvokeScriptTransaction, SetScriptTransaction} import com.wavesplatform.transaction.{Asset, utils as _, *} import org.scalamock.scalatest.MockFactory import org.scalatest.{EitherValues, Inside} +import scala.collection.immutable import scala.util.{Random, Try} class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCacheSettings with EitherValues with Inside with MockFactory { @@ -62,7 +63,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa .filter(v => v >= V3 && v <= lastVersion) .foreach(v => withDomain(settingsForRide(v))(assertion(v, _))) - private def testDiffTraced(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, from: StdLibVersion = V3, to: StdLibVersion = lastVersion)( + private def testDiffTraced(preconditions: Seq[BlockWithSigner], block: BlockWithSigner, from: StdLibVersion = V3, to: StdLibVersion)( assertion: ((StdLibVersion, TracedResult[ValidationError, Diff])) => Unit ): Unit = allVersions @@ -77,7 +78,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa private def testDiffAndState( preconditions: Seq[BlockWithSigner], block: BlockWithSigner, - from: StdLibVersion = V3, + from: StdLibVersion, to: StdLibVersion = lastVersion )( assertion: (Diff, Blockchain) => Unit @@ -86,10 +87,10 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa .filter(v => v >= from && v <= to) .foreach(v => assertNgDiffState(preconditions, block, settingsForRide(v).blockchainSettings.functionalitySettings)(assertion)) - private val assetAllowed = TestCompiler(V3).compileAsset("tx.fee > -1") - private val assetUsingThis = TestCompiler(V3).compileAsset("this == this") - private val assetBanned = TestCompiler(V3).compileAsset("false") - private val throwingAsset = TestCompiler(V3).compileAsset("throw()") + private def assetAllowed(version: StdLibVersion) = TestCompiler(version).compileAsset("tx.fee > -1") + private def assetBanned(version: StdLibVersion = V4) = TestCompiler(version).compileAsset("false") + private def throwingAsset(version: StdLibVersion = V4) = TestCompiler(version).compileAsset("throw()") + private val assetUsingThis = TestCompiler(V4).compileAsset("this == this") private val invoker = TxHelpers.signer(0) private val dApp = TxHelpers.signer(1) @@ -100,61 +101,23 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa private val amount = 123 - private def dataContract(bigData: Boolean = false, emptyData: Boolean = false): Script = { - val datas = - if (bigData) - List( - FUNCTION_CALL( - User("DataEntry"), - List(CONST_STRING("argument").explicitGet(), CONST_STRING("abcde" * 1024).explicitGet()) - ), - REF(GlobalValNames.Nil) - ) - else if (emptyData) - List( - FUNCTION_CALL( - User("DataEntry"), - List(CONST_STRING("").explicitGet(), CONST_STRING("abcde").explicitGet()) - ), - REF(GlobalValNames.Nil) - ) - else - List( - FUNCTION_CALL(User("DataEntry"), List(CONST_STRING("argument").explicitGet(), REF("a"))), - FUNCTION_CALL( - Native(1100), - List( - FUNCTION_CALL(User("DataEntry"), List(CONST_STRING("sender").explicitGet(), GETTER(GETTER(REF("i"), "caller"), "bytes"))), - REF(GlobalValNames.Nil) - ) - ) - ) - - ContractScriptImpl( - V3, - DApp( - DAppMeta(), - List.empty, - List( - CallableFunction( - CallableAnnotation("i"), - Terms.FUNC( - "f", - List("a"), - FUNCTION_CALL( - User(FieldNames.WriteSet), - List( - FUNCTION_CALL( - Native(1100), - datas - ) - ) - ) - ) - ) - ), - None - ) + private def dataContract(version: StdLibVersion, bigData: Boolean = false, emptyData: Boolean = false) = { + val data = + version match { + case V3 => + if (bigData) s"""[DataEntry("argument", "${"abcde" * 1024}")]""" + else if (emptyData) """[DataEntry("", "abcde")]""" + else """[DataEntry("argument", a), DataEntry("sender", i.caller.bytes)]""" + case _ => + if (bigData) s"""[StringEntry("argument", "${"abcde" * 1024}")]""" + else if (emptyData) """[StringEntry("", "abcde")]""" + else """[BinaryEntry("argument", a), BinaryEntry("sender", i.caller.bytes)]""" + } + TestCompiler(version).compileContract( + s""" + | @Callable(i) + | func f(a: ByteVector) = ${if (version > V3) data else s"WriteSet($data)"} + """.stripMargin ) } @@ -164,7 +127,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa argName: String = "a", funcName: String = "f", assets: List[Asset] = List(Waves), - version: StdLibVersion = V3 + version: StdLibVersion ): Script = { val transfers: immutable.Seq[FUNCTION_CALL] = assets.map(a => FUNCTION_CALL( @@ -202,7 +165,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa ) } - private def defaultTransferContract(recipientAddress: AddressOrAlias, assets: List[Asset] = List(Waves)): Script = { + private def defaultTransferContract(recipientAddress: AddressOrAlias, assets: List[Asset] = List(Waves), version: StdLibVersion = V4): Script = { val transfers: immutable.Seq[FUNCTION_CALL] = assets.map(a => FUNCTION_CALL( User(FieldNames.ScriptTransfer), @@ -222,7 +185,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } ContractScriptImpl( - V3, + version, DApp( DAppMeta(), List.empty, @@ -232,10 +195,12 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa Terms.FUNC( "default", Nil, - FUNCTION_CALL( - User(FieldNames.TransferSet), - List(payments) - ) + if (version == V3) + FUNCTION_CALL( + User(FieldNames.TransferSet), + List(payments) + ) + else payments ) ) ), @@ -244,22 +209,16 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa ) } - private def writeSet(count: Int): Script = { - val DataEntries = Array.tabulate(count)(i => s"""DataEntry("$i", $i)""").mkString(",") - TestCompiler(V3).compileContract(s""" - | - | {-#STDLIB_VERSION 3 #-} - | {-#CONTENT_TYPE DAPP#-} - | {-#SCRIPT_TYPE ACCOUNT#-} - | - | @Callable(i) - | func f(b: ByteVector) = { - | WriteSet([ - | $DataEntries - | ]) - |} - | - """.stripMargin) + private def writeSet(version: StdLibVersion, count: Int): Script = { + val dataEntries = + if (version > V3) Array.tabulate(count)(i => s"""IntegerEntry("$i", $i)""").mkString("[", ",", "]") + else Array.tabulate(count)(i => s"""DataEntry("$i", $i)""").mkString("[", ",", "]") + TestCompiler(version).compileContract( + s""" + | @Callable(i) + | func f(b: ByteVector) = ${if (version == V3) s"WriteSet($dataEntries)" else s"$dataEntries"} + """.stripMargin + ) } private def writeSetWithKeyLength(length: Int = 1, version: StdLibVersion = V3): Script = { @@ -287,8 +246,8 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa TestCompiler(version).compileContract(script) } - private val simpleContract = - TestCompiler(V3).compileContract( + private def simpleContract(version: StdLibVersion) = + TestCompiler(version).compileContract( s""" |@Callable(i) |func funcForTesting(str: String, num: Int) = @@ -301,11 +260,12 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa ) private def simplePreconditionsAndSetContract( - invocationParamsCount: Int + invocationParamsCount: Int, + version: StdLibVersion ): (List[GenesisTransaction], SetScriptTransaction, InvokeScriptTransaction) = { val genesis = TxHelpers.genesis(dAppAddress) val genesis2 = TxHelpers.genesis(invokerAddress) - val setContract = TxHelpers.setScript(dApp, simpleContract) + val setContract = TxHelpers.setScript(dApp, simpleContract(version)) val ci = TxHelpers.invoke(dAppAddress, Some("funcForTesting"), List.fill(invocationParamsCount)(FALSE), version = TxVersion.V1) (List(genesis, genesis2), setContract, ci) } @@ -376,7 +336,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa (List(genesis, genesis2), setContract, invokes(0), invokes(1), aliasTx) } - property("doesnt validate intermediate action balance before V6")(withDomain(DomainPresets.RideV5) { d => + property("doesn't validate intermediate action balance before V6")(withDomain(DomainPresets.RideV5) { d => val dApp = TxHelpers.defaultSigner d.helpers.creditWavesToDefaultSigner() @@ -495,62 +455,69 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("invoking contract results contract's state") { - val (genesis, setScript, ci) = preconditionsAndSetContract(dataContract()) - testDiffAndState(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci), Block.ProtoBlockVersion)) { - case (blockDiff, newState) => - blockDiff.scriptsComplexity should be > 0L - newState.accountData(dAppAddress, "sender").get.value shouldBe ByteStr(ci.sender.toAddress.bytes) - newState.accountData(dAppAddress, "argument").get.value shouldBe ci.funcCallOpt.get.args.head.asInstanceOf[CONST_BYTESTR].bs - - blockDiff.transaction(ci.id()).get.affected.contains(setScript.sender.toAddress) shouldBe true - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach(version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = preconditionsAndSetContract(dataContract(version)) + d.appendBlock(setScript) + d.appendBlock(ci) + d.liquidDiff.scriptsComplexity should be > 0L + d.blockchain.accountData(dAppAddress, "sender").get.value shouldBe ByteStr(ci.sender.toAddress.bytes) + d.blockchain.accountData(dAppAddress, "argument").get.value shouldBe ci.funcCallOpt.get.args.head.asInstanceOf[CONST_BYTESTR].bs + d.liquidDiff.transaction(ci.id()).get.affected.contains(setScript.sender.toAddress) shouldBe true + } + ) } property("can't more than 5kb of data") { - val (genesis, setScript, ci) = preconditionsAndSetContract(dataContract(bigData = true)) + val (genesis, setScript, ci) = preconditionsAndSetContract(dataContract(V4, bigData = true)) testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci)), from = V5) { _ should produceRejectOrFailedDiff("WriteSet size can't exceed") - } } property("can't use empty keys in v2") { - /*forAll(for { - r <- preconditionsAndSetContract(s => dataContractGen(s, emptyData = true), txVersion = TxVersion.V1) - } yield (r._1, r._2, r._3)) { - case (genesis, setScript, ci) => - assertDiffEi(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci)), fsWithV5) { - _.explicitGet() - } - }*/ - - val (genesis, setScript, ci) = preconditionsAndSetContract(dataContract(emptyData = true), txVersion = TxVersion.V2) - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci)), from = V4) { + val (genesisV3, setScriptV3, ciV3) = preconditionsAndSetContract(dataContract(V3, emptyData = true), txVersion = TxVersion.V1) + testDiff(Seq(TestBlock.create(genesisV3 :+ setScriptV3)), TestBlock.create(Seq(ciV3)), to = V3)( + _ shouldBe a[Right[?, ?]] + ) + val (genesis, setScript, ci) = preconditionsAndSetContract(dataContract(V4, emptyData = true), txVersion = TxVersion.V2) + testDiff(Seq(TestBlock.create(genesis :+ setScript)), TestBlock.create(Seq(ci)), from = V4)( _ should produceRejectOrFailedDiff("Empty keys aren't allowed") - - } + ) } property("invoking ScriptTransfer contract results in accounts state") { - val (genesis, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers()) - testDiffAndState(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci), Block.ProtoBlockVersion)) { case (blockDiff, _) => - blockDiff.scriptsComplexity should be > 0L - blockDiff.portfolios(thirdAddress).balance shouldBe amount - blockDiff.portfolios(setScript.sender.toAddress).balance shouldBe -amount - blockDiff.transaction(ci.id()) shouldBe defined - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach(version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version)) + d.appendBlock(setScript) + d.appendBlock(ci) + d.liquidDiff.scriptsComplexity should be > 0L + d.liquidDiff.portfolios(thirdAddress).balance shouldBe amount + d.liquidDiff.portfolios(setScript.sender.toAddress).balance shouldBe -amount + d.liquidDiff.transaction(ci.id()) shouldBe defined + } + ) } property("invoking default func ScriptTransfer contract results in accounts state") { - val (genesis, setScript, ci) = preconditionsAndSetContract(defaultTransferContract(thirdAddress), isCIDefaultFunc = true) - - testDiffAndState(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci), Block.ProtoBlockVersion)) { case (blockDiff, _) => - blockDiff.scriptsComplexity should be > 0L - blockDiff.portfolios(thirdAddress).balance shouldBe amount - blockDiff.portfolios(setScript.sender.toAddress).balance shouldBe -amount - blockDiff.transaction(ci.id()) shouldBe defined - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach(version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version)) + d.appendBlock(setScript) + d.appendBlock(ci) + d.liquidDiff.scriptsComplexity should be > 0L + d.liquidDiff.portfolios(thirdAddress).balance shouldBe amount + d.liquidDiff.portfolios(setScript.sender.toAddress).balance shouldBe -amount + d.liquidDiff.transaction(ci.id()) shouldBe defined + } + ) } property("invoking default func payment to alias contract results in accounts state") { @@ -572,7 +539,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa property("disallow ScriptTransfer by alias before RIDE V4 activation") { val alias = Alias.create("alias").explicitGet() val createAlias = TxHelpers.createAlias("alias", thirdAcc) - val (genesis, setScript, ci) = preconditionsAndSetContract(defaultTransferContract(alias), isCIDefaultFunc = true) + val (genesis, setScript, ci) = preconditionsAndSetContract(defaultTransferContract(alias, version = V3), isCIDefaultFunc = true) testDiff( Seq(TestBlock.create(genesis ++ Seq(TxHelpers.genesis(thirdAddress), setScript, createAlias))), @@ -588,40 +555,46 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("suitable verifier error message on incorrect proofs number") { - Seq(0, 2).foreach { proofCount => - val (genesis, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers()) - val proofs = Proofs( - List.fill(proofCount)(ByteStr.fromBytes(1, 1)) + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach(version => + Seq(0, 2).foreach { proofCount => + val proofs = Proofs(List.fill(proofCount)(ByteStr.fromBytes(1, 1))) + val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version)) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(setScript) + d.appendBlockE(ci.copy(proofs = proofs)) should produce("Transactions from non-scripted accounts must have exactly 1 proof") + } + } ) - - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci.copy(proofs = proofs)), Block.ProtoBlockVersion)) { - _ should produceRejectOrFailedDiff("Transactions from non-scripted accounts must have exactly 1 proof") - } - } } property("suitable verifier error message on incorrect proof") { - val (genesis, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers()) - val proofs = Proofs(List(ByteStr.fromBytes(1, 1))) - - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci.copy(proofs = proofs)), Block.ProtoBlockVersion)) { - _ should produceRejectOrFailedDiff("Proof doesn't validate as signature") - - } + val proofs = Proofs(List(ByteStr.fromBytes(1, 1))) + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version)) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(setScript) + d.appendBlockE(ci.copy(proofs = proofs)) should produce("Proof doesn't validate as signature") + } + } } property("invoke script by alias") { - val (genesis, setScript, ci, fakeCi, createAlias) = preconditionsAndSetContractWithAlias(dAppWithTransfers()) - testDiffAndState(Seq(TestBlock.create(genesis ++ Seq(setScript, createAlias))), TestBlock.create(Seq(ci), Block.ProtoBlockVersion)) { - case (blockDiff, newState) => - blockDiff.scriptsComplexity should be > 0L - newState.balance(thirdAddress, Waves) shouldBe amount - blockDiff.transaction(ci.id()) shouldBe defined - } - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(fakeCi), Block.ProtoBlockVersion)) { - _ should produceRejectOrFailedDiff("does not exist") - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val (_, setScript, ci, fakeCi, createAlias) = preconditionsAndSetContractWithAlias(dAppWithTransfers(version = version)) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(setScript, createAlias) + d.appendAndAssertSucceed(ci) + d.liquidDiff.scriptsComplexity should be > 0L + d.balance(thirdAddress, Waves) shouldBe amount + d.appendBlockE(fakeCi) should produce("does not exist") + } + } } Seq(V3, V4, V5, V6).foreach { version => @@ -645,7 +618,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa else "Actions count limit is exceeded" - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci)), from = version) { + testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci)), from = version, to = version) { _ should produceRejectOrFailedDiff(errMsg) } } @@ -653,7 +626,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa property("invoking contract receive payment") { testDomain { (version, d) => - val issue = TxHelpers.issue(script = Some(assetAllowed)) + val issue = TxHelpers.issue(script = Some(assetAllowed(version))) val asset = IssuedAsset(issue.id()) val (genesis, setScript, ci) = preconditionsAndSetContract( dAppWithTransfers(version = version), @@ -674,17 +647,18 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("successfully invoked contract trace should contain both attached and transferring asset script info") { - val transferringAsset = TxHelpers.issue(script = Some(assetAllowed)) - val attachedAsset = TxHelpers.issue(name = "test2", script = Some(assetAllowed)) + val transferringAsset = TxHelpers.issue(script = Some(assetAllowed(V3))) + val attachedAsset = TxHelpers.issue(name = "test2", script = Some(assetAllowed(V3))) val (genesis, setScript, ci) = preconditionsAndSetContract( - dAppWithTransfers(dAppAddress, assets = List(IssuedAsset(transferringAsset.id()))), + dAppWithTransfers(dAppAddress, assets = List(IssuedAsset(transferringAsset.id())), version = V3), payment = Some(Payment(1, IssuedAsset(attachedAsset.id()))), fee = TestValues.invokeFee(2) ) testDiffTraced( Seq(TestBlock.create(genesis ++ Seq(transferringAsset, attachedAsset, setScript))), - TestBlock.create(Seq(ci)) + TestBlock.create(Seq(ci)), + to = V7 ) { case (_, blockDiffEi) => inside(blockDiffEi.trace) { case List( @@ -701,123 +675,131 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("asset script ban invoking contract with payment and produce trace") { - val asset = TxHelpers.issue(script = Some(assetBanned)) - val (genesis, setScript, ci) = preconditionsAndSetContract( - dAppWithTransfers(), - payment = Some(Payment(1, IssuedAsset(asset.id()))), - fee = TestValues.invokeFee(1) - ) - testDiffTraced(Seq(TestBlock.create(genesis ++ Seq(asset, setScript))), TestBlock.create(Seq(ci))) { case (version, blockDiffEi) => - if (version == V3) - blockDiffEi.resultE should produceRejectOrFailedDiff("TransactionNotAllowedByScript") - else - blockDiffEi.resultE should produceRejectOrFailedDiff("Transaction is not allowed by script of the asset") - inside(blockDiffEi.trace) { case List(_, AssetVerifierTrace(assetId, Some(tne: TransactionNotAllowedByScript), _)) => - assetId shouldBe asset.id() - tne.isAssetScript shouldBe true + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val asset = TxHelpers.issue(script = Some(assetBanned(version))) + val (_, setScript, ci) = preconditionsAndSetContract( + dAppWithTransfers(version = version), + payment = Some(Payment(1, IssuedAsset(asset.id()))), + fee = TestValues.invokeFee(1) + ) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(asset, setScript) + val tracedDiff = d.transactionDiffer(ci) + val message = if (version == V3) "TransactionNotAllowedByScript" else "Transaction is not allowed by script of the asset" + tracedDiff.resultE should produceRejectOrFailedDiff(message) + inside(tracedDiff.trace) { case List(_, AssetVerifierTrace(assetId, Some(tne: TransactionNotAllowedByScript), _)) => + assetId shouldBe asset.id() + tne.isAssetScript shouldBe true + } + } } - - } } property("invoking contract make payment by asset") { - val issue = TxHelpers.issue(dApp, script = Some(assetAllowed), fee = 1.004.waves) - val asset = IssuedAsset(issue.id()) - - val (genesis, setScript, ci) = preconditionsAndSetContract( - dAppWithTransfers(assets = List(asset)), - fee = TestValues.invokeFee(1) - ) - - testDiffAndState(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(issue, ci), Block.ProtoBlockVersion)) { - case (blockDiff, newState) => - blockDiff.scriptsComplexity should be > 0L - newState.balance(dAppAddress, asset) shouldBe (issue.quantity.value - amount) - newState.balance(thirdAddress, asset) shouldBe amount - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val issue = TxHelpers.issue(dApp, script = Some(assetAllowed(version)), fee = 1.004.waves) + val asset = IssuedAsset(issue.id()) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = preconditionsAndSetContract( + dAppWithTransfers(assets = List(asset), version = version), + fee = TestValues.invokeFee(1) + ) + d.appendBlock(issue, setScript) + d.appendBlock(ci) + d.liquidDiff.scriptsComplexity should be > 0L + d.balance(dAppAddress, asset) shouldBe (issue.quantity.value - amount) + d.balance(thirdAddress, asset) shouldBe amount + } + } } property("invoking contract disable by payment smart asset") { - val issue = TxHelpers.issue(dApp, script = Some(assetBanned)) - val (genesis, setScript, ci) = - preconditionsAndSetContract(dAppWithTransfers(assets = List(IssuedAsset(issue.id()))), fee = TestValues.invokeFee(1)) - - testDiffTraced(Seq(TestBlock.create(genesis ++ Seq(issue, setScript))), TestBlock.create(Seq(ci))) { blockDiffEi => - blockDiffEi._2.resultE should produceRejectOrFailedDiff("Transaction is not allowed by script") - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val issue = TxHelpers.issue(dApp, script = Some(assetBanned(version))) + val contract = dAppWithTransfers(assets = List(IssuedAsset(issue.id())), version = version) + val (_, setScript, ci) = preconditionsAndSetContract(contract, fee = TestValues.invokeFee(1)) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(issue, setScript) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff("Transaction is not allowed by script") + } + } } property("invoking contract disable by one of payment smart asset with trace") { - val issue1 = TxHelpers.issue(issuer = dApp, script = Some(assetAllowed)) - val issue2 = TxHelpers.issue(issuer = dApp, name = "test2", script = Some(assetBanned)) - - val contract = dAppWithTransfers(assets = List(IssuedAsset(issue1.id()), IssuedAsset(issue2.id()))) - val (genesis, setScript, ci) = preconditionsAndSetContract(contract, fee = TestValues.invokeFee(2)) - - testDiffTraced(Seq(TestBlock.create(genesis ++ Seq(issue1, issue2, setScript))), TestBlock.create(Seq(ci))) { case (version, blockDiffEi) => - if (version == V3) - blockDiffEi.resultE should produceRejectOrFailedDiff("Transaction is not allowed by script") - else - blockDiffEi.resultE should produceRejectOrFailedDiff("Transaction is not allowed by script") - inside(blockDiffEi.trace) { - case List( - InvokeScriptTrace(_, `dAppAddress`, functionCall, Right(ScriptResultV3(_, transfers, _)), _, _), - AssetVerifierTrace(allowedAssetId, None, _), - AssetVerifierTrace(bannedAssetId, Some(_: FailedTransactionError), _) - ) => - dAppAddress shouldBe ci.dApp - functionCall shouldBe ci.funcCall - - allowedAssetId shouldBe issue1.id() - bannedAssetId shouldBe issue2.id() - - transfers.flatMap(_.assetId.toList) shouldBe List(allowedAssetId, bannedAssetId) + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val issue1 = TxHelpers.issue(issuer = dApp, script = Some(assetAllowed(version))) + val issue2 = TxHelpers.issue(issuer = dApp, name = "test2", script = Some(assetBanned(version))) + val contract = dAppWithTransfers(assets = List(IssuedAsset(issue1.id()), IssuedAsset(issue2.id())), version = version) + val (_, setScript, ci) = preconditionsAndSetContract(contract, fee = TestValues.invokeFee(2)) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(issue1, issue2, setScript) + val tracedDiff = d.transactionDiffer(ci) + tracedDiff.resultE should produceRejectOrFailedDiff("Transaction is not allowed by script") + inside(tracedDiff.trace) { + case List( + InvokeScriptTrace(_, `dAppAddress`, functionCall, Right(scriptResult), _, _), + AssetVerifierTrace(allowedAssetId, None, _), + AssetVerifierTrace(bannedAssetId, Some(_: FailedTransactionError), _) + ) => + dAppAddress shouldBe ci.dApp + functionCall shouldBe ci.funcCall + + allowedAssetId shouldBe issue1.id() + bannedAssetId shouldBe issue2.id() + + scriptResult.actions.flatMap(_.asInstanceOf[AssetTransfer].assetId) shouldBe List(allowedAssetId, bannedAssetId) + } + } } - - } } property("trace not contains attached asset script invocation result when transferring asset script produce error") { - val attachedAsset = TxHelpers.issue() - val transferringAsset = TxHelpers.issue(dApp, name = "test2", script = Some(throwingAsset)) - - val (genesis, setScript, ci) = preconditionsAndSetContract( - dAppWithTransfers(assets = List(IssuedAsset(transferringAsset.id()))), - payment = Some(Payment(1, IssuedAsset(attachedAsset.id()))), - fee = TestValues.invokeFee(1) - ) - - testDiffTraced( - Seq(TestBlock.create(genesis ++ Seq(transferringAsset, attachedAsset, setScript))), - TestBlock.create(Seq(ci)) - ) { case (version, blockDiffEi) => - if (version == V3) - blockDiffEi.resultE should produceRejectOrFailedDiff(s"Transaction is not allowed by script of the asset ${transferringAsset.id()}") - else - blockDiffEi.resultE should produceRejectOrFailedDiff(s"Transaction is not allowed by script of the asset ${transferringAsset.id()}") - inside(blockDiffEi.trace) { - case List( - InvokeScriptTrace(_, _, _, Right(ScriptResultV3(_, transfers, _)), _, _), - AssetVerifierTrace(transferringAssetId, Some(_), _) - ) => - transferringAssetId shouldBe transferringAsset.id() - transfers.head.assetId.get shouldBe transferringAsset.id() - + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val attachedAsset = TxHelpers.issue() + val transferringAsset = TxHelpers.issue(dApp, name = "test2", script = Some(throwingAsset(version))) + val (_, setScript, ci) = preconditionsAndSetContract( + dAppWithTransfers(assets = List(IssuedAsset(transferringAsset.id())), version = version), + payment = Some(Payment(1, IssuedAsset(attachedAsset.id()))), + fee = TestValues.invokeFee(1) + ) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(transferringAsset, attachedAsset, setScript) + val tracedDiff = d.transactionDiffer(ci) + tracedDiff.resultE should produceRejectOrFailedDiff(s"Transaction is not allowed by script of the asset ${transferringAsset.id()}") + inside(tracedDiff.trace) { + case List( + InvokeScriptTrace(_, _, _, Right(scriptResults), _, _), + AssetVerifierTrace(transferringAssetId, Some(_), _) + ) => + transferringAssetId shouldBe transferringAsset.id() + scriptResults.actions.head.asInstanceOf[AssetTransfer].assetId.get shouldBe transferringAsset.id() + } + } } - } } - property("Contract payment should be positive") { - val asset = TxHelpers.issue(dApp) - val contract = dAppWithTransfers(recipientAmount = -1, assets = List(IssuedAsset(asset.id()))) - val (genesis, setScript, ci) = preconditionsAndSetContract(contract) - - testDiff(Seq(TestBlock.create(genesis ++ Seq(asset, setScript))), TestBlock.create(Seq(ci))) { blockDiffEi => - blockDiffEi should produceRejectOrFailedDiff("Negative amount") - - } + property("contract payment should be positive") { + val asset = TxHelpers.issue(dApp) + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + val contract = dAppWithTransfers(recipientAmount = -1, assets = List(IssuedAsset(asset.id())), version = version) + val (_, setScript, ci) = preconditionsAndSetContract(contract) + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + d.appendBlock(setScript) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff("Negative") + } + } } property("payment should be positive") { @@ -825,82 +807,106 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("smart asset payment require extra fee only before V5 activation") { - val issue = TxHelpers.issue(dApp, script = Some(assetAllowed)) - val contract = dAppWithTransfers(assets = List(IssuedAsset(issue.id()))) - val (genesis, setScript, ci) = preconditionsAndSetContract(contract) - testDiff(Seq(TestBlock.create(genesis ++ Seq(issue, setScript))), TestBlock.create(Seq(ci)), to = V4)( - _ should produceRejectOrFailedDiff("does not exceed minimal value") - ) - testDiff(Seq(TestBlock.create(genesis ++ Seq(issue, setScript))), TestBlock.create(Seq(ci)), from = V5)( - _.explicitGet().errorMessage(ci.id()) shouldBe None - ) + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val issue = TxHelpers.issue(dApp, script = Some(assetAllowed(version))) + val contract = dAppWithTransfers(assets = List(IssuedAsset(issue.id())), version = version) + val (_, setScript, ci) = preconditionsAndSetContract(contract) + d.appendBlock(issue, setScript) + if (version < V5) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff("does not exceed minimal value") + else + d.appendAndAssertSucceed(ci) + } + } } property("contract with payment of smart asset require extra fee only before V5 activation") { - val issue = TxHelpers.issue(script = Some(assetAllowed)) - val payment = Payment(1, IssuedAsset(issue.id())) - val (genesis, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(), payment = Some(payment)) - testDiff(Seq(TestBlock.create(genesis ++ Seq(issue, setScript))), TestBlock.create(Seq(ci)), to = V4)( - _ should produceRejectOrFailedDiff("does not exceed minimal value") - ) - testDiff(Seq(TestBlock.create(genesis ++ Seq(issue, setScript))), TestBlock.create(Seq(ci)), from = V5)( - _.explicitGet().errorMessage(ci.id()) shouldBe None - ) + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val issue = TxHelpers.issue(script = Some(assetAllowed(version))) + val payment = Payment(1, IssuedAsset(issue.id())) + val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version), payment = Some(payment)) + d.appendBlock(setScript, issue) + if (version < V5) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff("does not exceed minimal value") + else + d.appendAndAssertSucceed(ci) + } + } } property("can't overflow payment + fee") { val payment = Some(Payment(ENOUGH_AMT, Waves)) - val (genesis, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(), payment = payment) - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci))) { - _ should produceRejectOrFailedDiff("Attempt to transfer unavailable funds") - + val (genesis, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = V4), payment = payment) + testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci)), from = V4) { + _ should produceRejectOrFailedDiff("negative waves balance") + } + val (_, setScriptV3, _) = preconditionsAndSetContract(dAppWithTransfers(version = V3), payment = payment) + testDiff(Seq(TestBlock.create(genesis ++ Seq(setScriptV3))), TestBlock.create(Seq(ci)), from = V3, to = V3) { + _ should produceRejectOrFailedDiff("negative waves balance") } } property("invoking contract with sponsored fee") { - val sponsorIssue = TxHelpers.issue(dApp, amount = ENOUGH_AMT) - val sponsorAsset = IssuedAsset(sponsorIssue.id()) - val sponsor = TxHelpers.sponsor(sponsorAsset, sender = dApp) - val (genesis, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(), sponsored = Some(sponsor)) - val t = TxHelpers.transfer(dApp, invokerAddress, sponsorIssue.quantity.value / 2, sponsorAsset) - - testDiffAndState( - Seq(TestBlock.create(genesis ++ Seq(sponsorIssue, t, sponsor, setScript))), - TestBlock.create(Seq(ci), Block.ProtoBlockVersion) - ) { case (blockDiff, newState) => - blockDiff.scriptsComplexity should be > 0L - blockDiff.errorMessage(ci.id()) shouldBe None - newState.balance(thirdAddress, Waves) shouldBe amount - newState.balance(ci.sender.toAddress, sponsorAsset) shouldBe (sponsorIssue.quantity.value / 2 - ci.fee.value) - newState.balance( - dAppAddress, - sponsorAsset - ) shouldBe (sponsorIssue.quantity.value - sponsorIssue.quantity.value / 2 + ci.fee.value) - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val sponsorIssue = TxHelpers.issue(dApp, amount = ENOUGH_AMT) + val sponsorAsset = IssuedAsset(sponsorIssue.id()) + val sponsor = TxHelpers.sponsor(sponsorAsset, sender = dApp) + val (_, setScript, ci) = preconditionsAndSetContract(dAppWithTransfers(version = version), sponsored = Some(sponsor)) + val t = TxHelpers.transfer(dApp, invokerAddress, sponsorIssue.quantity.value / 2, sponsorAsset) + d.appendBlock(sponsorIssue, t, sponsor, setScript) + d.appendBlock(ci) + d.liquidDiff.scriptsComplexity should be > 0L + d.liquidDiff.errorMessage(ci.id()) shouldBe None + d.balance(thirdAddress, Waves) shouldBe amount + d.balance(ci.sender.toAddress, sponsorAsset) shouldBe (sponsorIssue.quantity.value / 2 - ci.fee.value) + d.balance(dAppAddress, sponsorAsset) shouldBe (sponsorIssue.quantity.value - sponsorIssue.quantity.value / 2 + ci.fee.value) + } + } } property("argument passed to callable function has wrong type") { - val (genesis, setScript, ci) = simplePreconditionsAndSetContract(invocationParamsCount = 2) - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci))) { - _ should produceRejectOrFailedDiff("Can't apply (CONST_BOOLEAN) to 'parseInt(str: String)'") - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = simplePreconditionsAndSetContract(invocationParamsCount = 2, version) + d.appendBlock(setScript) + d.appendBlockE(ci) should produce("Can't apply (CONST_BOOLEAN) to 'parseInt(str: String)'") + } + } } property("can't write more than 100 entries") { - val (genesis, setScript, ci) = preconditionsAndSetContract(writeSet(ContractLimits.MaxWriteSetSize + 1)) - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci))) { - _ should produceRejectOrFailedDiff("Stored data count limit is exceeded") - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = preconditionsAndSetContract(writeSet(version, MaxWriteSetSize + 1), version = version) + d.appendBlock(setScript) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff("Stored data count limit is exceeded") + } + } } property("can write 100 entries") { - val (genesis, setScript, ci) = preconditionsAndSetContract(writeSet(ContractLimits.MaxWriteSetSize)) - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci))) { - _.explicitGet() - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = preconditionsAndSetContract(writeSet(version, ContractLimits.MaxWriteSetSize)) + d.appendBlock(setScript) + d.appendAndAssertSucceed(ci) + } + } } property("can't write entry with key size greater than limit") { @@ -961,12 +967,17 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("Function call args count should be equal @Callable func one") { - Seq(0, 3) - .foreach { invocationArgsCount => - val (genesis, setScript, ci) = simplePreconditionsAndSetContract(invocationArgsCount) - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci))) { - _ should produceRejectOrFailedDiff(s"takes 2 args but $invocationArgsCount were(was) given") - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + Seq(0, 3) + .foreach { invocationArgsCount => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = simplePreconditionsAndSetContract(invocationArgsCount, version) + d.appendBlock(setScript) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff(s"takes 2 args but $invocationArgsCount were(was) given") + } + } } } @@ -1005,32 +1016,40 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("Default function invocation should produce error if contract default function has arguments") { - val contract = dAppWithTransfers(funcName = "default", assets = List(Waves)) - val (genesis, setScript, ci) = preconditionsAndSetContract(contract, isCIDefaultFunc = true) - - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci))) { - _ should produceRejectOrFailedDiff(s"takes 1 args but 0 were(was) given") - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val contract = dAppWithTransfers(funcName = "default", assets = List(Waves), version = version) + val (_, setScript, ci) = preconditionsAndSetContract(contract, isCIDefaultFunc = true) + d.appendBlock(setScript) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff("takes 1 args but 0 were(was) given") + } + } } property("Default function invocation should produce error if contract does't have default function") { - val contract = dAppWithTransfers(funcName = "other", assets = List(Waves)) - val (genesis, setScript, ci) = preconditionsAndSetContract(contract, isCIDefaultFunc = true) - - testDiff(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(ci))) { - _ should produceRejectOrFailedDiff("@Callable function 'default' doesn't exist in the script") - - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val contract = dAppWithTransfers(funcName = "other", assets = List(Waves), version = version) + val (_, setScript, ci) = preconditionsAndSetContract(contract, isCIDefaultFunc = true) + d.appendBlock(setScript) + d.transactionDiffer(ci).resultE should produceRejectOrFailedDiff("@Callable function 'default' doesn't exist in the script") + } + } } property("self-payment and self-transfer V3") { val (genesis, setScript, ci) = preconditionsAndSetContract( - dAppWithTransfers(), + dAppWithTransfers(version = V3), dApp = invoker, payment = Some(Payment(1, Waves)), - fee = TestValues.invokeFee(1) + fee = TestValues.invokeFee(1), + version = V3 ) - testDiff(Seq(TestBlock.create(Seq(genesis.head, setScript))), TestBlock.create(Seq(ci))) { + testDiff(Seq(TestBlock.create(Seq(genesis.head, setScript))), TestBlock.create(Seq(ci)), to = V7) { _.explicitGet() } @@ -1065,7 +1084,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa property("transferring asset this value") { val issue = TxHelpers.issue(dApp, script = Some(assetUsingThis), fee = 1.004.waves) - val contract = dAppWithTransfers(assets = List(IssuedAsset(issue.id()))) + val contract = dAppWithTransfers(assets = List(IssuedAsset(issue.id())), version = V4) val (genesis, setScript, ci) = preconditionsAndSetContract(contract, fee = TestValues.invokeFee(1)) testDiffAndState(Seq(TestBlock.create(genesis ++ Seq(setScript))), TestBlock.create(Seq(issue, ci), Block.ProtoBlockVersion), from = V4) { @@ -1298,14 +1317,18 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val issue = TxHelpers.issue(dApp, amount = ENOUGH_AMT) val sponsorAsset = IssuedAsset(issue.id()) val sponsor = TxHelpers.sponsor(sponsorAsset, sender = dApp) - val (genesis, setScript, invoke) = - preconditionsAndSetContract(writeSet(1), sponsored = Some(sponsor), selfSend = true, fee = TestValues.invokeFee(1)) - - testDiff(Seq(TestBlock.create(genesis ++ Seq(issue, sponsor, setScript))), TestBlock.create(Seq(invoke))) { diff => - invoke.feeAssetId shouldBe sponsorAsset - invoke.dApp shouldBe invoke.sender.toAddress - diff.explicitGet().portfolios(invoke.sender.toAddress).balanceOf(sponsorAsset) shouldBe 0L - } + DirectiveDictionary[StdLibVersion].all + .filter(_ >= V3) + .foreach { version => + withDomain(settingsForRide(version), AddrWithBalance.enoughBalances(dApp, invoker)) { d => + val (_, setScript, ci) = preconditionsAndSetContract(writeSet(version, 1), sponsored = Some(sponsor), selfSend = true, fee = invokeFee(1)) + d.appendBlock(issue, sponsor, setScript) + d.appendAndAssertSucceed(ci) + ci.feeAssetId shouldBe sponsorAsset + ci.dApp shouldBe ci.sender.toAddress + d.liquidDiff.portfolios(ci.sender.toAddress).balanceOf(sponsorAsset) shouldBe 0L + } + } } private val throwContract: Script = { @@ -1332,7 +1355,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa val sponsorAsset = IssuedAsset(sponsorIssue.id()) val sponsorTx = TxHelpers.sponsor(sponsorAsset, sender = thirdAcc) - val issueTx = TxHelpers.issue(dApp, script = Some(throwingAsset)) + val issueTx = TxHelpers.issue(dApp, script = Some(throwingAsset())) val feeInWaves = FeeConstants(TransactionType.InvokeScript) * FeeValidation.FeeUnit val feeInAsset = Sponsorship.fromWaves(feeInWaves, sponsorTx.minSponsoredAssetFee.get.value) @@ -1403,50 +1426,39 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa } property("counts complexity correctly for failed transactions (validation fails)") { - def contract(asset: String) = TestCompiler(V4) - .compileContract(s""" - |{-# STDLIB_VERSION 4 #-} - |{-# CONTENT_TYPE DAPP #-} - |{-#SCRIPT_TYPE ACCOUNT#-} - | - |let a = base58'$asset' - | - |@Callable(inv) - |func sameComplexity(i: String) = { - | let check = ${"sigVerify(base58'', base58'', base58'') ||" * 10} true - | if (i == "throw" && check) then - | throw("Some error") - | else if (i == "insufficient fee" && check) then - | [ ${(1 to ContractLimits.MaxCallableActionsAmountBeforeV6(V4)) - .map(i => s"""Issue("Asset $i", "", 100, 8, true, unit, $i)""") - .mkString(",")} ] - | else if (i == "negative amount" && check) then - | [ ScriptTransfer(inv.caller, -1, a) ] - | else if (i == "overflow amount" && check) then - | [ ScriptTransfer(inv.caller, ${Long.MaxValue / 2}, a), ScriptTransfer(inv.caller, ${Long.MaxValue / 2 + 1}, a) ] - | else if (i == "self payment" && check) then - | [ ScriptTransfer(this, 10, unit) ] - | else if (i == "max actions" && check) then - | [ ${(0 to ContractLimits.MaxCallableActionsAmountBeforeV6(V4)) - .map(_ => "ScriptTransfer(inv.caller, 10, a)") - .mkString(",")} ] - | else if (i == "invalid data entries" && check) then - | [ ${(0 to ContractLimits.MaxWriteSetSize) - .map(x => s"""IntegerEntry("val", $x)""") - .mkString(",")},ScriptTransfer(inv.caller, 10, a)] - | else [] - |} - | - |""".stripMargin) + def contract(asset: String) = TestCompiler(V4).compileContract( + s""" + | let a = base58'$asset' + | + | @Callable(inv) + | func sameComplexity(i: String) = { + | let check = ${"sigVerify(base58'', base58'', base58'') ||" * 10} true + | if (i == "throw" && check) then + | throw("Some error") + | else if (i == "insufficient fee" && check) then + | [ ${(1 to MaxCallableActionsAmountBeforeV6(V4)).map(i => s"""Issue("Asset $i", "", 100, 8, true, unit, $i)""").mkString(",")} ] + | else if (i == "negative amount" && check) then + | [ ScriptTransfer(inv.caller, -1, a) ] + | else if (i == "overflow amount" && check) then + | [ ScriptTransfer(inv.caller, ${Long.MaxValue / 2}, a), ScriptTransfer(inv.caller, ${Long.MaxValue / 2 + 1}, a) ] + | else if (i == "self payment" && check) then + | [ ScriptTransfer(this, 10, unit) ] + | else if (i == "max actions" && check) then + | [ ${(0 to MaxCallableActionsAmountBeforeV6(V4)).map(_ => "ScriptTransfer(inv.caller, 10, a)").mkString(",")} ] + | else if (i == "invalid data entries" && check) then + | [ ${(0 to MaxWriteSetSize).map(x => s"""IntegerEntry("val", $x)""").mkString(",")},ScriptTransfer(inv.caller, 10, a)] + | else [] + | } + """.stripMargin + ) - val gTx1 = TxHelpers.genesis(dAppAddress) - val gTx2 = TxHelpers.genesis(invokerAddress) - val (assetScript, _) = ScriptCompiler.compile("false", ScriptEstimatorV3(fixOverflow = true, overhead = true)).explicitGet() - val iTx = TxHelpers.issue(dApp, amount = Long.MaxValue, script = Some(assetScript), fee = 1.004.waves) + val gTx1 = TxHelpers.genesis(dAppAddress) + val gTx2 = TxHelpers.genesis(invokerAddress) + val iTx = TxHelpers.issue(dApp, amount = Long.MaxValue, script = Some(assetBanned()), fee = 1.004.waves) val ssTx = TxHelpers.setScript(dApp, contract(iTx.assetId.toString)) - Seq("throw", "insufficient fee", "negative amount", "overflow amount", "self payment", "max actions", "invalid data entries", "ok").foreach { - arg => + Seq("throw", "insufficient fee", "negative amount", "overflow amount", "self payment", "max actions", "invalid data entries", "ok") + .foreach { arg => val invoke = TxHelpers.invoke(dAppAddress, Some("sameComplexity"), args = List(CONST_STRING(arg).explicitGet())) testDiffAndState(Seq(TestBlock.create(Seq(gTx1, gTx2, ssTx, iTx))), TestBlock.create(Seq(invoke), Block.ProtoBlockVersion), from = V4) { case (diff, _) => @@ -1456,7 +1468,7 @@ class InvokeScriptTransactionDiffTest extends PropSpec with WithDomain with DBCa diff.errorMessage(invoke.id()) shouldBe defined } - } + } } property("counts complexity correctly for failed transactions (asset script fails)") { diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverdraftTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverdraftTest.scala index 01028eb42bf..4a1e5aefb4e 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverdraftTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/OverdraftTest.scala @@ -6,7 +6,7 @@ import com.wavesplatform.db.WithDomain import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lagonaki.mocks.TestBlock import com.wavesplatform.lang.directives.DirectiveDictionary -import com.wavesplatform.lang.directives.values.{StdLibVersion, V3, V4} +import com.wavesplatform.lang.directives.values.{StdLibVersion, V3, V4, V8} import com.wavesplatform.lang.script.Script import com.wavesplatform.lang.v1.ContractLimits import com.wavesplatform.lang.v1.compiler.TestCompiler @@ -14,18 +14,20 @@ import com.wavesplatform.settings.FunctionalitySettings import com.wavesplatform.state.diffs.FeeValidation import com.wavesplatform.state.diffs.FeeValidation.FeeConstants import com.wavesplatform.test.* -import com.wavesplatform.transaction.{GenesisTransaction, TransactionType, TxHelpers, TxVersion} import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.assets.IssueTransaction -import com.wavesplatform.transaction.smart.{InvokeScriptTransaction, SetScriptTransaction} import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment +import com.wavesplatform.transaction.smart.{InvokeScriptTransaction, SetScriptTransaction} +import com.wavesplatform.transaction.{GenesisTransaction, TransactionType, TxHelpers, TxVersion} + +import scala.math.Ordering.Implicits.infixOrderingOps class OverdraftTest extends PropSpec with WithDomain { import DomainPresets.* - private val InvokeFee = FeeConstants(TransactionType.InvokeScript) * FeeValidation.FeeUnit + private val InvokeFee = FeeConstants(TransactionType.InvokeScript) * FeeValidation.FeeUnit private val SetScriptFee = FeeConstants(TransactionType.SetScript) * FeeValidation.FeeUnit - private val IssueFee = FeeConstants(TransactionType.Issue) * FeeValidation.FeeUnit + private val IssueFee = FeeConstants(TransactionType.Issue) * FeeValidation.FeeUnit private val dAppVersions: List[StdLibVersion] = DirectiveDictionary[StdLibVersion].all @@ -41,40 +43,37 @@ class OverdraftTest extends PropSpec with WithDomain { settingsForRide(DirectiveDictionary[StdLibVersion].all.last).blockchainSettings.functionalitySettings property("insufficient fee") { - dAppVersionWithSettings.foreach { - case (version, settings) => - val (genesis, setDApp, ci, _) = paymentPreconditions(withEnoughFee = false, withPayment = false, emptyResultDApp(version)) - - assertDiffEi(Seq(TestBlock.create(genesis :+ setDApp)), TestBlock.create(Seq(ci)), settings) { r => - r should produce( - s"Fee for InvokeScriptTransaction (1 in WAVES) does not exceed minimal value of $InvokeFee WAVES" - ) + dAppVersionWithSettings.foreach { case (version, settings) => + val (genesis, setDApp, ci, _) = paymentPreconditions(withEnoughFee = false, withPayment = false, emptyResultDApp(version)) + + assertDiffEi(Seq(TestBlock.create(genesis :+ setDApp)), TestBlock.create(Seq(ci)), settings) { r => + r should produce( + s"Fee for InvokeScriptTransaction (1 in WAVES) does not exceed minimal value of $InvokeFee WAVES" + ) } } } property("overdraft") { - dAppVersionWithSettings.foreach { - case (version, settings) => - val (genesis, setDApp, ci, _) = paymentPreconditions(withEnoughFee = true, withPayment = false, payingDApp(version)) - - assertDiffEi(Seq(TestBlock.create(genesis :+ setDApp)), TestBlock.create(Seq(ci)), settings) { r => - if (settings.preActivatedFeatures.contains(BlockchainFeatures.BlockV5.id)) - r should produce("AccountBalanceError") - else - r.explicitGet() - } + dAppVersionWithSettings.foreach { case (version, settings) => + val (genesis, setDApp, ci, _) = paymentPreconditions(withEnoughFee = true, withPayment = false, payingDApp(version)) + + assertDiffEi(Seq(TestBlock.create(genesis :+ setDApp)), TestBlock.create(Seq(ci)), settings) { r => + if (settings.preActivatedFeatures.contains(BlockchainFeatures.BlockV5.id)) + r should produce("AccountBalanceError") + else + r.explicitGet() + } } } property("overdraft with payment V3") { - dAppVersionWithSettings.foreach { - case (_, settings) => - val (genesis, setDApp, ci, issue) = paymentPreconditions(withEnoughFee = true, withPayment = true, payingDApp(V3)) + dAppVersionWithSettings.filter(_._1 < V8).foreach { case (_, settings) => + val (genesis, setDApp, ci, issue) = paymentPreconditions(withEnoughFee = true, withPayment = true, payingDApp(V3)) - assertDiffEi(Seq(TestBlock.create(genesis ++ List(setDApp, issue))), TestBlock.create(Seq(ci)), settings) { - _ should produce("leads to negative waves balance to (at least) temporary negative state") - } + assertDiffEi(Seq(TestBlock.create(genesis ++ List(setDApp, issue))), TestBlock.create(Seq(ci)), settings) { + _ should produce("leads to negative waves balance to (at least) temporary negative state") + } } } @@ -104,7 +103,8 @@ class OverdraftTest extends PropSpec with WithDomain { val payments = (1 to count).map(_ => Payment(issue.quantity.value / count + 1, IssuedAsset(issue.id()))) val invoke = TxHelpers.invoke(master.toAddress, func = None, invoker = invoker, payments = payments) - assertDiffEi(Seq(TestBlock.create(genesis ++ List(setDApp, issue))), TestBlock.create(Seq(invoke)), allActivatedSettings) { + val settings = settingsForRide(version.max(V4)).blockchainSettings.functionalitySettings + assertDiffEi(Seq(TestBlock.create(genesis ++ List(setDApp, issue))), TestBlock.create(Seq(invoke)), settings) { _ should produce("Attempt to transfer unavailable funds: Transaction application leads to negative asset") } } @@ -155,12 +155,12 @@ class OverdraftTest extends PropSpec with WithDomain { } private def dApp(body: String, version: StdLibVersion): Script = TestCompiler(version).compileContract(s""" - | {-# STDLIB_VERSION $version #-} - | {-# CONTENT_TYPE DAPP #-} - | {-# SCRIPT_TYPE ACCOUNT #-} - | - | @Callable(i) - | func default() = $body - | - |""".stripMargin) + | {-# STDLIB_VERSION $version #-} + | {-# CONTENT_TYPE DAPP #-} + | {-# SCRIPT_TYPE ACCOUNT #-} + | + | @Callable(i) + | func default() = $body + | + |""".stripMargin) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala index 282da64af72..482414a7f50 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/eth/EthereumInvokeTest.scala @@ -7,7 +7,7 @@ import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.db.WithDomain import com.wavesplatform.lang.directives.DirectiveDictionary -import com.wavesplatform.lang.directives.values.{StdLibVersion, V3, V5, V8} +import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.script.Script import com.wavesplatform.lang.script.v1.ExprScript import com.wavesplatform.lang.v1.compiler.{Terms, TestCompiler} @@ -22,7 +22,7 @@ import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.assets.{IssueTransaction, SetAssetScriptTransaction} import com.wavesplatform.transaction.smart.SetScriptTransaction import com.wavesplatform.transaction.transfer.TransferTransaction -import com.wavesplatform.transaction.{EthABIConverter, Asset, EthereumTransaction, GenesisTransaction} +import com.wavesplatform.transaction.{Asset, EthABIConverter, EthereumTransaction, GenesisTransaction} import com.wavesplatform.utils.EthHelpers import org.scalatest.Inside @@ -115,7 +115,7 @@ class EthereumInvokeTest extends PropSpec with WithDomain with EthHelpers with I val invoker = dummyEthInvoke.senderAddress() val invokerPk = dummyEthInvoke.signerPublicKey() - val emptyScript = Some(ExprScript(Terms.TRUE).explicitGet()) + val emptyScript = Some(ExprScript(V4, Terms.TRUE).explicitGet()) val issues = (1 to paymentCount).map(_ => IssueTransaction.selfSigned(2.toByte, dApp, "Asset", "", ENOUGH_AMT, 8, true, emptyScript, 1.waves, ts).explicitGet() @@ -172,7 +172,7 @@ class EthereumInvokeTest extends PropSpec with WithDomain with EthHelpers with I property("invoke with scripted payments") { val allVersions = DirectiveDictionary[StdLibVersion].all val lastVersion = allVersions.last - val dAppVersions = allVersions.filter(_ >= V3) + val dAppVersions = allVersions.filter(_ > V3) dAppVersions.foreach { v => assert(dAppVersion = v, assetScriptVersion = lastVersion, v.maxPayments, syncCall = v >= V5) @@ -180,5 +180,6 @@ class EthereumInvokeTest extends PropSpec with WithDomain with EthHelpers with I assert(dAppVersion = lastVersion, assetScriptVersion = v, v.maxPayments) assert(dAppVersion = lastVersion, assetScriptVersion = v, 0) } + assert(V3, V3, V3.maxPayments) } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/BrokenUnicodeTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/BrokenUnicodeTest.scala index 89b79cbe528..e889a6ad14c 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/BrokenUnicodeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/BrokenUnicodeTest.scala @@ -159,37 +159,43 @@ class BrokenUnicodeTest extends PropSpec with WithDomain with EitherValues { allowIllFormedStrings = true ) - private val versionsBeforeActivation = DirectiveDictionary[StdLibVersion].all.filter(_ < V5) - private val dAppVersionsBeforeActivation = versionsBeforeActivation.filter(_ >= V3) - private def allVersions(lastVersion: StdLibVersion) = DirectiveDictionary[StdLibVersion].all.filter(_ <= lastVersion) private def allDAppVersions(lastVersion: StdLibVersion) = allVersions(lastVersion).filter(_ >= V3) private def scenario(lastVersion: StdLibVersion) = { val recipient = TxHelpers.signer(0) - val invoker = TxHelpers.signer(1) + val invoker = TxHelpers.signer(1) - val availableVersions = allVersions(lastVersion) - val availableDAppVersions = allDAppVersions(lastVersion) + val availableVersions = if (lastVersion < V8) allVersions(lastVersion) else allVersions(lastVersion).filter(_ > V3) + val availableDAppVersions = if (lastVersion < V8) allDAppVersions(lastVersion) else allDAppVersions(lastVersion).filter(_ > V3) + val versionsBeforeActivation = if (lastVersion < V8) DirectiveDictionary[StdLibVersion].all.filter(_ < V5) else Seq(V4) + val dAppVersionsBeforeActivation = versionsBeforeActivation.filter(_ >= V3) val accWithFix = (1 to availableVersions.size).map(idx => TxHelpers.signer(idx + 1)).zip(availableVersions).toList - val accWithNoFix = (1 to versionsBeforeActivation.size).map(idx => TxHelpers.signer(idx + availableVersions.size + 1)).zip(versionsBeforeActivation).toList - val dAppWithFix = (1 to availableDAppVersions.size).map(idx => TxHelpers.signer(idx + availableVersions.size + versionsBeforeActivation.size + 1)).zip(availableDAppVersions).toList - val dAppWithNoFix = (1 to dAppVersionsBeforeActivation.size).map(idx => TxHelpers.signer(idx + availableVersions.size + versionsBeforeActivation.size + availableDAppVersions.size + 1)).zip(dAppVersionsBeforeActivation).toList + val accWithNoFix = + (1 to versionsBeforeActivation.size).map(idx => TxHelpers.signer(idx + availableVersions.size + 1)).zip(versionsBeforeActivation).toList + val dAppWithFix = (1 to availableDAppVersions.size) + .map(idx => TxHelpers.signer(idx + availableVersions.size + versionsBeforeActivation.size + 1)) + .zip(availableDAppVersions) + .toList + val dAppWithNoFix = (1 to dAppVersionsBeforeActivation.size) + .map(idx => TxHelpers.signer(idx + availableVersions.size + versionsBeforeActivation.size + availableDAppVersions.size + 1)) + .zip(dAppVersionsBeforeActivation) + .toList val genesis = (accWithFix ::: accWithNoFix ::: dAppWithFix ::: dAppWithNoFix) .map { case (acc, _) => TxHelpers.genesis(acc.toAddress) } val invokerGenesis = TxHelpers.genesis(invoker.toAddress) - val setNoFixVerifier = accWithNoFix.map { case (acc, v) => TxHelpers.setScript(acc, checkNoFixScript(v)) } - val setFixVerifier = accWithFix.map { case (acc, v) => TxHelpers.setScript(acc, checkFixScript(v)) } - val setNoFixDApp = dAppWithNoFix.map { case (acc, v) => TxHelpers.setScript(acc, checkNoFixDAppScript(v)) } - val setFixDApp = dAppWithFix.map { case (acc, v) => TxHelpers.setScript(acc, checkFixDAppScript(v)) } + val setNoFixVerifier = accWithNoFix.map { case (acc, v) => TxHelpers.setScript(acc, checkNoFixScript(v)) } + val setFixVerifier = accWithFix.map { case (acc, v) => TxHelpers.setScript(acc, checkFixScript(v)) } + val setNoFixDApp = dAppWithNoFix.map { case (acc, v) => TxHelpers.setScript(acc, checkNoFixDAppScript(v)) } + val setFixDApp = dAppWithFix.map { case (acc, v) => TxHelpers.setScript(acc, checkFixDAppScript(v)) } - val checkFixVerifier = accWithFix.map { case (acc, _) => TxHelpers.transfer(acc, recipient.toAddress, 1) } - val checkNoFixVerifier = accWithNoFix.map { case (acc, _) => TxHelpers.transfer(acc, recipient.toAddress, 1) } - val checkFixDApp = dAppWithFix.map { case (acc, _) => TxHelpers.invoke(acc.toAddress, func = None, invoker = invoker) } - val checkNoFixDApp = dAppWithNoFix.map { case (acc, _) => TxHelpers.invoke(acc.toAddress, func = None, invoker = invoker) } + val checkFixVerifier = accWithFix.map { case (acc, _) => TxHelpers.transfer(acc, recipient.toAddress, 1) } + val checkNoFixVerifier = accWithNoFix.map { case (acc, _) => TxHelpers.transfer(acc, recipient.toAddress, 1) } + val checkFixDApp = dAppWithFix.map { case (acc, _) => TxHelpers.invoke(acc.toAddress, func = None, invoker = invoker) } + val checkNoFixDApp = dAppWithNoFix.map { case (acc, _) => TxHelpers.invoke(acc.toAddress, func = None, invoker = invoker) } ( invokerGenesis :: genesis, diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala index f0924e62e60..87a67f98248 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/ContextFunctionsTest.scala @@ -15,9 +15,7 @@ import com.wavesplatform.lang.Testing.* import com.wavesplatform.lang.directives.values.* import com.wavesplatform.lang.directives.{DirectiveDictionary, DirectiveSet} import com.wavesplatform.lang.script.ContractScript -import com.wavesplatform.lang.script.v1.ExprScript -import com.wavesplatform.lang.utils.* -import com.wavesplatform.lang.v1.compiler.{ContractCompiler, ExpressionCompiler, TestCompiler} +import com.wavesplatform.lang.v1.compiler.{ContractCompiler, TestCompiler} import com.wavesplatform.lang.v1.estimator.v2.ScriptEstimatorV2 import com.wavesplatform.lang.v1.evaluator.ctx.impl.waves.WavesContext import com.wavesplatform.lang.v1.evaluator.ctx.impl.{CryptoContext, PureContext} @@ -60,15 +58,7 @@ class ContextFunctionsTest extends PropSpec with WithDomain with EthHelpers { scriptWithV1PureFunctions(dataTx, transfer), scriptWithV1WavesFunctions(dataTx, transfer), scriptWithCryptoFunctions - ).map(x => Parser.parseExpr(x).get.value) - .map { untypedScript => - val typedScript = { - val compilerScript = ExpressionCompiler(compilerContext(V1, Expression, isAssetScript = false), untypedScript).explicitGet()._1 - ExprScript(compilerScript).explicitGet() - } - - TxHelpers.setScript(recipient, typedScript) - } + ).map(scriptText => TxHelpers.setScript(recipient, TestCompiler(V2).compileExpression(scriptText))) (master, recipient, genesis, setScripts, dataTx, transfer, transfer2) } @@ -282,73 +272,71 @@ class ContextFunctionsTest extends PropSpec with WithDomain with EthHelpers { DirectiveDictionary[StdLibVersion].all .filter(_ >= V3) .foreach { version => - val (masterAcc, _, genesis, setScriptTransactions, dataTransaction, transferTx, transfer2) = preconditionsAndPayments - setScriptTransactions.foreach { setScriptTransaction => - val fs = settingsForRide(version).blockchainSettings.functionalitySettings - - assertDiffAndState(fs) { append => - append(genesis).explicitGet() - append(Seq(setScriptTransaction, dataTransaction)).explicitGet() + val (masterAcc, _, genesis, _, dataTransaction, transferTx, transfer2) = preconditionsAndPayments + val fs = settingsForRide(version).blockchainSettings.functionalitySettings - val quantity = 100000000L - val decimals = 6.toByte - val reissuable = true - val assetScript = None + assertDiffAndState(fs) { append => + append(genesis).explicitGet() + append(Seq(dataTransaction)).explicitGet() - val issueTx = TxHelpers.issue(masterAcc, quantity, decimals) + val quantity = 100000000L + val decimals = 6.toByte + val reissuable = true + val assetScript = None - val sponsoredFee = 100 - val sponsorTx = TxHelpers.sponsor(issueTx.asset, Some(sponsoredFee), masterAcc) + val issueTx = TxHelpers.issue(masterAcc, quantity, decimals) - append(Seq(transferTx, issueTx)).explicitGet() + val sponsoredFee = 100 + val sponsorTx = TxHelpers.sponsor(issueTx.asset, Some(sponsoredFee), masterAcc) - val assetId = issueTx.assetId + append(Seq(transferTx, issueTx)).explicitGet() - val sponsored = - if (version >= V4) - s"let sponsored = aInfo.minSponsoredFee == $sponsoredFee" - else - s"let sponsored = aInfo.sponsored == true" + val assetId = issueTx.assetId - val script = ScriptCompiler - .compile( - s""" - | {-# STDLIB_VERSION ${version.id} #-} - | {-# CONTENT_TYPE EXPRESSION #-} - | {-# SCRIPT_TYPE ACCOUNT #-} - | - | let aInfoOpt = assetInfo(base58'$assetId') - | let aInfo = aInfoOpt.value() - | let id = aInfo.id == base58'$assetId' - | let quantity = aInfo.quantity == $quantity - | let decimals = aInfo.decimals == $decimals - | let issuer = aInfo.issuer.bytes == base58'${issueTx.sender.toAddress}' - | let issuerPublicKey = aInfo.issuerPublicKey == base58'${issueTx.sender}' - | let scripted = aInfo.scripted == ${assetScript.nonEmpty} - | let reissuable = aInfo.reissuable == $reissuable - | $sponsored - | - | id && - | quantity && - | decimals && - | issuer && - | issuerPublicKey && - | scripted && - | reissuable && - | sponsored - | + val sponsored = + if (version >= V4) + s"let sponsored = aInfo.minSponsoredFee == $sponsoredFee" + else + s"let sponsored = aInfo.sponsored == true" + + val script = ScriptCompiler + .compile( + s""" + | {-# STDLIB_VERSION ${version.id} #-} + | {-# CONTENT_TYPE EXPRESSION #-} + | {-# SCRIPT_TYPE ACCOUNT #-} + | + | let aInfoOpt = assetInfo(base58'$assetId') + | let aInfo = aInfoOpt.value() + | let id = aInfo.id == base58'$assetId' + | let quantity = aInfo.quantity == $quantity + | let decimals = aInfo.decimals == $decimals + | let issuer = aInfo.issuer.bytes == base58'${issueTx.sender.toAddress}' + | let issuerPublicKey = aInfo.issuerPublicKey == base58'${issueTx.sender}' + | let scripted = aInfo.scripted == ${assetScript.nonEmpty} + | let reissuable = aInfo.reissuable == $reissuable + | $sponsored + | + | id && + | quantity && + | decimals && + | issuer && + | issuerPublicKey && + | scripted && + | reissuable && + | sponsored + | """.stripMargin, - estimator - ) - .explicitGet() - ._1 + estimator + ) + .explicitGet() + ._1 - val setScriptTx = TxHelpers.setScript(masterAcc, script) + val setScriptTx = TxHelpers.setScript(masterAcc, script) - append(Seq(sponsorTx)).explicitGet() - append(Seq(setScriptTx)).explicitGet() - append(Seq(transfer2)).explicitGet() - } + append(Seq(sponsorTx)).explicitGet() + append(Seq(setScriptTx)).explicitGet() + append(Seq(transfer2)).explicitGet() } } } @@ -398,8 +386,7 @@ class ContextFunctionsTest extends PropSpec with WithDomain with EthHelpers { .foreach { version => val (masterAcc, _, genesis, setScriptTransactions, dataTransaction, transferTx, transfer2) = preconditionsAndPayments for { - setScriptTransaction <- setScriptTransactions - withVrf <- Seq(version >= V4, true).distinct + withVrf <- Seq(version >= V4, true).distinct } yield { val generationSignature = if (withVrf) ByteStr(new Array[Byte](Block.GenerationVRFSignatureLength)) else ByteStr(new Array[Byte](Block.GenerationSignatureLength)) @@ -417,7 +404,7 @@ class ContextFunctionsTest extends PropSpec with WithDomain with EthHelpers { assertDiffAndState(settings) { append => append(genesis).explicitGet() - append(Seq(setScriptTransaction, dataTransaction)).explicitGet() + append(Seq(dataTransaction)).explicitGet() append(Seq(transferTx)).explicitGet() val script = ScriptCompiler diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideVersionDeactivationTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideVersionDeactivationTest.scala new file mode 100644 index 00000000000..56c47ffc681 --- /dev/null +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/RideVersionDeactivationTest.scala @@ -0,0 +1,49 @@ +package com.wavesplatform.state.diffs.smart.predef + +import com.wavesplatform.db.WithDomain +import com.wavesplatform.db.WithState.AddrWithBalance +import com.wavesplatform.features.BlockchainFeatures +import com.wavesplatform.lang.directives.values.* +import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.test.DomainPresets.* +import com.wavesplatform.test.{PropSpec, produce} +import com.wavesplatform.transaction.Asset.IssuedAsset +import com.wavesplatform.transaction.TxHelpers.* + +class RideVersionDeactivationTest extends PropSpec with WithDomain { + private def dApp = + TestCompiler(V3).compileContract( + """ + | @Callable(i) + | func default() = WriteSet([]) + """.stripMargin + ) + + private def verifier(version: StdLibVersion) = + TestCompiler(version).compileExpression("true") + + property(s"RIDE V1, V2, V3 are forbidden after TransactionStateSnapshot") { + withDomain( + TransactionStateSnapshot.setFeaturesHeight(BlockchainFeatures.TransactionStateSnapshot -> 4), + AddrWithBalance.enoughBalances(defaultSigner) + ) { d => + val issueTx = issue(script = Some(verifier(V7))) + val assetId = IssuedAsset(issueTx.id()) + d.appendBlock(issueTx) + def txs = Seq( + setScript(defaultSigner, dApp), + setScript(defaultSigner, verifier(V1)), + setScript(defaultSigner, verifier(V2)), + setScript(defaultSigner, verifier(V3)), + setAssetScript(defaultSigner, assetId, verifier(V1)), + setAssetScript(defaultSigner, assetId, verifier(V2)), + setAssetScript(defaultSigner, assetId, verifier(V3)), + issue(script = Some(verifier(V1))), + issue(script = Some(verifier(V2))), + issue(script = Some(verifier(V3))) + ) + d.appendAndAssertSucceed(txs*) + txs.foreach(d.appendBlockE(_) should produce("Script version below V4 is not allowed")) + } + } +} diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 054cd9ebfc6..e2d5fbc5fdd 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -6,7 +6,7 @@ import sbt.{Def, _} object Dependencies { // Node protobuf schemas private[this] val protoSchemasLib = - "com.wavesplatform" % "protobuf-schemas" % "1.5.0-83-SNAPSHOT" classifier "protobuf-src" intransitive () + "com.wavesplatform" % "protobuf-schemas" % "1.5.0-SNAPSHOT" classifier "protobuf-src" intransitive () def akkaModule(module: String): ModuleID = "com.typesafe.akka" %% s"akka-$module" % "2.6.20"