Skip to content

Commit

Permalink
NODE-2642 Transaction snapshots API (#3931)
Browse files Browse the repository at this point in the history
  • Loading branch information
xrtm000 authored Jan 13, 2024
1 parent 3533edc commit 813034c
Show file tree
Hide file tree
Showing 46 changed files with 1,041 additions and 86 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/check-pr.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
sbt lang/assembly
git clone https://github.com/waves-exchange/neutrino-contract
git clone https://github.com/waves-exchange/contracts
git clone https://oauth2:${{ secrets.DUCKS_GITHUB_TOKEN }}@github.com/akharazyan/wavesducks-public
git clone https://github.com/waves-ducks-core/wavesducks-public
git clone https://oauth2:${{ secrets.SWOPFI_GITLAB_TOKEN }}@gitlabwp.wvservices.com/swopfi/swopfi-smart-contracts
find neutrino-contract/script -name "*.ride" -type f -exec java -jar lang/jvm/target/file-compiler.jar {} +;
find contracts/ride -name "*.ride" -type f -exec java -jar lang/jvm/target/file-compiler.jar {} +;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package com.wavesplatform.api.grpc

import scala.concurrent.Future
import com.wavesplatform.account.AddressScheme
import com.wavesplatform.api.common.{CommonTransactionsApi, TransactionMeta}
import com.wavesplatform.api.grpc.TransactionsApiGrpcImpl.applicationStatusFromTxStatus
import com.wavesplatform.protobuf.*
import com.wavesplatform.protobuf.transaction.*
import com.wavesplatform.protobuf.utils.PBImplicitConversions.PBRecipientImplicitConversionOps
import com.wavesplatform.state.{Blockchain, TxMeta, InvokeScriptResult as VISR}
import com.wavesplatform.transaction.{Authorized, EthereumTransaction}
import com.wavesplatform.transaction.TxValidationError.GenericError
import io.grpc.{Status, StatusRuntimeException}
import com.wavesplatform.transaction.{Authorized, EthereumTransaction}
import io.grpc.stub.StreamObserver
import io.grpc.{Status, StatusRuntimeException}
import monix.execution.Scheduler
import monix.reactive.Observable

import scala.concurrent.Future

class TransactionsApiGrpcImpl(blockchain: Blockchain, commonApi: CommonTransactionsApi)(implicit sc: Scheduler)
extends TransactionsApiGrpc.TransactionsApi {

Expand Down Expand Up @@ -64,6 +65,20 @@ class TransactionsApiGrpcImpl(blockchain: Blockchain, commonApi: CommonTransacti
)
}

override def getTransactionSnapshots(
request: TransactionSnapshotsRequest,
responseObserver: StreamObserver[TransactionSnapshotResponse]
): Unit =
responseObserver.interceptErrors {
val snapshots =
for {
id <- Observable.fromIterable(request.transactionIds)
(snapshot, status) <- Observable.fromIterable(blockchain.transactionSnapshot(id.toByteStr))
pbSnapshot = PBSnapshots.toProtobuf(snapshot, status)
} yield TransactionSnapshotResponse(id, Some(pbSnapshot))
responseObserver.completeWith(snapshots)
}

override def getUnconfirmed(request: TransactionsRequest, responseObserver: StreamObserver[TransactionResponse]): Unit =
responseObserver.interceptErrors {
val unconfirmedTransactions = if (!request.sender.isEmpty) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.wavesplatform.api.grpc.test

import com.google.protobuf.ByteString
import com.wavesplatform.account.KeyPair
import com.wavesplatform.api.grpc.{ApplicationStatus, TransactionResponse, TransactionsApiGrpcImpl, TransactionsRequest}
import com.wavesplatform.api.grpc.{ApplicationStatus, TransactionResponse, TransactionSnapshotResponse, TransactionSnapshotsRequest, TransactionsApiGrpcImpl, TransactionsRequest}
import com.wavesplatform.block.Block
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.EitherExt2
Expand All @@ -11,16 +11,21 @@ import com.wavesplatform.db.WithDomain
import com.wavesplatform.db.WithState.AddrWithBalance
import com.wavesplatform.history.Domain
import com.wavesplatform.protobuf.transaction.{PBTransactions, Recipient}
import com.wavesplatform.state.TxMeta
import com.wavesplatform.protobuf.{ByteStrExt, PBSnapshots}
import com.wavesplatform.state.diffs.ENOUGH_AMT
import com.wavesplatform.state.{StateSnapshot, TxMeta}
import com.wavesplatform.test.*
import com.wavesplatform.test.DomainPresets.*
import com.wavesplatform.transaction.Asset.Waves
import com.wavesplatform.transaction.{TxHelpers, TxVersion}
import com.wavesplatform.transaction.TxHelpers.*
import com.wavesplatform.transaction.assets.exchange.{ExchangeTransaction, Order, OrderType}
import com.wavesplatform.transaction.{TxHelpers, TxVersion}
import com.wavesplatform.utils.DiffMatchers
import monix.execution.Scheduler.Implicits.global
import org.scalatest.{Assertion, BeforeAndAfterAll}

import scala.collection.immutable.VectorMap

class TransactionsApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffMatchers with WithDomain with GrpcApiHelpers {

val sender: KeyPair = TxHelpers.signer(1)
Expand Down Expand Up @@ -69,6 +74,67 @@ class TransactionsApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffM
}
}

"GetTransactionSnapshots" in withDomain(TransactionStateSnapshot, AddrWithBalance.enoughBalances(secondSigner)) { d =>
val recipient = signer(2).toAddress
val txs = Seq.fill(5)(transfer(amount = 1, fee = 100_000, from = secondSigner, to = recipient))

val firstThreeSnapshots = Seq(
StateSnapshot(balances =
VectorMap(
(secondAddress, Waves) -> (ENOUGH_AMT - 100_001),
(recipient, Waves) -> 1,
(defaultAddress, Waves) -> 200_040_000 // reward and 40% fee
)
),
StateSnapshot(balances =
VectorMap(
(secondAddress, Waves) -> (ENOUGH_AMT - 200_002),
(recipient, Waves) -> 2,
(defaultAddress, Waves) -> 200_080_000
)
),
StateSnapshot(balances =
VectorMap(
(secondAddress, Waves) -> (ENOUGH_AMT - 300_003),
(recipient, Waves) -> 3,
(defaultAddress, Waves) -> 200_120_000
)
)
)

def getSnapshots() = {
val request = TransactionSnapshotsRequest.of(txs.map(_.id().toByteString))
val (observer, response) = createObserver[TransactionSnapshotResponse]
getGrpcApi(d).getTransactionSnapshots(request, observer)
response.runSyncUnsafe().flatMap(_.snapshot).map(PBSnapshots.fromProtobuf(_, ByteStr.empty, 0)._1)
}

d.appendBlock(txs(0), txs(1))
d.appendMicroBlock(txs(2))

// both liquid and solid state
getSnapshots() shouldBe firstThreeSnapshots

// hardened state
d.appendBlock(txs(3), txs(4))
getSnapshots() shouldBe firstThreeSnapshots ++ Seq(
StateSnapshot(balances =
VectorMap(
(secondAddress, Waves) -> (ENOUGH_AMT - 400_004),
(recipient, Waves) -> 4,
(defaultAddress, Waves) -> 400_340_000 // 2 blocks reward, 100% fee from previous block and 40% fee from current
)
),
StateSnapshot(balances =
VectorMap(
(secondAddress, Waves) -> (ENOUGH_AMT - 500_005),
(recipient, Waves) -> 5,
(defaultAddress, Waves) -> 400_380_000
)
)
)
}

"NODE-973. GetTransactions should return correct data for orders with attachment" in {
def checkOrderAttachment(txResponse: TransactionResponse, expectedAttachment: ByteStr): Assertion = {
PBTransactions
Expand Down Expand Up @@ -143,7 +209,10 @@ class TransactionsApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffM
val challengedMiner = TxHelpers.signer(2)
val resender = TxHelpers.signer(3)
val recipient = TxHelpers.signer(4)
withDomain(TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)), balances = AddrWithBalance.enoughBalances(sender)) { d =>
withDomain(
TransactionStateSnapshot.configure(_.copy(lightNodeBlockFieldsAbsenceInterval = 0)),
balances = AddrWithBalance.enoughBalances(sender)
) { d =>
val grpcApi = getGrpcApi(d)
val challengingMiner = d.wallet.generateNewAccount().get

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,8 @@ trait Script {
}

object Script {

case class ComplexityInfo(verifierComplexity: Long, callableComplexities: Map[String, Long], maxComplexity: Long)

val checksumLength = 4

def fromBase64String(str: String): Either[ScriptParseError, Script] =
for {
bytes <- Base64.tryDecode(str).toEither.left.map(ex => ScriptParseError(s"Unable to decode base64: ${ex.getMessage}"))
Expand Down Expand Up @@ -93,9 +90,7 @@ object Script {
)
complexityInfo = verifierFuncOpt.fold(
ComplexityInfo(0L, callableComplexities, maxComplexity)
)(
v => ComplexityInfo(callableComplexities(v.u.name), callableComplexities - v.u.name, maxComplexity)
)
)(v => ComplexityInfo(callableComplexities(v.u.name), callableComplexities - v.u.name, maxComplexity))
} yield complexityInfo
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ class AmountAsStringSuite extends BaseTransactionSuite with OverflowBlock {
amount,
price,
ts,
ts + Order.MaxLiveTime,
ts + Order.MaxLiveTime / 2,
matcherFee
)
.explicitGet()
Expand All @@ -95,7 +95,7 @@ class AmountAsStringSuite extends BaseTransactionSuite with OverflowBlock {
amount,
price,
ts,
ts + Order.MaxLiveTime,
ts + Order.MaxLiveTime / 2,
matcherFee
)
.explicitGet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ class VRFProtobufActivationSuite extends BaseTransactionSuite {
amount,
price,
ts,
ts + Order.MaxLiveTime,
ts + Order.MaxLiveTime / 2,
matcherFee
)
.explicitGet()
Expand All @@ -237,7 +237,7 @@ class VRFProtobufActivationSuite extends BaseTransactionSuite {
amount,
price,
ts,
ts + Order.MaxLiveTime,
ts + Order.MaxLiveTime / 2,
matcherFee
)
.explicitGet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ExchangeTransactionGrpcSuite extends GrpcBaseTransactionSuite with NTPTime
val pair = AssetPair.createAssetPair("WAVES", exchAssetId).get
for ((o1ver, o2ver, tver) <- versions) {
val ts = ntpTime.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2
val buy = Order.buy(o1ver, buyer, matcher.publicKey, pair, amount, price, ts, expirationTimestamp, matcherFee).explicitGet()
val sell = Order.sell(o2ver, seller, matcher.publicKey, pair, amount, price, ts, expirationTimestamp, matcherFee).explicitGet()
val buyerWavesBalanceBefore = sender.wavesBalance(buyerAddress).available
Expand Down Expand Up @@ -98,7 +98,7 @@ class ExchangeTransactionGrpcSuite extends GrpcBaseTransactionSuite with NTPTime
val sellerAssetBalanceBefore = sender.assetsBalance(sellerAddress, Seq(feeAssetId.toString)).getOrElse(feeAssetId.toString, 0L)

val ts = ntpTime.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2
val assetPair = AssetPair.createAssetPair("WAVES", feeAssetId.toString).get
val buy =
Order.buy(o1ver, buyer, matcher.publicKey, assetPair, amount, price, ts, expirationTimestamp, matcherFee, matcherFeeOrder1).explicitGet()
Expand Down Expand Up @@ -133,7 +133,7 @@ class ExchangeTransactionGrpcSuite extends GrpcBaseTransactionSuite with NTPTime

val assetId = exchAsset.id().toString
val ts = ntpTime.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2
val price = 2 * Order.PriceConstant
val amount = 1
val pair = AssetPair.createAssetPair("WAVES", assetId).get
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ package object smartcontract {
val seller = accounts.tail.head // second one
val matcher = accounts.last
val ts = time.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2
val buyPrice = 1 * Order.PriceConstant
val sellPrice = (0.50 * Order.PriceConstant).toLong
val buyAmount = 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class ExchangeTransactionSuite extends BaseTransactionSuite with NTPTime {
val matcher = acc2

val ts = ntpTime.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2

val buyPrice = 2 * Order.PriceConstant
val sellPrice = 2 * Order.PriceConstant
Expand Down Expand Up @@ -197,7 +197,7 @@ class ExchangeTransactionSuite extends BaseTransactionSuite with NTPTime {

val matcher = thirdKeyPair
val ts = ntpTime.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2
var assetBalanceBefore: Long = 0L

if (matcherFeeOrder1 == Waves && matcherFeeOrder2 != Waves) {
Expand Down Expand Up @@ -283,7 +283,7 @@ class ExchangeTransactionSuite extends BaseTransactionSuite with NTPTime {

val matcher = thirdKeyPair
val ts = ntpTime.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2
val amount = 1
val nftWavesPrice = 1000 * math.pow(10, 8).toLong
val nftForAssetPrice = 1 * math.pow(10, 8).toLong
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ object FailedTransactionSuiteLike {
100,
100,
timestamp,
timestamp + Order.MaxLiveTime,
timestamp + Order.MaxLiveTime / 2,
buyMatcherFee,
Asset.fromString(Some(buyMatcherFeeAsset))
).explicitGet()
Expand All @@ -300,7 +300,7 @@ object FailedTransactionSuiteLike {
100,
100,
timestamp,
timestamp + Order.MaxLiveTime,
timestamp + Order.MaxLiveTime / 2,
sellMatcherFee,
Asset.fromString(Some(sellMatcherFeeAsset))
).explicitGet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ class SignAndBroadcastApiSuite extends BaseTransactionSuite with NTPTime with Be
val seller = secondKeyPair
val matcher = thirdKeyPair
val ts = ntpTime.correctedTime()
val expirationTimestamp = ts + Order.MaxLiveTime
val expirationTimestamp = ts + Order.MaxLiveTime / 2
val buyPrice = 1 * Order.PriceConstant
val sellPrice = (0.50 * Order.PriceConstant).toLong
val mf = 300000L
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ class TransferNFTSuite extends BaseTransactionSuite with NTPTime {
amount = 1,
price = 1.waves,
timestamp = ts,
expiration = ts + Order.MaxLiveTime,
expiration = ts + Order.MaxLiveTime / 2,
matcherFee = matcherFee
)
.explicitGet()
Expand All @@ -174,7 +174,7 @@ class TransferNFTSuite extends BaseTransactionSuite with NTPTime {
amount = 1,
price = 1.waves,
timestamp = ts,
expiration = ts + Order.MaxLiveTime,
expiration = ts + Order.MaxLiveTime / 2,
matcherFee = matcherFee
)
.explicitGet()
Expand Down
1 change: 1 addition & 0 deletions node/src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ waves {
# Max number of transactions
# returned by /transactions/address/{address}/limit/{limit}
transactions-by-address-limit = 1000
transaction-snapshots-limit = 100
distribution-address-limit = 1000
data-keys-request-limit = 1000
asset-details-limit = 100
Expand Down
Loading

0 comments on commit 813034c

Please sign in to comment.