Skip to content

Commit 10c8fcd

Browse files
authored
Merge branch 'version-1.5.x' into node-micro-block-inv-absence
2 parents 51f585a + 4f0cbb2 commit 10c8fcd

File tree

27 files changed

+436
-186
lines changed

27 files changed

+436
-186
lines changed

build.sbt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ Global / onChangedBuildSource := ReloadOnSourceChanges
1010

1111
enablePlugins(GitVersioning)
1212

13-
git.uncommittedSignifier := Some("DIRTY")
14-
git.useGitDescribe := true
13+
git.uncommittedSignifier := Some("DIRTY")
14+
git.useGitDescribe := true
1515
ThisBuild / git.useGitDescribe := true
16-
ThisBuild / PB.protocVersion := "3.24.4" // https://protobuf.dev/support/version-support/#java
16+
ThisBuild / PB.protocVersion := "3.24.4" // https://protobuf.dev/support/version-support/#java
1717

1818
lazy val lang =
1919
crossProject(JSPlatform, JVMPlatform)
@@ -50,7 +50,10 @@ lazy val `lang-testkit` = project
5050
.dependsOn(`lang-jvm`)
5151
.in(file("lang/testkit"))
5252
.settings(
53-
libraryDependencies ++= Dependencies.test.map(_.withConfigurations(Some("compile"))) ++ Dependencies.qaseReportDeps
53+
libraryDependencies ++=
54+
Dependencies.test.map(_.withConfigurations(Some("compile"))) ++ Dependencies.qaseReportDeps ++ Dependencies.logDeps ++ Seq(
55+
"com.typesafe.scala-logging" %% "scala-logging" % "3.9.5"
56+
)
5457
)
5558

5659
lazy val `lang-tests` = project

grpc-server/src/test/scala/com/wavesplatform/api/grpc/test/AccountsApiGrpcSpec.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ class AccountsApiGrpcSpec extends FreeSpec with BeforeAndAfterAll with DiffMatch
107107

108108
grpcApi.getActiveLeases(AccountRequest.of(ByteString.copyFrom(recipient.toAddress.bytes)), observer)
109109

