diff --git a/lang/shared/src/main/scala/com/wavesplatform/common/state/ByteStr.scala b/lang/shared/src/main/scala/com/wavesplatform/common/state/ByteStr.scala index be469868ec8..7cf6ab579db 100644 --- a/lang/shared/src/main/scala/com/wavesplatform/common/state/ByteStr.scala +++ b/lang/shared/src/main/scala/com/wavesplatform/common/state/ByteStr.scala @@ -67,7 +67,7 @@ object ByteStr { def fromLong(longValue: Long): ByteStr = { val buf = new Array[Byte](8) - var b = longValue + var b = longValue for (i <- (buf.length - 1) to 0 by -1) { buf(i) = b.toByte diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/smartcontract/Ride4DAppsActivationTestSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/smartcontract/Ride4DAppsActivationTestSuite.scala index 4245484a3b3..d5342a2e685 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/smartcontract/Ride4DAppsActivationTestSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/smartcontract/Ride4DAppsActivationTestSuite.scala @@ -4,16 +4,16 @@ import com.typesafe.config.{Config, ConfigFactory} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.it.NodeConfigs -import com.wavesplatform.it.api.SyncHttpApi._ -import com.wavesplatform.it.sync._ +import com.wavesplatform.it.api.SyncHttpApi.* +import com.wavesplatform.it.sync.* import com.wavesplatform.it.transactions.BaseTransactionSuite import com.wavesplatform.lang.v1.estimator.v2.ScriptEstimatorV2 -import com.wavesplatform.test._ -import com.wavesplatform.transaction.Asset +import com.wavesplatform.test.* +import com.wavesplatform.transaction.{Asset, AssetIdLength} import com.wavesplatform.transaction.smart.script.ScriptCompiler import org.scalatest.CancelAfterFailure -import scala.concurrent.duration._ +import scala.concurrent.duration.* class Ride4DAppsActivationTestSuite extends BaseTransactionSuite with CancelAfterFailure { private val estimator = ScriptEstimatorV2 @@ -113,7 +113,7 @@ class Ride4DAppsActivationTestSuite extends BaseTransactionSuite with CancelAfte test("can't set script with user function to asset before Ride4DApps activation") { assertBadRequestAndMessage( sender.setAssetScript( - Asset.IssuedAsset(ByteStr("Test".getBytes("UTF-8"))).id.toString, + Asset.IssuedAsset(ByteStr.fill(AssetIdLength)(1)).id.toString, smartAcc, issueFee, Some(scriptV2) diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/AliasTransactionSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/AliasTransactionSuite.scala index bf705ee838b..84120ef17c1 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/AliasTransactionSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/AliasTransactionSuite.scala @@ -5,6 +5,7 @@ import com.typesafe.config.Config import scala.util.{Random, Try} import com.wavesplatform.account.{AddressScheme, KeyPair} +import com.wavesplatform.api.http.ApiError.WrongJson import com.wavesplatform.features.BlockchainFeatures.RideV6 import com.wavesplatform.it.NodeConfigs import com.wavesplatform.it.NodeConfigs.Default @@ -120,7 +121,7 @@ class AliasTransactionSuite extends BaseTransactionSuite with TableDrivenPropert ("aliasName", "message"), ("", "Alias '' length should be between 4 and 30"), ("abc", "Alias 'abc' length should be between 4 and 30"), - (null, "failed to parse json message"), + (null, WrongJson.WrongJsonDataMessage), ("morethen_thirtycharactersinline", "Alias 'morethen_thirtycharactersinline' length should be between 4 and 30"), ("~!|#$%^&*()_+=\";:/?><|\\][{}", "Alias should contain only following characters: -.0123456789@_abcdefghijklmnopqrstuvwxyz"), ("multilnetest\ntest", "Alias should contain only following characters: -.0123456789@_abcdefghijklmnopqrstuvwxyz"), @@ -143,7 +144,7 @@ class AliasTransactionSuite extends BaseTransactionSuite with TableDrivenPropert val aliasFee = createAlias(thirdKeyPair, thirdAddressAlias) val aliasFull = fullAliasByAddress(thirdAddress, thirdAddressAlias) - //lease maximum value, to pass next thirdAddress + // lease maximum value, to pass next thirdAddress val leasingAmount = balance1 - minFee - 0.5.waves val leasingTx = sender.lease(firstKeyPair, aliasFull, leasingAmount, minFee).id @@ -154,7 +155,7 @@ class AliasTransactionSuite extends BaseTransactionSuite with TableDrivenPropert } - //previous test should not be commented to run this one + // previous test should not be commented to run this one test("Not able to create alias when insufficient funds") { for (v <- aliasTxSupportedVersions) { val balance = miner.accountBalances(firstAddress)._1 @@ -177,7 +178,15 @@ class AliasTransactionSuite extends BaseTransactionSuite with TableDrivenPropert ) val kp = KeyPair(Longs.toByteArray(Random.nextLong())) - val cat = CreateAliasTransaction(3.toByte, notMiner.publicKey, "abc12345", TxPositiveAmount.unsafeFrom(0.005.waves), System.currentTimeMillis(), Proofs.empty, AddressScheme.current.chainId) + val cat = CreateAliasTransaction( + 3.toByte, + notMiner.publicKey, + "abc12345", + TxPositiveAmount.unsafeFrom(0.005.waves), + System.currentTimeMillis(), + Proofs.empty, + AddressScheme.current.chainId + ) val signedCreateAlias = cat.copy( proofs = cat.signWith(notMiner.keyPair.privateKey).proofs.proofs ++ cat.signWith(kp.privateKey).proofs.proofs ) diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/DataTransactionSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/DataTransactionSuite.scala index 1efc4515d29..841d676cc9e 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/DataTransactionSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/DataTransactionSuite.scala @@ -3,23 +3,23 @@ package com.wavesplatform.it.sync.transactions import com.google.common.primitives.Ints import com.typesafe.config.Config import com.wavesplatform.account.{AddressScheme, KeyPair} -import com.wavesplatform.api.http.ApiError.{CustomValidationError, TooBigArrayAllocation} +import com.wavesplatform.api.http.ApiError.{CustomValidationError, TooBigArrayAllocation, WrongJson} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base58, EitherExt2} import com.wavesplatform.it.NodeConfigs -import com.wavesplatform.it.api.SyncHttpApi._ +import com.wavesplatform.it.api.SyncHttpApi.* import com.wavesplatform.it.api.{TransactionInfo, UnexpectedStatusCodeException} -import com.wavesplatform.it.sync.{calcDataFee, minFee, _} +import com.wavesplatform.it.sync.{calcDataFee, minFee, *} import com.wavesplatform.it.transactions.BaseTransactionSuite -import com.wavesplatform.test._ +import com.wavesplatform.test.* import com.wavesplatform.lang.v1.estimator.ScriptEstimatorV1 import com.wavesplatform.state.{BinaryDataEntry, BooleanDataEntry, DataEntry, EmptyDataEntry, IntegerDataEntry, StringDataEntry} import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.transaction.{DataTransaction, Proofs, TxVersion} import org.scalatest.{Assertion, Assertions, EitherValues} -import play.api.libs.json._ +import play.api.libs.json.* -import scala.concurrent.duration._ +import scala.concurrent.duration.* import scala.util.{Failure, Random, Try} class DataTransactionSuite extends BaseTransactionSuite with EitherValues { @@ -53,10 +53,10 @@ class DataTransactionSuite extends BaseTransactionSuite with EitherValues { ScriptCompiler .compile( """{-# STDLIB_VERSION 2 #-} - |{-# CONTENT_TYPE EXPRESSION #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - | - |true""".stripMargin, + |{-# CONTENT_TYPE EXPRESSION #-} + |{-# SCRIPT_TYPE ACCOUNT #-} + | + |true""".stripMargin, ScriptEstimatorV1 ) .explicitGet() @@ -127,7 +127,7 @@ class DataTransactionSuite extends BaseTransactionSuite with EitherValues { CustomValidationError("Duplicated keys found") ) - //able to "remove" nonexistent key (account state won't be changed, but transaction should be succesfully broadcasted) + // able to "remove" nonexistent key (account state won't be changed, but transaction should be succesfully broadcasted) sender.broadcastData( sender.keyPair, List(EmptyDataEntry("nonexistentkey")), @@ -224,7 +224,7 @@ class DataTransactionSuite extends BaseTransactionSuite with EitherValues { } test("max transaction size") { - //Max size of transaction V1 + // Max size of transaction V1 val maxKeySizeV1 = 100 val key = "\u6fae" * (maxKeySizeV1 - 1) val data = List.tabulate(26)(n => BinaryDataEntry(key + n.toChar, ByteStr(Array.fill(5599)(n.toByte)))) @@ -232,7 +232,7 @@ class DataTransactionSuite extends BaseTransactionSuite with EitherValues { val txId = sender.putData(firstKeyPair, data, fee, version = TxVersion.V1).id nodes.waitForTransaction(txId) - //Max size of transaction V2 + // Max size of transaction V2 val maxKeySizeV2 = 400 val key2 = "u" * (maxKeySizeV2 - 1) val data2 = List.tabulate(5)(n => BinaryDataEntry(key2 + n.toChar, ByteStr(Array.fill(Short.MaxValue)(n.toByte)))) @@ -312,9 +312,9 @@ class DataTransactionSuite extends BaseTransactionSuite with EitherValues { val keys = Seq("int", "bool", "int", "blob", "?&$#^123\\/.a:;'\"\r\n\t\u0000|%è&", "str", "inexisted_key", tooBigKey) val values = Seq[Any](-127, false, -127, ByteStr(Array[Byte](127.toByte, 0, 1, 1)), "specïal", "BBBB") - val list = sender.getDataList(secondAddress, keys *).map(_.value) - val jsonList = sender.getDataListJson(secondAddress, keys *).map(_.value) - val postList = sender.getDataListPost(secondAddress, keys *).map(_.value) + val list = sender.getDataList(secondAddress, keys*).map(_.value) + val jsonList = sender.getDataListJson(secondAddress, keys*).map(_.value) + val postList = sender.getDataListPost(secondAddress, keys*).map(_.value) list shouldBe values jsonList shouldBe list @@ -411,7 +411,7 @@ class DataTransactionSuite extends BaseTransactionSuite with EitherValues { def id(obj: JsObject): String = obj.value("id").as[String] val noProof = request - "proofs" - assertBadRequestAndResponse(sender.postJson("/transactions/broadcast", noProof), "failed to parse json message.*proofs.*missing") + assertBadRequestAndResponse(sender.postJson("/transactions/broadcast", noProof), s"${WrongJson.WrongJsonDataMessage}.*proofs.*missing") nodes.foreach(_.ensureTxDoesntExist(id(noProof))) val badProof = request ++ Json.obj("proofs" -> Seq(Base58.encode(Array.fill(64)(Random.nextInt().toByte)))) @@ -479,16 +479,15 @@ class DataTransactionSuite extends BaseTransactionSuite with EitherValues { test("try to make address with 1000 DataEntries") { for (v <- dataTxSupportedVersions) { - val dataSet = 0 until 200 flatMap ( - i => - List( - IntegerDataEntry(s"int$i", 1000 + i), - BooleanDataEntry(s"bool$i", false), - BinaryDataEntry(s"blob$i", ByteStr(Array[Byte](127.toByte, 0, 1, 1))), - StringDataEntry(s"str$i", s"hi there! + $i"), - IntegerDataEntry(s"integer$i", 1000 - i) - ) + val dataSet = 0 until 200 flatMap (i => + List( + IntegerDataEntry(s"int$i", 1000 + i), + BooleanDataEntry(s"bool$i", false), + BinaryDataEntry(s"blob$i", ByteStr(Array[Byte](127.toByte, 0, 1, 1))), + StringDataEntry(s"str$i", s"hi there! + $i"), + IntegerDataEntry(s"integer$i", 1000 - i) ) + ) val txIds = dataSet.grouped(100).map(_.toList).map(data => sender.putData(fourthKeyPair, data, calcDataFee(data, v), version = v).id) txIds foreach nodes.waitForTransaction diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/MassTransferTransactionSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/MassTransferTransactionSuite.scala index 4f2a823fbd4..4813573fc8b 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/MassTransferTransactionSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/MassTransferTransactionSuite.scala @@ -2,6 +2,7 @@ package com.wavesplatform.it.sync.transactions import com.google.protobuf.ByteString import com.wavesplatform.account.{Address, AddressScheme, Alias} +import com.wavesplatform.api.http.ApiError.WrongJson import com.wavesplatform.api.http.requests.{MassTransferRequest, SignedMassTransferRequest} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 @@ -262,7 +263,7 @@ class MassTransferTransactionSuite extends BaseTransactionSuite { def id(obj: JsObject) = obj.value("id").as[String] val noProof = signedMassTransfer - "proofs" - assertBadRequestAndResponse(sender.postJson("/transactions/broadcast", noProof), "failed to parse json message.*proofs.*missing") + assertBadRequestAndResponse(sender.postJson("/transactions/broadcast", noProof), s"${WrongJson.WrongJsonDataMessage}.*proofs.*missing") nodes.foreach(_.ensureTxDoesntExist(id(noProof))) val badProof = signedMassTransfer ++ Json.obj("proofs" -> Seq(fakeSignature.toString)) diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SetAssetScriptTransactionSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SetAssetScriptTransactionSuite.scala index ba2d763c855..25c3e4b0a4c 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SetAssetScriptTransactionSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SetAssetScriptTransactionSuite.scala @@ -98,11 +98,11 @@ class SetAssetScriptTransactionSuite extends BaseTransactionSuite { ) assertApiError( broadcastSetAssetScriptJson(assetWOScript, firstKeyPair, setAssetScriptFee, version = v), - AssertiveApiError(WrongJson.Id, "failed to parse json message", matchMessage = true) + AssertiveApiError(WrongJson.Id, WrongJson.WrongJsonDataMessage, matchMessage = true) ) assertApiError( broadcastSetAssetScriptJson(assetWOScript, firstKeyPair, setAssetScriptFee, Some(""), version = v), - AssertiveApiError(WrongJson.Id, "failed to parse json message", matchMessage = true) + AssertiveApiError(WrongJson.Id, WrongJson.WrongJsonDataMessage, matchMessage = true) ) miner.assertBalances(firstAddress, balance, eff) } @@ -158,7 +158,7 @@ class SetAssetScriptTransactionSuite extends BaseTransactionSuite { } assertApiError(broadcastSetAssetScriptJson(assetWAnotherOwner, secondKeyPair, setAssetScriptFee, Some(""), version = v)) { error => error.id shouldBe WrongJson.Id - error.message shouldBe "failed to parse json message" + error.message shouldBe WrongJson.WrongJsonDataMessage } } } @@ -180,10 +180,10 @@ class SetAssetScriptTransactionSuite extends BaseTransactionSuite { test("sender's waves balance is decreased by fee") { val script2 = ScriptCompiler( s""" - |match tx { - | case _: SetAssetScriptTransaction => true - | case _ => false - |} + |match tx { + | case _: SetAssetScriptTransaction => true + | case _ => false + |} """.stripMargin, isAssetScript = true, estimator @@ -253,7 +253,7 @@ class SetAssetScriptTransactionSuite extends BaseTransactionSuite { ), ( sastx(assetId = IssuedAsset(ByteStr.decodeBase58("9ekQuYn92natMnMq8KqeGK3Nn7cpKd3BvPEGgD6fFyyz9ekQuYn92natMnMq8").get)), - AssertiveApiError(WrongJson.Id, "failed to parse json", StatusCodes.BadRequest, true) + AssertiveApiError(WrongJson.Id, WrongJson.WrongJsonDataMessage, StatusCodes.BadRequest, true) ), ( sastx(assetId = IssuedAsset(ByteStr.decodeBase58("9ekQuYn92natMnMq8KqeGK3Nn7cpKd3BvPEGgD6fFyyz").get)), @@ -290,7 +290,7 @@ class SetAssetScriptTransactionSuite extends BaseTransactionSuite { val noProof = request - "proofs" assertApiError(sender.postJson("/transactions/broadcast", noProof)) { error => - error.message should include regex "failed to parse json message" + error.message should include regex WrongJson.WrongJsonDataMessage val validationErrors = (error.json \ "validationErrors" \ "obj.proofs").as[JsArray].value.flatMap(json => (json \ "msg").as[List[String]]) validationErrors should contain("error.path.missing") @@ -313,10 +313,10 @@ class SetAssetScriptTransactionSuite extends BaseTransactionSuite { test("try to update script to null") { for (v <- setAssetScrTxSupportedVersions) { assertApiError(broadcastSetAssetScriptJson(assetWScript, firstKeyPair, setAssetScriptFee, version = v)) { error => - error.message should include regex "failed to parse json message" + error.message should include regex WrongJson.WrongJsonDataMessage } assertApiError(broadcastSetAssetScriptJson(assetWScript, firstKeyPair, setAssetScriptFee, Some(""), version = v)) { error => - error.message should include regex "failed to parse json message" + error.message should include regex WrongJson.WrongJsonDataMessage } } } @@ -401,7 +401,7 @@ class SetAssetScriptTransactionSuite extends BaseTransactionSuite { nodes.waitForHeightAriseAndTxPresent(tx) - //try to change unchangeable script + // try to change unchangeable script val nonIssuerUnsignedTx2 = SetAssetScriptTransaction( version = v, accountA.publicKey, diff --git a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SignAndBroadcastApiSuite.scala b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SignAndBroadcastApiSuite.scala index f4aa5d312b0..e7ec0682e0f 100644 --- a/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SignAndBroadcastApiSuite.scala +++ b/node-it/src/test/scala/com/wavesplatform/it/sync/transactions/SignAndBroadcastApiSuite.scala @@ -2,6 +2,7 @@ package com.wavesplatform.it.sync.transactions import com.typesafe.config.Config import com.wavesplatform.account.{AddressScheme, PublicKey} +import com.wavesplatform.api.http.ApiError.WrongJson import com.wavesplatform.api.http.requests.TransferRequest import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base58, EitherExt2} @@ -59,9 +60,9 @@ class SignAndBroadcastApiSuite extends BaseTransactionSuite with NTPTime with Be for (v <- supportedVersions) { val json = Json.obj("type" -> CreateAliasTransaction.typeId, "sender" -> firstAddress, "alias" -> "alias", "fee" -> 100000) val js = if (Option(v).isDefined) json ++ Json.obj("version" -> v) else json - assertSignBadJson(js - "type", "failed to parse json message") + assertSignBadJson(js - "type", WrongJson.WrongJsonDataMessage) assertSignBadJson(js + ("type" -> JsNumber(-100)), "Bad transaction type") - assertSignBadJson(js - "alias", "failed to parse json message") + assertSignBadJson(js - "alias", WrongJson.WrongJsonDataMessage) } val obsoleteTx = @@ -78,7 +79,7 @@ class SignAndBroadcastApiSuite extends BaseTransactionSuite with NTPTime with Be "fee" -> 100000, "attachment" -> "W" * 524291 ) - assertSignBadJson(bigBaseTx, "failed to parse json message") + assertSignBadJson(bigBaseTx, WrongJson.WrongJsonDataMessage) } test("/transaction/calculateFee should handle coding size limit") { @@ -92,7 +93,7 @@ class SignAndBroadcastApiSuite extends BaseTransactionSuite with NTPTime with Be "amount" -> 1, "assetId" -> "W" * 524291 ) - assertBadRequestAndMessage(sender.calculateFee(json).feeAmount, "failed to parse json message") + assertBadRequestAndMessage(sender.calculateFee(json).feeAmount, WrongJson.WrongJsonDataMessage) } } @@ -137,10 +138,10 @@ class SignAndBroadcastApiSuite extends BaseTransactionSuite with NTPTime with Be assertBroadcastBadJson(jsonV2, "Proof doesn't validate") for (j <- List(jsonV1, jsonV2)) { - assertBroadcastBadJson(j - "type", "failed to parse json message") + assertBroadcastBadJson(j - "type", WrongJson.WrongJsonDataMessage) assertBroadcastBadJson(j - "type" + ("type" -> Json.toJson(88)), "Bad transaction type") assertBroadcastBadJson(j - "chainId" + ("chainId" -> Json.toJson(123)), "Wrong chain-id") - assertBroadcastBadJson(j - "alias", "failed to parse json message") + assertBroadcastBadJson(j - "alias", WrongJson.WrongJsonDataMessage) } } diff --git a/node/src/main/scala/com/wavesplatform/api/http/ApiError.scala b/node/src/main/scala/com/wavesplatform/api/http/ApiError.scala index f0cae25590c..d88819e0837 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/ApiError.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/ApiError.scala @@ -77,11 +77,12 @@ object ApiError { final case class WrongJson( cause: Option[Throwable] = None, - errors: scala.collection.Seq[(JsPath, scala.collection.Seq[JsonValidationError])] = Seq.empty + errors: scala.collection.Seq[(JsPath, scala.collection.Seq[JsonValidationError])] = Seq.empty, + msg: Option[String] = None ) extends ApiError { override val id = WrongJson.Id override val code = StatusCodes.BadRequest - override val message: String = WrongJson.Message + override val message: String = msg.getOrElse(WrongJson.WrongJsonMessage) override lazy val json: JsObject = Json.obj( "error" -> id, "message" -> message, @@ -90,8 +91,9 @@ object ApiError { ) } case object WrongJson { - val Id = 1 - val Message = "failed to parse json message" + val Id = 1 + val WrongJsonMessage = "failed to parse json message" + val WrongJsonDataMessage = "json data validation error, see validationErrors for details" } // API Auth diff --git a/node/src/main/scala/com/wavesplatform/api/http/package.scala b/node/src/main/scala/com/wavesplatform/api/http/package.scala index 5ff46fc819a..635aaa1953c 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/package.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/package.scala @@ -146,10 +146,10 @@ package object http { .result() val jsonExceptionHandler: ExceptionHandler = ExceptionHandler { - case JsResultException(err) => complete(WrongJson(errors = err)) - case PlayJsonException(cause, errors) => complete(WrongJson(cause, errors)) - case e: IllegalArgumentException => complete(ApiError.fromValidationError(GenericError(e))) - case e: AssertionError => complete(ApiError.fromValidationError(GenericError(e))) + case JsResultException(err) => complete(WrongJson(errors = err, msg = Some(WrongJson.WrongJsonDataMessage))) + case PlayJsonException(cause, errors) => complete(WrongJson(cause, errors)) + case e: IllegalArgumentException => complete(ApiError.fromValidationError(GenericError(e))) + case e: AssertionError => complete(ApiError.fromValidationError(GenericError(e))) case e: ExecutionException if e.getCause != null && e.getCause != e => jsonExceptionHandler(e.getCause) } diff --git a/node/src/main/scala/com/wavesplatform/api/http/requests/InvokeScriptRequest.scala b/node/src/main/scala/com/wavesplatform/api/http/requests/InvokeScriptRequest.scala index 84e99e1d587..1ef29ba8028 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/requests/InvokeScriptRequest.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/requests/InvokeScriptRequest.scala @@ -8,7 +8,7 @@ import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.v1.FunctionHeader import com.wavesplatform.lang.v1.compiler.Terms.* -import com.wavesplatform.transaction.Proofs +import com.wavesplatform.transaction.{Asset, Proofs} import com.wavesplatform.transaction.smart.{InvokeScriptTransaction, InvokeTransaction} import play.api.libs.json.* @@ -94,7 +94,7 @@ case class SignedInvokeScriptRequest( version: Option[Byte], senderPublicKey: String, fee: Long, - feeAssetId: Option[String], + feeAssetId: Option[Asset], dApp: String, call: Option[InvokeScriptRequest.FunctionCallPart], payment: Option[Seq[InvokeScriptTransaction.Payment]], @@ -105,7 +105,6 @@ case class SignedInvokeScriptRequest( for { _sender <- PublicKey.fromBase58String(senderPublicKey) _dappAddress <- AddressOrAlias.fromString(dApp, checkChainId = false) - _feeAssetId <- parseBase58ToAsset(feeAssetId.filter(_.nonEmpty), "invalid.feeAssetId") t <- InvokeScriptTransaction.create( version.getOrElse(2.toByte), _sender, @@ -113,7 +112,7 @@ case class SignedInvokeScriptRequest( call.map(InvokeScriptRequest.buildFunctionCall).filterNot(_ == InvokeTransaction.DefaultCall), payment.getOrElse(Seq()), fee, - _feeAssetId, + feeAssetId.getOrElse(Asset.Waves), timestamp, proofs, chainId.getOrElse(_dappAddress.chainId) diff --git a/node/src/main/scala/com/wavesplatform/api/http/requests/SignedMassTransferRequest.scala b/node/src/main/scala/com/wavesplatform/api/http/requests/SignedMassTransferRequest.scala index e27ba6aed9d..227c842a041 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/requests/SignedMassTransferRequest.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/requests/SignedMassTransferRequest.scala @@ -3,18 +3,18 @@ package com.wavesplatform.api.http.requests import com.wavesplatform.account.PublicKey import com.wavesplatform.common.state.ByteStr import com.wavesplatform.lang.ValidationError -import com.wavesplatform.transaction.Proofs +import com.wavesplatform.transaction.{Asset, Proofs} import com.wavesplatform.transaction.transfer.MassTransferTransaction.Transfer -import com.wavesplatform.transaction.transfer._ -import play.api.libs.functional.syntax._ -import play.api.libs.json._ +import com.wavesplatform.transaction.transfer.* +import play.api.libs.functional.syntax.* +import play.api.libs.json.* object SignedMassTransferRequest { implicit val jsonFormat: Format[SignedMassTransferRequest] = Format( ( (JsPath \ "version").readNullable[Byte] and (JsPath \ "senderPublicKey").read[String] and - (JsPath \ "assetId").readNullable[String] and + (JsPath \ "assetId").readNullable[Asset] and (JsPath \ "transfers").read[List[Transfer]] and (JsPath \ "fee").read[Long] and (JsPath \ "timestamp").read[Long] and @@ -28,7 +28,7 @@ object SignedMassTransferRequest { case class SignedMassTransferRequest( version: Option[Byte], senderPublicKey: String, - assetId: Option[String], + assetId: Option[Asset], transfers: List[Transfer], fee: Long, timestamp: Long, @@ -38,8 +38,16 @@ case class SignedMassTransferRequest( def toTx: Either[ValidationError, MassTransferTransaction] = for { _sender <- PublicKey.fromBase58String(senderPublicKey) - _assetId <- parseBase58ToAsset(assetId.filter(_.nonEmpty), "invalid.assetId") _transfers <- MassTransferTransaction.parseTransfersList(transfers) - t <- MassTransferTransaction.create(version.getOrElse(1.toByte), _sender, _assetId, _transfers, fee, timestamp, attachment, proofs) + t <- MassTransferTransaction.create( + version.getOrElse(1.toByte), + _sender, + assetId.getOrElse(Asset.Waves), + _transfers, + fee, + timestamp, + attachment, + proofs + ) } yield t } diff --git a/node/src/main/scala/com/wavesplatform/api/http/requests/SignedUpdateAssetInfoRequest.scala b/node/src/main/scala/com/wavesplatform/api/http/requests/SignedUpdateAssetInfoRequest.scala index 7f951e94747..45eb20dff60 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/requests/SignedUpdateAssetInfoRequest.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/requests/SignedUpdateAssetInfoRequest.scala @@ -13,7 +13,7 @@ case class SignedUpdateAssetInfoRequest( version: TxVersion, chainId: Byte, senderPublicKey: String, - assetId: String, + assetId: IssuedAsset, name: String, description: String, timestamp: TxTimestamp, @@ -24,13 +24,12 @@ case class SignedUpdateAssetInfoRequest( def toTx: Either[ValidationError, UpdateAssetInfoTransaction] = for { - _sender <- PublicKey.fromBase58String(senderPublicKey) - _assetId <- parseBase58(assetId, "invalid.assetId", AssetIdStringLength) + _sender <- PublicKey.fromBase58String(senderPublicKey) _feeAssetId <- feeAssetId .traverse(parseBase58(_, "invalid.assetId", AssetIdStringLength).map(IssuedAsset(_))) .map(_ getOrElse Waves) tx <- UpdateAssetInfoTransaction - .create(version, _sender, _assetId, name, description, timestamp, fee, _feeAssetId, proofs, chainId) + .create(version, _sender, assetId.id, name, description, timestamp, fee, _feeAssetId, proofs, chainId) } yield tx } diff --git a/node/src/main/scala/com/wavesplatform/api/http/requests/SponsorFeeRequest.scala b/node/src/main/scala/com/wavesplatform/api/http/requests/SponsorFeeRequest.scala index c5012888b83..62104e7ff8f 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/requests/SponsorFeeRequest.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/requests/SponsorFeeRequest.scala @@ -4,7 +4,7 @@ import com.wavesplatform.account.PublicKey import com.wavesplatform.lang.ValidationError import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.assets.SponsorFeeTransaction -import com.wavesplatform.transaction.{AssetIdStringLength, Proofs} +import com.wavesplatform.transaction.Proofs import play.api.libs.json.{Format, Json} object SponsorFeeRequest { @@ -24,7 +24,7 @@ case class SponsorFeeRequest( case class SignedSponsorFeeRequest( version: Option[Byte], senderPublicKey: String, - assetId: String, + assetId: IssuedAsset, minSponsoredAssetFee: Option[Long], fee: Long, timestamp: Long, @@ -33,7 +33,6 @@ case class SignedSponsorFeeRequest( def toTx: Either[ValidationError, SponsorFeeTransaction] = for { _sender <- PublicKey.fromBase58String(senderPublicKey) - _asset <- parseBase58(assetId, "invalid.assetId", AssetIdStringLength).map(IssuedAsset(_)) - t <- SponsorFeeTransaction.create(version.getOrElse(1.toByte), _sender, _asset, minSponsoredAssetFee.filterNot(_ == 0), fee, timestamp, proofs) + t <- SponsorFeeTransaction.create(version.getOrElse(1.toByte), _sender, assetId, minSponsoredAssetFee.filterNot(_ == 0), fee, timestamp, proofs) } yield t } diff --git a/node/src/main/scala/com/wavesplatform/transaction/Asset.scala b/node/src/main/scala/com/wavesplatform/transaction/Asset.scala index 8a4c09b3010..988bc8b7478 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/Asset.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/Asset.scala @@ -13,23 +13,24 @@ sealed trait Asset object Asset { final case class IssuedAsset(id: ByteStr) extends Asset { override def toString: String = id.toString - override def hashCode(): Int = id.hashCode() + override def hashCode(): Int = id.hashCode() } object IssuedAsset { - private val interner = Interners.newWeakInterner[IssuedAsset]() + private val interner = Interners.newWeakInterner[IssuedAsset]() def apply(id: ByteStr): IssuedAsset = interner.intern(new IssuedAsset(id)) } case object Waves extends Asset + val WavesName = "WAVES" + implicit val assetReads: Reads[IssuedAsset] = Reads { - case JsString(str) if str.length > AssetIdStringLength => - JsError(s"Too long assetId: length of $str exceeds $AssetIdStringLength") case JsString(str) => Base58.tryDecodeWithLimit(str) match { - case Success(arr) => JsSuccess(IssuedAsset(ByteStr(arr))) - case _ => JsError("Expected base58-encoded assetId") + case Success(arr) if arr.length != AssetIdLength => JsError(s"Invalid validation. Size of asset id $str not equal $AssetIdLength bytes") + case Success(arr) => JsSuccess(IssuedAsset(ByteStr(arr))) + case _ => JsError("Expected base58-encoded assetId") } case _ => JsError("Expected base58-encoded assetId") } @@ -37,11 +38,7 @@ object Asset { JsString(asset.id.toString) } - implicit val assetIdReads: Reads[Asset] = Reads { - case json: JsString => if (json.value.isEmpty) JsSuccess(Waves) else assetReads.reads(json) - case JsNull => JsSuccess(Waves) - case _ => JsError("Expected base58-encoded assetId or null") - } + implicit val assetIdReads: Reads[Asset] = assetReads(false) implicit val assetIdWrites: Writes[Asset] = Writes { case Waves => JsNull case IssuedAsset(id) => JsString(id.toString) @@ -85,4 +82,11 @@ object Asset { case asset @ IssuedAsset(_) => onAsset(asset) } } + + def assetReads(allowWavesStr: Boolean): Reads[Asset] = Reads { + case json: JsString => + if (json.value.isEmpty || (allowWavesStr && json.value == WavesName)) JsSuccess(Waves) else assetReads.reads(json) + case JsNull => JsSuccess(Waves) + case _ => JsError("Expected base58-encoded assetId or null") + } } diff --git a/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/AssetPair.scala b/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/AssetPair.scala index 4c4b2aa0c36..27ec45bd677 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/AssetPair.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/AssetPair.scala @@ -4,7 +4,7 @@ import com.google.common.primitives.Bytes import com.wavesplatform.common.state.ByteStr import com.wavesplatform.serialization.Deser import com.wavesplatform.transaction.* -import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves, WavesName} import com.wavesplatform.transaction.assets.exchange.Validation.booleanOperators import net.ceedubs.ficus.readers.ValueReader import play.api.libs.json.{JsObject, Json} @@ -32,7 +32,6 @@ case class AssetPair( } object AssetPair { - val WavesName = "WAVES" implicit class AssetPairExt(val p: AssetPair) extends AnyVal { def checkedAssets: Seq[IssuedAsset] = Seq(p.priceAsset, p.amountAsset).collect { case ia: Asset.IssuedAsset => ia } diff --git a/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/OrderJson.scala b/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/OrderJson.scala index c1df10f2775..b70821f6b6a 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/OrderJson.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/assets/exchange/OrderJson.scala @@ -130,15 +130,7 @@ object OrderJson { ) } - private val assetReads: Reads[Asset] = { - case JsNull | JsString("") => JsSuccess(Waves) - case JsString(s) => - AssetPair.extractAssetId(s) match { - case Failure(_) => JsError(JsPath, JsonValidationError("error.incorrect.base58")) - case Success(assetId) => JsSuccess(assetId) - } - case _ => JsError(JsPath, JsonValidationError("error.expected.jsstring")) - } + val assetReads: Reads[Asset] = Asset.assetReads(true) implicit val assetPairReads: Reads[AssetPair] = { val r = (JsPath \ "amountAsset").readWithDefault[Asset](Waves)(assetReads) and @@ -203,9 +195,7 @@ object OrderJson { (JsPath \ "signature").readNullable[Array[Byte]] and (JsPath \ "proofs").readNullable[Array[Array[Byte]]] and (JsPath \ "version").read[Byte] and - (JsPath \ "matcherFeeAssetId") - .readNullable[Array[Byte]] - .map(arrOpt => Asset.fromCompatId(arrOpt.map(ByteStr(_)))) and + (JsPath \ "matcherFeeAssetId").readNullable[Asset].map(_.getOrElse(Waves)) and (JsPath \ "eip712Signature") .readNullable[String] .map(_.map(EthEncoding.toBytes)) and diff --git a/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala index a6538325943..18fa667aacd 100644 --- a/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/AssetsBroadcastRouteSpec.scala @@ -160,12 +160,12 @@ class AssetsBroadcastRouteSpec } forAll(invalidBase58) { a => posting(tr.copy(assetId = Some(a))) should produce( - WrongJson(errors = Seq(JsPath \ "assetId" -> Seq(JsonValidationError(s"Too long assetId: length of $a exceeds 44")))) + WrongJson(errors = Seq(JsPath \ "assetId" -> Seq(JsonValidationError(s"Expected base58-encoded assetId")))) ) } forAll(invalidBase58) { a => posting(tr.copy(feeAssetId = Some(a))) should produce( - WrongJson(errors = Seq(JsPath \ "feeAssetId" -> Seq(JsonValidationError(s"Too long assetId: length of $a exceeds 44")))) + WrongJson(errors = Seq(JsPath \ "feeAssetId" -> Seq(JsonValidationError(s"Expected base58-encoded assetId")))) ) } forAll(nonPositiveLong) { fee => diff --git a/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala index a5c327d0e8b..1a9d4ec3a59 100644 --- a/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala @@ -215,7 +215,9 @@ class DebugApiRouteSpec } val route = handleAllExceptions(routeWithBlockchain(blockchain)) validatePost(TxHelpers.invoke()) ~> route ~> check { - responseAs[String] shouldBe """{"error":0,"message":"Error is unknown"}""" + responseAs[ + String + ] shouldBe """{"error":0,"message":"Error is unknown"}""" response.status shouldBe StatusCodes.InternalServerError } } diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala index 697cd74dec7..1080204d70f 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionBroadcastSpec.scala @@ -6,16 +6,20 @@ import com.wavesplatform.api.common.CommonTransactionsApi import com.wavesplatform.api.http.{RouteTimeout, TransactionsApiRoute} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.* +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.{AccountScriptInfo, Blockchain} import com.wavesplatform.test.TestTime +import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.GenericError +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.smart.script.trace.{AccountVerifierTrace, TracedResult} -import com.wavesplatform.transaction.{Asset, Proofs, TxHelpers, TxPositiveAmount, TxVersion} +import com.wavesplatform.transaction.{Asset, AssetIdLength, Proofs, TxHelpers, TxPositiveAmount, TxVersion} import com.wavesplatform.utils.{EthEncoding, EthHelpers, SharedSchedulerMixin} import com.wavesplatform.wallet.Wallet import org.scalamock.scalatest.PathMockFactory @@ -643,4 +647,42 @@ class TransactionBroadcastSpec } } } + + "transactions with asset id field" - { + "return error when asset id with wrong size is passed" in { + val validSizeAssetId = ByteStr.fill(AssetIdLength)(1) + val wrongAssetIds = Seq( + Array.fill(AssetIdLength - 1)(1.toByte), + Array.fill(AssetIdLength + 1)(1.toByte) + ).map(arr => IssuedAsset(ByteStr(arr))) + + val txs = wrongAssetIds.flatMap { asset => + Seq( + TxHelpers.burn(asset = asset), + TxHelpers.massTransfer(asset = asset), + TxHelpers.reissue(asset = asset), + TxHelpers.setAssetScript(TxHelpers.defaultSigner, asset, TxHelpers.exprScript(V5)("true")), + TxHelpers.sponsor(asset = asset), + TxHelpers.transfer(asset = asset), + TxHelpers.updateAssetInfo(validSizeAssetId).copy(assetId = asset) + ) ++ + Seq(TxHelpers.invoke(feeAssetId = asset), TxHelpers.invoke(payments = Seq(Payment(1, asset)))) ++ + Seq( + TxHelpers.exchange(TxHelpers.order(OrderType.BUY, asset, Waves), TxHelpers.order(OrderType.SELL, asset, Waves)), + TxHelpers.exchange(TxHelpers.order(OrderType.BUY, Waves, asset), TxHelpers.order(OrderType.SELL, Waves, asset)), + TxHelpers.exchange( + TxHelpers.order(OrderType.BUY, IssuedAsset(validSizeAssetId), Waves, asset), + TxHelpers.order(OrderType.SELL, IssuedAsset(validSizeAssetId), Waves, asset) + ) + ) + } + + txs.foreach { tx => + Post(routePath("/broadcast"), tx.json()) ~> route ~> check { + val result = responseAs[JsObject].toString + result should include regex ("Invalid validation. Size of asset id.*not equal 32 bytes") + } + } + } + } } diff --git a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala index e87b777a11d..c8ffd14e89d 100644 --- a/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/TransactionsRouteSpec.scala @@ -1174,12 +1174,13 @@ class TransactionsRouteSpec .signWith(defaultSigner.privateKey) Post(routePath("/broadcast"), tx.json()) ~> route should produce( - WrongJson(errors = - Seq( + WrongJson( + errors = Seq( JsPath \ "attachment" -> Seq( JsonValidationError(s"base58-encoded string length ($attachmentSizeInSymbols) exceeds maximum length of 192") ) - ) + ), + msg = Some("json data validation error, see validationErrors for details") ) ) } diff --git a/node/src/test/scala/com/wavesplatform/http/UtilsRouteEvaluateSpec.scala b/node/src/test/scala/com/wavesplatform/http/UtilsRouteEvaluateSpec.scala index 18712008400..dd63bcf37d8 100644 --- a/node/src/test/scala/com/wavesplatform/http/UtilsRouteEvaluateSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/UtilsRouteEvaluateSpec.scala @@ -22,7 +22,7 @@ import com.wavesplatform.test.DomainPresets.{RideV5, RideV6} import com.wavesplatform.test.NumericExt import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.TxHelpers.* -import com.wavesplatform.transaction.{Asset, TxHelpers} +import com.wavesplatform.transaction.{Asset, AssetIdLength, TxHelpers} import com.wavesplatform.utils.{Schedulers, Time} import io.netty.util.HashedWheelTimer import monix.execution.schedulers.SchedulerService @@ -839,7 +839,10 @@ class UtilsRouteEvaluateSpec } // illegal payment asset id - Post(routePath(s"/script/evaluate/$secondAddress"), Json.parse("""{"payment":[{"amount":1,"assetId":"xxxxx"}]}""")) ~> route ~> check { + Post( + routePath(s"/script/evaluate/$secondAddress"), + Json.parse(s"""{"payment":[{"amount":1,"assetId":"${ByteStr.fill(AssetIdLength)(1)}"}]}""") + ) ~> route ~> check { responseAs[String] should include("Accounts balance errors") }