From dbdb722ba78459a3a8087ae9d6b5a5c3e7210b07 Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Thu, 23 Nov 2023 16:59:52 +0400 Subject: [PATCH] wip --- .../state/TxStateSnapshotHashBuilder.scala | 6 +- .../scala/com/wavesplatform/TestValues.scala | 6 +- .../http/DebugApiRouteSpec.scala | 2615 ++++++++--------- .../wavesplatform/http/LeaseRouteSpec.scala | 707 +++-- .../http/ProtoVersionTransactionsSpec.scala | 3 +- .../http/TransactionBroadcastSpec.scala | 3 +- .../http/TransactionsRouteSpec.scala | 591 ++-- .../wavesplatform/mining/BlockV5Test.scala | 2 +- .../mining/BlockWithMaxBaseTargetTest.scala | 2 +- .../mining/MiningFailuresSuite.scala | 2 +- .../state/BlockChallengeTest.scala | 18 +- .../wavesplatform/state/RollbackSpec.scala | 5 +- .../state/diffs/CommonValidationTest.scala | 5 +- .../diffs/ExchangeTransactionDiffTest.scala | 3 +- .../MassTransferTransactionDiffTest.scala | 61 +- .../state/diffs/OverflowTest.scala | 55 +- .../diffs/ScriptComplexityCountTest.scala | 5 +- .../diffs/ci/sync/SyncInvokeLeaseTest.scala | 4 +- .../smart/predef/CommonFunctionsTest.scala | 62 +- .../smart/predef/MatcherBlockchainTest.scala | 1 - .../snapshot/StateSnapshotStorageTest.scala | 16 +- .../wavesplatform/transaction/TxHelpers.scala | 6 +- 22 files changed, 1991 insertions(+), 2187 deletions(-) diff --git a/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala b/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala index e5fafc966ea..09f31f7897e 100644 --- a/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala +++ b/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala @@ -54,9 +54,9 @@ object TxStateSnapshotHashBuilder { changedKeys += address.bytes ++ alias.name.getBytes(StandardCharsets.UTF_8) } - snapshot.accountScriptsByAddress.foreach { case (address, sv) => - changedKeys += address.bytes ++ (sv match { - case Some(s) => s.script.bytes().arr ++ s.publicKey.arr ++ Longs.toByteArray(s.verifierComplexity) + snapshot.accountScripts.foreach { case (pk, sv) => + changedKeys += pk.arr ++ (sv match { + case Some(s) => s.script.bytes().arr ++ Longs.toByteArray(s.verifierComplexity) case None => Array.emptyByteArray }) } diff --git a/node/src/test/scala/com/wavesplatform/TestValues.scala b/node/src/test/scala/com/wavesplatform/TestValues.scala index 6623c6b0e2a..e25d435d1e3 100644 --- a/node/src/test/scala/com/wavesplatform/TestValues.scala +++ b/node/src/test/scala/com/wavesplatform/TestValues.scala @@ -23,7 +23,7 @@ object TestValues { def invokeFee(scripts: Int = 0, issues: Int = 0): Long = invokeFee + scripts * ScriptExtraFee + issues * FeeConstants(TransactionType.Issue) * FeeUnit - val (script, scriptComplexity) = ScriptCompiler + lazy val (script, scriptComplexity) = ScriptCompiler .compile( """ |{-# STDLIB_VERSION 2 #-} @@ -35,7 +35,7 @@ object TestValues { ) .explicitGet() - val (assetScript, assetScriptComplexity) = ScriptCompiler + lazy val (assetScript, assetScriptComplexity) = ScriptCompiler .compile( """ |{-# STDLIB_VERSION 2 #-} @@ -47,7 +47,7 @@ object TestValues { ) .explicitGet() - val (rejectAssetScript, rejectAssetScriptComplexity) = ScriptCompiler + lazy val (rejectAssetScript, rejectAssetScriptComplexity) = ScriptCompiler .compile( """ |{-# STDLIB_VERSION 2 #-} diff --git a/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala index 29089460466..56109d50022 100644 --- a/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala @@ -3,50 +3,37 @@ package com.wavesplatform.http import akka.http.scaladsl.model.{ContentTypes, HttpEntity, StatusCodes} import com.typesafe.config.ConfigObject import com.wavesplatform.* -import com.wavesplatform.account.{Alias, KeyPair} -import com.wavesplatform.api.common.CommonTransactionsApi +import com.wavesplatform.account.KeyPair import com.wavesplatform.api.http.ApiError.ApiKeyNotValid import com.wavesplatform.api.http.DebugApiRoute.AccountMiningInfo -import com.wavesplatform.api.http.{DebugApiRoute, RouteTimeout, handleAllExceptions} +import com.wavesplatform.api.http.{DebugApiRoute, RouteTimeout} import com.wavesplatform.block.Block import com.wavesplatform.common.state.ByteStr -import com.wavesplatform.common.utils.* -import com.wavesplatform.db.WithDomain import com.wavesplatform.db.WithState.AddrWithBalance -import com.wavesplatform.features.BlockchainFeatures -import com.wavesplatform.history.Domain import com.wavesplatform.lagonaki.mocks.TestBlock -import com.wavesplatform.lang.directives.values.V6 -import com.wavesplatform.lang.script.v1.ExprScript -import com.wavesplatform.lang.v1.compiler.Terms.TRUE +import com.wavesplatform.lang.directives.values.{V4, V5, V6} import com.wavesplatform.lang.v1.compiler.TestCompiler -import com.wavesplatform.lang.v1.estimator.v3.ScriptEstimatorV3 import com.wavesplatform.lang.v1.evaluator.ctx.impl.PureContext import com.wavesplatform.lang.v1.traits.domain.Recipient.Address import com.wavesplatform.lang.v1.traits.domain.{Issue, Lease, Recipient} import com.wavesplatform.mining.{Miner, MinerDebugInfo} import com.wavesplatform.network.PeerDatabase -import com.wavesplatform.settings.{TestFunctionalitySettings, WalletSettings, WavesSettings} +import com.wavesplatform.settings.WavesSettings import com.wavesplatform.state.StateHash.SectionId -import com.wavesplatform.state.TxMeta.Status import com.wavesplatform.state.diffs.ENOUGH_AMT -import com.wavesplatform.state.reader.LeaseDetails -import com.wavesplatform.state.{AccountScriptInfo, AssetDescription, AssetScriptInfo, Blockchain, Height, NG, StateHash, TxMeta} +import com.wavesplatform.state.{Blockchain, StateHash} import com.wavesplatform.test.* import com.wavesplatform.transaction.TxHelpers.* import com.wavesplatform.transaction.assets.exchange.OrderType import com.wavesplatform.transaction.smart.InvokeScriptTransaction import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment -import com.wavesplatform.transaction.smart.script.ScriptCompiler -import com.wavesplatform.transaction.{ERC20Address, Transaction, TxHelpers, TxVersion} +import com.wavesplatform.transaction.{Transaction, TxHelpers, TxVersion} import com.wavesplatform.utils.SharedSchedulerMixin -import com.wavesplatform.wallet.Wallet import monix.eval.Task -import org.scalamock.scalatest.PathMockFactory -import org.scalatest.{Assertion, OptionValues} +import org.scalatest.OptionValues import play.api.libs.json.{JsArray, JsObject, JsValue, Json} -import java.util.concurrent.TimeUnit +import java.util.concurrent.{ConcurrentHashMap, TimeUnit} import scala.concurrent.duration.* import scala.util.Random @@ -56,24 +43,18 @@ class DebugApiRouteSpec with RestAPISettingsHelper with TestWallet with NTPTime - with PathMockFactory - with BlockchainStubHelpers - with WithDomain + with SharedDomain with OptionValues with SharedSchedulerMixin { - import DomainPresets.* - val wavesSettings: WavesSettings = WavesSettings.default().copy(restAPISettings = restAPISettings) - val configObject: ConfigObject = wavesSettings.config.root() + override def settings: WavesSettings = DomainPresets.ContinuationTransaction.copy( + dbSettings = DomainPresets.ContinuationTransaction.dbSettings.copy(storeStateHashes = true), + restAPISettings = restAPISettings + ) + private val configObject: ConfigObject = settings.config.root() - trait Blockchain1 extends Blockchain with NG - val blockchain: Blockchain1 = stub[Blockchain1] - (blockchain.hasAccountScript _).when(*).returns(false) - (() => blockchain.microblockIds).when().returns(Seq.empty) - (blockchain.heightOf _).when(*).returns(None) - (() => blockchain.height).when().returns(0) - (blockchain.balanceSnapshots _).when(*, *, *).returns(Seq.empty) - (blockchain.effectiveBalanceBanHeights _).when(*).returns(Seq.empty) + private val richAccount = TxHelpers.signer(905) + override def genesisBalances: Seq[AddrWithBalance] = Seq(AddrWithBalance(richAccount.toAddress, 50_000.waves)) val miner: Miner & MinerDebugInfo = new Miner with MinerDebugInfo { override def scheduleMining(blockchain: Option[Blockchain]): Unit = () @@ -89,36 +70,33 @@ class DebugApiRouteSpec StateHash(randomHash, hashes) } - val wallet: Wallet = Wallet(WalletSettings(None, Some("password"), Some(ByteStr(TxHelpers.defaultSigner.seed)))) val debugApiRoute: DebugApiRoute = DebugApiRoute( - wavesSettings, + settings, ntpTime, - blockchain, - wallet, - null, - stub[CommonTransactionsApi], - null, + domain.blockchain, + domain.wallet, + domain.accountsApi, + domain.transactionsApi, + domain.assetsApi, PeerDatabase.NoOp, - null, + new ConcurrentHashMap(), (_, _) => Task.raiseError(new NotImplementedError("")), - null, + domain.utxPool, miner, null, null, null, null, configObject, - _ => Seq.empty, - { - case 2 => Some(testStateHash) - case _ => None - }, - () => Some(blockchain), + domain.rocksDBWriter.loadBalanceHistory, + domain.rocksDBWriter.loadStateHash, + () => Some(domain.blockchain), new RouteTimeout(60.seconds)(sharedScheduler), sharedScheduler ) - import debugApiRoute.* + + private val route = seal(debugApiRoute.route) routePath("/configInfo") - { "requires api-key header" in { @@ -128,38 +106,49 @@ class DebugApiRouteSpec } routePath("/balances/history/{address}") - { - val acc1 = TxHelpers.defaultSigner - val acc2 = TxHelpers.secondSigner + val acc1 = TxHelpers.signer(1001) + val acc2 = TxHelpers.signer(1002) - val initBalance = 5.waves + val initBalance = 10.waves - "works" in withDomain(balances = Seq(AddrWithBalance(acc2.toAddress, initBalance), AddrWithBalance(acc1.toAddress))) { d => + "works" in { val tx1 = TxHelpers.transfer(acc2, acc1.toAddress, 1.waves) val tx2 = TxHelpers.transfer(acc1, acc2.toAddress, 3.waves) val tx3 = TxHelpers.transfer(acc2, acc1.toAddress, 4.waves) val tx4 = TxHelpers.transfer(acc1, acc2.toAddress, 5.waves) - d.appendBlock(tx1) - d.appendBlock(tx2) - d.appendBlock() - d.appendBlock(tx3) - d.appendBlock(tx4) - d.appendBlock() + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + acc1.toAddress -> initBalance, + acc2.toAddress -> initBalance + ), + fee = 0.002.waves + ) + ) + val initialHeight = domain.blockchain.height + domain.appendBlock(tx1) + domain.appendBlock(tx2) + domain.appendBlock() + domain.appendBlock(tx3) + domain.appendBlock(tx4) + domain.appendBlock() val expectedBalance2 = initBalance - tx1.fee.value - tx1.amount.value val expectedBalance3 = expectedBalance2 + tx2.amount.value val expectedBalance5 = expectedBalance3 - tx3.fee.value - tx3.amount.value val expectedBalance6 = expectedBalance5 + tx4.amount.value - Get(routePath(s"/balances/history/${acc2.toAddress}")) ~> routeWithBlockchain(d) ~> check { + Get(routePath(s"/balances/history/${acc2.toAddress}")) ~> route ~> check { status shouldBe StatusCodes.OK responseAs[JsArray] shouldBe Json.toJson( Seq( - 6 -> expectedBalance6, - 5 -> expectedBalance5, - 3 -> expectedBalance3, - 2 -> expectedBalance2, - 1 -> initBalance + initialHeight + 5 -> expectedBalance6, + initialHeight + 4 -> expectedBalance5, + initialHeight + 2 -> expectedBalance3, + initialHeight + 1 -> expectedBalance2, + initialHeight + 0 -> initBalance ).map { case (height, balance) => Json.obj("height" -> height, "balance" -> balance) } @@ -170,37 +159,35 @@ class DebugApiRouteSpec routePath("/stateHash") - { "works" - { - val settingsWithStateHashes = DomainPresets.SettingsFromDefaultConfig.copy( - dbSettings = DomainPresets.SettingsFromDefaultConfig.dbSettings.copy(storeStateHashes = true) - ) - - "at nonexistent height" in withDomain(settingsWithStateHashes) { d => - d.appendBlock(TestBlock.create(Nil).block) - Get(routePath("/stateHash/2")) ~> routeWithBlockchain(d) ~> check { + "at nonexistent height" in { + Get(routePath(s"/stateHash/${domain.blockchain.height}")) ~> route ~> check { status shouldBe StatusCodes.NotFound } } - "at existing height" in expectStateHashAt2("2") - "last" in expectStateHashAt2("last") + "at existing height" in { + (0 until (3 - domain.blockchain.height).min(0)) foreach { _ => + domain.appendBlock() + } - def expectStateHashAt2(suffix: String): Assertion = withDomain(settingsWithStateHashes) { d => - val genesisBlock = TestBlock.create(Nil).block - d.appendBlock(genesisBlock) + val lastButOneHeight = domain.blockchain.height - 1 + val lastButOneHeader = domain.blockchain.blockHeader(lastButOneHeight).value + val lastButOneStateHash = domain.rocksDBWriter.loadStateHash(lastButOneHeight).value + val expectedResponse = Json.toJson(lastButOneStateHash).as[JsObject] ++ Json.obj( + "blockId" -> lastButOneHeader.id().toString, + "baseTarget" -> lastButOneHeader.header.baseTarget, + "height" -> lastButOneHeight, + "version" -> Version.VersionString + ) - val blockAt2 = TestBlock.create(0, genesisBlock.id(), Nil).block - d.appendBlock(blockAt2) - d.appendBlock(TestBlock.create(0, blockAt2.id(), Nil).block) + Get(routePath(s"/stateHash/last")) ~> route ~> check { + status shouldBe StatusCodes.OK + responseAs[JsObject] shouldBe expectedResponse + } - val stateHashAt2 = d.rocksDBWriter.loadStateHash(2).value - Get(routePath(s"/stateHash/$suffix")) ~> routeWithBlockchain(d) ~> check { + Get(routePath(s"/stateHash/$lastButOneHeight")) ~> route ~> check { status shouldBe StatusCodes.OK - responseAs[JsObject] shouldBe (Json.toJson(stateHashAt2).as[JsObject] ++ Json.obj( - "blockId" -> blockAt2.id().toString, - "baseTarget" -> blockAt2.header.baseTarget, - "height" -> 2, - "version" -> Version.VersionString - )) + responseAs[JsObject] shouldBe expectedResponse } } } @@ -210,11 +197,10 @@ class DebugApiRouteSpec def validatePost(tx: Transaction) = Post(routePath("/validate"), HttpEntity(ContentTypes.`application/json`, tx.json().toString())) - "takes the priority pool into account" in withDomain(balances = Seq(AddrWithBalance(TxHelpers.defaultAddress))) { d => - d.appendBlock(TxHelpers.transfer(to = TxHelpers.secondAddress, amount = 1.waves + TestValues.fee)) + "takes the priority pool into account" in { + domain.appendBlock(TxHelpers.transfer(to = TxHelpers.secondAddress, amount = 1.waves + TestValues.fee)) - val route = routeWithBlockchain(d.blockchain) - val tx = TxHelpers.transfer(TxHelpers.secondSigner, TestValues.address, 1.waves) + val tx = TxHelpers.transfer(TxHelpers.secondSigner, TestValues.address, 1.waves) validatePost(tx) ~> route ~> check { val json = responseAs[JsValue] (json \ "valid").as[Boolean] shouldBe true @@ -223,12 +209,6 @@ class DebugApiRouteSpec } "valid tx" in { - val blockchain = createBlockchainStub() - (blockchain.balance _).when(TxHelpers.defaultSigner.publicKey.toAddress, *).returns(Long.MaxValue) - (blockchain.wavesBalances _).when(Seq(TxHelpers.defaultAddress)).returns(Map(TxHelpers.defaultAddress -> Long.MaxValue)) - - val route = routeWithBlockchain(blockchain) - val tx = TxHelpers.transfer(TxHelpers.defaultSigner, TestValues.address, 1.waves) validatePost(tx) ~> route ~> check { val json = responseAs[JsValue] @@ -238,12 +218,6 @@ class DebugApiRouteSpec } "invalid tx" in { - val blockchain = createBlockchainStub() - (blockchain.balance _).when(TxHelpers.defaultSigner.publicKey.toAddress, *).returns(0) - (blockchain.wavesBalances _).when(Seq(TxHelpers.defaultAddress)).returns(Map(TxHelpers.defaultAddress -> 0)) - - val route = routeWithBlockchain(blockchain) - val tx = TxHelpers.transfer(TxHelpers.defaultSigner, TestValues.address, ENOUGH_AMT) validatePost(tx) ~> route ~> check { val json = responseAs[JsValue] @@ -254,159 +228,91 @@ class DebugApiRouteSpec } "NoSuchElementException" in { - val blockchain = createBlockchainStub { b => - (b.accountScript _).when(*).throws(new NoSuchElementException()) - } - val route = handleAllExceptions(routeWithBlockchain(blockchain)) validatePost(TxHelpers.invoke()) ~> route ~> check { - responseAs[ - String - ] shouldBe """{"error":0,"message":"Error is unknown"}""" - response.status shouldBe StatusCodes.InternalServerError + (responseAs[JsValue] \ "error").as[String] should include("No contract at address") + response.status shouldBe StatusCodes.OK } } "exchange tx with fail script" in { - val blockchain = createBlockchainStub { blockchain => - (blockchain.balance _).when(TxHelpers.defaultAddress, *).returns(Long.MaxValue) - (blockchain.wavesBalances _).when(Seq(TxHelpers.defaultAddress)).returns(Map(TxHelpers.defaultAddress -> Long.MaxValue)) - - val (assetScript, comp) = - ScriptCompiler.compile("if true then throw(\"error\") else false", ScriptEstimatorV3(fixOverflow = true, overhead = true)).explicitGet() - (blockchain.assetScript _).when(TestValues.asset).returns(Some(AssetScriptInfo(assetScript, comp))) - (blockchain.assetDescription _) - .when(TestValues.asset) - .returns( - Some( - AssetDescription( - null, - null, - null, - null, - 0, - reissuable = false, - null, - Height(1), - Some(AssetScriptInfo(assetScript, comp)), - 0, - nft = false, - 0, - Height(1) - ) - ) - ) - blockchain.stub.activateAllFeatures() - } + val assetIssuer = TxHelpers.signer(1021) + val o1sender = TxHelpers.signer(1022) + val o2sender = TxHelpers.signer(1023) + + val issue = TxHelpers.issue(assetIssuer, script = Some(TestCompiler(V5).compileExpression("true"))) + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq(o1sender.toAddress -> 1.waves, o2sender.toAddress -> 1.waves, assetIssuer.toAddress -> 3.waves), + fee = 0.003.waves + ), + issue, + TxHelpers.massTransfer(assetIssuer, Seq(o1sender.toAddress -> 50, o2sender.toAddress -> 50), issue.asset, fee = 0.006.waves), + TxHelpers.setAssetScript(assetIssuer, issue.asset, TestCompiler(V5).compileExpression("if true then throw(\"error\") else false")) + ) - val route = routeWithBlockchain(blockchain) - val tx = TxHelpers.exchangeFromOrders(TxHelpers.orderV3(OrderType.BUY, TestValues.asset), TxHelpers.orderV3(OrderType.SELL, TestValues.asset)) - jsonPost(routePath("/validate"), tx.json()) ~> route ~> check { + jsonPost( + routePath("/validate"), + TxHelpers.exchangeFromOrders(TxHelpers.orderV3(OrderType.BUY, issue.asset), TxHelpers.orderV3(OrderType.SELL, issue.asset)).json() + ) ~> route ~> check { val json = responseAs[JsValue] (json \ "valid").as[Boolean] shouldBe false (json \ "validationTime").as[Int] shouldBe 1000 +- 1000 (json \ "error").as[String] should include("not allowed by script of the asset") (json \ "trace").as[JsArray] shouldBe Json.parse( - "[{\"type\":\"asset\",\"context\":\"orderAmount\",\"id\":\"5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx\",\"result\":\"failure\",\"vars\":[{\"name\":\"throw.@args\",\"type\":\"Array\",\"value\":[{\"type\":\"String\",\"value\":\"error\"}]},{\"name\":\"throw.@complexity\",\"type\":\"Int\",\"value\":1},{\"name\":\"@complexityLimit\",\"type\":\"Int\",\"value\":2147483646}],\"error\":\"error\"}]" + s"[{\"type\":\"asset\",\"context\":\"orderAmount\",\"id\":\"${issue.id()}\",\"result\":\"failure\",\"vars\":[{\"name\":\"throw.@args\",\"type\":\"Array\",\"value\":[{\"type\":\"String\",\"value\":\"error\"}]},{\"name\":\"throw.@complexity\",\"type\":\"Int\",\"value\":1},{\"name\":\"@complexityLimit\",\"type\":\"Int\",\"value\":2147483646}],\"error\":\"error\"}]" ) } } "invoke tx with asset failing" in { - val blockchain = createBlockchainStub { blockchain => - (blockchain.balance _).when(*, *).returns(Long.MaxValue / 2) - (blockchain.wavesBalances _).when(*).returns(Map(TxHelpers.defaultAddress -> Long.MaxValue / 2)) - - val (assetScript, assetScriptComplexity) = ScriptCompiler - .compile( - "let test = true\n" + - "if test then throw(\"error\") else !test", - ScriptEstimatorV3(fixOverflow = true, overhead = true) - ) - .explicitGet() - - (blockchain.assetScript _).when(TestValues.asset).returns(Some(AssetScriptInfo(assetScript, assetScriptComplexity))) - - (blockchain.assetDescription _) - .when(TestValues.asset) - .returns( - Some( - AssetDescription( - TestValues.asset.id, - TxHelpers.defaultSigner.publicKey, - null, - null, - 0, - reissuable = true, - BigInt(1), - Height(1), - Some(AssetScriptInfo(assetScript, assetScriptComplexity)), - 0, - nft = false, - 0, - Height(1) - ) - ) - ) - - (blockchain.resolveERC20Address _).when(ERC20Address(TestValues.asset)).returns(Some(TestValues.asset)) - (blockchain.resolveERC20Address _).when(*).returns(None) - - val (dAppScript, _) = ScriptCompiler - .compile( - s""" - |{-# STDLIB_VERSION 4 #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - |{-# CONTENT_TYPE DAPP #-} - | - |@Callable(i) - |func default() = [] - | - |@Callable(i) - |func dataAndTransfer() = [ - | IntegerEntry("key", 1), - | BooleanEntry("key", true), - | StringEntry("key", "str"), - | BinaryEntry("key", base58''), - | DeleteEntry("key"), - | ScriptTransfer(Address(base58'${TxHelpers.secondAddress}'), 1, base58'${TestValues.asset}') - |] - | - |@Callable(i) - |func issue() = { - | let decimals = 4 - | [Issue("name", "description", 1000, decimals, true, unit, 0)] - |} - | - |@Callable(i) - |func reissue() = [Reissue(base58'${TestValues.asset}', 1, false)] - | - |@Callable(i) - |func burn() = [Burn(base58'${TestValues.asset}', 1)] - |""".stripMargin, - ScriptEstimatorV3(fixOverflow = true, overhead = true) - ) - .explicitGet() - - (blockchain.accountScript _) - .when(*) - .returns( - Some( - AccountScriptInfo( - TxHelpers.defaultSigner.publicKey, - dAppScript, - 0L, - Map(3 -> Seq("default", "dataAndTransfer", "issue", "reissue", "burn", "sponsorFee").map(_ -> 1L).toMap) - ) - ) - ) - - (blockchain.hasAccountScript _).when(*).returns(true) - blockchain.stub.activateAllFeatures() - } + val dapp = TxHelpers.signer(1031) + val invoker = TxHelpers.signer(1032) + + val failingAssetScript = TestCompiler(V4).compileExpression("""let test = true + |if (test) then throw("error") else !test + |""".stripMargin) + val issue = TxHelpers.issue(dapp, script = Some(TestCompiler(V4).compileExpression("true"))) + domain.appendBlock( + TxHelpers.transfer(richAccount, dapp.toAddress, 10.waves), + TxHelpers.transfer(richAccount, invoker.toAddress, 10.waves), + issue, + TxHelpers.transfer(dapp, invoker.toAddress, 1, issue.asset), + TxHelpers.setScript( + dapp, + TestCompiler(V4) + .compileContract(s"""@Callable(i) + |func default() = [] + | + |@Callable(i) + |func dataAndTransfer() = [ + | IntegerEntry("key", 1), + | BooleanEntry("key", true), + | StringEntry("key", "str"), + | BinaryEntry("key", base58''), + | DeleteEntry("key"), + | ScriptTransfer(Address(base58'${TxHelpers.secondAddress}'), 1, base58'${issue.asset}') + |] + | + |@Callable(i) + |func issue() = { + | let decimals = 4 + | [Issue("name", "description", 1000, decimals, true, unit, 0)] + |} + | + |@Callable(i) + |func reissue() = [Reissue(base58'${issue.asset}', 1, false)] + | + |@Callable(i) + |func burn() = [Burn(base58'${issue.asset}', 1)] + |""".stripMargin) + ), + TxHelpers.setScript(invoker, TestCompiler(V4).compileExpression("true")), + TxHelpers.setAssetScript(dapp, issue.asset, failingAssetScript) + ) - val route = routeWithBlockchain(blockchain) def testFunction(name: String, result: InvokeScriptTransaction => String) = withClue(s"function $name") { - val tx = TxHelpers.invoke(TxHelpers.defaultAddress, func = Some(name), fee = 102500000) + val tx = TxHelpers.invoke(dapp.toAddress, func = Some(name), fee = 102500000, invoker = invoker) jsonPost(routePath("/validate"), tx.json()) ~> route ~> check { val json = responseAs[JsValue] @@ -421,7 +327,7 @@ class DebugApiRouteSpec } def testPayment(result: InvokeScriptTransaction => String) = withClue("payment") { - val tx = TxHelpers.invoke(TxHelpers.secondAddress, fee = 1300000, payments = Seq(Payment(1L, TestValues.asset))) + val tx = TxHelpers.invoke(dapp.toAddress, fee = 1300000, payments = Seq(Payment(1L, issue.asset)), invoker = invoker) jsonPost(routePath("/validate"), tx.json()) ~> route ~> check { val json = responseAs[JsValue] @@ -437,12 +343,12 @@ class DebugApiRouteSpec testPayment(tx => s"""[ { | "type" : "verifier", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${invoker.toAddress}", | "result" : "success", | "error" : null |}, { | "type" : "dApp", - | "id" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", + | "id" : "${dapp.toAddress}", | "function" : "default", | "args" : [ ], | "invocations" : [ ], @@ -473,14 +379,14 @@ class DebugApiRouteSpec | }, | "assetId" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } | } | } ] | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${invoker.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -495,7 +401,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${invoker.toAddress}" | } | } | }, @@ -515,12 +421,12 @@ class DebugApiRouteSpec | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 51994 + | "value" : 51998 | } ] |}, { | "type" : "asset", | "context" : "payment", - | "id" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", + | "id" : "${issue.id()}", | "result" : "failure", | "vars" : [ { | "name" : "test", @@ -549,12 +455,12 @@ class DebugApiRouteSpec "dataAndTransfer", tx => s"""[ { | "type" : "verifier", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${invoker.toAddress}", | "result" : "success", | "error" : null |}, { | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${dapp.toAddress}", | "function" : "dataAndTransfer", | "args" : [ ], | "invocations" : [ ], @@ -581,7 +487,7 @@ class DebugApiRouteSpec | } ], | "transfers" : [ { | "address" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", - | "asset" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", + | "asset" : "${issue.asset}", | "amount" : 1 | } ], | "issues" : [ ], @@ -603,7 +509,7 @@ class DebugApiRouteSpec | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${invoker.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -618,7 +524,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${invoker.toAddress}" | } | } | }, @@ -749,7 +655,7 @@ class DebugApiRouteSpec | "value" : 1 | }, { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } ] | }, { | "name" : "ScriptTransfer.@complexity", @@ -780,7 +686,7 @@ class DebugApiRouteSpec | }, | "asset" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } | } | }, { @@ -826,7 +732,7 @@ class DebugApiRouteSpec | }, | "asset" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } | } | } ] @@ -882,7 +788,7 @@ class DebugApiRouteSpec | }, | "asset" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } | } | } ] @@ -950,7 +856,7 @@ class DebugApiRouteSpec | }, | "asset" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } | } | } ] @@ -1030,7 +936,7 @@ class DebugApiRouteSpec | }, | "asset" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } | } | } ] @@ -1122,7 +1028,7 @@ class DebugApiRouteSpec | }, | "asset" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | } | } | } ] @@ -1139,7 +1045,7 @@ class DebugApiRouteSpec |}, { | "type" : "asset", | "context" : "transfer", - | "id" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", + | "id" : "${issue.asset}", | "result" : "failure", | "vars" : [ { | "name" : "test", @@ -1169,12 +1075,12 @@ class DebugApiRouteSpec "issue", tx => s"""[ { | "type" : "verifier", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${invoker.toAddress}", | "result" : "success", | "error" : null |}, { | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${dapp.toAddress}", | "function" : "issue", | "args" : [ ], | "invocations" : [ ], @@ -1209,7 +1115,7 @@ class DebugApiRouteSpec | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${invoker.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -1224,7 +1130,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${invoker.toAddress}" | } | } | }, @@ -1329,12 +1235,12 @@ class DebugApiRouteSpec "reissue", tx => s"""[ { | "type" : "verifier", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${invoker.toAddress}", | "result" : "success", | "error" : null |}, { | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${dapp.toAddress}", | "function" : "reissue", | "args" : [ ], | "invocations" : [ ], @@ -1343,7 +1249,7 @@ class DebugApiRouteSpec | "transfers" : [ ], | "issues" : [ ], | "reissues" : [ { - | "assetId" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", + | "assetId" : "${issue.asset}", | "isReissuable" : false, | "quantity" : 1 | } ], @@ -1364,7 +1270,7 @@ class DebugApiRouteSpec | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${invoker.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -1379,7 +1285,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${invoker.toAddress}" | } | } | }, @@ -1397,7 +1303,7 @@ class DebugApiRouteSpec | "type" : "Array", | "value" : [ { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | }, { | "type" : "Int", | "value" : 1 @@ -1421,7 +1327,7 @@ class DebugApiRouteSpec | "value" : { | "assetId" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | }, | "quantity" : { | "type" : "Int", @@ -1448,7 +1354,7 @@ class DebugApiRouteSpec |}, { | "type" : "asset", | "context" : "reissue", - | "id" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", + | "id" : "${issue.asset}", | "result" : "failure", | "vars" : [ { | "name" : "test", @@ -1478,12 +1384,12 @@ class DebugApiRouteSpec "burn", tx => s"""[ { | "type" : "verifier", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${invoker.toAddress}", | "result" : "success", | "error" : null |}, { | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${dapp.toAddress}", | "function" : "burn", | "args" : [ ], | "invocations" : [ ], @@ -1493,7 +1399,7 @@ class DebugApiRouteSpec | "issues" : [ ], | "reissues" : [ ], | "burns" : [ { - | "assetId" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", + | "assetId" : "${issue.asset}", | "quantity" : 1 | } ], | "sponsorFees" : [ ], @@ -1512,7 +1418,7 @@ class DebugApiRouteSpec | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${invoker.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -1527,7 +1433,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${invoker.toAddress}" | } | } | }, @@ -1545,7 +1451,7 @@ class DebugApiRouteSpec | "type" : "Array", | "value" : [ { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | }, { | "type" : "Int", | "value" : 1 @@ -1566,7 +1472,7 @@ class DebugApiRouteSpec | "value" : { | "assetId" : { | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" + | "value" : "${issue.asset}" | }, | "quantity" : { | "type" : "Int", @@ -1589,7 +1495,7 @@ class DebugApiRouteSpec |}, { | "type" : "asset", | "context" : "burn", - | "id" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", + | "id" : "${issue.asset}", | "result" : "failure", | "vars" : [ { | "name" : "test", @@ -1618,10 +1524,16 @@ class DebugApiRouteSpec } "invoke tx returning leases" in { - val dAppPk = TxHelpers.defaultSigner.publicKey - val dAppAddress = dAppPk.toAddress - val invoke = TxHelpers.invoke(dAppPk.toAddress) - val canceledLeaseId = ByteStr(bytes32gen.sample.get) + val dAppPk = TxHelpers.signer(1040) + val dAppAddress = dAppPk.toAddress + + val leaseRecipient = TxHelpers.address(1041) + val aliasOwner = TxHelpers.signer(1042) + val caller = TxHelpers.signer(1043) + + val invoke = TxHelpers.invoke(dAppPk.toAddress, invoker = caller) + val originalLease = TxHelpers.lease(dAppPk, leaseRecipient) + val canceledLeaseId = originalLease.id() val amount1 = 100 val recipient1 = Recipient.Address(ByteStr.decodeBase58("3NAgxLPGnw3RGv9JT6NTDaG5D1iLUehg2xd").get) @@ -1633,82 +1545,26 @@ class DebugApiRouteSpec val nonce2 = 2 val leaseId2 = Lease.calculateId(Lease(recipient2, amount2, nonce2), invoke.id()) - val leaseCancelAmount = 786 - - val blockchain = createBlockchainStub { blockchain => - (blockchain.balance _).when(*, *).returns(Long.MaxValue) - (blockchain.wavesBalances _).when(*).returns(Map(TxHelpers.defaultAddress -> Long.MaxValue)) - - (blockchain.resolveAlias _).when(Alias.create(recipient2.name).explicitGet()).returning(Right(TxHelpers.secondAddress)) - - blockchain.stub.activateAllFeatures() - - val (dAppScript, _) = ScriptCompiler - .compile( - s""" - |{-# STDLIB_VERSION 5 #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - |{-# CONTENT_TYPE DAPP #-} - | - |@Callable(i) - |func default() = { - | strict a = parseBigIntValue("${PureContext.BigIntMax}") - | let test = 1 - | if (test == 1) - | then - | [ - | Lease(Address(base58'${recipient1.bytes}'), $amount1, $nonce1), - | Lease(Alias("${recipient2.name}"), $amount2, $nonce2), - | LeaseCancel(base58'$canceledLeaseId') - | ] - | else [] - |} - |""".stripMargin, - ScriptEstimatorV3(fixOverflow = true, overhead = true) - ) - .explicitGet() - - (blockchain.accountScript _) - .when(*) - .returns( - Some( - AccountScriptInfo( - dAppPk, - dAppScript, - 0L, - Map(3 -> Seq("default", "test1").map(_ -> 0L).toMap) - ) - ) - ) - - (blockchain.hasAccountScript _).when(*).returns(true) - - (blockchain.transactionMeta _) - .when(canceledLeaseId) - .returns(Some(TxMeta(Height(1), Status.Succeeded, 0L))) - .anyNumberOfTimes() - - (blockchain.leaseDetails _) - .when(canceledLeaseId) - .returns(Some(LeaseDetails(dAppPk, TxHelpers.defaultAddress, leaseCancelAmount, LeaseDetails.Status.Active, invoke.id(), 1))) - .anyNumberOfTimes() - - (blockchain.leaseDetails _) - .when(*) - .returns(None) - .anyNumberOfTimes() - - (blockchain.resolveAlias _) - .when(*) - .returns(Right(accountGen.sample.get.toAddress)) - .anyNumberOfTimes() - } - val route = debugApiRoute - .copy( - blockchain = blockchain, - priorityPoolBlockchain = () => Some(blockchain) - ) - .route + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq(dAppPk.toAddress -> 20.waves, aliasOwner.toAddress -> 10.waves, caller.toAddress -> 5.waves), + fee = 0.003.waves + ), + originalLease, + TxHelpers.createAlias("some_alias", aliasOwner), + TxHelpers.setScript( + dAppPk, + TestCompiler(V6).compileContract(s"""@Callable(i) + |func default() = [ + | LeaseCancel(base58'$canceledLeaseId'), + | Lease(Address(base58'3NAgxLPGnw3RGv9JT6NTDaG5D1iLUehg2xd'), 100, 0), + | Lease(Alias("some_alias"), 20, 2) + |] + |""".stripMargin) + ), + TxHelpers.setScript(caller, TestCompiler(V6).compileExpression("true")) + ) Post(routePath("/validate"), HttpEntity(ContentTypes.`application/json`, invoke.json().toString())) ~> route ~> check { val json = responseAs[JsValue] @@ -1726,7 +1582,7 @@ class DebugApiRouteSpec | "sender" : "$dAppAddress", | "recipient" : "${recipient1.bytes}", | "amount" : 100, - | "height" : 1, + | "height" : ${domain.blockchain.height}, | "status" : "active", | "cancelHeight" : null, | "cancelTransactionId" : null @@ -1734,36 +1590,35 @@ class DebugApiRouteSpec | "id" : "$leaseId2", | "originTransactionId" : "${invoke.id()}", | "sender" : "$dAppAddress", - | "recipient" : "${TxHelpers.secondAddress}", + | "recipient" : "${aliasOwner.toAddress}", | "amount" : 20, - | "height" : 1, + | "height" : ${domain.blockchain.height}, | "status" : "active", | "cancelHeight" : null, | "cancelTransactionId" : null | } ], | "leaseCancels" : [ { | "id" : "$canceledLeaseId", - | "originTransactionId" : "${invoke.id()}", + | "originTransactionId" : "${originalLease.id()}", | "sender" : "$dAppAddress", - | "recipient" : "${TxHelpers.defaultAddress}", - | "amount" : $leaseCancelAmount, - | "height" : 1, + | "recipient" : "$leaseRecipient", + | "amount" : ${originalLease.amount.value}, + | "height" : ${domain.blockchain.height}, | "status" : "canceled", - | "cancelHeight" : 1, + | "cancelHeight" : ${domain.blockchain.height}, | "cancelTransactionId" : "${invoke.id()}" | } ], | "invokes" : [ ] |}""".stripMargin) (json \ "trace").as[JsArray] should matchJson( - s""" - |[ { + s"""[ { | "type" : "verifier", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${caller.toAddress}", | "result" : "success", | "error" : null |}, { | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${dAppAddress}", | "function" : "default", | "args" : [ ], | "invocations" : [ ], @@ -1777,33 +1632,33 @@ class DebugApiRouteSpec | "leases" : [ { | "id" : "$leaseId1", | "originTransactionId" : "${invoke.id()}", - | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", - | "recipient" : "3NAgxLPGnw3RGv9JT6NTDaG5D1iLUehg2xd", + | "sender" : "$dAppAddress", + | "recipient" : "${recipient1.bytes}", | "amount" : 100, - | "height" : 1, + | "height" : ${domain.blockchain.height}, | "status" : "active", | "cancelHeight" : null, | "cancelTransactionId" : null | }, { | "id" : "$leaseId2", | "originTransactionId" : "${invoke.id()}", - | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", - | "recipient" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", + | "sender" : "$dAppAddress", + | "recipient" : "${aliasOwner.toAddress}", | "amount" : 20, - | "height" : 1, + | "height" : ${domain.blockchain.height}, | "status" : "active", | "cancelHeight" : null, | "cancelTransactionId" : null | } ], | "leaseCancels" : [ { | "id" : "$canceledLeaseId", - | "originTransactionId" : "${invoke.id()}", - | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", - | "recipient" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", - | "amount" : $leaseCancelAmount, - | "height" : 1, + | "originTransactionId" : "${originalLease.id()}", + | "sender" : "$dAppAddress", + | "recipient" : "$leaseRecipient", + | "amount" : ${originalLease.amount.value}, + | "height" : ${domain.blockchain.height}, | "status" : "canceled", - | "cancelHeight" : 1, + | "cancelHeight" : ${domain.blockchain.height}, | "cancelTransactionId" : "${invoke.id()}" | } ], | "invokes" : [ ] @@ -1818,7 +1673,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${caller.toAddress}" | } | } | }, @@ -1828,7 +1683,7 @@ class DebugApiRouteSpec | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${caller.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -1836,7 +1691,7 @@ class DebugApiRouteSpec | }, | "originCallerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${caller.publicKey}" | }, | "transactionId" : { | "type" : "ByteVector", @@ -1847,7 +1702,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${caller.toAddress}" | } | } | }, @@ -1861,70 +1716,26 @@ class DebugApiRouteSpec | "type" : "Array", | "value" : [ ] | }, { - | "name" : "parseBigIntValue.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "String", - | "value" : "6703903964971298549787012499102923063739682910296196688861780721860882015036773488400937149083451713845015929093243025426876941405973284973216824503042047" - | } ] - | }, { - | "name" : "parseBigIntValue.@complexity", - | "type" : "Int", - | "value" : 65 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 25935 - | }, { - | "name" : "a", - | "type" : "BigInt", - | "value" : 6.703903964971298549787012499102923E+153 - | }, { - | "name" : "==.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "BigInt", - | "value" : 6.703903964971298549787012499102923E+153 - | }, { - | "type" : "BigInt", - | "value" : 6.703903964971298549787012499102923E+153 - | } ] - | }, { - | "name" : "==.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 25934 - | }, { - | "name" : "test", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "==.@args", + | "name" : "LeaseCancel.@args", | "type" : "Array", | "value" : [ { - | "type" : "Int", - | "value" : 1 - | }, { - | "type" : "Int", - | "value" : 1 + | "type" : "ByteVector", + | "value" : "${originalLease.id()}" | } ] | }, { - | "name" : "==.@complexity", + | "name" : "LeaseCancel.@complexity", | "type" : "Int", | "value" : 1 | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 25933 + | "value" : 51999 | }, { | "name" : "Address.@args", | "type" : "Array", | "value" : [ { | "type" : "ByteVector", - | "value" : "3NAgxLPGnw3RGv9JT6NTDaG5D1iLUehg2xd" + | "value" : "${recipient1.bytes}" | } ] | }, { | "name" : "Address.@complexity", @@ -1933,7 +1744,7 @@ class DebugApiRouteSpec | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 25932 + | "value" : 51998 | }, { | "name" : "Lease.@args", | "type" : "Array", @@ -1942,7 +1753,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3NAgxLPGnw3RGv9JT6NTDaG5D1iLUehg2xd" + | "value" : "${recipient1.bytes}" | } | } | }, { @@ -1959,7 +1770,7 @@ class DebugApiRouteSpec | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 25931 + | "value" : 51997 | }, { | "name" : "Alias.@args", | "type" : "Array", @@ -1974,7 +1785,7 @@ class DebugApiRouteSpec | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 25930 + | "value" : 51996 | }, { | "name" : "Lease.@args", | "type" : "Array", @@ -2000,45 +1811,7 @@ class DebugApiRouteSpec | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 25929 - | }, { - | "name" : "LeaseCancel.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "ByteVector", - | "value" : "$canceledLeaseId" - | } ] - | }, { - | "name" : "LeaseCancel.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 25928 - | }, { - | "name" : "cons.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "LeaseCancel", - | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "$canceledLeaseId" - | } - | } - | }, { - | "type" : "Array", - | "value" : [ ] - | } ] - | }, { - | "name" : "cons.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 25927 + | "value" : 51995 | }, { | "name" : "cons.@args", | "type" : "Array", @@ -2065,15 +1838,7 @@ class DebugApiRouteSpec | } | }, { | "type" : "Array", - | "value" : [ { - | "type" : "LeaseCancel", - | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "$canceledLeaseId" - | } - | } - | } ] + | "value" : [ ] | } ] | }, { | "name" : "cons.@complexity", @@ -2082,7 +1847,7 @@ class DebugApiRouteSpec | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 25926 + | "value" : 51994 | }, { | "name" : "cons.@args", | "type" : "Array", @@ -2094,7 +1859,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3NAgxLPGnw3RGv9JT6NTDaG5D1iLUehg2xd" + | "value" : "${recipient1.bytes}" | } | } | }, @@ -2130,12 +1895,69 @@ class DebugApiRouteSpec | "value" : 2 | } | } + | } ] + | } ] + | }, { + | "name" : "cons.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51993 + | }, { + | "name" : "cons.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "LeaseCancel", + | "value" : { + | "leaseId" : { + | "type" : "ByteVector", + | "value" : "${originalLease.id()}" + | } + | } + | }, { + | "type" : "Array", + | "value" : [ { + | "type" : "Lease", + | "value" : { + | "recipient" : { + | "type" : "Address", + | "value" : { + | "bytes" : { + | "type" : "ByteVector", + | "value" : "${recipient1.bytes}" + | } + | } + | }, + | "amount" : { + | "type" : "Int", + | "value" : 100 + | }, + | "nonce" : { + | "type" : "Int", + | "value" : 0 + | } + | } | }, { - | "type" : "LeaseCancel", + | "type" : "Lease", | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "$canceledLeaseId" + | "recipient" : { + | "type" : "Alias", + | "value" : { + | "alias" : { + | "type" : "String", + | "value" : "some_alias" + | } + | } + | }, + | "amount" : { + | "type" : "Int", + | "value" : 20 + | }, + | "nonce" : { + | "type" : "Int", + | "value" : 2 | } | } | } ] @@ -2147,695 +1969,674 @@ class DebugApiRouteSpec | }, { | "name" : "@complexityLimit", | "type" : "Int", - | "value" : 25925 + | "value" : 51992 | } ] |} ] - | - """.stripMargin + |""".stripMargin ) - (json \ "height").as[Int] shouldBe 1 + (json \ "height").as[Int] shouldBe domain.blockchain.height } } "invoke tx returning leases with error" in { - val dApp1Kp = signer(1) - val dApp2Kp = signer(2) - val leaseAddress = signer(3).toAddress + val dApp1Kp = signer(911) + val dApp2Kp = signer(912) + val leaseAddress = signer(913).toAddress val amount = 123 - withDomain(RideV6, AddrWithBalance.enoughBalances(dApp1Kp, dApp2Kp)) { d => - val dApp1 = TestCompiler(V6).compileContract( - s""" - | @Callable(i) - | func default() = { - | strict r = Address(base58'${dApp2Kp.toAddress}').invoke("default", [], []) - | if (true) then throw() else [] - | } + val dApp1 = TestCompiler(V6).compileContract( + s""" + | @Callable(i) + | func default() = { + | strict r = Address(base58'${dApp2Kp.toAddress}').invoke("default", [], []) + | if (true) then throw() else [] + | } """.stripMargin - ) - val leaseTx = lease(dApp2Kp, leaseAddress) - val dApp2 = TestCompiler(V6).compileContract( - s""" - | @Callable(i) - | func default() = { - | let lease = Lease(Address(base58'$leaseAddress'), $amount) - | let cancel1 = LeaseCancel(calculateLeaseId(lease)) - | let cancel2 = LeaseCancel(base58'${leaseTx.id()}') - | [lease, cancel1, cancel2] - | } + ) + val leaseTx = lease(dApp2Kp, leaseAddress) + val dApp2 = TestCompiler(V6).compileContract( + s""" + | @Callable(i) + | func default() = { + | let lease = Lease(Address(base58'$leaseAddress'), $amount) + | let cancel1 = LeaseCancel(calculateLeaseId(lease)) + | let cancel2 = LeaseCancel(base58'${leaseTx.id()}') + | [lease, cancel1, cancel2] + | } """.stripMargin - ) - d.appendBlock(leaseTx) - d.appendBlock(setScript(dApp1Kp, dApp1), setScript(dApp2Kp, dApp2)) + ) + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + dApp1Kp.toAddress -> 20.waves, + dApp2Kp.toAddress -> 20.waves + ), + fee = 0.002.waves + ), + leaseTx, + setScript(dApp1Kp, dApp1), + setScript(dApp2Kp, dApp2) + ) + val leaseTxHeight = domain.blockchain.height - val route = debugApiRoute.copy(blockchain = d.blockchain, priorityPoolBlockchain = () => Some(d.blockchain)).route - val invoke = TxHelpers.invoke(dApp1Kp.toAddress) - val leaseId = Lease.calculateId(Lease(Address(ByteStr(leaseAddress.bytes)), amount, 0), invoke.id()) + val invoke = TxHelpers.invoke(dApp1Kp.toAddress) + val leaseId = Lease.calculateId(Lease(Address(ByteStr(leaseAddress.bytes)), amount, 0), invoke.id()) - Post(routePath("/validate"), HttpEntity(ContentTypes.`application/json`, invoke.json().toString())) ~> route ~> check { - val json = responseAs[JsValue] - json should matchJson( - s""" - |{ - | "valid": false, - | "validationTime": ${(json \ "validationTime").as[Int]}, - | "trace": [ - | { - | "type": "dApp", - | "id": "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", - | "function": "default", - | "args": [], - | "invocations": [ - | { - | "type": "dApp", - | "id": "3MsY23LPQnvPZnBKpvs6YcnCvGjLVD42pSy", - | "function": "default", - | "args": [], - | "invocations": [], - | "result": { - | "data": [], - | "transfers": [], - | "issues": [], - | "reissues": [], - | "burns": [], - | "sponsorFees": [], - | "leases": [ - | { - | "id": "$leaseId", - | "originTransactionId": null, - | "sender": null, - | "recipient": "$leaseAddress", - | "amount": $amount, - | "height": null, - | "status": "canceled", - | "cancelHeight": null, - | "cancelTransactionId": null - | } - | ], - | "leaseCancels": [ - | { - | "id": "$leaseId", - | "originTransactionId": null, - | "sender": null, - | "recipient": null, - | "amount": null, - | "height": null, - | "status": "canceled", - | "cancelHeight": null, - | "cancelTransactionId": null - | }, { - | "id" : "${leaseTx.id()}", - | "originTransactionId" : "${leaseTx.id()}", - | "sender" : "${dApp2Kp.toAddress}", - | "recipient" : "$leaseAddress", - | "amount" : ${leaseTx.amount}, - | "height" : 2, - | "status" : "active", - | "cancelHeight" : null, - | "cancelTransactionId" : null - | } - | - | ], - | "invokes": [] - | }, - | "error": null, - | "vars": [ - | { - | "name": "i", - | "type": "Invocation", - | "value": { - | "originCaller": { - | "type": "Address", - | "value": { - | "bytes": { - | "type": "ByteVector", - | "value": "$defaultAddress" - | } - | } - | }, - | "payments": { - | "type": "Array", - | "value": [] - | }, - | "callerPublicKey": { - | "type": "ByteVector", - | "value": "8h47fXqSctZ6sb3q6Sst9qH1UNzR5fjez2eEP6BvEfcr" - | }, - | "feeAssetId": { - | "type": "Unit", - | "value": {} - | }, - | "originCallerPublicKey": { - | "type": "ByteVector", - | "value": "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" - | }, - | "transactionId": { - | "type": "ByteVector", - | "value": "${invoke.id()}" - | }, - | "caller": { - | "type": "Address", - | "value": { - | "bytes": { - | "type": "ByteVector", - | "value": "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC" - | } - | } - | }, - | "fee": { - | "type": "Int", - | "value": 500000 - | } - | } - | }, - | { - | "name": "default.@args", - | "type": "Array", - | "value": [] - | }, - | { - | "name": "Address.@args", - | "type": "Array", - | "value": [ - | { - | "type": "ByteVector", - | "value": "$leaseAddress" - | } - | ] - | }, - | { - | "name": "Address.@complexity", - | "type": "Int", - | "value": 1 - | }, - | { - | "name": "@complexityLimit", - | "type": "Int", - | "value": 51923 - | }, - | { - | "name": "Lease.@args", - | "type": "Array", - | "value": [ - | { - | "type": "Address", - | "value": { - | "bytes": { - | "type": "ByteVector", - | "value": "$leaseAddress" - | } - | } - | }, - | { - | "type": "Int", - | "value": $amount - | } - | ] - | }, - | { - | "name": "Lease.@complexity", - | "type": "Int", - | "value": 1 - | }, - | { - | "name": "@complexityLimit", - | "type": "Int", - | "value": 51922 - | }, - | { - | "name": "lease", - | "type": "Lease", - | "value": { - | "recipient": { - | "type": "Address", - | "value": { - | "bytes": { - | "type": "ByteVector", - | "value": "$leaseAddress" - | } - | } - | }, - | "amount": { - | "type": "Int", - | "value": $amount - | }, - | "nonce": { - | "type": "Int", - | "value": 0 - | } - | } - | }, - | { - | "name": "calculateLeaseId.@args", - | "type": "Array", - | "value": [ - | { - | "type": "Lease", - | "value": { - | "recipient": { - | "type": "Address", - | "value": { - | "bytes": { - | "type": "ByteVector", - | "value": "$leaseAddress" - | } - | } - | }, - | "amount": { - | "type": "Int", - | "value": $amount - | }, - | "nonce": { - | "type": "Int", - | "value": 0 - | } - | } - | } - | ] - | }, - | { - | "name": "calculateLeaseId.@complexity", - | "type": "Int", - | "value": 1 - | }, - | { - | "name": "@complexityLimit", - | "type": "Int", - | "value": 51921 - | }, - | { - | "name": "LeaseCancel.@args", - | "type": "Array", - | "value": [ - | { - | "type": "ByteVector", - | "value": "$leaseId" - | } - | ] - | }, - | { - | "name": "cancel1", - | "type": "LeaseCancel", - | "value": { - | "leaseId": { - | "type": "ByteVector", - | "value": "$leaseId" - | } - | } - | }, - | { - | "name": "LeaseCancel.@complexity", - | "type": "Int", - | "value": 1 - | }, - | { - | "name": "@complexityLimit", - | "type": "Int", - | "value": 51920 - | }, - | { - | "name" : "LeaseCancel.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "ByteVector", - | "value" : "${leaseTx.id()}" - | } ] - | }, { - | "name" : "cancel2", - | "type" : "LeaseCancel", - | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "${leaseTx.id()}" - | } - | } - | }, { - | "name" : "LeaseCancel.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51919 - | }, - | { - | "name": "cons.@args", - | "type": "Array", - | "value": [ - | { - | "type": "LeaseCancel", - | "value": { - | "leaseId": { - | "type": "ByteVector", - | "value": "${leaseTx.id()}" - | } - | } - | }, - | { - | "type": "Array", - | "value": [] - | } - | ] - | }, - | { - | "name": "cons.@complexity", - | "type": "Int", - | "value": 1 - | }, - | { - | "name": "@complexityLimit", - | "type": "Int", - | "value": 51918 - | }, { - | "name" : "cons.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "LeaseCancel", - | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "$leaseId" - | } - | } - | }, { - | "type" : "Array", - | "value" : [ { - | "type" : "LeaseCancel", - | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "${leaseTx.id()}" - | } - | } - | } ] - | } ] - | }, { - | "name" : "cons.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51917 - | }, { - | "name": "cons.@args", - | "type": "Array", - | "value": [ - | { - | "type": "Lease", - | "value": { - | "recipient": { - | "type": "Address", - | "value": { - | "bytes": { - | "type": "ByteVector", - | "value": "$leaseAddress" - | } - | } - | }, - | "amount": { - | "type": "Int", - | "value": $amount - | }, - | "nonce": { - | "type": "Int", - | "value": 0 - | } - | } - | }, { - | "type" : "Array", - | "value" : [ { - | "type" : "LeaseCancel", - | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "$leaseId" - | } - | } - | }, { - | "type" : "LeaseCancel", - | "value" : { - | "leaseId" : { - | "type" : "ByteVector", - | "value" : "${leaseTx.id()}" - | } - | } - | } ] - | } ] - | }, { - | "name": "cons.@complexity", - | "type": "Int", - | "value": 1 - | }, - | { - | "name": "@complexityLimit", - | "type": "Int", - | "value": 51916 - | } - | ] - | } - | ], - | "result": "failure", - | "error": "InvokeRejectError(error = Explicit script termination)", - | "vars" : [ { - | "name" : "i", - | "type" : "Invocation", - | "value" : { - | "originCaller" : { - | "type" : "Address", - | "value" : { - | "bytes" : { - | "type" : "ByteVector", - | "value" : "$defaultAddress" - | } - | } - | }, - | "payments" : { - | "type" : "Array", - | "value" : [ ] - | }, - | "callerPublicKey" : { - | "type" : "ByteVector", - | "value" : "${defaultSigner.publicKey}" - | }, - | "feeAssetId" : { - | "type" : "Unit", - | "value" : { } - | }, - | "originCallerPublicKey" : { - | "type" : "ByteVector", - | "value" : "${defaultSigner.publicKey}" - | }, - | "transactionId" : { - | "type" : "ByteVector", - | "value" : "${invoke.id()}" - | }, - | "caller" : { - | "type" : "Address", - | "value" : { - | "bytes" : { - | "type" : "ByteVector", - | "value" : "$defaultAddress" - | } - | } - | }, - | "fee" : { - | "type" : "Int", - | "value" : 500000 - | } - | } - | }, { - | "name" : "default.@args", - | "type" : "Array", - | "value" : [ ] - | }, { - | "name" : "Address.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "ByteVector", - | "value" : "3MsY23LPQnvPZnBKpvs6YcnCvGjLVD42pSy" - | } ] - | }, { - | "name" : "Address.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51999 - | }, { - | "name" : "invoke.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "Address", - | "value" : { - | "bytes" : { - | "type" : "ByteVector", - | "value" : "3MsY23LPQnvPZnBKpvs6YcnCvGjLVD42pSy" - | } - | } - | }, { - | "type" : "String", - | "value" : "default" - | }, { - | "type" : "Array", - | "value" : [ ] - | }, { - | "type" : "Array", - | "value" : [ ] - | } ] - | }, { - | "name" : "invoke.@complexity", - | "type" : "Int", - | "value" : 75 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51924 - | }, { - | "name" : "default.@complexity", - | "type" : "Int", - | "value" : 8 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51916 - | }, { - | "name" : "r", - | "type" : "Unit", - | "value" : { } - | }, { - | "name" : "==.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "Unit", - | "value" : { } - | }, { - | "type" : "Unit", - | "value" : { } - | } ] - | }, { - | "name" : "==.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51915 - | }, { - | "name" : "throw.@args", - | "type" : "Array", - | "value" : [ ] - | }, { - | "name" : "throw.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51914 - | }, { - | "name" : "throw.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "String", - | "value" : "Explicit script termination" - | } ] - | }, { - | "name" : "throw.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51913 - | } ] - | } - | ], - | "height": 3, - | "error": "Error while executing dApp: Explicit script termination", - | "transaction": { - | "type": 16, - | "id": "${invoke.id()}", - | "fee": 500000, - | "feeAssetId": null, - | "timestamp": ${(json \ "transaction" \ "timestamp").as[Long]}, - | "version": 2, - | "chainId": 84, - | "sender": "$defaultAddress", - | "senderPublicKey": "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ", - | "proofs": [ "${(json \ "transaction" \ "proofs" \ 0).as[String]}" ], - | "dApp": "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", - | "payment": [], - | "call": { - | "function": "default", - | "args": [] - | } - | } - |} + Post(routePath("/validate"), HttpEntity(ContentTypes.`application/json`, invoke.json().toString())) ~> route ~> check { + val json = responseAs[JsValue] + json should matchJson( + s""" + |{ + | "valid": false, + | "validationTime": ${(json \ "validationTime").as[Int]}, + | "trace": [ + | { + | "type": "dApp", + | "id": "3N79VJyPMGWTf1mgfqUZw3Ce6GKoERBoc6Y", + | "function": "default", + | "args": [], + | "invocations": [ + | { + | "type": "dApp", + | "id": "3NCMNUhdTDxGv1Q21ZC6Kuk6hGuPNFoMvmh", + | "function": "default", + | "args": [], + | "invocations": [], + | "result": { + | "data": [], + | "transfers": [], + | "issues": [], + | "reissues": [], + | "burns": [], + | "sponsorFees": [], + | "leases": [ + | { + | "id": "$leaseId", + | "originTransactionId": null, + | "sender": null, + | "recipient": "$leaseAddress", + | "amount": $amount, + | "height": null, + | "status": "canceled", + | "cancelHeight": null, + | "cancelTransactionId": null + | } + | ], + | "leaseCancels": [ + | { + | "id": "$leaseId", + | "originTransactionId": null, + | "sender": null, + | "recipient": null, + | "amount": null, + | "height": null, + | "status": "canceled", + | "cancelHeight": null, + | "cancelTransactionId": null + | }, { + | "id" : "${leaseTx.id()}", + | "originTransactionId" : "${leaseTx.id()}", + | "sender" : "${dApp2Kp.toAddress}", + | "recipient" : "$leaseAddress", + | "amount" : ${leaseTx.amount}, + | "height" : $leaseTxHeight, + | "status" : "active", + | "cancelHeight" : null, + | "cancelTransactionId" : null + | } + | + | ], + | "invokes": [] + | }, + | "error": null, + | "vars": [ + | { + | "name": "i", + | "type": "Invocation", + | "value": { + | "originCaller": { + | "type": "Address", + | "value": { + | "bytes": { + | "type": "ByteVector", + | "value": "$defaultAddress" + | } + | } + | }, + | "payments": { + | "type": "Array", + | "value": [] + | }, + | "callerPublicKey": { + | "type": "ByteVector", + | "value": "FAGx8acozxdhVGLpKzZy2RqNsoPEjZUQu4zvizNzbZyX" + | }, + | "feeAssetId": { + | "type": "Unit", + | "value": {} + | }, + | "originCallerPublicKey": { + | "type": "ByteVector", + | "value": "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | }, + | "transactionId": { + | "type": "ByteVector", + | "value": "${invoke.id()}" + | }, + | "caller": { + | "type": "Address", + | "value": { + | "bytes": { + | "type": "ByteVector", + | "value": "3N79VJyPMGWTf1mgfqUZw3Ce6GKoERBoc6Y" + | } + | } + | }, + | "fee": { + | "type": "Int", + | "value": 500000 + | } + | } + | }, + | { + | "name": "default.@args", + | "type": "Array", + | "value": [] + | }, + | { + | "name": "Address.@args", + | "type": "Array", + | "value": [ + | { + | "type": "ByteVector", + | "value": "$leaseAddress" + | } + | ] + | }, + | { + | "name": "Address.@complexity", + | "type": "Int", + | "value": 1 + | }, + | { + | "name": "@complexityLimit", + | "type": "Int", + | "value": 51923 + | }, + | { + | "name": "Lease.@args", + | "type": "Array", + | "value": [ + | { + | "type": "Address", + | "value": { + | "bytes": { + | "type": "ByteVector", + | "value": "$leaseAddress" + | } + | } + | }, + | { + | "type": "Int", + | "value": $amount + | } + | ] + | }, + | { + | "name": "Lease.@complexity", + | "type": "Int", + | "value": 1 + | }, + | { + | "name": "@complexityLimit", + | "type": "Int", + | "value": 51922 + | }, + | { + | "name": "lease", + | "type": "Lease", + | "value": { + | "recipient": { + | "type": "Address", + | "value": { + | "bytes": { + | "type": "ByteVector", + | "value": "$leaseAddress" + | } + | } + | }, + | "amount": { + | "type": "Int", + | "value": $amount + | }, + | "nonce": { + | "type": "Int", + | "value": 0 + | } + | } + | }, + | { + | "name": "calculateLeaseId.@args", + | "type": "Array", + | "value": [ + | { + | "type": "Lease", + | "value": { + | "recipient": { + | "type": "Address", + | "value": { + | "bytes": { + | "type": "ByteVector", + | "value": "$leaseAddress" + | } + | } + | }, + | "amount": { + | "type": "Int", + | "value": $amount + | }, + | "nonce": { + | "type": "Int", + | "value": 0 + | } + | } + | } + | ] + | }, + | { + | "name": "calculateLeaseId.@complexity", + | "type": "Int", + | "value": 1 + | }, + | { + | "name": "@complexityLimit", + | "type": "Int", + | "value": 51921 + | }, + | { + | "name": "LeaseCancel.@args", + | "type": "Array", + | "value": [ + | { + | "type": "ByteVector", + | "value": "$leaseId" + | } + | ] + | }, + | { + | "name": "cancel1", + | "type": "LeaseCancel", + | "value": { + | "leaseId": { + | "type": "ByteVector", + | "value": "$leaseId" + | } + | } + | }, + | { + | "name": "LeaseCancel.@complexity", + | "type": "Int", + | "value": 1 + | }, + | { + | "name": "@complexityLimit", + | "type": "Int", + | "value": 51920 + | }, + | { + | "name" : "LeaseCancel.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "ByteVector", + | "value" : "${leaseTx.id()}" + | } ] + | }, { + | "name" : "cancel2", + | "type" : "LeaseCancel", + | "value" : { + | "leaseId" : { + | "type" : "ByteVector", + | "value" : "${leaseTx.id()}" + | } + | } + | }, { + | "name" : "LeaseCancel.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51919 + | }, + | { + | "name": "cons.@args", + | "type": "Array", + | "value": [ + | { + | "type": "LeaseCancel", + | "value": { + | "leaseId": { + | "type": "ByteVector", + | "value": "${leaseTx.id()}" + | } + | } + | }, + | { + | "type": "Array", + | "value": [] + | } + | ] + | }, + | { + | "name": "cons.@complexity", + | "type": "Int", + | "value": 1 + | }, + | { + | "name": "@complexityLimit", + | "type": "Int", + | "value": 51918 + | }, { + | "name" : "cons.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "LeaseCancel", + | "value" : { + | "leaseId" : { + | "type" : "ByteVector", + | "value" : "$leaseId" + | } + | } + | }, { + | "type" : "Array", + | "value" : [ { + | "type" : "LeaseCancel", + | "value" : { + | "leaseId" : { + | "type" : "ByteVector", + | "value" : "${leaseTx.id()}" + | } + | } + | } ] + | } ] + | }, { + | "name" : "cons.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51917 + | }, { + | "name": "cons.@args", + | "type": "Array", + | "value": [ + | { + | "type": "Lease", + | "value": { + | "recipient": { + | "type": "Address", + | "value": { + | "bytes": { + | "type": "ByteVector", + | "value": "$leaseAddress" + | } + | } + | }, + | "amount": { + | "type": "Int", + | "value": $amount + | }, + | "nonce": { + | "type": "Int", + | "value": 0 + | } + | } + | }, { + | "type" : "Array", + | "value" : [ { + | "type" : "LeaseCancel", + | "value" : { + | "leaseId" : { + | "type" : "ByteVector", + | "value" : "$leaseId" + | } + | } + | }, { + | "type" : "LeaseCancel", + | "value" : { + | "leaseId" : { + | "type" : "ByteVector", + | "value" : "${leaseTx.id()}" + | } + | } + | } ] + | } ] + | }, { + | "name": "cons.@complexity", + | "type": "Int", + | "value": 1 + | }, + | { + | "name": "@complexityLimit", + | "type": "Int", + | "value": 51916 + | } + | ] + | } + | ], + | "result": "failure", + | "error": "InvokeRejectError(error = Explicit script termination)", + | "vars" : [ { + | "name" : "i", + | "type" : "Invocation", + | "value" : { + | "originCaller" : { + | "type" : "Address", + | "value" : { + | "bytes" : { + | "type" : "ByteVector", + | "value" : "$defaultAddress" + | } + | } + | }, + | "payments" : { + | "type" : "Array", + | "value" : [ ] + | }, + | "callerPublicKey" : { + | "type" : "ByteVector", + | "value" : "${defaultSigner.publicKey}" + | }, + | "feeAssetId" : { + | "type" : "Unit", + | "value" : { } + | }, + | "originCallerPublicKey" : { + | "type" : "ByteVector", + | "value" : "${defaultSigner.publicKey}" + | }, + | "transactionId" : { + | "type" : "ByteVector", + | "value" : "${invoke.id()}" + | }, + | "caller" : { + | "type" : "Address", + | "value" : { + | "bytes" : { + | "type" : "ByteVector", + | "value" : "$defaultAddress" + | } + | } + | }, + | "fee" : { + | "type" : "Int", + | "value" : 500000 + | } + | } + | }, { + | "name" : "default.@args", + | "type" : "Array", + | "value" : [ ] + | }, { + | "name" : "Address.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "ByteVector", + | "value" : "3NCMNUhdTDxGv1Q21ZC6Kuk6hGuPNFoMvmh" + | } ] + | }, { + | "name" : "Address.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51999 + | }, { + | "name" : "invoke.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "Address", + | "value" : { + | "bytes" : { + | "type" : "ByteVector", + | "value" : "3NCMNUhdTDxGv1Q21ZC6Kuk6hGuPNFoMvmh" + | } + | } + | }, { + | "type" : "String", + | "value" : "default" + | }, { + | "type" : "Array", + | "value" : [ ] + | }, { + | "type" : "Array", + | "value" : [ ] + | } ] + | }, { + | "name" : "invoke.@complexity", + | "type" : "Int", + | "value" : 75 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51924 + | }, { + | "name" : "default.@complexity", + | "type" : "Int", + | "value" : 8 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51916 + | }, { + | "name" : "r", + | "type" : "Unit", + | "value" : { } + | }, { + | "name" : "==.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "Unit", + | "value" : { } + | }, { + | "type" : "Unit", + | "value" : { } + | } ] + | }, { + | "name" : "==.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51915 + | }, { + | "name" : "throw.@args", + | "type" : "Array", + | "value" : [ ] + | }, { + | "name" : "throw.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51914 + | }, { + | "name" : "throw.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "String", + | "value" : "Explicit script termination" + | } ] + | }, { + | "name" : "throw.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51913 + | } ] + | } + | ], + | "height": ${domain.blockchain.height}, + | "error": "Error while executing dApp: Explicit script termination", + | "transaction": { + | "type": 16, + | "id": "${invoke.id()}", + | "fee": 500000, + | "feeAssetId": null, + | "timestamp": ${invoke.timestamp}, + | "version": 2, + | "chainId": 84, + | "sender": "${invoke.senderAddress}", + | "senderPublicKey": "${invoke.sender}", + | "proofs": [ "${(json \ "transaction" \ "proofs" \ 0).as[String]}" ], + | "dApp": "3N79VJyPMGWTf1mgfqUZw3Ce6GKoERBoc6Y", + | "payment": [], + | "call": { + | "function": "default", + | "args": [] + | } + | } + |} """.stripMargin - ) - } + ) } } "invoke tx with nested call" in { - val dAppPk = TxHelpers.defaultSigner.publicKey - val dAppAddress = dAppPk.toAddress - val invoke = TxHelpers.invoke(dAppPk.toAddress, func = Some("test1")) - - val blockchain = createBlockchainStub { blockchain => - (blockchain.balance _).when(*, *).returns(Long.MaxValue) - (blockchain.wavesBalances _).when(*).returns(Map(TxHelpers.defaultAddress -> Long.MaxValue)) - blockchain.stub.activateAllFeatures() - - val (dAppScript, _) = ScriptCompiler - .compile( - s""" - |{-# STDLIB_VERSION 5 #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - |{-# CONTENT_TYPE DAPP #-} - | - |@Callable(i) - |func test() = { - | strict a = parseBigIntValue("${PureContext.BigIntMax}") - | let test = 1 - | if (test == 1) - | then [IntegerEntry("key", 1)] - | else [] - |} - | - |@Callable(i) - |func test1() = { - | strict result = reentrantInvoke(this, "test", [], []) - | if (result == unit) then [] else [] - |} - |""".stripMargin, - ScriptEstimatorV3(fixOverflow = true, overhead = true) - ) - .explicitGet() - - (blockchain.accountScript _) - .when(*) - .returns( - Some( - AccountScriptInfo( - dAppPk, - dAppScript, - 0L, - Map(3 -> Seq("test", "test1").map(_ -> 0L).toMap) - ) - ) - ) - - (blockchain.hasAccountScript _).when(dAppAddress).returns(true) - } - val route = debugApiRoute - .copy( - blockchain = blockchain, - priorityPoolBlockchain = () => Some(blockchain) - ) - .route + val dAppPk = TxHelpers.signer(1050) + val caller = TxHelpers.signer(1051) + val invoke = TxHelpers.invoke(dAppPk.toAddress, func = Some("test1"), invoker = caller) + + val dAppScript = TestCompiler(V5).compileContract( + s"""@Callable(i) + |func test() = { + | strict a = parseBigIntValue("${PureContext.BigIntMax}") + | let test = 1 + | if (test == 1) + | then [IntegerEntry("key", 1)] + | else [] + |} + | + |@Callable(i) + |func test1() = { + | strict result = reentrantInvoke(this, "test", [], []) + | if (result == unit) then [] else [] + |} + |""".stripMargin + ) + domain.appendBlock( + TxHelpers.transfer(richAccount, dAppPk.toAddress, 10.waves), + TxHelpers.transfer(richAccount, caller.toAddress, 10.waves), + TxHelpers.setScript(dAppPk, dAppScript), + TxHelpers.setScript(caller, TestCompiler(V5).compileExpression("true")) + ) Post(routePath("/validate"), HttpEntity(ContentTypes.`application/json`, invoke.json().toString())) ~> route ~> check { val json = responseAs[JsValue] @@ -2850,7 +2651,7 @@ class DebugApiRouteSpec | "leases" : [ ], | "leaseCancels" : [ ], | "invokes" : [ { - | "dApp" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "dApp" : "${dAppPk.toAddress}", | "call" : { | "function" : "test", | "args" : [ ] @@ -2878,17 +2679,17 @@ class DebugApiRouteSpec s""" |[ { | "type" : "verifier", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${caller.toAddress}", | "result" : "success", | "error" : null |}, { | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${dAppPk.toAddress}", | "function" : "test1", | "args" : [ ], | "invocations" : [ { | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", + | "id" : "${dAppPk.toAddress}", | "function" : "test", | "args" : [ ], | "invocations" : [ ], @@ -2917,7 +2718,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${caller.toAddress}" | } | } | }, @@ -2927,7 +2728,7 @@ class DebugApiRouteSpec | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${dAppPk.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -2935,7 +2736,7 @@ class DebugApiRouteSpec | }, | "originCallerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${caller.publicKey}" | }, | "transactionId" : { | "type" : "ByteVector", @@ -2946,7 +2747,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${dAppPk.toAddress}" | } | } | }, @@ -3086,7 +2887,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${caller.toAddress}" | } | } | }, @@ -3096,7 +2897,7 @@ class DebugApiRouteSpec | }, | "callerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${caller.publicKey}" | }, | "feeAssetId" : { | "type" : "Unit", @@ -3104,7 +2905,7 @@ class DebugApiRouteSpec | }, | "originCallerPublicKey" : { | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" + | "value" : "${caller.publicKey}" | }, | "transactionId" : { | "type" : "ByteVector", @@ -3115,7 +2916,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${caller.toAddress}" | } | } | }, @@ -3136,7 +2937,7 @@ class DebugApiRouteSpec | "value" : { | "bytes" : { | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" + | "value" : "${dAppPk.toAddress}" | } | } | }, { @@ -3209,53 +3010,30 @@ class DebugApiRouteSpec |} ] """.stripMargin ) - (json \ "height").as[Int] shouldBe 1 + (json \ "height").as[Int] shouldBe domain.blockchain.height } } "transfer transaction with asset fail" in { - val blockchain = createBlockchainStub { blockchain => - (blockchain.balance _).when(*, *).returns(Long.MaxValue / 2) - (blockchain.wavesBalances _).when(*).returns(Map(TxHelpers.defaultAddress -> Long.MaxValue / 2)) - - val (assetScript, assetScriptComplexity) = - ScriptCompiler.compile("false", ScriptEstimatorV3(fixOverflow = true, overhead = true)).explicitGet() - (blockchain.assetScript _).when(TestValues.asset).returns(Some(AssetScriptInfo(assetScript, assetScriptComplexity))) - (blockchain.assetDescription _) - .when(TestValues.asset) - .returns( - Some( - AssetDescription( - TestValues.asset.id, - TxHelpers.defaultSigner.publicKey, - null, - null, - 0, - reissuable = true, - BigInt(1), - Height(1), - Some(AssetScriptInfo(assetScript, assetScriptComplexity)), - 0, - nft = false, - 0, - Height(1) - ) - ) - ) - } - val route = routeWithBlockchain(blockchain) - val tx = TxHelpers.transfer(TxHelpers.defaultSigner, TxHelpers.defaultAddress, 1, TestValues.asset) + val assetOwner = TxHelpers.signer(1060) + val issue = TxHelpers.issue(assetOwner, script = Some(TestCompiler(V5).compileAsset("false"))) + domain.appendBlock( + TxHelpers.transfer(richAccount, assetOwner.toAddress, 5.waves), + issue + ) + + val tx = TxHelpers.transfer(assetOwner, TxHelpers.defaultAddress, 1, issue.asset) jsonPost(routePath("/validate"), tx.json()) ~> route ~> check { val json = responseAs[JsObject] - (json \ "trace").as[JsArray] should matchJson("""[ { - | "type" : "asset", - | "context" : "transfer", - | "id" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", - | "result" : "failure", - | "vars" : [ ], - | "error" : null - | } ]""".stripMargin) + (json \ "trace").as[JsArray] should matchJson(s"""[ { + | "type" : "asset", + | "context" : "transfer", + | "id" : "${issue.id()}", + | "result" : "failure", + | "vars" : [ ], + | "error" : null + | } ]""".stripMargin) (json \ "valid").as[Boolean] shouldBe false (json \ "transaction").as[JsObject] shouldBe tx.json() @@ -3263,52 +3041,50 @@ class DebugApiRouteSpec } "txs with empty and small verifier" in { - val blockchain = createBlockchainStub { blockchain => - val settings = TestFunctionalitySettings.Enabled.copy( - featureCheckBlocksPeriod = 1, - blocksForFeatureActivation = 1, - preActivatedFeatures = Map( - BlockchainFeatures.SmartAccounts.id -> 0, - BlockchainFeatures.SmartAssets.id -> 0, - BlockchainFeatures.Ride4DApps.id -> 0, - BlockchainFeatures.FeeSponsorship.id -> 0, - BlockchainFeatures.DataTransaction.id -> 0, - BlockchainFeatures.BlockReward.id -> 0, - BlockchainFeatures.BlockV5.id -> 0, - BlockchainFeatures.SynchronousCalls.id -> 0 + val transferFee = 100000 + + val acc1 = TxHelpers.signer(1070) + val acc2 = TxHelpers.signer(1071) + val acc3 = TxHelpers.signer(1072) + + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + acc1.toAddress -> 2.waves, + acc2.toAddress -> 2.waves, + acc3.toAddress -> 2.waves + ), + fee = 0.003.waves + ), + TxHelpers.setScript( + acc2, + TestCompiler(V5).compileExpression( + "sigVerify(base58'', base58'', base58'') && sigVerify(base58'', base58'', base58'') && false" + ) + ), + TxHelpers.setScript( + acc3, + TestCompiler(V5).compileExpression( + "sigVerify_16Kb(tx.bodyBytes, tx.proofs[0], tx.senderPublicKey)" ) ) - (() => blockchain.settings).when().returns(WavesSettings.default().blockchainSettings.copy(functionalitySettings = settings)) - (() => blockchain.activatedFeatures).when().returns(settings.preActivatedFeatures) - (blockchain.balance _).when(*, *).returns(ENOUGH_AMT) - (blockchain.wavesBalances _) - .when(*) - .returns(Map(TxHelpers.defaultAddress -> ENOUGH_AMT, TxHelpers.secondAddress -> ENOUGH_AMT, TxHelpers.address(3) -> ENOUGH_AMT)) - - val script = ExprScript(TRUE).explicitGet() - def info(complexity: Int) = Some(AccountScriptInfo(TxHelpers.secondSigner.publicKey, script, complexity)) - - (blockchain.accountScript _).when(TxHelpers.defaultSigner.toAddress).returns(info(199)) - (blockchain.accountScript _).when(TxHelpers.secondSigner.toAddress).returns(info(201)) - (blockchain.accountScript _).when(TxHelpers.signer(3).toAddress).returns(None) - } - val route = routeWithBlockchain(blockchain) - val transferFee = 100000 + ) - val tx = TxHelpers.transfer(TxHelpers.defaultSigner, TxHelpers.secondSigner.toAddress, 1.waves, fee = transferFee, version = TxVersion.V2) + val tx = TxHelpers.transfer(acc1, TxHelpers.secondSigner.toAddress, 1.waves, fee = transferFee, version = TxVersion.V2) validatePost(tx) ~> route ~> check { val json = responseAs[JsValue] (json \ "valid").as[Boolean] shouldBe true } - val tx2 = TxHelpers.transfer(TxHelpers.secondSigner, TestValues.address, 1.waves, fee = transferFee, version = TxVersion.V2) + val tx2 = TxHelpers.transfer(acc2, TestValues.address, 1.waves, fee = transferFee, version = TxVersion.V2) validatePost(tx2) ~> route ~> check { val json = responseAs[JsValue] (json \ "valid").as[Boolean] shouldBe false (json \ "error").as[String] should include("Requires 400000 extra fee") } - val tx3 = TxHelpers.transfer(TxHelpers.signer(3), TestValues.address, 1.waves, fee = transferFee, version = TxVersion.V2) + val tx3 = TxHelpers.transfer(acc3, TestValues.address, 1.waves, fee = transferFee, version = TxVersion.V2) validatePost(tx3) ~> route ~> check { val json = responseAs[JsValue] (json \ "valid").as[Boolean] shouldBe true @@ -3316,178 +3092,166 @@ class DebugApiRouteSpec } "InvokeExpression" in { - def assert(wavesSettings: WavesSettings): Assertion = { - val blockchain = createBlockchainStub { blockchain => - val settings = wavesSettings.blockchainSettings.functionalitySettings - (() => blockchain.settings).when().returns(WavesSettings.default().blockchainSettings.copy(functionalitySettings = settings)) - (() => blockchain.activatedFeatures).when().returns(settings.preActivatedFeatures) - (blockchain.balance _).when(*, *).returns(ENOUGH_AMT) - (blockchain.accountScript _).when(*).returns(None) - (blockchain.assetScript _).when(*).returns(None) - (blockchain.assetDescription _).when(TestValues.asset).returns(Some(TestValues.assetDescription)) - } - val route = routeWithBlockchain(blockchain) - - val expression = TestCompiler(V6).compileFreeCall( - s""" - | let assetId = base58'${TestValues.asset}' - | [ Reissue(assetId, 1, true) ] + val sender = TxHelpers.signer(1080) + val issue = TxHelpers.issue(sender) + val expression = TestCompiler(V6).compileFreeCall( + s""" + | let assetId = base58'${issue.asset}' + | [ Reissue(assetId, 1, true) ] """.stripMargin - ) - val invokeExpression = TxHelpers.invokeExpression(expression) - jsonPost(routePath("/validate"), invokeExpression.json()) ~> route ~> check { - val json = responseAs[JsValue] - (json \ "expression").as[String] shouldBe expression.bytes.value().base64 - (json \ "valid").as[Boolean] shouldBe true - (json \ "trace").as[JsArray] should matchJson( - s""" - |[ { - | "type" : "dApp", - | "id" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", - | "function" : "default", - | "args" : [ ], - | "invocations" : [ ], - | "result" : { - | "data" : [ ], - | "transfers" : [ ], - | "issues" : [ ], - | "reissues" : [ { - | "assetId" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx", - | "isReissuable" : true, - | "quantity" : 1 - | } ], - | "burns" : [ ], - | "sponsorFees" : [ ], - | "leases" : [ ], - | "leaseCancels" : [ ], - | "invokes" : [ ] - | }, - | "error" : null, - | "vars" : [ { - | "name" : "i", - | "type" : "Invocation", - | "value" : { - | "originCaller" : { - | "type" : "Address", - | "value" : { - | "bytes" : { - | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" - | } - | } - | }, - | "payments" : { - | "type" : "Array", - | "value" : [ ] - | }, - | "callerPublicKey" : { - | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" - | }, - | "feeAssetId" : { - | "type" : "Unit", - | "value" : { } - | }, - | "originCallerPublicKey" : { - | "type" : "ByteVector", - | "value" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ" - | }, - | "transactionId" : { - | "type" : "ByteVector", - | "value" : "${invokeExpression.id()}" - | }, - | "caller" : { - | "type" : "Address", - | "value" : { - | "bytes" : { - | "type" : "ByteVector", - | "value" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9" - | } - | } - | }, - | "fee" : { - | "type" : "Int", - | "value" : 1000000 - | } - | } - | }, { - | "name" : "default.@args", - | "type" : "Array", - | "value" : [ ] - | }, { - | "name" : "assetId", - | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" - | }, { - | "name" : "Reissue.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" - | }, { - | "type" : "Int", - | "value" : 1 - | }, { - | "type" : "Boolean", - | "value" : true - | } ] - | }, { - | "name" : "Reissue.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51999 - | }, { - | "name" : "cons.@args", - | "type" : "Array", - | "value" : [ { - | "type" : "Reissue", - | "value" : { - | "assetId" : { - | "type" : "ByteVector", - | "value" : "5PjDJaGfSPJj4tFzMRCiuuAasKg5n8dJKXKenhuwZexx" - | }, - | "quantity" : { - | "type" : "Int", - | "value" : 1 - | }, - | "isReissuable" : { - | "type" : "Boolean", - | "value" : true - | } - | } - | }, { - | "type" : "Array", - | "value" : [ ] - | } ] - | }, { - | "name" : "cons.@complexity", - | "type" : "Int", - | "value" : 1 - | }, { - | "name" : "@complexityLimit", - | "type" : "Int", - | "value" : 51998 - | } ] - |} ] - | + ) + domain.appendBlock( + TxHelpers.transfer(richAccount, sender.toAddress, 2.waves), + issue + ) + val invokeExpression = TxHelpers.invokeExpression(expression, sender) + jsonPost(routePath("/validate"), invokeExpression.json()) ~> route ~> check { + val json = responseAs[JsValue] + (json \ "expression").as[String] shouldBe expression.bytes.value().base64 + (json \ "valid").as[Boolean] shouldBe true + (json \ "trace").as[JsArray] should matchJson( + s""" + |[ { + | "type" : "dApp", + | "id" : "${sender.toAddress}", + | "function" : "default", + | "args" : [ ], + | "invocations" : [ ], + | "result" : { + | "data" : [ ], + | "transfers" : [ ], + | "issues" : [ ], + | "reissues" : [ { + | "assetId" : "${issue.id()}", + | "isReissuable" : true, + | "quantity" : 1 + | } ], + | "burns" : [ ], + | "sponsorFees" : [ ], + | "leases" : [ ], + | "leaseCancels" : [ ], + | "invokes" : [ ] + | }, + | "error" : null, + | "vars" : [ { + | "name" : "i", + | "type" : "Invocation", + | "value" : { + | "originCaller" : { + | "type" : "Address", + | "value" : { + | "bytes" : { + | "type" : "ByteVector", + | "value" : "${sender.toAddress}" + | } + | } + | }, + | "payments" : { + | "type" : "Array", + | "value" : [ ] + | }, + | "callerPublicKey" : { + | "type" : "ByteVector", + | "value" : "${sender.publicKey}" + | }, + | "feeAssetId" : { + | "type" : "Unit", + | "value" : { } + | }, + | "originCallerPublicKey" : { + | "type" : "ByteVector", + | "value" : "${sender.publicKey}" + | }, + | "transactionId" : { + | "type" : "ByteVector", + | "value" : "${invokeExpression.id()}" + | }, + | "caller" : { + | "type" : "Address", + | "value" : { + | "bytes" : { + | "type" : "ByteVector", + | "value" : "${sender.toAddress}" + | } + | } + | }, + | "fee" : { + | "type" : "Int", + | "value" : 1000000 + | } + | } + | }, { + | "name" : "default.@args", + | "type" : "Array", + | "value" : [ ] + | }, { + | "name" : "assetId", + | "type" : "ByteVector", + | "value" : "${issue.id()}" + | }, { + | "name" : "Reissue.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "ByteVector", + | "value" : "${issue.id()}" + | }, { + | "type" : "Int", + | "value" : 1 + | }, { + | "type" : "Boolean", + | "value" : true + | } ] + | }, { + | "name" : "Reissue.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51999 + | }, { + | "name" : "cons.@args", + | "type" : "Array", + | "value" : [ { + | "type" : "Reissue", + | "value" : { + | "assetId" : { + | "type" : "ByteVector", + | "value" : "${issue.id()}" + | }, + | "quantity" : { + | "type" : "Int", + | "value" : 1 + | }, + | "isReissuable" : { + | "type" : "Boolean", + | "value" : true + | } + | } + | }, { + | "type" : "Array", + | "value" : [ ] + | } ] + | }, { + | "name" : "cons.@complexity", + | "type" : "Int", + | "value" : 1 + | }, { + | "name" : "@complexityLimit", + | "type" : "Int", + | "value" : 51998 + | } ] + |} ] + | """.stripMargin - ) - } + ) } - - assert(ContinuationTransaction) - intercept[Exception](assert(RideV6)).getMessage should include( - s"${BlockchainFeatures.ContinuationTransaction.description} feature has not been activated yet" - ) } } routePath("/minerInfo") - { "returns info from wallet if miner private keys not specified in config" in { - val acc = wallet.generateNewAccount() + val acc = domain.wallet.generateNewAccount() acc shouldBe defined Get(routePath("/minerInfo")) ~> ApiKeyHeader ~> route ~> check { @@ -3496,7 +3260,7 @@ class DebugApiRouteSpec } "returns info only for miner private keys from config when specified" in { - val minerAccs = Seq(TxHelpers.signer(1), TxHelpers.signer(2)) + val minerAccs = Seq(TxHelpers.signer(1090), TxHelpers.signer(1091)) val minerConfig = debugApiRoute.ws.minerSettings.copy(privateKeys = minerAccs.map(_.privateKey)) val debugRoute = debugApiRoute.copy(ws = debugApiRoute.ws.copy(minerSettings = minerConfig)) @@ -3506,19 +3270,6 @@ class DebugApiRouteSpec } } - private def routeWithBlockchain(blockchain: Blockchain & NG) = - debugApiRoute.copy(blockchain = blockchain, priorityPoolBlockchain = () => Some(blockchain)).route - - private def routeWithBlockchain(d: Domain) = - debugApiRoute - .copy( - blockchain = d.blockchain, - priorityPoolBlockchain = () => Some(d.blockchain), - loadBalanceHistory = d.rocksDBWriter.loadBalanceHistory, - loadStateHash = d.rocksDBWriter.loadStateHash - ) - .route - private[this] def jsonPost(path: String, json: JsValue) = { Post(path, HttpEntity(ContentTypes.`application/json`, json.toString())) } diff --git a/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala index 6d46bd41bf8..3cbbe708206 100644 --- a/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/LeaseRouteSpec.scala @@ -2,114 +2,94 @@ package com.wavesplatform.http import akka.http.scaladsl.model.{ContentTypes, FormData, HttpEntity} import akka.http.scaladsl.server.Route -import com.wavesplatform.NTPTime import com.wavesplatform.account.{Address, AddressOrAlias, KeyPair} import com.wavesplatform.api.common.CommonAccountsApi import com.wavesplatform.api.http.RouteTimeout import com.wavesplatform.api.http.leasing.LeaseApiRoute import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 +import com.wavesplatform.db.WithState import com.wavesplatform.db.WithState.AddrWithBalance -import com.wavesplatform.history.Domain import com.wavesplatform.lang.directives.values.{V5, V6} -import com.wavesplatform.lang.v1.FunctionHeader -import com.wavesplatform.lang.v1.compiler.Terms.{CONST_BYTESTR, CONST_LONG, FUNCTION_CALL} +import com.wavesplatform.lang.v1.compiler.Terms.{CONST_BYTESTR, CONST_LONG} import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.settings.WavesSettings import com.wavesplatform.state.TxMeta.Status -import com.wavesplatform.state.diffs.ENOUGH_AMT import com.wavesplatform.state.{BinaryDataEntry, Blockchain, LeaseDetails, LeaseStaticInfo} import com.wavesplatform.test.* -import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.transaction.EthTxGenerator.Arg -import com.wavesplatform.transaction.TxHelpers.{defaultSigner, secondSigner, signer} -import com.wavesplatform.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} -import com.wavesplatform.transaction.smart.SetScriptTransaction -import com.wavesplatform.transaction.smart.script.trace.TracedResult +import com.wavesplatform.transaction.TxHelpers.signer +import com.wavesplatform.transaction.lease.LeaseTransaction import com.wavesplatform.transaction.utils.EthConverters.* -import com.wavesplatform.transaction.utils.Signed -import com.wavesplatform.transaction.{Asset, Authorized, EthTxGenerator, Transaction, TxHelpers, TxPositiveAmount, TxVersion} +import com.wavesplatform.transaction.{Authorized, EthTxGenerator, Transaction, TxHelpers, TxPositiveAmount, TxVersion} import com.wavesplatform.utils.SharedSchedulerMixin -import org.scalacheck.Gen +import org.scalactic.source.Position +import org.scalatest.OptionValues import play.api.libs.json.{JsArray, JsObject, Json} -import scala.concurrent.Future import scala.concurrent.duration.* -class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper with NTPTime with SharedDomain with SharedSchedulerMixin { - private def route(domain: Domain) = - LeaseApiRoute( - restAPISettings, - domain.wallet, - domain.blockchain, - (_, _) => Future.successful(TracedResult(Right(true))), - ntpTime, - CommonAccountsApi(() => domain.blockchainUpdater.snapshotBlockchain, domain.rdb, domain.blockchain), - new RouteTimeout(60.seconds)(sharedScheduler) - ).route - - private def withRoute(balances: Seq[AddrWithBalance], settings: WavesSettings = mostRecent)(f: (Domain, Route) => Unit): Unit = - f(domain, route(domain)) +class LeaseRouteSpec extends RouteSpec("/leasing") with OptionValues with RestAPISettingsHelper with SharedDomain with SharedSchedulerMixin { + private val richAccount = TxHelpers.signer(200) + + override def genesisBalances: Seq[WithState.AddrWithBalance] = Seq(AddrWithBalance(richAccount.toAddress, 500_000.waves)) + override def settings: WavesSettings = DomainPresets.ContinuationTransaction + + private val route = + seal( + LeaseApiRoute( + restAPISettings, + domain.wallet, + domain.blockchain, + DummyTransactionPublisher.accepting, + ntpTime, + CommonAccountsApi(() => domain.blockchainUpdater.snapshotBlockchain, domain.rdb, domain.blockchain), + new RouteTimeout(60.seconds)(sharedScheduler) + ).route + ) + + private val leaseContract = TestCompiler(V5) + .compileContract(""" + |{-# STDLIB_VERSION 4 #-} + |{-# CONTENT_TYPE DAPP #-} + |{-# SCRIPT_TYPE ACCOUNT #-} + | + |@Callable(inv) + |func leaseTo(recipient: ByteVector, amount: Int) = { + | let lease = Lease(Address(recipient), amount) + | [ + | lease, + | BinaryEntry("leaseId", lease.calculateLeaseId()) + | ] + |} + | + |@Callable(inv) + |func cancelLease(id: ByteVector) = { + | [ + | LeaseCancel(id) + | ] + |} + |""".stripMargin) private def setScriptTransaction(sender: KeyPair) = - SetScriptTransaction - .selfSigned( - TxVersion.V2, - sender, - Some(TestCompiler(V5).compileContract(""" - |{-# STDLIB_VERSION 4 #-} - |{-# CONTENT_TYPE DAPP #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - | - |@Callable(inv) - |func leaseTo(recipient: ByteVector, amount: Int) = { - | let lease = Lease(Address(recipient), amount) - | [ - | lease, - | BinaryEntry("leaseId", lease.calculateLeaseId()) - | ] - |} - | - |@Callable(inv) - |func cancelLease(id: ByteVector) = { - | [ - | LeaseCancel(id) - | ] - |} - |""".stripMargin)), - 0.01.waves, - ntpTime.getTimestamp() - ) - .explicitGet() + TxHelpers.setScript(sender, leaseContract) private def invokeLeaseCancel(sender: KeyPair, leaseId: ByteStr) = - Signed.invokeScript( - TxVersion.V2, - sender, - sender.toAddress, - Some( - FUNCTION_CALL( - FunctionHeader.User("cancelLease"), - List(CONST_BYTESTR(leaseId).explicitGet()) - ) - ), - Seq.empty, - 0.005.waves, - Asset.Waves, - ntpTime.getTimestamp() - ) + TxHelpers.invoke(sender.toAddress, Some("cancelLease"), Seq(CONST_BYTESTR(leaseId).explicitGet())) private def leaseCancelTransaction(sender: KeyPair, leaseId: ByteStr) = - LeaseCancelTransaction.selfSigned(TxVersion.V3, sender, leaseId, 0.001.waves, ntpTime.getTimestamp()).explicitGet() + TxHelpers.leaseCancel(leaseId, sender, version = TxVersion.V3) private def checkDetails(id: ByteStr, details: LeaseDetails, json: JsObject): Unit = { (json \ "id").as[ByteStr] shouldEqual id (json \ "originTransactionId").as[ByteStr] shouldEqual details.sourceId (json \ "sender").as[String] shouldEqual details.sender.toAddress.toString - (json \ "amount").as[Long] shouldEqual details.amount + (json \ "amount").as[Long] shouldEqual details.amount.value } - private def checkActiveLeasesFor(address: AddressOrAlias, route: Route, expectedDetails: Seq[(ByteStr, LeaseDetails)]): Unit = + private def checkActiveLeasesFor(address: AddressOrAlias, route: Route, expectedDetails: Seq[(ByteStr, LeaseDetails)])(implicit + pos: Position + ): Unit = Get(routePath(s"/active/$address")) ~> route ~> check { val resp = responseAs[Seq[JsObject]] resp.size shouldEqual expectedDetails.size @@ -124,209 +104,189 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi LeaseDetails.Status.Active ) - private def leaseGen(sender: KeyPair, maxAmount: Long, timestamp: Long): Gen[LeaseTransaction] = - for { - fee <- smallFeeGen - recipient <- accountGen - amount <- Gen.chooseNum(1, (maxAmount - fee).max(1)) - version <- Gen.oneOf(1.toByte, 2.toByte, 3.toByte) - } yield LeaseTransaction.selfSigned(version, sender, recipient.toAddress, amount, fee, timestamp).explicitGet() - "returns active leases which were" - { - val sender = TxHelpers.signer(1) - val leaseTx = leaseGen(sender, ENOUGH_AMT, ntpTime.correctedTime()) - - "created and cancelled by Lease/LeaseCancel transactions" in forAll(leaseTx) { leaseTransaction => - withRoute(Seq(AddrWithBalance(sender.toAddress))) { (d, r) => - d.appendBlock(leaseTransaction) - val expectedDetails = Seq(leaseTransaction.id() -> toDetails(leaseTransaction, d.blockchain)) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(leaseTransaction.sender.toAddress, r, expectedDetails) - checkActiveLeasesFor(leaseTransaction.recipient, r, expectedDetails) + val transactionVersions = Table("lease transaction version", 1.toByte, 2.toByte, 3.toByte) + + "created and cancelled by Lease/LeaseCancel transactions" in { + val lessor = TxHelpers.signer(201) + val leaseRecipient = TxHelpers.address(202) + + domain.appendBlock(TxHelpers.transfer(richAccount, lessor.toAddress, 30.006.waves)) + + forAll(transactionVersions) { v => + val leaseTransaction = TxHelpers.lease(lessor, leaseRecipient, version = v) + val expectedDetails = Seq(leaseTransaction.id() -> toDetails(leaseTransaction, domain.blockchain)) + + domain.appendBlock(leaseTransaction) + + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(leaseTransaction.sender.toAddress, route, expectedDetails) + checkActiveLeasesFor(leaseTransaction.recipient, route, expectedDetails) } - d.appendMicroBlock(leaseCancelTransaction(sender, leaseTransaction.id())) + domain.appendMicroBlock(TxHelpers.leaseCancel(leaseTransaction.id(), lessor)) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(leaseTransaction.sender.toAddress, r, Seq.empty) - checkActiveLeasesFor(leaseTransaction.recipient, r, Seq.empty) + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(leaseTransaction.sender.toAddress, route, Seq.empty) + checkActiveLeasesFor(leaseTransaction.recipient, route, Seq.empty) } } } - "created by LeaseTransaction and canceled by InvokeScriptTransaction" in forAll(leaseTx) { leaseTransaction => - withRoute(Seq(AddrWithBalance(sender.toAddress))) { (d, r) => - d.appendBlock(leaseTransaction) - val expectedDetails = Seq(leaseTransaction.id() -> toDetails(leaseTransaction, d.blockchain)) + "created by LeaseTransaction and canceled by InvokeScriptTransaction" in { + val lessor = TxHelpers.signer(202) + val leaseRecipient = TxHelpers.address(203) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(leaseTransaction.sender.toAddress, r, expectedDetails) - checkActiveLeasesFor(leaseTransaction.recipient, r, expectedDetails) + domain.appendBlock(TxHelpers.transfer(richAccount, lessor.toAddress, 35.waves)) + forAll(transactionVersions) { v => + val leaseTransaction = TxHelpers.lease(lessor, leaseRecipient, version = v) + domain.appendBlock(leaseTransaction) + val expectedDetails = Seq(leaseTransaction.id() -> toDetails(leaseTransaction, domain.blockchain)) + + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(leaseTransaction.sender.toAddress, route, expectedDetails) + checkActiveLeasesFor(leaseTransaction.recipient, route, expectedDetails) } - d.appendMicroBlock( - setScriptTransaction(sender), - invokeLeaseCancel(sender, leaseTransaction.id()) + domain.appendMicroBlock( + setScriptTransaction(lessor), + invokeLeaseCancel(lessor, leaseTransaction.id()) ) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(leaseTransaction.sender.toAddress, r, Seq.empty) - checkActiveLeasesFor(leaseTransaction.recipient, r, Seq.empty) + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(leaseTransaction.sender.toAddress, route, Seq.empty) + checkActiveLeasesFor(leaseTransaction.recipient, route, Seq.empty) } } } - val setScriptAndInvoke = { - val sender = TxHelpers.signer(1) - val recipient = TxHelpers.signer(2) + val dappAddress = TxHelpers.signer(200) + val leaseRecipient = TxHelpers.address(201) + def setScriptAndInvoke = ( - sender, - setScriptTransaction(sender), - Signed - .invokeScript( - TxVersion.V2, - sender, - sender.toAddress, - Some( - FUNCTION_CALL( - FunctionHeader.User("leaseTo"), - List(CONST_BYTESTR(ByteStr(recipient.toAddress.bytes)).explicitGet(), CONST_LONG(10_000.waves)) - ) - ), - Seq.empty, - 0.005.waves, - Asset.Waves, - ntpTime.getTimestamp() - ), - recipient.toAddress + setScriptTransaction(dappAddress), + TxHelpers.invoke( + dappAddress.toAddress, + Some("leaseTo"), + Seq(CONST_BYTESTR(ByteStr(leaseRecipient.bytes)).explicitGet(), CONST_LONG(10_000.waves)), + invoker = dappAddress + ) ) - } - "created by InvokeScriptTransaction and canceled by CancelLeaseTransaction" in forAll(setScriptAndInvoke) { - case (sender, setScript, invoke, recipient) => - withRoute(Seq(AddrWithBalance(sender.toAddress))) { (d, r) => - d.appendBlock(setScript) - d.appendBlock(invoke) - val leaseId = d.blockchain - .accountData(sender.toAddress, "leaseId") - .collect { case i: BinaryDataEntry => - i.value - } - .get - val expectedDetails = - Seq( - leaseId -> LeaseDetails( - LeaseStaticInfo(setScript.sender, recipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), - LeaseDetails.Status.Active - ) - ) - - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(sender.toAddress, r, expectedDetails) - checkActiveLeasesFor(recipient, r, expectedDetails) - } + "created by InvokeScriptTransaction and canceled by CancelLeaseTransaction" in { + val (setScript, invoke) = setScriptAndInvoke + domain.appendBlock(TxHelpers.transfer(richAccount, dappAddress.toAddress, 20_000.waves), setScript, invoke) + val leaseId = domain.blockchain + .accountData(dappAddress.toAddress, "leaseId") + .collect { case i: BinaryDataEntry => + i.value + } + .value - d.appendMicroBlock(leaseCancelTransaction(sender, leaseId)) + val expectedDetails = Seq( + leaseId -> LeaseDetails( + LeaseStaticInfo(dappAddress.publicKey, leaseRecipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), + LeaseDetails.Status.Active + ) + ) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(sender.toAddress, r, Seq.empty) - checkActiveLeasesFor(recipient, r, Seq.empty) - } - } + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(dappAddress.toAddress, route, expectedDetails) + checkActiveLeasesFor(leaseRecipient, route, expectedDetails) + } + + domain.appendMicroBlock(leaseCancelTransaction(dappAddress, leaseId)) + + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(dappAddress.toAddress, route, Seq.empty) + checkActiveLeasesFor(leaseRecipient, route, Seq.empty) + } } - "created and canceled by InvokeScriptTransaction" in forAll(setScriptAndInvoke) { case (sender, setScript, invoke, recipient) => - withRoute(Seq(AddrWithBalance(sender.toAddress))) { (d, r) => - d.appendBlock(setScript) - d.appendBlock(invoke) - val invokeStatus = d.blockchain.transactionMeta(invoke.id()).get.status - assert(invokeStatus == Status.Succeeded, "Invoke has failed") + "created and canceled by InvokeScriptTransaction" in { + val (setScript, invoke) = setScriptAndInvoke - val leaseId = d.blockchain - .accountData(sender.toAddress, "leaseId") - .collect { case i: BinaryDataEntry => - i.value - } - .get - val expectedDetails = - Seq( - leaseId -> LeaseDetails( - LeaseStaticInfo(setScript.sender, recipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), - LeaseDetails.Status.Active - ) - ) + domain.appendBlock(TxHelpers.transfer(richAccount, dappAddress.toAddress, 20_000.waves), setScript, invoke) + val invokeStatus = domain.blockchain.transactionMeta(invoke.id()).get.status + assert(invokeStatus == Status.Succeeded, "Invoke has failed") - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(sender.toAddress, r, expectedDetails) - checkActiveLeasesFor(recipient, r, expectedDetails) + val leaseId = domain.blockchain + .accountData(dappAddress.toAddress, "leaseId") + .collect { case i: BinaryDataEntry => + i.value } + .get + val expectedDetails = + Seq( + leaseId -> LeaseDetails( + LeaseStaticInfo(dappAddress.publicKey, leaseRecipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), + LeaseDetails.Status.Active + ) + ) - d.appendMicroBlock(invokeLeaseCancel(sender, leaseId)) + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(dappAddress.toAddress, route, expectedDetails) + checkActiveLeasesFor(leaseRecipient, route, expectedDetails) + } - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(sender.toAddress, r, Seq.empty) - checkActiveLeasesFor(recipient, r, Seq.empty) - } + domain.appendMicroBlock(invokeLeaseCancel(dappAddress, leaseId)) + + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(dappAddress.toAddress, route, Seq.empty) + checkActiveLeasesFor(leaseRecipient, route, Seq.empty) } } - val invokeExpression = for { - sender <- accountGen - recipient <- accountGen - invokeExp <- invokeExpressionTransactionGen( - sender, + "created by InvokeExpressionTransaction and canceled by CancelLeaseTransaction" in { + val sender = TxHelpers.signer(210) + val recipient = TxHelpers.address(211) + + val invoke = TxHelpers.invokeExpression( TestCompiler(V6).compileFreeCall( s""" - |let lease = Lease(Address(base58'${recipient.toAddress.toString}'), ${10000.waves}) + |let lease = Lease(Address(base58'${recipient.toString}'), ${10000.waves}) |[ | lease, | BinaryEntry("leaseId", lease.calculateLeaseId()) |]""".stripMargin ), - 0.01.waves + sender ) - } yield ( - sender, - invokeExp, - recipient.toAddress - ) - "created by InvokeExpressionTransaction and canceled by CancelLeaseTransaction" in forAll(invokeExpression) { case (sender, invoke, recipient) => - withRoute(AddrWithBalance.enoughBalances(sender), DomainPresets.ContinuationTransaction) { (d, r) => - d.appendBlock(invoke) - val leaseId = d.blockchain - .accountData(sender.toAddress, "leaseId") - .collect { case i: BinaryDataEntry => - i.value - } - .get - val expectedDetails = - Seq( - leaseId -> LeaseDetails( - LeaseStaticInfo(sender.publicKey, recipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), - LeaseDetails.Status.Active - ) + domain.appendBlock(TxHelpers.transfer(richAccount, sender.toAddress, 10001.waves), invoke) + val leaseId = domain.blockchain + .accountData(sender.toAddress, "leaseId") + .collect { case i: BinaryDataEntry => + i.value + } + .get + val expectedDetails = + Seq( + leaseId -> LeaseDetails( + LeaseStaticInfo(sender.publicKey, recipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), + LeaseDetails.Status.Active ) + ) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(sender.toAddress, r, expectedDetails) - checkActiveLeasesFor(recipient, r, expectedDetails) - } + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(sender.toAddress, route, expectedDetails) + checkActiveLeasesFor(recipient, route, expectedDetails) + } - d.appendMicroBlock(leaseCancelTransaction(sender, leaseId)) + domain.appendMicroBlock(leaseCancelTransaction(sender, leaseId)) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(sender.toAddress, r, Seq.empty) - checkActiveLeasesFor(recipient, r, Seq.empty) - } + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(sender.toAddress, route, Seq.empty) + checkActiveLeasesFor(recipient, route, Seq.empty) } } "created by EthereumTransaction and canceled by CancelLeaseTransaction" in { - val sender = signer(2).toEthKeyPair - val dApp = defaultSigner - val recipient = secondSigner.toAddress + val sender = signer(211).toEthKeyPair + val dApp = signer(212) + val recipient = TxHelpers.address(213) + val invoke = EthTxGenerator.generateEthInvoke( keyPair = sender, @@ -335,124 +295,113 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi args = Seq(Arg.Bytes(ByteStr(recipient.bytes)), Arg.Integer(10000.waves)), payments = Seq.empty ) - withRoute(Seq(AddrWithBalance(dApp.toAddress), AddrWithBalance(invoke.sender.toAddress))) { (d, r) => - d.appendBlock(setScriptTransaction(dApp)) - d.appendBlock(invoke) - val leaseId = d.blockchain - .accountData(dApp.toAddress, "leaseId") - .collect { case i: BinaryDataEntry => - i.value - } - .get - val expectedDetails = + + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, Seq( - leaseId -> LeaseDetails( - LeaseStaticInfo(dApp.publicKey, recipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), - LeaseDetails.Status.Active - ) + sender.toWavesAddress -> 0.005.waves, + dApp.toAddress -> 10_0001.waves + ), + fee = 0.002.waves + ), + setScriptTransaction(dApp), + invoke + ) + val leaseId = domain.blockchain + .accountData(dApp.toAddress, "leaseId") + .collect { case i: BinaryDataEntry => + i.value + } + .get + val expectedDetails = + Seq( + leaseId -> LeaseDetails( + LeaseStaticInfo(dApp.publicKey, recipient, TxPositiveAmount(10_000_00000000L), invoke.id(), 1), + LeaseDetails.Status.Active ) + ) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(dApp.toAddress, r, expectedDetails) - checkActiveLeasesFor(recipient, r, expectedDetails) - } + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(dApp.toAddress, route, expectedDetails) + checkActiveLeasesFor(recipient, route, expectedDetails) + } - d.appendMicroBlock(leaseCancelTransaction(dApp, leaseId)) + domain.appendMicroBlock(leaseCancelTransaction(dApp, leaseId)) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(dApp.toAddress, r, Seq.empty) - checkActiveLeasesFor(recipient, r, Seq.empty) - } + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(dApp.toAddress, route, Seq.empty) + checkActiveLeasesFor(recipient, route, Seq.empty) } } - val nestedInvocation = { - val proxy = TxHelpers.signer(1) - val target = TxHelpers.signer(2) - val recipient = TxHelpers.signer(3) - - ( - (proxy, target, recipient.toAddress), - Seq( - setScriptTransaction(target), - SetScriptTransaction - .selfSigned( - TxVersion.V2, - proxy, - Some(TestCompiler(V5).compileContract(""" - |{-# STDLIB_VERSION 4 #-} - |{-# CONTENT_TYPE DAPP #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - | - |@Callable(inv) - |func callProxy(targetDapp: ByteVector, recipient: ByteVector, amount: Int) = { - | strict result = invoke(Address(targetDapp), "leaseTo", [recipient, amount], []) - | [] - |} - |""".stripMargin)), - 0.01.waves, - ntpTime.getTimestamp() - ) - .explicitGet() - ) + "created by nested invocations" in { + val proxy = TxHelpers.signer(221) + val target = TxHelpers.signer(222) + val recipient = TxHelpers.signer(223) + + val ist = TxHelpers.invoke( + proxy.toAddress, + Some("callProxy"), + List( + CONST_BYTESTR(ByteStr(target.toAddress.bytes)).explicitGet(), + CONST_BYTESTR(ByteStr(recipient.toAddress.bytes)).explicitGet(), + CONST_LONG(10_000.waves) + ), + invoker = proxy ) - } - "created by nested invocations" in { - val ((proxy, target, recipient), transactions) = nestedInvocation - withRoute(Seq(AddrWithBalance(proxy.toAddress), AddrWithBalance(target.toAddress))) { (d, r) => - d.appendBlock(transactions*) - val ist = Signed - .invokeScript( - TxVersion.V2, - proxy, - proxy.toAddress, - Some( - FUNCTION_CALL( - FunctionHeader.User("callProxy"), - List( - CONST_BYTESTR(ByteStr(target.toAddress.bytes)).explicitGet(), - CONST_BYTESTR(ByteStr(recipient.bytes)).explicitGet(), - CONST_LONG(10_000.waves) - ) - ) - ), - Seq.empty, - 0.005.waves, - Asset.Waves, - ntpTime.getTimestamp() - ) + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + proxy.toAddress -> 1.waves, + target.toAddress -> 10_001.waves + ), + fee = 0.002.waves + ), + setScriptTransaction(target), + TxHelpers.setScript( + proxy, + TestCompiler(V5) + .compileContract("""@Callable(inv) + |func callProxy(targetDapp: ByteVector, recipient: ByteVector, amount: Int) = { + | strict result = invoke(Address(targetDapp), "leaseTo", [recipient, amount], []) + | [] + |} + |""".stripMargin) + ), + ist + ) - d.appendBlock(ist) - val leaseId = d.blockchain - .accountData(target.toAddress, "leaseId") - .collect { case i: BinaryDataEntry => - i.value - } - .get + val leaseId = domain.blockchain + .accountData(target.toAddress, "leaseId") + .collect { case i: BinaryDataEntry => + i.value + } + .get - val expectedDetails = - Seq( - leaseId -> LeaseDetails( - LeaseStaticInfo(target.publicKey, recipient, TxPositiveAmount(10_000_00000000L), ist.id(), 1), - LeaseDetails.Status.Active - ) + val expectedDetails = + Seq( + leaseId -> LeaseDetails( + LeaseStaticInfo(target.publicKey, recipient.toAddress, TxPositiveAmount(10_000_00000000L), ist.id(), 1), + LeaseDetails.Status.Active ) + ) - d.liquidAndSolidAssert { () => - checkActiveLeasesFor(target.toAddress, r, expectedDetails) - checkActiveLeasesFor(recipient, r, expectedDetails) - } + domain.liquidAndSolidAssert { () => + checkActiveLeasesFor(target.toAddress, route, expectedDetails) + checkActiveLeasesFor(recipient.toAddress, route, expectedDetails) } } } "returns leases created by invoke only for lease sender or recipient" in { - val invoker = TxHelpers.signer(1) - val dApp1 = TxHelpers.signer(2) - val dApp2 = TxHelpers.signer(3) - val leaseRecipient1 = TxHelpers.signer(4) - val leaseRecipient2 = TxHelpers.signer(5) + val invoker = TxHelpers.signer(231) + val dApp1 = TxHelpers.signer(232) + val dApp2 = TxHelpers.signer(233) + val leaseRecipient1 = TxHelpers.signer(234) + val leaseRecipient2 = TxHelpers.signer(235) val leaseAmount1 = 1 val leaseAmount2 = 2 @@ -484,42 +433,58 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi |} |""".stripMargin) - def checkForInvoke(invokeTx: Transaction & Authorized): Unit = - withRoute(AddrWithBalance.enoughBalances(dApp1, dApp2) :+ AddrWithBalance(invokeTx.sender.toAddress)) { case (d, r) => - def getLeaseId(address: Address) = - d.blockchain - .accountData(address, "leaseId") - .collect { case i: BinaryDataEntry => - i.value - } - .get - - d.appendBlock( - TxHelpers.setScript(dApp1, dAppScript1), - TxHelpers.setScript(dApp2, dAppScript2) - ) + def checkForInvoke(invokeTx: Transaction & Authorized): Unit = { + def getLeaseId(address: Address) = + domain.blockchain + .accountData(address, "leaseId") + .collect { case i: BinaryDataEntry => + i.value + } + .get + + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + invoker.toAddress -> 1.waves, + dApp1.toAddress -> 1.waves, + dApp2.toAddress -> 1.waves, + invoker.toEthKeyPair.toWavesAddress -> 1.waves + ), + fee = 0.005.waves + ), + TxHelpers.setScript(dApp1, dAppScript1), + TxHelpers.setScript(dApp2, dAppScript2) + ) - d.appendBlock(invokeTx) + domain.appendBlock(invokeTx) - val leaseDetails1 = Seq( - getLeaseId(dApp1.toAddress) -> LeaseDetails( - LeaseStaticInfo(dApp1.publicKey, leaseRecipient1.toAddress, TxPositiveAmount.unsafeFrom(leaseAmount1), invokeTx.id(), 3), - LeaseDetails.Status.Active - ) + val lease1Id = getLeaseId(dApp1.toAddress) + val leaseDetails1 = Seq( + lease1Id -> LeaseDetails( + LeaseStaticInfo(dApp1.publicKey, leaseRecipient1.toAddress, TxPositiveAmount.unsafeFrom(leaseAmount1), invokeTx.id(), 3), + LeaseDetails.Status.Active ) - val leaseDetails2 = Seq( - getLeaseId(dApp2.toAddress) -> LeaseDetails( - LeaseStaticInfo(dApp2.publicKey, leaseRecipient2.toAddress, TxPositiveAmount.unsafeFrom(leaseAmount2), invokeTx.id(), 3), - LeaseDetails.Status.Active - ) + ) + val lease2Id = getLeaseId(dApp2.toAddress) + val leaseDetails2 = Seq( + lease2Id -> LeaseDetails( + LeaseStaticInfo(dApp2.publicKey, leaseRecipient2.toAddress, TxPositiveAmount.unsafeFrom(leaseAmount2), invokeTx.id(), 3), + LeaseDetails.Status.Active ) + ) - checkActiveLeasesFor(invokeTx.sender.toAddress, r, Seq.empty) - checkActiveLeasesFor(dApp1.toAddress, r, leaseDetails1) - checkActiveLeasesFor(dApp2.toAddress, r, leaseDetails2) - checkActiveLeasesFor(leaseRecipient1.toAddress, r, leaseDetails1) - checkActiveLeasesFor(leaseRecipient2.toAddress, r, leaseDetails2) - } + checkActiveLeasesFor(invokeTx.sender.toAddress, route, Seq.empty) + checkActiveLeasesFor(dApp1.toAddress, route, leaseDetails1) + checkActiveLeasesFor(dApp2.toAddress, route, leaseDetails2) + checkActiveLeasesFor(leaseRecipient1.toAddress, route, leaseDetails1) + checkActiveLeasesFor(leaseRecipient2.toAddress, route, leaseDetails2) + + domain.appendBlock( + TxHelpers.leaseCancel(lease1Id, dApp1), + TxHelpers.leaseCancel(lease2Id, dApp2) + ) + } checkForInvoke(TxHelpers.invoke(dApp1.toAddress, Some("foo"), invoker = invoker)) checkForInvoke(EthTxGenerator.generateEthInvoke(invoker.toEthKeyPair, dApp1.toAddress, "foo", Seq.empty, Seq.empty)) @@ -530,9 +495,11 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi val lease = TxHelpers.lease() val leaseCancel = TxHelpers.leaseCancel(lease.id()) domain.appendBlock(lease) + val leaseHeight = domain.blockchain.height domain.appendBlock(leaseCancel) + val leaseCancelHeight = domain.blockchain.height - Get(routePath(s"/info/${lease.id()}")) ~> route(domain) ~> check { + Get(routePath(s"/info/${lease.id()}")) ~> route ~> check { val response = responseAs[JsObject] response should matchJson(s"""{ | "id" : "${lease.id()}", @@ -540,9 +507,9 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", | "recipient" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", | "amount" : 1000000000, - | "height" : 1, + | "height" : $leaseHeight, | "status" : "canceled", - | "cancelHeight" : 2, + | "cancelHeight" : $leaseCancelHeight, | "cancelTransactionId" : "${leaseCancel.id()}" |}""".stripMargin) } @@ -553,9 +520,9 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", | "recipient" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", | "amount" : 1000000000, - | "height" : 1, + | "height" : $leaseHeight, | "status" : "canceled", - | "cancelHeight" : 2, + | "cancelHeight" : $leaseCancelHeight, | "cancelTransactionId" : "${leaseCancel.id()}" |}, { @@ -564,13 +531,13 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", | "recipient" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", | "amount" : 1000000000, - | "height" : 1, + | "height" : $leaseHeight, | "status" : "canceled", - | "cancelHeight" : 2, + | "cancelHeight" : $leaseCancelHeight, | "cancelTransactionId" : "${leaseCancel.id()}" |}]""".stripMargin) - Get(routePath(s"/info?id=${lease.id()}&id=${lease.id()}")) ~> route(domain) ~> check { + Get(routePath(s"/info?id=${lease.id()}&id=${lease.id()}")) ~> route ~> check { val response = responseAs[JsArray] response should matchJson(leasesListJson) } @@ -578,7 +545,7 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi Post( routePath(s"/info"), HttpEntity(ContentTypes.`application/json`, Json.obj("ids" -> Seq(lease.id().toString, lease.id().toString)).toString()) - ) ~> route(domain) ~> check { + ) ~> route ~> check { val response = responseAs[JsArray] response should matchJson(leasesListJson) } @@ -589,7 +556,7 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi ContentTypes.`application/json`, Json.obj("ids" -> (0 to restAPISettings.transactionsByAddressLimit).map(_ => lease.id().toString)).toString() ) - ) ~> route(domain) ~> check { + ) ~> route ~> check { val response = responseAs[JsObject] response should matchJson("""{ | "error" : 10, @@ -600,12 +567,12 @@ class LeaseRouteSpec extends RouteSpec("/leasing") with RestAPISettingsHelper wi Post( routePath(s"/info"), FormData("id" -> lease.id().toString, "id" -> lease.id().toString) - ) ~> route(domain) ~> check { + ) ~> route ~> check { val response = responseAs[JsArray] response should matchJson(leasesListJson) } - Get(routePath(s"/info?id=nonvalid&id=${leaseCancel.id()}")) ~> route(domain) ~> check { + Get(routePath(s"/info?id=nonvalid&id=${leaseCancel.id()}")) ~> route ~> check { val response = responseAs[JsObject] response should matchJson(s""" |{ diff --git a/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala b/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala index 667863482bf..9004252c75f 100644 --- a/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/ProtoVersionTransactionsSpec.scala @@ -13,8 +13,7 @@ import com.wavesplatform.lang.v1.compiler.Terms.{CONST_LONG, FUNCTION_CALL} import com.wavesplatform.protobuf.transaction.{PBSignedTransaction, PBTransactions} import com.wavesplatform.protobuf.utils.PBUtils import com.wavesplatform.settings.Constants -import com.wavesplatform.state.Blockchain -import com.wavesplatform.state.reader.SnapshotBlockchain +import com.wavesplatform.state.{Blockchain, SnapshotBlockchain} import com.wavesplatform.transaction.* import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.assets.* diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala index f05881ba678..d714fd1db6a 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala @@ -10,8 +10,7 @@ import com.wavesplatform.lang.directives.values.V5 import com.wavesplatform.lang.v1.estimator.v3.ScriptEstimatorV3 import com.wavesplatform.lang.v1.traits.domain.{Lease, Recipient} import com.wavesplatform.network.TransactionPublisher -import com.wavesplatform.state.reader.SnapshotBlockchain -import com.wavesplatform.state.{AccountScriptInfo, Blockchain} +import com.wavesplatform.state.{AccountScriptInfo, Blockchain, SnapshotBlockchain} import com.wavesplatform.test.TestTime import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.GenericError diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala index 6599f6b53b8..e0e495eb798 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala @@ -2,9 +2,8 @@ package com.wavesplatform.http import akka.http.scaladsl.model.* import akka.http.scaladsl.model.headers.Accept -import akka.http.scaladsl.server.Route import com.wavesplatform.account.KeyPair -import com.wavesplatform.api.http.ApiError.* +import com.wavesplatform.api.http.ApiError.{ScriptExecutionError as _, *} import com.wavesplatform.api.http.{CustomJson, RouteTimeout, TransactionsApiRoute} import com.wavesplatform.block.Block import com.wavesplatform.block.Block.TransactionProof @@ -12,27 +11,26 @@ import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base58, *} import com.wavesplatform.db.WithState.AddrWithBalance import com.wavesplatform.features.BlockchainFeatures as BF -import com.wavesplatform.history.{Domain, defaultSigner} +import com.wavesplatform.history.defaultSigner import com.wavesplatform.lang.directives.values.{V5, V7} import com.wavesplatform.lang.v1.FunctionHeader -import com.wavesplatform.lang.v1.compiler.Terms.{CONST_BOOLEAN, CONST_LONG, FUNCTION_CALL} +import com.wavesplatform.lang.v1.compiler.Terms.{ARR, CONST_BOOLEAN, CONST_BYTESTR, CONST_LONG, CONST_STRING, FUNCTION_CALL} import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.settings.WavesSettings -import com.wavesplatform.state.InvokeScriptResult +import com.wavesplatform.state.{BinaryDataEntry, EmptyDataEntry, InvokeScriptResult, StringDataEntry} import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} +import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.TxHelpers.defaultAddress -import com.wavesplatform.transaction.TxValidationError.GenericError +import com.wavesplatform.transaction.TxValidationError.ScriptExecutionError import com.wavesplatform.transaction.assets.exchange.{Order, OrderType} -import com.wavesplatform.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} import com.wavesplatform.transaction.serialization.impl.InvokeScriptTxSerializer +import com.wavesplatform.transaction.smart.InvokeScriptTransaction import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.smart.script.trace.AccountVerifierTrace -import com.wavesplatform.transaction.smart.{InvokeScriptTransaction, SetScriptTransaction} -import com.wavesplatform.transaction.transfer.{MassTransferTransaction, TransferTransaction} +import com.wavesplatform.transaction.transfer.TransferTransaction import com.wavesplatform.transaction.utils.EthConverters.* import com.wavesplatform.transaction.utils.Signed -import com.wavesplatform.transaction.{Asset, AssetIdLength, CreateAliasTransaction, EthTxGenerator, TxHelpers, TxNonNegativeAmount, TxVersion} +import com.wavesplatform.transaction.{Asset, AssetIdLength, EthTxGenerator, Transaction, TxHelpers, TxVersion} import com.wavesplatform.utils.{EthEncoding, EthHelpers, SharedSchedulerMixin} import com.wavesplatform.{BlockGen, TestValues} import org.scalacheck.Gen @@ -41,6 +39,7 @@ import org.scalatest.{Assertion, OptionValues} import play.api.libs.json.* import play.api.libs.json.Json.JsValueWrapper +import scala.concurrent.Future import scala.concurrent.duration.* import scala.util.Random @@ -58,17 +57,19 @@ class TransactionsRouteSpec private val richAccount = TxHelpers.signer(10001) private val richAddress = richAccount.toAddress - override def settings: WavesSettings = DomainPresets.TransactionStateSnapshot + override def settings: WavesSettings = DomainPresets.TransactionStateSnapshot.copy(restAPISettings = restAPISettings) override def genesisBalances: Seq[AddrWithBalance] = Seq(AddrWithBalance(richAddress, 1_000_000.waves)) + + private val transactionsApiRoute = new TransactionsApiRoute( - restAPISettings, + settings.restAPISettings, domain.transactionsApi, domain.wallet, domain.blockchain, () => domain.blockchain, () => domain.utxPool.size, - DummyTransactionPublisher.accepting, + (tx, _) => Future.successful(domain.utxPool.putIfNew(tx, forceValidate = true)), testTime, new RouteTimeout(60.seconds)(sharedScheduler) ) @@ -96,57 +97,50 @@ class TransactionsRouteSpec } "asset" in { - val asset: IssuedAsset = TestValues.asset + val issuer = TxHelpers.signer(270) + val issue = TxHelpers.issue(issuer) + + domain.appendBlock( + TxHelpers.transfer(richAccount, issuer.toAddress, 20.waves), + issue, + TxHelpers.sponsor(issue.asset, Some(5L), issuer) + ) + val transferTx = Json.obj( "type" -> 4, "version" -> 2, "amount" -> 1000000, - "feeAssetId" -> asset.id.toString, + "feeAssetId" -> issue.asset.id.toString, "senderPublicKey" -> TestValues.keyPair.publicKey, "recipient" -> TestValues.address ) Post(routePath("/calculateFee"), transferTx) ~> route ~> check { status shouldEqual StatusCodes.OK - (responseAs[JsObject] \ "feeAssetId").as[String] shouldBe asset.id.toString + (responseAs[JsObject] \ "feeAssetId").as[String] shouldBe issue.asset.id.toString (responseAs[JsObject] \ "feeAmount").as[Long] shouldEqual 5 } } } - private def mkRoute(d: Domain): Route = - seal( - new TransactionsApiRoute( - restAPISettings, - d.commonApi.transactions, - d.wallet, - d.blockchain, - () => d.blockchain.snapshotBlockchain, - () => 0, - (t, _) => d.commonApi.transactions.broadcastTransaction(t), - ntpTime, - new RouteTimeout(60.seconds)(sharedScheduler) - ).route - ) - "returns lease details for lease cancel transaction" in { val sender = TxHelpers.signer(20) val recipient = TxHelpers.signer(21) - val balances = Seq( - AddrWithBalance(sender.toAddress, 10.waves), - AddrWithBalance(recipient.toAddress, 10.waves) - ) + val lease = TxHelpers.lease(sender, recipient.toAddress, 5.waves) + val leaseCancel = TxHelpers.leaseCancel(lease.id(), sender) - val lease = LeaseTransaction.selfSigned(2.toByte, sender, recipient.toAddress, 5.waves, 0.001.waves, ntpTime.getTimestamp()).explicitGet() - val leaseCancel = LeaseCancelTransaction.selfSigned(2.toByte, sender, lease.id(), 0.001.waves, ntpTime.getTimestamp()).explicitGet() - val sealedRoute = mkRoute(domain) + domain.appendBlock( + TxHelpers.transfer(richAccount, sender.toAddress, 6.waves), + lease + ) - domain.appendBlock(lease) + val leaseHeight = domain.blockchain.height - def expectedJson(status: String, cancelHeight: Option[Int] = None, cancelTransactionId: Option[ByteStr] = None): JsObject = + def expectedJson(status: String, height: Int, cancelHeight: Option[Int] = None, cancelTransactionId: Option[ByteStr] = None): JsObject = Json .parse(s"""{ + | "applicationStatus": "succeeded", | "type" : 9, | "id" : "${leaseCancel.id()}", | "sender" : "${sender.toAddress}", @@ -165,7 +159,7 @@ class TransactionsRouteSpec | "sender" : "${sender.toAddress}", | "recipient" : "${recipient.toAddress}", | "amount" : ${5.waves}, - | "height" : 2, + | "height" : $height, | "status" : "$status", | "cancelHeight" : ${cancelHeight.getOrElse("null")}, | "cancelTransactionId" : ${cancelTransactionId.fold("null")("\"" + _ + "\"")} @@ -176,29 +170,199 @@ class TransactionsRouteSpec domain.utxPool.putIfNew(leaseCancel) withClue(routePath("/unconfirmed")) { - Get(routePath(s"/unconfirmed")) ~> sealedRoute ~> check { - responseAs[Seq[JsObject]].head should matchJson(expectedJson("active") - "spentComplexity") + Get(routePath(s"/unconfirmed")) ~> route ~> check { + responseAs[Seq[JsObject]].head should matchJson(expectedJson("active", leaseHeight) - "spentComplexity" - "applicationStatus") } } domain.appendBlock(leaseCancel) - val cancelTransactionJson = expectedJson("canceled", Some(3), Some(leaseCancel.id())) ++ Json.obj("height" -> 3) + val cancelHeight = domain.blockchain.height + val cancelTransactionJson = + expectedJson("canceled", leaseHeight, Some(cancelHeight), Some(leaseCancel.id())) ++ Json.obj("height" -> cancelHeight) withClue(routePath("/address/{address}/limit/{limit}")) { - Get(routePath(s"/address/${recipient.toAddress}/limit/10")) ~> sealedRoute ~> check { + Get(routePath(s"/address/${recipient.toAddress}/limit/10")) ~> route ~> check { val json = (responseAs[JsArray] \ 0 \ 0).as[JsObject] json should matchJson(cancelTransactionJson) } } withClue(routePath("/info/{id}")) { - Get(routePath(s"/info/${leaseCancel.id()}")) ~> sealedRoute ~> check { + Get(routePath(s"/info/${leaseCancel.id()}")) ~> route ~> check { responseAs[JsObject] should matchJson(cancelTransactionJson) } } } + "provides state changes in both transactions by address and by id" in { + val dapp = TxHelpers.signer(230) + val invoker = TxHelpers.signer(231) + + val invoke = TxHelpers.invoke(dapp.toAddress) + + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + dapp.toAddress -> 1.waves, + invoker.toAddress -> 1.waves + ), + fee = 0.002.waves + ), + TxHelpers.setScript( + dapp, + TestCompiler(V7) + .compileContract(s"""@Callable(i) + |func default() = [ + | StringEntry("key3", "some string"), + | BinaryEntry("key4", base58'encoded'), + | DeleteEntry("key5"), + | ScriptTransfer(Address(base58'${invoker.toAddress}'), 100, unit) + |] + |""".stripMargin) + ), + invoke + ) + + val expectedStateChanges = Json.toJsObject( + InvokeScriptResult( + Seq(StringDataEntry("key3", "some string"), BinaryDataEntry("key4", ByteStr.decodeBase58("encoded").get), EmptyDataEntry("key5")), + Seq(InvokeScriptResult.Payment(invoker.toAddress, Asset.Waves, 100)) + ) + ) + + Get(routePath(s"/address/${invoker.toAddress}/limit/1")) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsArray] \ 0 \ 0 \ "stateChanges").as[JsObject] shouldBe expectedStateChanges + } + + Get(routePath(s"/info/${invoke.id()}")) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsObject] \ "stateChanges").as[JsObject] shouldBe expectedStateChanges + } + + Get(routePath(s"/info?id=${invoke.id()}")) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsArray] \ 0 \ "stateChanges").as[JsObject] shouldBe expectedStateChanges + } + + Post( + "/transactions/info", + HttpEntity(ContentTypes.`application/json`, Json.obj("ids" -> Json.arr(invoke.id())).toString().getBytes) + ) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsArray] \ 0 \ "stateChanges").as[JsObject] shouldBe expectedStateChanges + } + } + + "provides lease and lease cancel actions stateChanges" in { + val dapp = TxHelpers.signer(235) + val caller = TxHelpers.signer(236) + val leaseRecipient = TxHelpers.address(237) + + val originalLease = TxHelpers.lease(dapp, leaseRecipient, 20_00000000L) + val invoke = + TxHelpers.invoke(dapp.toAddress, Some("testLease"), Seq(CONST_LONG(1000), CONST_BYTESTR(originalLease.id()).explicitGet()), invoker = caller) + + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + dapp.toAddress -> 50.waves, + caller.toAddress -> 1.waves + ), + fee = 0.002.waves + ), + originalLease, + TxHelpers.setScript( + dapp, + TestCompiler(V7) + .compileContract(s"""@Callable(i) + |func testLease(amount: Int, id: ByteVector) = { + |let lease = Lease(Address(base58'$leaseRecipient'), amount) + |let leaseId = calculateLeaseId(lease) + |[ + | LeaseCancel(id), + | lease, + | BinaryEntry("leaseId", leaseId) + |] + |} + |""".stripMargin) + ) + ) + + val originalHeight = domain.blockchain.height + + domain.appendBlock(invoke) + + val newLeaseId = domain.blockchain + .accountData(dapp.toAddress, "leaseId") + .collect { case BinaryDataEntry(_, id) => id } + .value + + val expectedJson = + s"""{ + | "data": [ { + | "type": "binary", + | "key": "leaseId", + | "value": "${newLeaseId.base64}" + | } ], + | "transfers": [], + | "issues": [], + | "reissues": [], + | "burns": [], + | "sponsorFees": [], + | "leases" : [ { + | "id" : "$newLeaseId", + | "originTransactionId" : "${invoke.id()}", + | "sender" : "${dapp.toAddress}", + | "recipient" : "$leaseRecipient", + | "amount" : 1000, + | "height" : ${originalHeight + 1}, + | "status" : "active", + | "cancelHeight" : null, + | "cancelTransactionId" : null + | } ], + | "leaseCancels" : [ { + | "id" : "${originalLease.id()}", + | "originTransactionId" : "${originalLease.id()}", + | "sender" : "${dapp.toAddress}", + | "recipient" : "$leaseRecipient", + | "amount" : ${20.waves}, + | "height" : $originalHeight, + | "status" : "canceled", + | "cancelHeight" : ${originalHeight + 1}, + | "cancelTransactionId" : "${invoke.id()}" + | } ], + | "invokes": [] + |}""".stripMargin + + + Get(routePath(s"/address/${dapp.toAddress}/limit/1")) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsArray] \ 0 \ 0 \ "stateChanges").as[JsObject] should matchJson(expectedJson) + } + + Get(routePath(s"/info/${invoke.id()}")) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsObject] \ "stateChanges").as[JsObject] should matchJson(expectedJson) + } + + Get(routePath(s"/info?id=${invoke.id()}")) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsArray] \ 0 \ "stateChanges").as[JsObject] should matchJson(expectedJson) + } + + Post( + routePath("/info"), + HttpEntity(ContentTypes.`application/json`, Json.obj("ids" -> Json.arr(invoke.id())).toString()) + ) ~> route ~> check { + status shouldEqual StatusCodes.OK + (responseAs[JsArray] \ 0 \ "stateChanges").as[JsObject] should matchJson(expectedJson) + } + } + routePath("/address/{address}/limit/{limit}") - { val txByAddressLimit = settings.restAPISettings.transactionsByAddressLimit "handles parameter errors with corresponding responses" - { @@ -207,7 +371,9 @@ class TransactionsRouteSpec } "invalid base58 encoding" in { - Get(routePath(s"/address/${"1" * 23 + "0"}/limit/1")) ~> route should produce(InvalidAddress) + Get(routePath(s"/address/${"1" * 23 + "0"}/limit/1")) ~> route should produce( + CustomValidationError("requirement failed: Wrong char '0' in Base58 string '111111111111111111111110'") + ) } "invalid limit" - { @@ -241,94 +407,15 @@ class TransactionsRouteSpec } } - "provides stateChanges" in { - val dapp = TxHelpers.signer(230) - val invoker = TxHelpers.signer(231) - - val invoke = TxHelpers.invoke(dapp.toAddress) - - domain.appendBlock( - TxHelpers.massTransfer( - richAccount, - Seq( - MassTransferTransaction.ParsedTransfer(dapp.toAddress, TxNonNegativeAmount(1_00000000L)), - MassTransferTransaction.ParsedTransfer(invoker.toAddress, TxNonNegativeAmount(1_00000000L)) - ) - ), - TxHelpers.setScript( - dapp, - TestCompiler(V7).compileContract(s"""@Callable(i) - |func default() = [ - | StringEntry("key3", "some string"), - | BinaryEntry("key4", base58'encoded'), - | DeleteEntry("key5"), - | ScriptTransfer(Address(base58'${invoker.toAddress}'), 100, unit), - |] - |""".stripMargin) - ), - invoke - ) - - Get(routePath(s"/address/${invoker.toAddress}/limit/1")) ~> route ~> check { - status shouldEqual StatusCodes.OK - (responseAs[JsArray] \ 0 \ 0 \ "stateChanges").as[JsObject] shouldBe Json.toJsObject(InvokeScriptResult()) - } - } - - "provides lease and lease cancel actions stateChanges" in { - val dapp = TxHelpers.signer(235) - val caller = TxHelpers.signer(236) - val leaseRecipient = TxHelpers.address(237) - val invoke = TxHelpers.invoke(dapp.toAddress, invoker = caller) - val originalLease = TxHelpers.lease(dapp, leaseRecipient, 20_00000000L) - - domain.appendBlock( - TxHelpers.massTransfer( - richAccount, - Seq( - MassTransferTransaction.ParsedTransfer(dapp.toAddress, TxNonNegativeAmount(50_00000000L)), - MassTransferTransaction.ParsedTransfer(caller.toAddress, TxNonNegativeAmount(1_00000000L)) - ) - ), - originalLease, - TxHelpers.setScript( - dapp, - TestCompiler(V7).compileContract(s"""@Callable(i) - |func testLease(amount: Int, id: ByteVector) = [ - | LeaseCancel(id), - | Lease(Address(base58'${leaseRecipient}'), amount) - |] - |""".stripMargin) - ) - ) - - Get(routePath(s"/address/${dapp.toAddress}/limit/1")) ~> route ~> check { - status shouldEqual StatusCodes.OK - val json = (responseAs[JsArray] \ 0 \ 0 \ "stateChanges").as[JsObject] - json should matchJson(s"""{ - | "data": [], - | "transfers": [], - | "issues": [], - | "reissues": [], - | "burns": [], - | "sponsorFees": [], - | "leases": [ - | - | ], - | "invokes": [] - |}""".stripMargin) - } - } - "large-significand-format" in { - val tx = TxHelpers.transfer() + val tx = TxHelpers.transfer(richAccount, TxHelpers.address(930), 10.waves) domain.appendBlock(tx) Get(routePath(s"/address/$richAddress/limit/1")) ~> Accept(CustomJson.jsonWithNumbersAsStrings) ~> route ~> check { val result = responseAs[JsArray] \ 0 \ 0 (result \ "amount").as[String] shouldBe tx.amount.value.toString (result \ "fee").as[String] shouldBe tx.fee.value.toString - (result \ "height").as[Int] shouldBe 1 + (result \ "height").as[Int] shouldBe domain.blockchain.height (result \ "spentComplexity").as[Int] shouldBe 0 (result \ "version").as[Int] shouldBe tx.version (result \ "type").as[Int] shouldBe tx.tpe.id @@ -358,16 +445,16 @@ class TransactionsRouteSpec | "version" : 1, | "chainId" : 84, | "bytes" : "${EthEncoding.toHexString(ethTransfer.bytes())}", - | "sender" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "senderPublicKey" : "5vwTDMooR7Hp57MekN7qHz7fHNVrkn2Nx4CiWdq4cyBR4LNnZWYAr7UfBbzhmSvtNkv6e45aJ4Q4aKCSinyHVw33", - | "height" : 1, - | "spentComplexity": 15, + | "sender" : "${ethAccount.toWavesAddress}", + | "senderPublicKey" : "${ethTransfer.sender}", + | "height" : ${domain.blockchain.height}, + | "spentComplexity": 0, | "applicationStatus" : "succeeded", | "payload" : { | "type" : "transfer", - | "recipient" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", + | "recipient" : "$transferRecipient", | "asset" : null, - | "amount" : 10 + | "amount" : ${5.waves} | } |}""".stripMargin) } @@ -379,6 +466,24 @@ class TransactionsRouteSpec val transaction = EthTxGenerator.generateEthInvoke(caller, dapp.toAddress, "test", Seq(EthTxGenerator.Arg.Integer(255)), Seq.empty) + domain.appendBlock( + TxHelpers.massTransfer( + richAccount, + Seq( + dapp.toAddress -> 1.waves, + caller.toWavesAddress -> 1.waves + ), + fee = 0.002.waves + ), + TxHelpers.setScript( + dapp, + TestCompiler(V7).compileContract("""@Callable(i) + |func test(arg: Int) = [] + |""".stripMargin) + ), + transaction + ) + Get(routePath(s"/info/${transaction.id()}")) ~> route ~> check { responseAs[JsObject] should matchJson(s"""{ | "type" : 18, @@ -389,17 +494,20 @@ class TransactionsRouteSpec | "version" : 1, | "chainId" : 84, | "bytes" : "${EthEncoding.toHexString(transaction.bytes())}", - | "sender" : "3NByUD1YE9SQPzmf2KqVqrjGMutNSfc4oBC", - | "senderPublicKey" : "5vwTDMooR7Hp57MekN7qHz7fHNVrkn2Nx4CiWdq4cyBR4LNnZWYAr7UfBbzhmSvtNkv6e45aJ4Q4aKCSinyHVw33", - | "height" : 1, - | "spentComplexity": 15, + | "sender" : "${caller.toWavesAddress}", + | "senderPublicKey" : "${transaction.sender}", + | "height" : ${domain.blockchain.height}, + | "spentComplexity": 1, | "applicationStatus" : "succeeded", | "payload" : { | "type" : "invocation", - | "dApp" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", + | "dApp" : "${dapp.toAddress}", | "call" : { | "function" : "test", - | "args" : [ ] + | "args" : [ { + | "type" : "integer", + | "value" : 255 + | } ] | }, | "payment" : [ ], | "stateChanges" : { @@ -422,13 +530,15 @@ class TransactionsRouteSpec val lessor = TxHelpers.signer(250) val leaseRecipient = TxHelpers.address(251) - val lease = TxHelpers.lease(lessor, leaseRecipient) + val lease = TxHelpers.lease(lessor, leaseRecipient, 22.waves) domain.appendBlock( TxHelpers.transfer(richAccount, lessor.toAddress, 25.waves), lease ) + val leaseHeight = domain.blockchain.height + val leaseCancel = TxHelpers.leaseCancel(lease.id(), lessor) domain.appendBlock(leaseCancel) val cancelHeight = domain.blockchain.height @@ -438,8 +548,8 @@ class TransactionsRouteSpec json shouldBe Json.parse(s"""{ | "type" : 9, | "id" : "${leaseCancel.id()}", - | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", - | "senderPublicKey" : "9BUoYQYq7K38mkk61q8aMH9kD9fKSVL1Fib7FbH6nUkQ", + | "sender" : "${lessor.toAddress}", + | "senderPublicKey" : "${lessor.publicKey}", | "fee" : 100000, | "feeAssetId" : null, | "timestamp" : ${leaseCancel.timestamp}, @@ -447,18 +557,18 @@ class TransactionsRouteSpec | "version" : 2, | "leaseId" : "${lease.id()}", | "chainId" : 84, - | "height" : 1, + | "height" : $cancelHeight, | "applicationStatus" : "succeeded", | "spentComplexity": 0, | "lease" : { | "id" : "${lease.id()}", | "originTransactionId" : "${lease.id()}", - | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", - | "recipient" : "3MuVqVJGmFsHeuFni5RbjRmALuGCkEwzZtC", - | "amount" : 1000000000, - | "height" : 1, + | "sender" : "${lessor.toAddress}", + | "recipient" : "$leaseRecipient", + | "amount" : ${22.waves}, + | "height" : $leaseHeight, | "status" : "canceled", - | "cancelHeight" : 2, + | "cancelHeight" : $cancelHeight, | "cancelTransactionId" : "${leaseCancel.id()}" | } |}""".stripMargin) @@ -482,9 +592,9 @@ class TransactionsRouteSpec status shouldEqual StatusCodes.OK val extraFields = Seq( - (if (domain.blockchain.isFeatureActivated(BF.BlockV5, height)) - Json.obj("applicationStatus" -> JsString(if (succeed) "succeeded" else "script_execution_failed")) - else Json.obj()), + if (domain.blockchain.isFeatureActivated(BF.BlockV5, height)) + Json.obj("applicationStatus" -> JsString(if (succeed) "succeeded" else "script_execution_failed")) + else Json.obj(), Json.obj("height" -> height, "spentComplexity" -> 0) ).reduce(_ ++ _) @@ -494,19 +604,6 @@ class TransactionsRouteSpec Get(routePath(s"/info/${tx.id().toString}")) ~> route ~> check(validateResponse()) } - "provides stateChanges" in { - // todo: test together with /transactions/address - val transaction = TxHelpers.invoke() - Get(routePath(s"/info/${transaction.id()}")) ~> route ~> check { - status shouldEqual StatusCodes.OK - (responseAs[JsObject] \ "stateChanges").as[JsObject] shouldBe Json.toJsObject(InvokeScriptResult()) - } - } - - "provides lease and lease cancel action stateChanges" in { - // todo: test together with /transactions/address - } - "handles multiple ids" in { val inputLimitErrMsg = TooBigArrayAllocation(transactionsApiRoute.settings.transactionsByAddressLimit).message val emptyInputErrMsg = "Transaction ID was not specified" @@ -603,17 +700,19 @@ class TransactionsRouteSpec routePath("/unconfirmed") - { "returns the list of unconfirmed transactions" in { + domain.utxPool.removeAll(domain.utxPool.all) val txs = Seq.tabulate(20)(a => TxHelpers.transfer(richAccount, amount = (a + 1).waves)) txs.foreach(t => domain.utxPool.putIfNew(t)) Get(routePath("/unconfirmed")) ~> route ~> check { val txIds = responseAs[Seq[JsValue]].map(v => (v \ "id").as[String]) - txIds should contain(allElementsOf(txs.map(_.id().toString))) + txIds should contain allElementsOf (txs.map(_.id().toString)) } domain.utxPool.removeAll(txs) } routePath("/unconfirmed/size") - { "returns the size of unconfirmed transactions" in { + domain.utxPool.removeAll(domain.utxPool.all) val txs = Seq.tabulate(20)(a => TxHelpers.transfer(richAccount, amount = (a + 1).waves)) txs.foreach(t => domain.utxPool.putIfNew(t)) Get(routePath("/unconfirmed/size")) ~> route ~> check { @@ -704,16 +803,25 @@ class TransactionsRouteSpec f(sender, ist) } - "shows trace when trace is enabled" in withInvokeScriptTransaction { (sender, ist) => - val accountTrace = AccountVerifierTrace(sender.toAddress, Some(GenericError("Error in account script"))) + "shows trace when trace is enabled" in { + val sender = TxHelpers.signer(1201) + val ist = TxHelpers.transfer(sender, defaultAddress, 1.waves) + domain.appendBlock( + TxHelpers.transfer(richAccount, sender.toAddress, 2.waves), + TxHelpers.setScript(sender, TestCompiler(V7).compileExpression("throw(\"error\")")) + ) Post(routePath("/broadcast?trace=true"), ist.json()) ~> route ~> check { val result = responseAs[JsObject] - (result \ "trace").as[JsValue] shouldBe Json.arr(accountTrace.json) + (result \ "trace").as[JsValue] shouldBe Json.arr(AccountVerifierTrace(sender.toAddress, + Some(ScriptExecutionError("error", List( + "throw.@args" -> Right(ARR(IndexedSeq(CONST_STRING("error").explicitGet()), false).explicitGet()), + "throw.@complexity" -> Right(CONST_LONG(1)), + "@complexityLimit" -> Right(CONST_LONG(2147483646)), + ), None))).json) } } - "does not show trace when trace is disabled" in withInvokeScriptTransaction { (sender, ist) => - val accountTrace = AccountVerifierTrace(sender.toAddress, Some(GenericError("Error in account script"))) + "does not show trace when trace is disabled" in withInvokeScriptTransaction { (_, ist) => Post(routePath("/broadcast"), ist.json()) ~> route ~> check { (responseAs[JsObject] \ "trace") shouldBe empty } @@ -723,44 +831,42 @@ class TransactionsRouteSpec } "generates valid trace with vars" in { - val sender = domain.wallet.generateNewAccount().get - val aliasOwner = domain.wallet.generateNewAccount().get - val recipient = domain.wallet.generateNewAccount().get + val sender = TxHelpers.signer(1030) + val aliasOwner = TxHelpers.signer(1031) + val recipient = TxHelpers.address(1032) - val balances = Seq( - AddrWithBalance(sender.toAddress, 1000.waves), - AddrWithBalance(aliasOwner.toAddress, 1000.waves) - ) - - val lease = LeaseTransaction.selfSigned(2.toByte, sender, recipient.toAddress, 50.waves, 0.001.waves, ntpTime.getTimestamp()).explicitGet() + val lease = TxHelpers.lease(sender, recipient, 50.waves) domain.appendBlock( - CreateAliasTransaction.selfSigned(2.toByte, aliasOwner, "test_alias", 0.001.waves, ntpTime.getTimestamp()).explicitGet(), - SetScriptTransaction - .selfSigned( - 2.toByte, - sender, - Some(TestCompiler(V5).compileContract(s"""{-# STDLIB_VERSION 5 #-} - |{-# CONTENT_TYPE DAPP #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - | - |@Callable(i) - |func default() = { - | let leaseToAddress = Lease(Address(base58'${recipient.toAddress}'), ${10.waves}) - | let leaseToAlias = Lease(Alias("test_alias"), ${20.waves}) - | strict leaseId = leaseToAddress.calculateLeaseId() - | - | [ - | leaseToAddress, - | leaseToAlias, - | LeaseCancel(base58'${lease.id()}') - | ] - |} - |""".stripMargin)), - 0.01.waves, - ntpTime.getTimestamp() - ) - .explicitGet(), + TxHelpers.massTransfer( + richAccount, + Seq( + sender.toAddress -> 100.waves, + aliasOwner.toAddress -> 1.waves + ), + fee = 0.002.waves + ), + TxHelpers.createAlias("test_alias", aliasOwner), + TxHelpers.setScript( + sender, + TestCompiler(V5).compileContract(s"""{-# STDLIB_VERSION 5 #-} + |{-# CONTENT_TYPE DAPP #-} + |{-# SCRIPT_TYPE ACCOUNT #-} + | + |@Callable(i) + |func default() = { + | let leaseToAddress = Lease(Address(base58'${recipient}'), ${10.waves}) + | let leaseToAlias = Lease(Alias("test_alias"), ${20.waves}) + | strict leaseId = leaseToAddress.calculateLeaseId() + | + | [ + | leaseToAddress, + | leaseToAlias, + | LeaseCancel(base58'${lease.id()}') + | ] + |} + |""".stripMargin) + ), lease ) @@ -775,6 +881,8 @@ class TransactionsRouteSpec "i", "default.@args", "Address.@args", + "Address.@complexity", + "@complexityLimit", "Lease.@args", "Lease.@complexity", "@complexityLimit", @@ -787,11 +895,15 @@ class TransactionsRouteSpec "==.@complexity", "@complexityLimit", "Alias.@args", + "Alias.@complexity", + "@complexityLimit", "Lease.@args", "Lease.@complexity", "@complexityLimit", "leaseToAlias", "LeaseCancel.@args", + "LeaseCancel.@complexity", + "@complexityLimit", "cons.@args", "cons.@complexity", "@complexityLimit", @@ -845,31 +957,6 @@ class TransactionsRouteSpec } routePath("/merkleProof") - { - val transactionsGen = for { - txsSize <- Gen.choose(1, 10) - txs <- Gen.listOfN(txsSize, randomTransactionGen) - } yield txs - - val invalidBlockGen = for { - txs <- transactionsGen - signer <- accountGen - version <- Gen.choose(Block.GenesisBlockVersion, Block.RewardBlockVersion) - block <- versionedBlockGen(txs, signer, version) - } yield block - - val invalidBlocksGen = - for { - blockchainHeight <- Gen.choose(1, 10) - blocks <- Gen.listOfN(blockchainHeight, invalidBlockGen) - } yield blocks - - val merkleProofs = for { - index <- Gen.choose(0, 50) - tx <- randomTransactionGen - proofsLength <- Gen.choose(1, 5) - proofBytes <- Gen.listOfN(proofsLength, bytes32gen) - } yield (tx, TransactionProof(tx.id(), index, proofBytes)) - def validateSuccess(expectedProofs: Seq[TransactionProof], response: HttpResponse): Unit = { response.status shouldBe StatusCodes.OK @@ -894,8 +981,9 @@ class TransactionsRouteSpec } "returns merkle proofs" in { - forAll(Gen.choose(10, 20).flatMap(n => Gen.listOfN(n, merkleProofs))) { transactionsAndProofs => - val (transactions, proofs) = transactionsAndProofs.unzip + { + val transactions = Seq.empty[Transaction] + val proofs = Seq.empty[TransactionProof] val queryParams = transactions.map(t => s"id=${t.id()}").mkString("?", "&", "") val requestBody = Json.obj("ids" -> transactions.map(_.id().toString)) @@ -911,19 +999,17 @@ class TransactionsRouteSpec } "returns error in case of all transactions are filtered" in { - forAll(invalidBlocksGen) { blocks => - val txIdsToBlock = blocks.flatMap(b => b.transactionData.map(tx => (tx.id().toString, b))).toMap + val genesisTransactions = domain.blocksApi.blockAtHeight(1).value._2.collect { case (_, tx) => tx.id() } - val queryParams = txIdsToBlock.keySet.map(id => s"id=$id").mkString("?", "&", "") - val requestBody = Json.obj("ids" -> txIdsToBlock.keySet) + val queryParams = genesisTransactions.map(id => s"id=$id").mkString("?", "&", "") + val requestBody = Json.obj("ids" -> genesisTransactions) - Get(routePath(s"/merkleProof$queryParams")) ~> route ~> check { - validateFailure(response) - } + Get(routePath(s"/merkleProof$queryParams")) ~> route ~> check { + validateFailure(response) + } - Post(routePath("/merkleProof"), requestBody) ~> route ~> check { - validateFailure(response) - } + Post(routePath("/merkleProof"), requestBody) ~> route ~> check { + validateFailure(response) } } @@ -956,14 +1042,14 @@ class TransactionsRouteSpec result.size shouldBe idsCount (1 to idsCount).zip(responseAs[JsArray].value) foreach { case (_, json) => (json \ "id").as[String] shouldBe tx.id().toString - (json \ "transactionIndex").as[Int] shouldBe 0 + (json \ "transactionIndex").as[Int] shouldBe 1 } } - val sender = TxHelpers.signer(1) + val sender = TxHelpers.signer(1090) val transferTx = TxHelpers.transfer(from = sender) - domain.appendBlock(transferTx) + domain.appendBlock(TxHelpers.transfer(richAccount, sender.toAddress, 100.waves), transferTx) val maxLimitIds = Seq.fill(transactionsApiRoute.settings.transactionsByAddressLimit)(transferTx.id().toString) val moreThanLimitIds = transferTx.id().toString +: maxLimitIds @@ -997,8 +1083,8 @@ class TransactionsRouteSpec (txInfo \ "order1" \ "attachment").asOpt[ByteStr] shouldBe Some(expectedAttachment) } - val sender = TxHelpers.signer(1) - val issuer = TxHelpers.signer(2) + val sender = TxHelpers.signer(1100) + val issuer = TxHelpers.signer(1101) val attachment = ByteStr.fill(32)(1) val issue = TxHelpers.issue(issuer) val exchange = @@ -1008,8 +1094,11 @@ class TransactionsRouteSpec version = TxVersion.V3 ) - domain.appendBlock(issue) - domain.appendBlock(exchange) + domain.appendBlock( + TxHelpers.massTransfer(richAccount, Seq(sender.toAddress -> 10.waves, issuer.toAddress -> 10.waves), fee = 0.002.waves), + issue, + exchange + ) domain.liquidAndSolidAssert { () => Get(s"/transactions/info/${exchange.id()}") ~> route ~> check { diff --git a/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala b/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala index 21cb7bc5394..12625590359 100644 --- a/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala +++ b/node/src/test/scala/com/wavesplatform/mining/BlockV5Test.scala @@ -454,7 +454,7 @@ class BlockV5Test extends FlatSpec with WithDomain with OptionValues with Either ): Unit = { withRocksDBWriter(settings.blockchainSettings) { blockchain => val bcu: BlockchainUpdaterImpl = - new BlockchainUpdaterImpl(blockchain, settings, time, ignoreBlockchainUpdateTriggers, (_, _) => Seq.empty) { + new BlockchainUpdaterImpl(blockchain, settings, time, ignoreBlockchainUpdateTriggers, (_, _) => Map.empty) { override def activatedFeatures: Map[Short, Int] = super.activatedFeatures -- disabledFeatures.get() } try f(bcu) diff --git a/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala b/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala index 1137945f7b1..7d3e52e4a89 100644 --- a/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala +++ b/node/src/test/scala/com/wavesplatform/mining/BlockWithMaxBaseTargetTest.scala @@ -130,7 +130,7 @@ class BlockWithMaxBaseTargetTest extends FreeSpec with WithNewDBForEachTest with ) val bcu = - new BlockchainUpdaterImpl(defaultWriter, settings, ntpTime, ignoreBlockchainUpdateTriggers, (_, _) => Seq.empty) + new BlockchainUpdaterImpl(defaultWriter, settings, ntpTime, ignoreBlockchainUpdateTriggers, (_, _) => Map.empty) val pos = PoSSelector(bcu, settings.synchronizationSettings.maxBaseTarget) val utxPoolStub = new UtxPoolImpl(ntpTime, bcu, settings0.utxSettings, settings.maxTxErrorLogSize, settings0.minerSettings.enable) diff --git a/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala b/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala index 9c5fb734ddd..9aa7ad379a9 100644 --- a/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala +++ b/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala @@ -99,7 +99,7 @@ class MiningFailuresSuite extends FlatSpec with PathMockFactory with WithNewDBFo ) var minedBlock: Block = null - (blockchainUpdater.processBlock _).when(*, *, *, *, *, *).returning(Left(BlockFromFuture(100))).repeated(10) + (blockchainUpdater.processBlock _).when(*, *, *, *, *, *).returning(Left(BlockFromFuture(100, 100))).repeated(10) (blockchainUpdater.processBlock _) .when(*, *, *, *, *, *) .onCall { (block, _, _, _, _, _) => diff --git a/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala b/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala index 2755cf490ae..fc8d2dfdf57 100644 --- a/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/BlockChallengeTest.scala @@ -5,8 +5,8 @@ import akka.http.scaladsl.server.Route import akka.http.scaladsl.testkit.* import com.wavesplatform.TestValues import com.wavesplatform.account.{Address, KeyPair, SeedKeyPair} -import com.wavesplatform.api.http.TransactionsApiRoute.{ApplicationStatus, Status} import com.wavesplatform.api.http.* +import com.wavesplatform.api.http.TransactionsApiRoute.{ApplicationStatus, Status} import com.wavesplatform.block.{Block, ChallengedHeader, MicroBlock} import com.wavesplatform.common.merkle.Merkle import com.wavesplatform.common.state.ByteStr @@ -36,9 +36,8 @@ import com.wavesplatform.test.DomainPresets.WavesSettingsOps import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.TxValidationError.{BlockAppendError, GenericError, InvalidStateHash, MicroBlockAppendError} import com.wavesplatform.transaction.assets.exchange.OrderType -import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer import com.wavesplatform.transaction.utils.EthConverters.* -import com.wavesplatform.transaction.{EthTxGenerator, Transaction, TxHelpers, TxNonNegativeAmount, TxVersion} +import com.wavesplatform.transaction.{EthTxGenerator, Transaction, TxHelpers, TxVersion} import com.wavesplatform.utils.{JsonMatchers, Schedulers, SharedSchedulerMixin} import io.netty.channel.Channel import io.netty.channel.embedded.EmbeddedChannel @@ -55,8 +54,15 @@ import java.util.concurrent.locks.ReentrantLock import scala.concurrent.duration.DurationInt import scala.concurrent.{Await, Promise} -class BlockChallengeTest extends PropSpec - with WithDomain with ScalatestRouteTest with ApiMarshallers with JsonMatchers with SharedSchedulerMixin with ParallelTestExecution with BeforeAndAfterAll { +class BlockChallengeTest + extends PropSpec + with WithDomain + with ScalatestRouteTest + with ApiMarshallers + with JsonMatchers + with SharedSchedulerMixin + with ParallelTestExecution + with BeforeAndAfterAll { implicit val appenderScheduler: SchedulerService = Scheduler.singleThread("appender") val settings: WavesSettings = @@ -479,7 +485,7 @@ class BlockChallengeTest extends PropSpec lease, TxHelpers.leaseCancel(lease.id(), recipient), TxHelpers - .massTransfer(recipient, Seq(ParsedTransfer(recipientEth.toWavesAddress, TxNonNegativeAmount.unsafeFrom(1.waves))), fee = TestValues.fee), + .massTransfer(recipient, Seq(recipientEth.toWavesAddress -> 1.waves), fee = TestValues.fee), TxHelpers.reissue(issue.asset, recipient), TxHelpers.setAssetScript(recipient, issueSmart.asset, assetScript, fee = 2.waves), TxHelpers.transfer(recipient, recipientEth.toWavesAddress, 100.waves), diff --git a/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala b/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala index aef45ce2ff6..0855f0263a2 100644 --- a/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala +++ b/node/src/test/scala/com/wavesplatform/state/RollbackSpec.scala @@ -42,7 +42,10 @@ class RollbackSpec extends FreeSpec with WithDomain { List( TxHelpers.massTransfer( sender, - Seq(ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(amount)), ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(amount))), + Seq( + recipient -> amount, + recipient -> amount + ), fee = 10000, timestamp = nextTs, version = TxVersion.V1 diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/CommonValidationTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/CommonValidationTest.scala index b7a05d10476..6fc1a3bade6 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/CommonValidationTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/CommonValidationTest.scala @@ -14,9 +14,8 @@ import com.wavesplatform.settings.{Constants, FunctionalitySettings, TestFunctio import com.wavesplatform.test.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.assets.exchange.OrderType -import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer import com.wavesplatform.transaction.transfer.* -import com.wavesplatform.transaction.{GenesisTransaction, Transaction, TxHelpers, TxNonNegativeAmount, TxVersion} +import com.wavesplatform.transaction.{GenesisTransaction, Transaction, TxHelpers, TxVersion} class CommonValidationTest extends PropSpec with WithState { @@ -198,7 +197,7 @@ class CommonValidationTest extends PropSpec with WithState { chainId = invChainId ), TxHelpers.issue(master, amount, chainId = invChainId), - TxHelpers.massTransfer(master, Seq(ParsedTransfer(invChainAddr, TxNonNegativeAmount.unsafeFrom(amount))), chainId = invChainId), + TxHelpers.massTransfer(master, Seq(invChainAddr -> amount), chainId = invChainId), TxHelpers.leaseCancel(asset.id, master, version = TxVersion.V3, chainId = invChainId), TxHelpers.setScript(master, script, version = TxVersion.V2, chainId = invChainId), TxHelpers.setAssetScript(master, asset, script, version = TxVersion.V2, chainId = invChainId), diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala index 94bc8008ade..5d97e43a286 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ExchangeTransactionDiffTest.scala @@ -32,7 +32,6 @@ import com.wavesplatform.transaction.assets.IssueTransaction import com.wavesplatform.transaction.assets.exchange.* import com.wavesplatform.transaction.assets.exchange.OrderPriceMode.{AssetDecimals, FixedDecimals, Default as DefaultPriceMode} import com.wavesplatform.transaction.smart.script.ScriptCompiler -import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer import com.wavesplatform.transaction.transfer.{MassTransferTransaction, TransferTransaction} import com.wavesplatform.transaction.utils.EthConverters.* import com.wavesplatform.utils.{EthEncoding, EthHelpers} @@ -2253,7 +2252,7 @@ class ExchangeTransactionDiffTest extends PropSpec with Inside with WithDomain w val massTransfer = TxHelpers.massTransfer( from = buyer, - to = sellers.map(seller => ParsedTransfer(seller.toAddress, TxNonNegativeAmount.unsafeFrom(issue2.quantity.value / sellOrdersCount))), + to = sellers.map(seller => seller.toAddress -> (issue2.quantity.value / sellOrdersCount)), asset = issue2.asset, fee = 1000L, version = TxVersion.V1 diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala index 5ad175a2321..7f34f4c738e 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/MassTransferTransactionDiffTest.scala @@ -10,7 +10,7 @@ import com.wavesplatform.settings.{FunctionalitySettings, TestFunctionalitySetti import com.wavesplatform.test.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer -import com.wavesplatform.transaction.{Asset, GenesisTransaction, TxHelpers, TxNonNegativeAmount, TxVersion} +import com.wavesplatform.transaction.{Asset, GenesisTransaction, TxHelpers, TxVersion} class MassTransferTransactionDiffTest extends PropSpec with WithState { @@ -30,7 +30,7 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { val setup = { val (genesis, master) = baseSetup - val transfers = (1 to transferCount).map(idx => ParsedTransfer(TxHelpers.address(idx + 1), TxNonNegativeAmount.unsafeFrom(100000L + idx))) + val transfers = (1 to transferCount).map(idx => TxHelpers.address(idx + 1) -> (100000L + idx)) val issue = TxHelpers.issue(master, ENOUGH_AMT, version = TxVersion.V1) Seq(Some(issue.id()), None).map { issueIdOpt => @@ -41,36 +41,34 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { } } - setup.foreach { - case (genesis, issue, transfer) => - assertDiffAndState(Seq(block(Seq(genesis, issue))), block(Seq(transfer)), fs) { - case (totalDiff, newState) => - assertBalanceInvariant(totalDiff) - - val totalAmount = transfer.transfers.map(_.amount.value).sum - val fees = issue.fee.value + transfer.fee.value + setup.foreach { case (genesis, issue, transfer) => + assertDiffAndState(Seq(block(Seq(genesis, issue))), block(Seq(transfer)), fs) { case (totalDiff, newState) => + assertBalanceInvariant(totalDiff) + + val totalAmount = transfer.transfers.map(_.amount.value).sum + val fees = issue.fee.value + transfer.fee.value + transfer.assetId match { + case aid @ IssuedAsset(_) => + newState.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees + newState.balance(transfer.sender.toAddress, aid) shouldBe ENOUGH_AMT - totalAmount + case Waves => + newState.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees - totalAmount + } + for (ParsedTransfer(recipient, amount) <- transfer.transfers) { + if (transfer.sender.toAddress != recipient) { transfer.assetId match { case aid @ IssuedAsset(_) => - newState.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees - newState.balance(transfer.sender.toAddress, aid) shouldBe ENOUGH_AMT - totalAmount + newState.balance(recipient.asInstanceOf[Address], aid) shouldBe amount.value case Waves => - newState.balance(transfer.sender.toAddress) shouldBe ENOUGH_AMT - fees - totalAmount - } - for (ParsedTransfer(recipient, amount) <- transfer.transfers) { - if (transfer.sender.toAddress != recipient) { - transfer.assetId match { - case aid @ IssuedAsset(_) => - newState.balance(recipient.asInstanceOf[Address], aid) shouldBe amount.value - case Waves => - newState.balance(recipient.asInstanceOf[Address]) shouldBe amount.value - } - } + newState.balance(recipient.asInstanceOf[Address]) shouldBe amount.value } + } } + } } } - import com.wavesplatform.transaction.transfer.MassTransferTransaction.{MaxTransferCount => Max} + import com.wavesplatform.transaction.transfer.MassTransferTransaction.MaxTransferCount as Max Seq(0, 1, Max) foreach testDiff // test edge cases testDiff(5) } @@ -79,7 +77,7 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { val setup = { val (genesis, master) = baseSetup val recipient = Alias.create("alias").explicitGet() - val transfer = TxHelpers.massTransfer(master, Seq(ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(100000L))), version = TxVersion.V1) + val transfer = TxHelpers.massTransfer(master, Seq(recipient -> 100000L), version = TxVersion.V1) (genesis, transfer) } @@ -96,7 +94,7 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { val recipient = TxHelpers.address(2) val asset = IssuedAsset(ByteStr.fill(32)(1)) val transfer = - TxHelpers.massTransfer(master, Seq(ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(100000L))), asset, version = TxVersion.V1) + TxHelpers.massTransfer(master, Seq(recipient -> 100000L), asset, version = TxVersion.V1) (genesis, transfer) } @@ -110,7 +108,7 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { property("MassTransfer cannot overspend funds") { val setup = { val (genesis, master) = baseSetup - val recipients = Seq(2, 3).map(idx => ParsedTransfer(TxHelpers.address(idx), TxNonNegativeAmount.unsafeFrom(ENOUGH_AMT / 2 + 1))) + val recipients = Seq(2, 3).map(idx => TxHelpers.address(idx) -> (ENOUGH_AMT / 2 + 1)) val issue = TxHelpers.issue(master, ENOUGH_AMT, version = TxVersion.V1) Seq(Some(issue.id()), None).map { issueIdOpt => val maybeAsset = Asset.fromCompatId(issueIdOpt) @@ -120,11 +118,10 @@ class MassTransferTransactionDiffTest extends PropSpec with WithState { } } - setup.foreach { - case (genesis, transfer) => - assertDiffEi(Seq(block(Seq(genesis))), block(Seq(transfer)), fs) { blockDiffEi => - blockDiffEi should produce("Attempt to transfer unavailable funds") - } + setup.foreach { case (genesis, transfer) => + assertDiffEi(Seq(block(Seq(genesis))), block(Seq(transfer)), fs) { blockDiffEi => + blockDiffEi should produce("Attempt to transfer unavailable funds") + } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/OverflowTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/OverflowTest.scala index a0da315ebac..3c2f74af965 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/OverflowTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/OverflowTest.scala @@ -10,8 +10,7 @@ import com.wavesplatform.test.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxHelpers.issue import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment -import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer -import com.wavesplatform.transaction.{TransactionType, TxHelpers, TxNonNegativeAmount} +import com.wavesplatform.transaction.{TransactionType, TxHelpers} class OverflowTest extends PropSpec with WithDomain { import DomainPresets.* @@ -43,7 +42,7 @@ class OverflowTest extends PropSpec with WithDomain { numPairs(massTransferFee).foreach { case (recipientBalance, transferAmount) => val balances = Seq(AddrWithBalance(sender.toAddress, Long.MaxValue), AddrWithBalance(recipient, recipientBalance)) withDomain(RideV5, balances) { d => - d.appendBlockE(TxHelpers.massTransfer(sender, Seq(ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(transferAmount))), fee = massTransferFee)) should produce( + d.appendBlockE(TxHelpers.massTransfer(sender, Seq(recipient -> transferAmount), fee = massTransferFee)) should produce( "Waves balance sum overflow" ) } @@ -55,8 +54,8 @@ class OverflowTest extends PropSpec with WithDomain { (the[Exception] thrownBy TxHelpers.massTransfer( sender, Seq( - ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(balance1)), - ParsedTransfer(recipient, TxNonNegativeAmount.unsafeFrom(balance2)) + recipient -> balance1, + recipient -> balance2 ) )).getMessage shouldBe "OverflowError" } @@ -115,35 +114,33 @@ class OverflowTest extends PropSpec with WithDomain { | ] """.stripMargin ) - numPairs(0).foreach { - case (amount1, amount2) => - withDomain(RideV5, AddrWithBalance.enoughBalances(sender, recipientKp)) { d => - d.appendBlock(TxHelpers.setScript(recipientKp, dApp(amount1, amount2))) - val invoke = TxHelpers.invoke(recipient, invoker = sender) - d.appendAndAssertFailed(invoke, "ScriptTransfer overflow") - } + numPairs(0).foreach { case (amount1, amount2) => + withDomain(RideV5, AddrWithBalance.enoughBalances(sender, recipientKp)) { d => + d.appendBlock(TxHelpers.setScript(recipientKp, dApp(amount1, amount2))) + val invoke = TxHelpers.invoke(recipient, invoker = sender) + d.appendAndAssertFailed(invoke, "ScriptTransfer overflow") + } } } property("invoke Reissue overflow") { - numPairs(0).foreach { - case (amount1, amount2) => - withDomain(RideV5, AddrWithBalance.enoughBalances(sender, recipientKp)) { d => - val issueTx = issue(recipientKp, amount = amount1) - val asset = IssuedAsset(issueTx.id()) - val dApp = TestCompiler(V5).compileContract( - s""" - | @Callable(i) - | func default() = - | [ - | Reissue(base58'$asset', $amount2, false) - | ] + numPairs(0).foreach { case (amount1, amount2) => + withDomain(RideV5, AddrWithBalance.enoughBalances(sender, recipientKp)) { d => + val issueTx = issue(recipientKp, amount = amount1) + val asset = IssuedAsset(issueTx.id()) + val dApp = TestCompiler(V5).compileContract( + s""" + | @Callable(i) + | func default() = + | [ + | Reissue(base58'$asset', $amount2, false) + | ] """.stripMargin - ) - val invoke = TxHelpers.invoke(recipient, invoker = sender) - d.appendBlock(issueTx, TxHelpers.setScript(recipientKp, dApp)) - d.appendAndAssertFailed(invoke, "Asset total value overflow") - } + ) + val invoke = TxHelpers.invoke(recipient, invoker = sender) + d.appendBlock(issueTx, TxHelpers.setScript(recipientKp, dApp)) + d.appendAndAssertFailed(invoke, "Asset total value overflow") + } } } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ScriptComplexityCountTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ScriptComplexityCountTest.scala index 82808fcb643..3551e6b70fa 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ScriptComplexityCountTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ScriptComplexityCountTest.scala @@ -12,8 +12,7 @@ import com.wavesplatform.test.* import com.wavesplatform.test.DomainPresets.RideV6 import com.wavesplatform.transaction.TxHelpers.* import com.wavesplatform.transaction.assets.exchange.* -import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer -import com.wavesplatform.transaction.{GenesisTransaction, Transaction, TxNonNegativeAmount, TxVersion} +import com.wavesplatform.transaction.{GenesisTransaction, Transaction, TxVersion} import org.scalatest.Inside // noinspection NameBooleanParameters @@ -57,7 +56,7 @@ class ScriptComplexityCountTest extends PropSpec with WithDomain with Inside { val dataTx = data(master, Seq(BooleanDataEntry("q", true))) val tr1 = transfer(master, acc.toAddress, 10000000000L) val tr2 = transfer(master, acc.toAddress, additionalAmount, issueScr.asset) - val massTransfers = Seq(ParsedTransfer(acc.toAddress, TxNonNegativeAmount.unsafeFrom(1))) + val massTransfers = Seq(acc.toAddress -> 1L) val mt1 = massTransfer(master, massTransfers, version = TxVersion.V1, fee = 1.waves) val mt2 = massTransfer(master, massTransfers, issueScr.asset, fee = 1.waves) val l = lease(master, acc.toAddress, 1) diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala index 3e5b88c01c2..c6e3a75610e 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/ci/sync/SyncInvokeLeaseTest.scala @@ -45,11 +45,11 @@ class SyncInvokeLeaseTest extends PropSpec with WithDomain { val lease1 = d.liquidDiff.leaseState.head._2 lease1.status shouldBe a[Cancelled] lease1.recipientAddress shouldBe dApp2Address - lease1.amount shouldBe 1 + lease1.amount.value shouldBe 1 val lease2 = d.liquidDiff.leaseState.last._2 lease2.status shouldBe Active lease2.recipientAddress shouldBe dApp2Address - lease2.amount shouldBe 555 + lease2.amount.value shouldBe 555 } } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CommonFunctionsTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CommonFunctionsTest.scala index 846ea80cec3..7c52bf51ab5 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CommonFunctionsTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/CommonFunctionsTest.scala @@ -6,13 +6,11 @@ import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.lang.Testing.* import com.wavesplatform.lang.v1.compiler.Terms.CONST_BYTESTR import com.wavesplatform.lang.v1.evaluator.ctx.impl.* -import com.wavesplatform.test.* import com.wavesplatform.state.IntegerDataEntry -import com.wavesplatform.test.{NumericExt, PropSpec} +import com.wavesplatform.test.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.transfer.MassTransferTransaction -import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer -import com.wavesplatform.transaction.{TxHelpers, TxNonNegativeAmount, TxVersion} +import com.wavesplatform.transaction.{TxHelpers, TxVersion} import org.scalatest.Assertions import shapeless.Coproduct @@ -43,11 +41,11 @@ class CommonFunctionsTest extends PropSpec { val result = runScript( """ - |match tx { - | case ttx : TransferTransaction => isDefined(ttx.assetId) - | case _ => throw() - | } - |""".stripMargin, + |match tx { + | case ttx : TransferTransaction => isDefined(ttx.assetId) + | case _ => throw() + | } + |""".stripMargin, Coproduct(transfer) ) result shouldEqual evaluated(transfer.assetId != Waves) @@ -85,25 +83,25 @@ class CommonFunctionsTest extends PropSpec { resultAmount shouldBe evaluated(massTransfer.transfers(0).amount.value) val resultAddress = runScript( """ - |match tx { - | case mttx : MassTransferTransaction => - | match mttx.transfers[0].recipient { - | case address : Address => address.bytes - | case _ => throw() - | } - | case _ => throw() - | } - |""".stripMargin, + |match tx { + | case mttx : MassTransferTransaction => + | match mttx.transfers[0].recipient { + | case address : Address => address.bytes + | case _ => throw() + | } + | case _ => throw() + | } + |""".stripMargin, Coproduct(massTransfer) ) resultAddress shouldBe evaluated(ByteStr(massTransfer.transfers(0).address.bytes)) val resultLen = runScript( """ - |match tx { - | case mttx : MassTransferTransaction => size(mttx.transfers) - | case _ => throw() - | } - |""".stripMargin, + |match tx { + | case mttx : MassTransferTransaction => size(mttx.transfers) + | case _ => throw() + | } + |""".stripMargin, Coproduct(massTransfer) ) resultLen shouldBe evaluated(massTransfer.transfers.size.toLong) @@ -206,14 +204,14 @@ class CommonFunctionsTest extends PropSpec { property("shadowing of variable considered external") { runScript( s""" - |match { - | let aaa = 1 - | tx - |} { - | case tx: TransferTransaction => tx == tx - | case _ => throw() - | } - |""".stripMargin + |match { + | let aaa = 1 + | tx + |} { + | case tx: TransferTransaction => tx == tx + | case _ => throw() + | } + |""".stripMargin ) should produce("already defined") } @@ -299,7 +297,7 @@ class CommonFunctionsTest extends PropSpec { val recipients = (1 to 10).map(idx => TxHelpers.address(idx + 1)) TxHelpers.massTransfer( from = sender, - to = recipients.map(addr => ParsedTransfer(addr, TxNonNegativeAmount.unsafeFrom(1.waves))), + to = recipients.map(addr => (addr, 1.waves)), version = TxVersion.V1 ) } diff --git a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/MatcherBlockchainTest.scala b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/MatcherBlockchainTest.scala index 96a8a45c238..68aab412353 100644 --- a/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/MatcherBlockchainTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/diffs/smart/predef/MatcherBlockchainTest.scala @@ -13,7 +13,6 @@ import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.traits.domain.Recipient import com.wavesplatform.settings.BlockchainSettings import com.wavesplatform.state.* -import com.wavesplatform.state.reader.LeaseDetails import com.wavesplatform.test.PropSpec import com.wavesplatform.transaction.Asset.Waves import com.wavesplatform.transaction.smart.script.ScriptRunner diff --git a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala index 4c9999a1a59..ead04ad34b7 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala @@ -5,7 +5,6 @@ import com.wavesplatform.TestValues.fee import com.wavesplatform.account.{Address, Alias, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 -import com.wavesplatform.crypto.KeyLength import com.wavesplatform.db.WithDomain import com.wavesplatform.lang.directives.values.V6 import com.wavesplatform.lang.v1.compiler.TestCompiler @@ -15,15 +14,12 @@ import com.wavesplatform.state.* import com.wavesplatform.state.TxMeta.Status.{Failed, Succeeded} import com.wavesplatform.state.diffs.BlockDiffer.CurrentBlockFeePart import com.wavesplatform.state.diffs.ENOUGH_AMT -import com.wavesplatform.state.LeaseDetails -import com.wavesplatform.state.LeaseDetails.Status.{Active, Cancelled} import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.test.{NumericExt, PropSpec} import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxHelpers.* import com.wavesplatform.transaction.assets.exchange.OrderType.{BUY, SELL} -import com.wavesplatform.transaction.transfer.MassTransferTransaction.ParsedTransfer -import com.wavesplatform.transaction.{EthTxGenerator, Transaction, TxHelpers, TxNonNegativeAmount} +import com.wavesplatform.transaction.{EthTxGenerator, Transaction, TxHelpers} import scala.collection.immutable.VectorMap import scala.math.pow @@ -50,7 +46,11 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { if (failed) d.appendAndAssertFailed(tx) else d.appendAndAssertSucceed(tx) d.appendBlock() val status = if (failed) Failed else Succeeded - PBSnapshots.fromProtobuf(d.rocksDBWriter.transactionSnapshot(tx.id()).get, tx.id(), d.blockchain.height) shouldBe (expectedSnapshotWithMiner, status) + PBSnapshots.fromProtobuf( + d.rocksDBWriter.transactionSnapshot(tx.id()).get, + tx.id(), + d.blockchain.height + ) shouldBe (expectedSnapshotWithMiner, status) } // Genesis @@ -215,8 +215,8 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { sender, fee = fee, to = Seq( - ParsedTransfer(TxHelpers.signer(4).toAddress, TxNonNegativeAmount(123)), - ParsedTransfer(TxHelpers.signer(5).toAddress, TxNonNegativeAmount(456)) + TxHelpers.signer(4).toAddress -> 123, + TxHelpers.signer(5).toAddress -> 456 ) ), StateSnapshot( diff --git a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala index e1aebcc9597..f8846df5db8 100644 --- a/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala +++ b/node/src/test/scala/com/wavesplatform/transaction/TxHelpers.scala @@ -107,14 +107,16 @@ object TxHelpers { def massTransfer( from: KeyPair = defaultSigner, - to: Seq[ParsedTransfer] = Seq(ParsedTransfer(secondAddress, TxNonNegativeAmount.unsafeFrom(1.waves))), + to: Seq[(AddressOrAlias, Long)] = Seq(secondAddress -> 1.waves), asset: Asset = Waves, fee: Long = FeeConstants(TransactionType.MassTransfer) * FeeUnit, timestamp: TxTimestamp = timestamp, version: Byte = TxVersion.V2, chainId: Byte = AddressScheme.current.chainId ): MassTransferTransaction = - MassTransferTransaction.selfSigned(version, from, asset, to, fee, timestamp, ByteStr.empty, chainId).explicitGet() + MassTransferTransaction.selfSigned(version, from, asset, + to.map { case (r, a) => MassTransferTransaction.ParsedTransfer(r, TxNonNegativeAmount.unsafeFrom(a)) }, + fee, timestamp, ByteStr.empty, chainId).explicitGet() def issue( issuer: KeyPair = defaultSigner,