110-
result.runSyncUnsafe() shouldBe List(
110+
result.runSyncUnsafe() should contain theSameElementsAs List(
111111
LeaseResponse.of(
112112
ByteString.copyFrom(lease3.id().arr),
113113
ByteString.copyFrom(lease3.id().arr),
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<configuration>
3+
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
4+
<encoder>
5+
<pattern>%date %-5level [%.15thread] %logger{26} - %msg%n</pattern>
6+
</encoder>
7+
</appender>
8+
9+
<logger name="scorex.crypto.signatures.Curve25519$" level="INFO"/>
10+
11+
<root level="${logback.test.level:-DEBUG}">
12+
<appender-ref ref="STDOUT" />
13+
</root>
14+
</configuration>

lang/testkit/src/test/scala/com/wavesplatform/report/QaseRunCompleter.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.wavesplatform.report
22

3+
import com.typesafe.scalalogging.StrictLogging
34
import com.wavesplatform.report.QaseReporter.{CheckPRRunIdKey, QaseProjects, TestResult}
45
import io.qase.api.QaseClient
56
import io.qase.api.config.QaseConfig.{PROJECT_CODE_KEY, RUN_ID_KEY}
7+
import io.qase.api.exceptions.QaseException
68
import io.qase.api.services.impl.ReportersResultOperationsImpl
79
import io.qase.client.ApiClient
810
import io.qase.client.api.{CasesApi, ResultsApi, RunsApi}
@@ -15,8 +17,8 @@ import scala.annotation.tailrec
1517
import scala.io.Source
1618
import scala.util.Using
1719

18-
object QaseRunCompleter extends App {
19-
if (QaseClient.getConfig.isEnabled) {
20+
object QaseRunCompleter extends App with StrictLogging {
21+
if (QaseClient.getConfig.isEnabled) try {
2022

2123
val apiClient: ApiClient = QaseClient.getApiClient
2224
val runsApi = new RunsApi(apiClient)
@@ -78,6 +80,9 @@ object QaseRunCompleter extends App {
7880
}
7981
}(_.foreach(f => Files.delete(f.toPath)))
8082
}
83+
} catch {
84+
case e: QaseException =>
85+
logger.error(s"Qase error: ${e.getCode} ${e.getResponseBody}", e)
8186
}
8287

8388
@tailrec

node/src/main/protobuf/waves/database.proto

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,7 @@ message LeaseDetails {
116116
Expired expired = 12;
117117
}
118118
}
119+
120+
message LeaseIds {
121+
repeated bytes ids = 1;
122+
}

node/src/main/resources/application.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ waves {
1515
db {
1616
directory = ${waves.directory}"/data"
1717
store-transactions-by-address = true
18+
store-lease-states-by-address = true
1819
store-invoke-script-results = true
1920
store-state-hashes = false
2021
# Limits the size of caches which are used during block validation. Lower values slightly decrease memory consumption,

node/src/main/scala/com/wavesplatform/api/common/AddressTransactions.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import com.wavesplatform.account.Address
55
import com.wavesplatform.api.common.AddressTransactions.TxByAddressIterator.BatchSize
66
import com.wavesplatform.common.state.ByteStr
77
import com.wavesplatform.database.protobuf.EthereumTransactionMeta
8-
import com.wavesplatform.database.{AddressId, DBExt, DBResource, Key, KeyTags, Keys, RDB, readTransactionHNSeqAndType}
8+
import com.wavesplatform.database.{AddressId, DBExt, DBResource, Key, Keys, RDB, readTransactionHNSeqAndType}
99
import com.wavesplatform.state.{Height, InvokeScriptResult, StateSnapshot, TransactionId, TxMeta, TxNum}
1010
import com.wavesplatform.transaction.{Authorized, EthereumTransaction, GenesisTransaction, Transaction, TransactionType}
1111
import monix.eval.Task
@@ -118,7 +118,7 @@ object AddressTransactions {
118118
.filter { case (_, tx) => types.isEmpty || types.contains(tx.tpe) }
119119
.collect { case (m, tx: Authorized) if sender.forall(_ == tx.sender.toAddress) => (m, tx, None) }
120120

121-
class TxByAddressIterator(
121+
private class TxByAddressIterator(
122122
db: DBResource,
123123
txHandle: RDB.TxHandle,
124124
addressId: AddressId,
@@ -127,9 +127,7 @@ object AddressTransactions {
127127
sender: Option[Address],
128128
types: Set[Transaction.Type]
129129
) extends AbstractIterator[Seq[(TxMeta, Transaction, Option[TxNum])]] {
130-
val prefix: Array[Byte] = KeyTags.AddressTransactionHeightTypeAndNums.prefixBytes ++ addressId.toByteArray
131-
val seqNr: Int = db.get(Keys.addressTransactionSeqNr(addressId))
132-
130+
private val seqNr = db.get(Keys.addressTransactionSeqNr(addressId))
133131
db.withSafePrefixIterator(_.seekForPrev(Keys.addressTransactionHN(addressId, seqNr).keyBytes))()
134132

135133
final override def computeNext(): Seq[(TxMeta, Transaction, Option[TxNum])] = db.withSafePrefixIterator { dbIterator =>

node/src/main/scala/com/wavesplatform/api/common/CommonAccountsApi.scala

Lines changed: 6 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,14 @@ import com.google.common.base.Charsets
44
import com.google.common.collect.AbstractIterator
55
import com.wavesplatform.account.{Address, Alias}
66
import com.wavesplatform.api.common.AddressPortfolio.{assetBalanceIterator, nftIterator}
7-
import com.wavesplatform.api.common.TransactionMeta.Ethereum
7+
import com.wavesplatform.api.common.lease.AddressLeaseInfo
88
import com.wavesplatform.common.state.ByteStr
9-
import com.wavesplatform.common.utils.EitherExt2
109
import com.wavesplatform.database.{DBExt, DBResource, KeyTags, Keys, RDB}
1110
import com.wavesplatform.features.BlockchainFeatures
1211
import com.wavesplatform.lang.ValidationError
1312
import com.wavesplatform.protobuf.transaction.PBRecipients
14-
import com.wavesplatform.state.LeaseDetails.Status
15-
import com.wavesplatform.state.{AccountScriptInfo, AssetDescription, Blockchain, DataEntry, Height, InvokeScriptResult, SnapshotBlockchain, TxMeta}
13+
import com.wavesplatform.state.{AccountScriptInfo, AssetDescription, Blockchain, DataEntry, SnapshotBlockchain}
1614
import com.wavesplatform.transaction.Asset.IssuedAsset
17-
import com.wavesplatform.transaction.EthereumTransaction.Invocation
18-
import com.wavesplatform.transaction.lease.LeaseTransaction
19-
import com.wavesplatform.transaction.{EthereumTransaction, TransactionType}
2015
import monix.eval.Task
2116
import monix.reactive.Observable
2217

@@ -127,75 +122,13 @@ object CommonAccountsApi {
127122
override def resolveAlias(alias: Alias): Either[ValidationError, Address] = blockchain.resolveAlias(alias)
128123

129124
override def activeLeases(address: Address): Observable[LeaseInfo] =
130-
addressTransactions(
131-
rdb,
132-
Some(Height(blockchain.height) -> compositeBlockchain().snapshot),
133-
address,
134-
None,
135-
Set(TransactionType.Lease, TransactionType.InvokeScript, TransactionType.InvokeExpression, TransactionType.Ethereum),
136-
None
137-
).flatMapIterable {
138-
case TransactionMeta(leaseHeight, lt: LeaseTransaction, TxMeta.Status.Succeeded) if leaseIsActive(lt.id()) =>
139-
Seq(
140-
LeaseInfo(
141-
lt.id(),
142-
lt.id(),
143-
lt.sender.toAddress,
144-
blockchain.resolveAlias(lt.recipient).explicitGet(),
145-
lt.amount.value,
146-
leaseHeight,
147-
LeaseInfo.Status.Active
148-
)
149-
)
150-
case TransactionMeta.Invoke(invokeHeight, originTransaction, TxMeta.Status.Succeeded, _, Some(scriptResult)) =>
151-
extractLeases(address, scriptResult, originTransaction.id(), invokeHeight)
152-
case Ethereum(height, tx @ EthereumTransaction(_: Invocation, _, _, _), TxMeta.Status.Succeeded, _, _, Some(scriptResult)) =>
153-
extractLeases(address, scriptResult, tx.id(), height)
154-
case _ => Seq()
155-
}
156-
157-
private def extractLeases(subject: Address, result: InvokeScriptResult, txId: ByteStr, height: Height): Seq[LeaseInfo] = {
158-
(for {
159-
lease <- result.leases
160-
details <- blockchain.leaseDetails(lease.id) if details.isActive
161-
sender = details.sender.toAddress
162-
recipient <- blockchain.resolveAlias(lease.recipient).toOption if subject == sender || subject == recipient
163-
} yield LeaseInfo(
164-
lease.id,
165-
txId,
166-
sender,
167-
recipient,
168-
lease.amount,
169-
height,
170-
LeaseInfo.Status.Active
171-
)) ++ {
172-
result.invokes.flatMap(i => extractLeases(subject, i.stateChanges, txId, height))
173-
}
174-
}
175-
176-
def leaseInfo(leaseId: ByteStr): Option[LeaseInfo] = blockchain.leaseDetails(leaseId) map { ld =>
177-
LeaseInfo(
178-
leaseId,
179-
ld.sourceId,
180-
ld.sender.toAddress,
181-
ld.recipientAddress,
182-
ld.amount.value,
183-
ld.height,
184-
ld.status match {
185-
case Status.Active => LeaseInfo.Status.Active
186-
case Status.Cancelled(_, _) => LeaseInfo.Status.Canceled
187-
case Status.Expired(_) => LeaseInfo.Status.Expired
188-
},
189-
ld.status.cancelHeight,
190-
ld.status.cancelTransactionId
191-
)
192-
}
125+
AddressLeaseInfo.activeLeases(rdb, compositeBlockchain().snapshot, address)
193126

194-
private[this] def leaseIsActive(id: ByteStr): Boolean =
195-
blockchain.leaseDetails(id).exists(_.isActive)
127+
def leaseInfo(leaseId: ByteStr): Option[LeaseInfo] =
128+
blockchain.leaseDetails(leaseId).map(LeaseInfo.fromLeaseDetails(leaseId, _))
196129
}
197130

198-
class AddressDataIterator(
131+
private class AddressDataIterator(
199132
db: DBResource,
200133
address: Address,
201134
entriesFromDiff: Array[DataEntry[?]],

node/src/main/scala/com/wavesplatform/api/common/LeaseInfo.scala

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,33 @@ package com.wavesplatform.api.common
33
import com.wavesplatform.account.Address
44
import com.wavesplatform.api.common.LeaseInfo.Status
55
import com.wavesplatform.common.state.ByteStr
6+
import com.wavesplatform.state.LeaseDetails
67

78
object LeaseInfo {
89
type Status = Status.Value
9-
//noinspection TypeAnnotation
10+
// noinspection TypeAnnotation
1011
object Status extends Enumeration {
1112
val Active = Value(1)
1213
val Canceled = Value(0)
1314
val Expired = Value(2)
1415
}
16+
17+
def fromLeaseDetails(id: ByteStr, details: LeaseDetails): LeaseInfo =
18+
LeaseInfo(
19+
id,
20+
details.sourceId,
21+
details.sender.toAddress,
22+
details.recipientAddress,
23+
details.amount.value,
24+
details.height,
25+
details.status match {
26+
case LeaseDetails.Status.Active => LeaseInfo.Status.Active
27+
case LeaseDetails.Status.Cancelled(_, _) => LeaseInfo.Status.Canceled
28+
case LeaseDetails.Status.Expired(_) => LeaseInfo.Status.Expired
29+
},
30+
details.status.cancelHeight,
31+
details.status.cancelTransactionId
32+
)
1533
}
1634

1735
case class LeaseInfo(
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.wavesplatform.api.common.lease
2+
3+
import com.wavesplatform.account.Address
4+
import com.wavesplatform.api.common.LeaseInfo
5+
import com.wavesplatform.common.state.ByteStr
6+
import com.wavesplatform.database.{AddressId, DBExt, DBResource, Keys, RDB}
7+
import com.wavesplatform.state.{LeaseDetails, StateSnapshot}
8+
import monix.eval.Task
9+
import monix.reactive.Observable
10+
11+
import scala.jdk.CollectionConverters.IteratorHasAsScala
12+
13+
object AddressLeaseInfo {
14+
def activeLeases(
15+
rdb: RDB,
16+
snapshot: StateSnapshot,
17+
subject: Address
18+
): Observable[LeaseInfo] = {
19+
val snapshotLeases = leasesFromSnapshot(snapshot, subject)
20+
val dbLeases = leasesFromDb(rdb, subject)
21+
Observable.fromIterable(snapshotLeases) ++ dbLeases.filterNot(info => snapshot.cancelledLeases.contains(info.id))
22+
}
23+
24+
private def leasesFromSnapshot(snapshot: StateSnapshot, subject: Address): Seq[LeaseInfo] =
25+
snapshot.newLeases.collect {
26+
case (id, leaseStatic)
27+
if !snapshot.cancelledLeases.contains(id) &&
28+
(subject == leaseStatic.sender.toAddress || subject == leaseStatic.recipientAddress) =>
29+
LeaseInfo(
30+
id,
31+
leaseStatic.sourceId,
32+
leaseStatic.sender.toAddress,
33+
leaseStatic.recipientAddress,
34+
leaseStatic.amount.value,
35+
leaseStatic.height,
36+
LeaseInfo.Status.Active
37+
)
38+
}.toSeq
39+
40+
private def leasesFromDb(rdb: RDB, subject: Address): Observable[LeaseInfo] =
41+
for {
42+
dbResource <- rdb.db.resourceObservable
43+
(leaseId, details) <- dbResource
44+
.get(Keys.addressId(subject))
45+
.map(fromLeaseDbIterator(dbResource, _))
46+
.getOrElse(Observable.empty)
47+
} yield LeaseInfo.fromLeaseDetails(leaseId, details)
48+
49+
private def fromLeaseDbIterator(dbResource: DBResource, addressId: AddressId): Observable[(ByteStr, LeaseDetails)] =
50+
Observable
51+
.fromIterator(Task(new LeaseByAddressIterator(dbResource, addressId).asScala))
52+
.concatMapIterable(identity)
53+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.wavesplatform.api.common.lease
2+
3+
import com.google.common.collect.AbstractIterator
4+
import com.wavesplatform.common.state.ByteStr
5+
import com.wavesplatform.database
6+
import com.wavesplatform.database.{AddressId, DBResource, Keys}
7+
import com.wavesplatform.state.LeaseDetails
8+
9+
import scala.collection.mutable
10+
11+
private class LeaseByAddressIterator(resource: DBResource, addressId: AddressId) extends AbstractIterator[Seq[(ByteStr, LeaseDetails)]] {
12+
private val seqNr = resource.get(Keys.addressLeaseSeqNr(addressId))
13+
resource.withSafePrefixIterator(_.seekForPrev(Keys.addressLeaseSeq(addressId, seqNr).keyBytes))()
14+
15+
final override def computeNext(): Seq[(ByteStr, LeaseDetails)] =
16+
resource.withSafePrefixIterator { iterator =>
17+
val buffer = mutable.Map[ByteStr, LeaseDetails]()
18+
while (iterator.isValid) {
19+
for {
20+
id <- database.readLeaseIdSeq(iterator.value())
21+
details <- database.loadLease(resource, id) if details.isActive
22+
} buffer.update(id, details)
23+
iterator.prev()
24+
}
25+
if (buffer.nonEmpty)
26+
buffer.toSeq
27+
else
28+
endOfData()
29+
}(
30+
endOfData()
31+
)
32+
}

node/src/main/scala/com/wavesplatform/database/KeyTags.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ object KeyTags extends Enumeration {
6161
EthereumTransactionMeta,
6262
NthTransactionStateSnapshotAtHeight,
6363
MaliciousMinerBanHeights,
64-
BlockStateHash = Value
64+
BlockStateHash,
65+
AddressLeaseInfoSeqNr,
66+
AddressLeaseInfoSeq = Value
6567

6668
final implicit class KeyTagExt(val t: KeyTag) extends AnyVal {
6769
@inline def prefixBytes: Array[Byte] = Shorts.toByteArray(t.id.toShort)

node/src/main/scala/com/wavesplatform/database/Keys.scala

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,7 @@ object DataNode {
5555

5656
object Keys {
5757
import KeyHelpers.*
58-
import KeyTags.{
59-
AddressId as AddressIdTag,
60-
EthereumTransactionMeta as EthereumTransactionMetaTag,
61-
InvokeScriptResult as InvokeScriptResultTag,
62-
LeaseDetails as LeaseDetailsTag,
63-
*
64-
}
58+
import KeyTags.{AddressId as AddressIdTag, EthereumTransactionMeta as EthereumTransactionMetaTag, InvokeScriptResult as InvokeScriptResultTag, LeaseDetails as LeaseDetailsTag, *}
6559

6660
val version: Key[Int] = intKey(Version, default = 1)
6761
val height: Key[Height] =
@@ -190,6 +184,17 @@ object Keys {
190184
writeTransactionHNSeqAndType
191185
)
192186

187+
def addressLeaseSeqNr(addressId: AddressId): Key[Int] =
188+
bytesSeqNr(AddressLeaseInfoSeqNr, addressId.toByteArray)
189+
190+
def addressLeaseSeq(addressId: AddressId, seqNr: Int): Key[Option[Seq[ByteStr]]] =
191+
Key.opt(
192+
AddressLeaseInfoSeq,
193+
hBytes(addressId.toByteArray, seqNr),
194+
readLeaseIdSeq,
195+
writeLeaseIdSeq
196+
)
197+
193198
def transactionMetaById(txId: TransactionId, cfh: RDB.TxMetaHandle): Key[Option[TransactionMeta]] =
194199
Key.opt(
195200
TransactionMetaById,

0 commit comments

Comments
 (0)