Skip to content

Commit

Permalink
NODE-2265: Insert stateChanges in transactions routes responses (REST…
Browse files Browse the repository at this point in the history
… API & gRPC) (wavesplatform#3313)
  • Loading branch information
Karasiq authored Dec 15, 2020
1 parent f4c9fe5 commit 6273ff3
Show file tree
Hide file tree
Showing 46 changed files with 450 additions and 284 deletions.
16 changes: 10 additions & 6 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
*/

import sbt.Keys._
import sbt._
import sbt.{Project, _}
import sbtcrossproject.CrossPlugin.autoImport.{CrossType, crossProject}

val langPublishSettings = Seq(
Expand Down Expand Up @@ -35,7 +35,7 @@ lazy val lang =
PB.protoSources := Seq(PB.externalIncludePath.value, baseDirectory.value.getParentFile / "shared" / "src" / "main" / "protobuf"),
includeFilter in PB.generate := { (f: File) =>
(** / "DAppMeta.proto").matches(f.toPath) ||
(** / "waves" / "*.proto").matches(f.toPath)
(** / "waves" / "*.proto").matches(f.toPath)
},
PB.deleteTargetDirectory := false
)
Expand Down Expand Up @@ -112,7 +112,8 @@ inScope(Global)(
"-Ywarn-unused:-implicits",
"-Xlint",
"-opt:l:inline",
"-opt-inline-from:**"
"-opt-inline-from:**",
"-Wconf:cat=deprecation&site=com.wavesplatform.api.grpc.*:s" // Ignore gRPC warnings
),
crossPaths := false,
scalafmtOnCompile := false,
Expand Down Expand Up @@ -171,10 +172,13 @@ checkPRRaw := Def
.value

def checkPR: Command = Command.command("checkPR") { state =>
val updatedState = Project
val newState = Project
.extract(state)
.appendWithoutSession(Seq(Global / scalacOptions ++= Seq("-Xfatal-warnings")), state)
Project.extract(updatedState).runTask(checkPRRaw, updatedState)
.appendWithoutSession(
Seq(Global / scalacOptions ++= Seq("-Xfatal-warnings")),
state
)
Project.extract(newState).runTask(checkPRRaw, newState)
state
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ package com.wavesplatform.api.grpc

import com.wavesplatform.account.AddressScheme
import com.wavesplatform.api.common.CommonTransactionsApi
import com.wavesplatform.api.common.CommonTransactionsApi.TransactionMeta
import com.wavesplatform.protobuf._
import com.wavesplatform.protobuf.transaction._
import com.wavesplatform.protobuf.utils.PBImplicitConversions.PBRecipientImplicitConversionOps
import com.wavesplatform.state.{InvokeScriptResult => VISR}
import com.wavesplatform.transaction.Authorized
import io.grpc.stub.StreamObserver
import io.grpc.{Status, StatusRuntimeException}
Expand All @@ -18,41 +20,43 @@ class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Sch
override def getTransactions(request: TransactionsRequest, responseObserver: StreamObserver[TransactionResponse]): Unit =
responseObserver.interceptErrors {
val transactionIds = request.transactionIds.map(_.toByteStr)
val stream = request.recipient match {
val stream: Observable[TransactionMeta] = request.recipient match {
// By recipient
case Some(subject) =>
val recipientAddrOrAlias = subject
.toAddressOrAlias(AddressScheme.current.chainId)
.fold(e => throw new IllegalArgumentException(e.toString), identity)

val maybeSender = Option(request.sender)
.collect { case s if !s.isEmpty => s.toAddress }

commonApi.transactionsByAddress(
subject
.toAddressOrAlias(AddressScheme.current.chainId)
.fold(e => throw new IllegalArgumentException(e.toString), identity),
Option(request.sender).collect { case s if !s.isEmpty => s.toAddress },
recipientAddrOrAlias,
maybeSender,
Set.empty,
None
)

// By sender
case None if !request.sender.isEmpty =>
val senderAddress = request.sender.toAddress
commonApi.transactionsByAddress(
senderAddress,
Some(senderAddress),
Set.empty,
None
)

// By ids
case None =>
if (request.sender.isEmpty) {
Observable.fromIterable(transactionIds.flatMap(commonApi.transactionById)).map {
case (h, e, s) => (h, e.fold(identity, _._1), s)
}
} else {
val senderAddress = request.sender.toAddress
commonApi.transactionsByAddress(
senderAddress,
Some(senderAddress),
Set.empty,
None
)
}
Observable.fromIterable(transactionIds.flatMap(commonApi.transactionById))
}

val transactionIdSet = transactionIds.toSet

responseObserver.completeWith(
stream
.filter { case (_, t, _) => transactionIdSet.isEmpty || transactionIdSet(t.id()) }
.map {
case (h, tx, false) => TransactionResponse(tx.id().toByteString, h, Some(tx.toPB), ApplicationStatus.SCRIPT_EXECUTION_FAILED)
case (h, tx, _) => TransactionResponse(tx.id().toByteString, h, Some(tx.toPB), ApplicationStatus.SUCCEEDED)
}
.filter { case TransactionMeta(_, tx, _) => transactionIdSet.isEmpty || transactionIdSet(tx.id()) }
.map(TransactionsApiGrpcImpl.toTransactionResponse)
)
}

Expand All @@ -74,13 +78,11 @@ class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Sch

override def getStateChanges(request: TransactionsRequest, responseObserver: StreamObserver[InvokeScriptResultResponse]): Unit =
responseObserver.interceptErrors {
import com.wavesplatform.state.{InvokeScriptResult => VISR}

val result = Observable(request.transactionIds: _*)
.flatMap(txId => Observable.fromIterable(commonApi.transactionById(txId.toByteStr)))
.collect {
case (_, Right((tx, Some(isr))), _) =>
InvokeScriptResultResponse.of(Some(PBTransactions.protobuf(tx)), Some(VISR.toPB(isr)))
case CommonTransactionsApi.TransactionMeta.Invoke(_, transaction, _, invokeScriptResult) =>
InvokeScriptResultResponse.of(Some(PBTransactions.protobuf(transaction)), invokeScriptResult.map(VISR.toPB))
}

responseObserver.completeWith(result)
Expand All @@ -93,9 +95,9 @@ class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Sch
.unconfirmedTransactionById(txId.toByteStr)
.map(_ => TransactionStatus(txId, TransactionStatus.Status.UNCONFIRMED))
.orElse {
commonApi.transactionById(txId.toByteStr).map {
case (h, _, false) => TransactionStatus(txId, TransactionStatus.Status.CONFIRMED, h, ApplicationStatus.SCRIPT_EXECUTION_FAILED)
case (h, _, _) => TransactionStatus(txId, TransactionStatus.Status.CONFIRMED, h, ApplicationStatus.SUCCEEDED)
commonApi.transactionById(txId.toByteStr).map { m =>
val status = if (m.succeeded) ApplicationStatus.SUCCEEDED else ApplicationStatus.SCRIPT_EXECUTION_FAILED
TransactionStatus(txId, TransactionStatus.Status.CONFIRMED, m.height, status)
}
}
.getOrElse(TransactionStatus(txId, TransactionStatus.Status.NOT_EXISTS))
Expand All @@ -115,3 +117,17 @@ class TransactionsApiGrpcImpl(commonApi: CommonTransactionsApi)(implicit sc: Sch
_ <- result.resultE.toFuture
} yield tx).wrapErrors
}

private object TransactionsApiGrpcImpl {
def toTransactionResponse(meta: TransactionMeta): TransactionResponse = {
val TransactionMeta(height, tx, succeeded) = meta
val transactionId = tx.id().toByteString
val status = if (succeeded) ApplicationStatus.SUCCEEDED else ApplicationStatus.SCRIPT_EXECUTION_FAILED
val invokeScriptResult = meta match {
case TransactionMeta.Invoke(_, _, _, r) => r.map(VISR.toPB)
case _ => None
}

TransactionResponse(transactionId, height, Some(tx.toPB), status, invokeScriptResult)
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.wavesplatform.it

import com.wavesplatform.http.DebugMessage
import com.wavesplatform.api.http.DebugMessage
import com.wavesplatform.it.api.AsyncHttpApi._
import com.wavesplatform.utils.ScorexLogging
import org.scalatest.{Args, Status, Suite, SuiteMixin}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,14 @@ object AsyncGrpcApi {
def stateChanges(
request: TransactionsRequest
): Future[Seq[(com.wavesplatform.transaction.Transaction, StateChangesDetails)]] = {
val (obs, result) = createCallObserver[InvokeScriptResultResponse]
transactions.getStateChanges(request, obs)
val (obs, result) = createCallObserver[TransactionResponse]
transactions.getTransactions(request, obs)
result.runToFuture.map { r =>
import com.wavesplatform.state.{InvokeScriptResult => VISR}
r.map { r =>
val tx = PBTransactions.vanillaUnsafe(r.getTransaction)
assert(r.getResult.transfers.forall(_.address.size() == 20))
val result = Json.toJson(VISR.fromPB(r.getResult)).as[StateChangesDetails]
assert(r.getInvokeScriptResult.transfers.forall(_.address.size() == 20))
val result = Json.toJson(VISR.fromPB(r.getInvokeScriptResult)).as[StateChangesDetails]
(tx, result)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,13 @@ import java.util.{NoSuchElementException, UUID}

import com.google.protobuf.ByteString
import com.wavesplatform.account.{AddressOrAlias, AddressScheme, KeyPair}
import com.wavesplatform.api.http.ConnectReq
import com.wavesplatform.api.http.DebugMessage._
import com.wavesplatform.api.http.RewardApiRoute.RewardStatus
import com.wavesplatform.api.http.requests.{IssueRequest, TransferRequest}
import com.wavesplatform.api.http.{ConnectReq, DebugMessage, RollbackParams, `X-Api-Key`}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.{Base58, Base64, EitherExt2}
import com.wavesplatform.features.api.ActivationStatus
import com.wavesplatform.http.DebugMessage._
import com.wavesplatform.http.{DebugMessage, RollbackParams, `X-Api-Key`}
import com.wavesplatform.it.Node
import com.wavesplatform.it.util.GlobalTimer.{instance => timer}
import com.wavesplatform.it.util._
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,12 @@ import java.net.InetSocketAddress
import akka.http.scaladsl.model.StatusCodes.BadRequest
import akka.http.scaladsl.model.{StatusCode, StatusCodes}
import com.wavesplatform.account.{AddressOrAlias, KeyPair}
import com.wavesplatform.api.http.ApiError
import com.wavesplatform.api.http.RewardApiRoute.RewardStatus
import com.wavesplatform.api.http.requests.IssueRequest
import com.wavesplatform.api.http.{ApiError, DebugMessage}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.EitherExt2
import com.wavesplatform.features.api.{ActivationStatus, FeatureActivationStatus}
import com.wavesplatform.http.DebugMessage
import com.wavesplatform.it.Node
import com.wavesplatform.it.sync._
import com.wavesplatform.lang.v1.compiler.Terms
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package com.wavesplatform.it.sync

import com.typesafe.config.Config
import com.wavesplatform.account.{KeyPair, PublicKey}
import com.wavesplatform.api.http.DebugMessage
import com.wavesplatform.block.Block
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.{Base58, EitherExt2}
import com.wavesplatform.consensus.FairPoSCalculator
import com.wavesplatform.consensus.nxt.NxtLikeConsensusBlockData
import com.wavesplatform.crypto
import com.wavesplatform.http.DebugMessage
import com.wavesplatform.it.api.AsyncNetworkApi.NodeAsyncNetworkApi
import com.wavesplatform.it.api.SyncHttpApi._
import com.wavesplatform.it.transactions.NodesFromDocker
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,70 +33,6 @@ class InvokeScriptTransactionStateChangesSuite extends BaseTransactionSuite with
private lazy val recipientAddress: String = recipient.toAddress.toString
private lazy val callerAddress: String = caller.toAddress.toString

test("prepare") {
simpleAsset = sender.issue(contract, "simple", "", 9000, 0).id
assetSponsoredByDApp = sender.issue(contract, "DApp asset", "", 9000, 0).id
assetSponsoredByRecipient = sender.issue(recipient, "Recipient asset", "", 9000, 0, waitForTx = true).id
sender.massTransfer(contract, List(Transfer(callerAddress, 3000), Transfer(recipientAddress, 3000)), 0.01.waves, assetId = Some(simpleAsset))
sender.massTransfer(
contract,
List(Transfer(callerAddress, 3000), Transfer(recipientAddress, 3000)),
0.01.waves,
assetId = Some(assetSponsoredByDApp)
)
sender.massTransfer(
recipient,
List(Transfer(callerAddress, 3000), Transfer(contractAddress, 3000)),
0.01.waves,
assetId = Some(assetSponsoredByRecipient)
)
sender.sponsorAsset(contract, assetSponsoredByDApp, 1, fee = sponsorReducedFee + smartFee)
sender.sponsorAsset(recipient, assetSponsoredByRecipient, 5, fee = sponsorReducedFee + smartFee)

val script = ScriptCompiler
.compile(
"""
|{-# STDLIB_VERSION 3 #-}
|{-# CONTENT_TYPE DAPP #-}
|{-# SCRIPT_TYPE ACCOUNT #-}
|
|@Callable(i)
|func write(value: Int) = {
| WriteSet([DataEntry("result", value)])
|}
|
|@Callable(i)
|func sendAsset(recipient: String, amount: Int, assetId: String) = {
| TransferSet([ScriptTransfer(Address(recipient.fromBase58String()), amount, assetId.fromBase58String())])
|}
|
|@Callable(i)
|func writeAndSendWaves(value: Int, recipient: String, amount: Int) = {
| ScriptResult(
| WriteSet([DataEntry("result", value)]),
| TransferSet([ScriptTransfer(Address(recipient.fromBase58String()), amount, unit)])
| )
|}
""".stripMargin,
ScriptEstimatorV2
)
.explicitGet()
._1
.bytes()
.base64
sender.setScript(contract, Some(script), setScriptFee, waitForTx = true)

initCallerTxs = sender.transactionsByAddress(callerAddress, 100).length
initDAppTxs = sender.transactionsByAddress(contractAddress, 100).length
initRecipientTxs = sender.transactionsByAddress(recipientAddress, 100).length
initCallerStateChanges = sender.debugStateChangesByAddress(callerAddress, 100).length
initDAppStateChanges = sender.debugStateChangesByAddress(contractAddress, 100).length
initRecipientStateChanges = sender.debugStateChangesByAddress(recipientAddress, 100).length
initCallerTxs shouldBe initCallerStateChanges
initDAppTxs shouldBe initDAppStateChanges
initRecipientTxs shouldBe initRecipientStateChanges
}

test("write") {
val data = 10

Expand Down Expand Up @@ -367,6 +303,74 @@ class InvokeScriptTransactionStateChangesSuite extends BaseTransactionSuite with

}

protected override def beforeAll(): Unit = {
super.beforeAll()

simpleAsset = sender.issue(contract, "simple", "", 9000, 0).id
assetSponsoredByDApp = sender.issue(contract, "DApp asset", "", 9000, 0).id
assetSponsoredByRecipient = sender.issue(recipient, "Recipient asset", "", 9000, 0, waitForTx = true).id
sender.massTransfer(contract, List(Transfer(callerAddress, 3000), Transfer(recipientAddress, 3000)), 0.01.waves, assetId = Some(simpleAsset))
sender.massTransfer(
contract,
List(Transfer(callerAddress, 3000), Transfer(recipientAddress, 3000)),
0.01.waves,
assetId = Some(assetSponsoredByDApp)
)
sender.massTransfer(
recipient,
List(Transfer(callerAddress, 3000), Transfer(contractAddress, 3000)),
0.01.waves,
assetId = Some(assetSponsoredByRecipient)
)
sender.sponsorAsset(contract, assetSponsoredByDApp, 1, fee = sponsorReducedFee + smartFee)
sender.sponsorAsset(recipient, assetSponsoredByRecipient, 5, fee = sponsorReducedFee + smartFee)

val script = ScriptCompiler
.compile(
"""
|{-# STDLIB_VERSION 3 #-}
|{-# CONTENT_TYPE DAPP #-}
|{-# SCRIPT_TYPE ACCOUNT #-}
|
|@Callable(i)
|func write(value: Int) = {
| WriteSet([DataEntry("result", value)])
|}
|
|@Callable(i)
|func sendAsset(recipient: String, amount: Int, assetId: String) = {
| TransferSet([ScriptTransfer(Address(recipient.fromBase58String()), amount, assetId.fromBase58String())])
|}
|
|@Callable(i)
|func writeAndSendWaves(value: Int, recipient: String, amount: Int) = {
| ScriptResult(
| WriteSet([DataEntry("result", value)]),
| TransferSet([ScriptTransfer(Address(recipient.fromBase58String()), amount, unit)])
| )
|}
""".stripMargin,
ScriptEstimatorV2
)
.explicitGet()
._1
.bytes()
.base64
sender.setScript(contract, Some(script), setScriptFee, waitForTx = true)
nodes.waitForEmptyUtx()
nodes.waitForHeightArise()

initCallerTxs = sender.transactionsByAddress(callerAddress, 100).length
initDAppTxs = sender.transactionsByAddress(contractAddress, 100).length
initRecipientTxs = sender.transactionsByAddress(recipientAddress, 100).length
initCallerStateChanges = sender.debugStateChangesByAddress(callerAddress, 100).length
initDAppStateChanges = sender.debugStateChangesByAddress(contractAddress, 100).length
initRecipientStateChanges = sender.debugStateChangesByAddress(recipientAddress, 100).length
initCallerTxs shouldBe initCallerStateChanges
initDAppTxs shouldBe initDAppStateChanges
initRecipientTxs shouldBe initRecipientStateChanges
}

def txInfoShouldBeEqual(info: TransactionInfo, stateChanges: DebugStateChanges)(implicit pos: Position): Unit = {
info._type shouldBe stateChanges._type
info.id shouldBe stateChanges.id
Expand Down
Loading

0 comments on commit 6273ff3

Please sign in to comment.