Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

NODE-2476 Check asset id field size #3716

Merged
merged 9 commits into from
Jul 7, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 9 additions & 8 deletions node/src/main/scala/com/wavesplatform/api/http/ApiError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,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.Message)
override lazy val json: JsObject = Json.obj(
"error" -> id,
"message" -> message,
Expand All @@ -91,7 +92,7 @@ object ApiError {
val Message = "failed to parse json message"
}

//API Auth
// API Auth
case object ApiKeyNotValid extends ApiError {
override val id = 2
override val code = StatusCodes.Forbidden
Expand All @@ -110,7 +111,7 @@ object ApiError {
override val code: StatusCode = StatusCodes.BadRequest
}

//VALIDATION
// VALIDATION
case object InvalidSignature extends ApiError {
override val id = 101
override val code = StatusCodes.BadRequest
Expand Down Expand Up @@ -145,7 +146,7 @@ object ApiError {
override val id: Int = StateCheckFailed.Id
override val message: String = StateCheckFailed.message(errorMsg)
override val code: StatusCode = StateCheckFailed.Code
override lazy val json = details.fold(JsObject.empty)(identity) ++ Json.obj("error" -> id, "message" -> message, "transaction" -> tx.json())
override lazy val json = details.fold(JsObject.empty)(identity) ++ Json.obj("error" -> id, "message" -> message, "transaction" -> tx.json())
}

case object StateCheckFailed {
Expand Down Expand Up @@ -278,7 +279,7 @@ object ApiError {
val Code = StatusCodes.BadRequest
}

//TRANSACTIONS
// TRANSACTIONS
case object TransactionDoesNotExist extends ApiError {
override val id: Int = 311
override val message: String = "transactions does not exist"
Expand Down Expand Up @@ -360,8 +361,8 @@ object ApiError {
"details" -> Json
.toJson(
errs
.map {
case (addr, err) => addr.toString -> err
.map { case (addr, err) =>
addr.toString -> err
}
)
)
Expand Down
14 changes: 7 additions & 7 deletions node/src/main/scala/com/wavesplatform/api/http/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ package object http {
val intToByteReads = implicitly[Reads[Int]].map(_.toByte)
val stringToByteReads = implicitly[Reads[String]]
.map(s => Try(s.toByte))
.collect(JsonValidationError("Can't parse version")) {
case Success(v) => v
.collect(JsonValidationError("Can't parse version")) { case Success(v) =>
v
}

defaultByteReads orElse
Expand Down Expand Up @@ -131,7 +131,7 @@ package object http {
.result()

val jsonExceptionHandler: ExceptionHandler = ExceptionHandler {
case JsResultException(err) => complete(WrongJson(errors = err))
case JsResultException(err) => complete(WrongJson(errors = err, msg = Some("json data validation error, see \"validationErrors\" for details")))
case PlayJsonException(cause, errors) => complete(WrongJson(cause, errors))
case e: NoSuchElementException => complete(WrongJson(Some(e)))
case e: IllegalArgumentException => complete(ApiError.fromValidationError(GenericError(e)))
Expand Down Expand Up @@ -161,15 +161,15 @@ package object http {
* This directive can't handle __fatal__ errors from:
*
* - Monix [[monix.eval.Task tasks]] with async boundaries:
* {{{
* {{{
* get(complete(Task(throw new StackOverflowError()).executeAsync.runToFuture))
* get(complete(Task.evalAsync(throw new StackOverflowError()).runToFuture))
* get(complete(Task.deferFuture(Future(throw new StackOverflowError())).runToFuture))
* }}}
* }}}
* - Async futures (i.e. which are not available at the time of handling):
* {{{
* {{{
* get(complete(Future(throw new StackOverflowException())))
* }}}
* }}}
*/
def handleAllExceptions: Directive0 =
Directive { inner => ctx =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*

Expand Down Expand Up @@ -96,7 +96,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]],
Expand All @@ -107,15 +107,14 @@ 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,
_dappAddress,
call.map(InvokeScriptRequest.buildFunctionCall).filterNot(_ == InvokeTransaction.DefaultCall),
payment.getOrElse(Seq()),
fee,
_feeAssetId,
feeAssetId.getOrElse(Asset.Waves),
timestamp,
proofs,
chainId.getOrElse(_dappAddress.chainId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ case class SignedUpdateAssetInfoRequest(
version: TxVersion,
chainId: Byte,
senderPublicKey: String,
assetId: String,
assetId: IssuedAsset,
name: String,
description: String,
timestamp: TxTimestamp,
Expand All @@ -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

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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,
Expand All @@ -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
}
26 changes: 15 additions & 11 deletions node/src/main/scala/com/wavesplatform/transaction/Asset.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,32 @@ 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")
}
implicit val assetWrites: Writes[IssuedAsset] = Writes { 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)
Expand Down Expand Up @@ -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")
}
}
Loading