diff --git a/grpc-server/src/main/scala/com/wavesplatform/api/grpc/AccountsApiGrpcImpl.scala b/grpc-server/src/main/scala/com/wavesplatform/api/grpc/AccountsApiGrpcImpl.scala index 5e14d9a99d..fbcf7b3e76 100644 --- a/grpc-server/src/main/scala/com/wavesplatform/api/grpc/AccountsApiGrpcImpl.scala +++ b/grpc-server/src/main/scala/com/wavesplatform/api/grpc/AccountsApiGrpcImpl.scala @@ -63,8 +63,15 @@ class AccountsApiGrpcImpl(commonApi: CommonAccountsApi)(implicit sc: Scheduler) override def getScript(request: AccountRequest): Future[ScriptResponse] = Future { commonApi.script(request.address.toAddress()) match { - case Some(desc) => ScriptResponse(PBTransactions.toPBScript(Some(desc.script)), desc.script.expr.toString, desc.verifierComplexity, desc.publicKey.toByteString) - case None => ScriptResponse() + case Some(desc) => + ScriptResponse( + PBTransactions.toPBScript(Some(desc.script)), + desc.script.expr.toString, + desc.verifierComplexity, + desc.publicKey.toByteString + ) + case None => + ScriptResponse() } } diff --git a/grpc-server/src/main/scala/com/wavesplatform/events/events.scala b/grpc-server/src/main/scala/com/wavesplatform/events/events.scala index a83d09fdc2..c08030cb2f 100644 --- a/grpc-server/src/main/scala/com/wavesplatform/events/events.scala +++ b/grpc-server/src/main/scala/com/wavesplatform/events/events.scala @@ -23,7 +23,7 @@ import com.wavesplatform.transaction.assets.exchange.ExchangeTransaction import com.wavesplatform.transaction.lease.LeaseTransaction import com.wavesplatform.transaction.smart.InvokeScriptTransaction import com.wavesplatform.transaction.transfer.{MassTransferTransaction, TransferTransaction} -import com.wavesplatform.transaction.{Asset, Authorized, CreateAliasTransaction, EthereumTransaction} +import com.wavesplatform.transaction.{Asset, Authorized, CreateAliasTransaction, EthereumTransaction, Transaction} import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -389,7 +389,11 @@ object StateUpdate { private lazy val WavesAlias = Alias.fromString("alias:W:waves", Some('W'.toByte)).explicitGet() private lazy val WavesAddress = Address.fromString("3PGd1eQR8EhLkSogpmu9Ne7hSH1rQ5ALihd", Some('W'.toByte)).explicitGet() - def atomic(blockchainBeforeWithMinerReward: Blockchain, snapshot: StateSnapshot): StateUpdate = { + def atomic( + blockchainBeforeWithMinerReward: Blockchain, + snapshot: StateSnapshot, + txWithLeases: Iterable[(Transaction, Map[ByteStr, LeaseSnapshot])] + ): StateUpdate = { val blockchain = blockchainBeforeWithMinerReward val blockchainAfter = SnapshotBlockchain(blockchain, snapshot) @@ -426,18 +430,20 @@ object StateUpdate { assetAfter = blockchainAfter.assetDescription(asset) } yield AssetStateUpdate(asset.id, assetBefore, assetAfter) - val updatedLeases = snapshot.leaseStates.map { case (leaseId, newState) => - LeaseUpdate( - leaseId, - if (newState.isActive) LeaseStatus.Active else LeaseStatus.Inactive, - newState.amount, - newState.sender, - newState.recipient match { - case `WavesAlias` => WavesAddress - case other => blockchainAfter.resolveAlias(other).explicitGet() - }, - newState.sourceId - ) + val updatedLeases = txWithLeases.flatMap { case (sourceTxId, leases) => + leases.map { case (leaseId, newState) => + LeaseUpdate( + leaseId, + if (newState.isActive) LeaseStatus.Active else LeaseStatus.Inactive, + newState.amount, + newState.sender, + newState.recipient match { + case `WavesAlias` => WavesAddress + case other => blockchainAfter.resolveAlias(other).explicitGet() + }, + newState.toDetails(blockchain, Some(sourceTxId), blockchain.leaseDetails(leaseId)).sourceId + ) + } }.toVector val updatedScripts = snapshot.accountScriptsByAddress.map { case (address, newScript) => @@ -546,13 +552,17 @@ object StateUpdate { val accBlockchain = SnapshotBlockchain(blockchainBeforeWithReward, accSnapshot) ( accSnapshot |+| txInfo.snapshot, - updates :+ atomic(accBlockchain, txInfo.snapshot) + updates :+ atomic(accBlockchain, txInfo.snapshot, Seq((txInfo.transaction, txInfo.snapshot.leaseStates))) ) } val blockchainAfter = SnapshotBlockchain(blockchainBeforeWithReward, totalSnapshot) val metadata = transactionsMetadata(blockchainAfter, totalSnapshot) val refAssets = referencedAssets(blockchainAfter, txsStateUpdates) - val keyBlockUpdate = atomic(blockchainBeforeWithReward, keyBlockSnapshot) + val keyBlockUpdate = atomic( + blockchainBeforeWithReward, + keyBlockSnapshot, + keyBlockSnapshot.transactions.map { case (_, txInfo) => (txInfo.transaction, txInfo.snapshot.leaseStates) } + ) (keyBlockUpdate, txsStateUpdates, metadata, refAssets) } } diff --git a/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala b/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala index 97e19d5ba5..b67c81bf8b 100644 --- a/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala +++ b/node/src/main/scala/com/wavesplatform/api/http/TransactionJsonSerializer.scala @@ -16,18 +16,18 @@ import com.wavesplatform.lang.v1.compiler.Terms.{ARR, CONST_BOOLEAN, CONST_BYTES import com.wavesplatform.lang.v1.serialization.SerdeV1 import com.wavesplatform.protobuf.transaction.PBAmounts import com.wavesplatform.state.InvokeScriptResult.{AttachedPayment, Burn, Call, ErrorMessage, Invocation, Issue, Lease, LeaseCancel, Reissue, SponsorFee} -import com.wavesplatform.state.{Blockchain, DataEntry, InvokeScriptResult, TxMeta} import com.wavesplatform.state.reader.LeaseDetails +import com.wavesplatform.state.{Blockchain, DataEntry, InvokeScriptResult, TxMeta} import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} -import com.wavesplatform.transaction.{Asset, PBSince, Transaction} import com.wavesplatform.transaction.lease.{LeaseCancelTransaction, LeaseTransaction} import com.wavesplatform.transaction.serialization.impl.InvokeScriptTxSerializer import com.wavesplatform.transaction.smart.InvokeScriptTransaction import com.wavesplatform.transaction.smart.InvokeScriptTransaction.Payment import com.wavesplatform.transaction.transfer.MassTransferTransaction +import com.wavesplatform.transaction.{Asset, PBSince, Transaction} import com.wavesplatform.utils.EthEncoding +import play.api.libs.json.* import play.api.libs.json.JsonConfiguration.Aux -import play.api.libs.json.{JsArray, JsBoolean, JsNumber, JsObject, JsString, JsValue, Json, JsonConfiguration, OWrites, OptionHandlers} final case class TransactionJsonSerializer(blockchain: Blockchain, commonApi: CommonTransactionsApi) { @@ -525,6 +525,6 @@ object TransactionJsonSerializer { object LeaseRef { import com.wavesplatform.utils.byteStrFormat implicit val config: Aux[Json.MacroOptions] = JsonConfiguration(optionHandlers = OptionHandlers.WritesNull) - implicit val jsonWrites: OWrites[LeaseRef] = Json.writes[LeaseRef] + implicit val jsonWrites: OWrites[LeaseRef] = Json.writes[LeaseRef] } } diff --git a/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala b/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala index b16f420538..4b11f41289 100644 --- a/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala +++ b/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala @@ -7,8 +7,8 @@ import com.google.common.hash.{BloomFilter, Funnels} import com.google.common.primitives.Ints import com.wavesplatform.account.{Address, Alias} import com.wavesplatform.api.common.WavesBalanceIterator -import com.wavesplatform.block.BlockSnapshot import com.wavesplatform.block.Block.BlockId +import com.wavesplatform.block.BlockSnapshot import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.database @@ -17,8 +17,7 @@ import com.wavesplatform.database.protobuf.{StaticAssetInfo, TransactionMeta, Bl import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lang.ValidationError import com.wavesplatform.protobuf.block.PBBlocks -import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot -import com.wavesplatform.protobuf.snapshot.TransactionStatus as PBStatus +import com.wavesplatform.protobuf.snapshot.{TransactionStateSnapshot, TransactionStatus as PBStatus} import com.wavesplatform.protobuf.{ByteStrExt, ByteStringExt} import com.wavesplatform.settings.{BlockchainSettings, DBSettings} import com.wavesplatform.state.* @@ -485,7 +484,14 @@ class RocksDBWriter( expiredKeys ++= updateHistory(rw, Keys.assetDetailsHistory(asset), threshold, Keys.assetDetails(asset)) } - for ((id, details) <- snapshot.leaseStates) { + val txLeases = for { + (_, txInfo) <- snapshot.transactions + (id, lease) <- txInfo.snapshot.leaseStates + } yield (Some(txInfo.transaction), id, lease) + val txLeaseIdSet = txLeases.map(_._2).toSet + val allLeases = txLeases ++ snapshot.leaseStates.collect { case (id, lease) if !txLeaseIdSet.contains(id) => (None, id, lease) } + allLeases.foreach { case (txOpt, id, lease) => + val details = lease.toDetails(this, txOpt, leaseDetails(id)) rw.put(Keys.leaseDetails(id)(height), Some(Right(details))) expiredKeys ++= updateHistory(rw, Keys.leaseDetailsHistory(id), threshold, Keys.leaseDetails(id)) } diff --git a/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala b/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala index 90ccafbdb2..99d193f72b 100644 --- a/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala +++ b/node/src/main/scala/com/wavesplatform/state/BlockchainUpdaterImpl.scala @@ -437,14 +437,13 @@ class BlockchainUpdaterImpl( val snapshotsById = for { lt <- leaseTransactions - ltMeta <- transactionMeta(lt.id()).toSeq recipient <- rocksdb.resolveAlias(lt.recipient).toSeq portfolios = Map( lt.sender.toAddress -> Portfolio(0, LeaseBalance(0, -lt.amount.value)), recipient -> Portfolio(0, LeaseBalance(-lt.amount.value, 0)) ) leaseStates = Map( - lt.id() -> LeaseDetails(lt.sender, lt.recipient, lt.amount.value, LeaseDetails.Status.Expired(height), lt.id(), ltMeta.height) + lt.id() -> LeaseSnapshot(lt.sender, lt.recipient, lt.amount.value, LeaseDetails.Status.Expired(height)) ) snapshot = StateSnapshot.build(rocksdb, portfolios, leaseStates = leaseStates).explicitGet() } yield lt.id() -> snapshot diff --git a/node/src/main/scala/com/wavesplatform/state/LeaseSnapshot.scala b/node/src/main/scala/com/wavesplatform/state/LeaseSnapshot.scala new file mode 100644 index 0000000000..e49fa4a09d --- /dev/null +++ b/node/src/main/scala/com/wavesplatform/state/LeaseSnapshot.scala @@ -0,0 +1,30 @@ +package com.wavesplatform.state +import com.wavesplatform.account.{AddressOrAlias, PublicKey} +import com.wavesplatform.common.state.ByteStr +import com.wavesplatform.state.reader.LeaseDetails +import com.wavesplatform.transaction.Transaction +import com.wavesplatform.transaction.lease.LeaseCancelTransaction + +import scala.util.Try + +case class LeaseSnapshot(sender: PublicKey, recipient: AddressOrAlias, amount: Long, status: LeaseDetails.Status) { + val isActive: Boolean = status == LeaseDetails.Status.Active + + def toDetails(blockchain: Blockchain, txOpt: Option[Transaction], innerDetails: => Option[LeaseDetails]): LeaseDetails = { + def height(id: ByteStr) = blockchain.transactionMeta(id).map(_.height).getOrElse(blockchain.height) + val (sourceId, sourceHeight) = txOpt match { + case Some(c: LeaseCancelTransaction) => (c.leaseId, height(c.leaseId)) + case Some(i) if isActive => (i.id(), blockchain.height) // produced by lease or invoke (including eth) + case Some(i) if !isActive && innerDetails.isEmpty => (i.id(), blockchain.height) // produced and cancelled by the same invoke + case _ => + def id = innerDetails.get.sourceId // cancelled by invoke and produced by other transaction from the state + Try((id, height(id))).getOrElse((ByteStr.empty, 0)) + } + LeaseDetails(sender, recipient, amount, status, sourceId, sourceHeight) + } +} + +object LeaseSnapshot { + def fromDetails(details: LeaseDetails): LeaseSnapshot = + LeaseSnapshot(details.sender, details.recipient, details.amount, details.status) +} diff --git a/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala b/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala index e4c812f361..f3782f2707 100644 --- a/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala +++ b/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala @@ -3,15 +3,16 @@ import cats.data.Ior import cats.implicits.{catsSyntaxEitherId, catsSyntaxSemigroup, toBifunctorOps, toTraverseOps} import cats.kernel.Monoid import com.google.protobuf.ByteString -import com.wavesplatform.account.{Address, AddressScheme, Alias, PublicKey} +import com.wavesplatform.account.{Address, Alias, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 +import com.wavesplatform.crypto.KeyLength import com.wavesplatform.database.protobuf.EthereumTransactionMeta import com.wavesplatform.lang.ValidationError import com.wavesplatform.lang.script.ScriptReader import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot.AssetStatic -import com.wavesplatform.protobuf.transaction.{PBAmounts, PBRecipients, PBTransactions} +import com.wavesplatform.protobuf.transaction.{PBAmounts, PBTransactions} import com.wavesplatform.protobuf.{AddressExt, ByteStrExt, ByteStringExt} import com.wavesplatform.state.reader.LeaseDetails.Status import com.wavesplatform.state.reader.{LeaseDetails, SnapshotBlockchain} @@ -30,7 +31,7 @@ case class StateSnapshot( assetNamesAndDescriptions: Map[IssuedAsset, AssetInfo] = Map(), assetScripts: Map[IssuedAsset, AssetScriptInfo] = Map(), sponsorships: Map[IssuedAsset, SponsorshipValue] = Map(), - leaseStates: Map[ByteStr, LeaseDetails] = Map(), + leaseStates: Map[ByteStr, LeaseSnapshot] = Map(), aliases: Map[Alias, Address] = Map(), orderFills: Map[ByteStr, VolumeAndFee] = Map(), accountScripts: Map[PublicKey, Option[AccountScriptInfo]] = Map(), @@ -63,24 +64,14 @@ case class StateSnapshot( orderFills.map { case (orderId, VolumeAndFee(volume, fee)) => S.OrderFill(orderId.toByteString, volume, fee) }.toSeq, - leaseStates.map { case (leaseId, LeaseDetails(sender, recipient, amount, status, sourceId, height)) => + leaseStates.map { case (leaseId, LeaseSnapshot(sender, recipient, amount, status)) => val pbStatus = status match { case Status.Active => - S.LeaseState.Status.Active(S.LeaseState.Active()) - case Status.Cancelled(cancelHeight, txId) => - S.LeaseState.Status.Cancelled(S.LeaseState.Cancelled(cancelHeight, txId.fold(ByteString.EMPTY)(_.toByteString))) - case Status.Expired(expiredHeight) => - S.LeaseState.Status.Cancelled(S.LeaseState.Cancelled(expiredHeight)) + S.LeaseState.Status.Active(S.LeaseState.Active(amount, sender.toByteString, recipient.asInstanceOf[Address].toByteString)) + case _: Status.Cancelled | _: Status.Expired => + S.LeaseState.Status.Cancelled(S.LeaseState.Cancelled()) } - S.LeaseState( - leaseId.toByteString, - pbStatus, - amount, - sender.toByteString, - ByteString.copyFrom(recipient.asInstanceOf[Address].bytes), - sourceId.toByteString, - height - ) + S.LeaseState(leaseId.toByteString, pbStatus) }.toSeq, accountScripts.map { case (publicKey, scriptOpt) => scriptOpt.fold( @@ -171,24 +162,25 @@ object StateSnapshot { .map(s => s.assetId.toIssuedAssetId -> SponsorshipValue(s.minFee)) .toMap - val leaseStates: Map[ByteStr, LeaseDetails] = - pbSnapshot.leaseStates - .map(ls => - ls.leaseId.toByteStr -> LeaseDetails( - ls.sender.toPublicKey, - PBRecipients.toAddress(ls.recipient.toByteArray, AddressScheme.current.chainId).explicitGet(), - ls.amount, - ls.status match { - case TransactionStateSnapshot.LeaseState.Status.Cancelled(c) => - LeaseDetails.Status.Cancelled(c.height, if (c.transactionId.isEmpty) None else Some(c.transactionId.toByteStr)) - case _ => - LeaseDetails.Status.Active - }, - ls.originTransactionId.toByteStr, - ls.height - ) - ) - .toMap + val leaseStates: Map[ByteStr, LeaseSnapshot] = + pbSnapshot.leaseStates.map { ls => + ls.status match { + case TransactionStateSnapshot.LeaseState.Status.Active(value) => + ls.leaseId.toByteStr -> LeaseSnapshot( + value.sender.toPublicKey, + value.recipient.toAddress(), + value.amount, + LeaseDetails.Status.Active + ) + case _: TransactionStateSnapshot.LeaseState.Status.Cancelled | TransactionStateSnapshot.LeaseState.Status.Empty => + ls.leaseId.toByteStr -> LeaseSnapshot( + PublicKey(ByteStr.fill(KeyLength)(0)), + Address(Array.fill(Address.HashLength)(0)), + 0, + LeaseDetails.Status.Cancelled(0, None) + ) + } + }.toMap val aliases: Map[Alias, Address] = pbSnapshot.aliases @@ -254,7 +246,7 @@ object StateSnapshot { updatedAssets: Map[IssuedAsset, Ior[AssetInfo, AssetVolumeInfo]] = Map(), assetScripts: Map[IssuedAsset, AssetScriptInfo] = Map(), sponsorships: Map[IssuedAsset, Sponsorship] = Map(), - leaseStates: Map[ByteStr, LeaseDetails] = Map(), + leaseStates: Map[ByteStr, LeaseSnapshot] = Map(), aliases: Map[Alias, Address] = Map(), accountData: Map[Address, Map[String, DataEntry[?]]] = Map(), accountScripts: Map[PublicKey, Option[AccountScriptInfo]] = Map(), @@ -381,9 +373,9 @@ object StateSnapshot { private def resolvedLeaseStates( blockchain: Blockchain, - leaseStates: Map[ByteStr, LeaseDetails], + leaseStates: Map[ByteStr, LeaseSnapshot], aliases: Map[Alias, Address] - ): Map[ByteStr, LeaseDetails] = + ): Map[ByteStr, LeaseSnapshot] = leaseStates.view .mapValues(details => details.copy(recipient = details.recipient match { diff --git a/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala b/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala index d2b4dc46af..7d99d89319 100644 --- a/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala +++ b/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala @@ -76,7 +76,17 @@ object TxStateSnapshotHashBuilder { } addEntry(KeyType.AssetScript, asset.id.arr)(scriptInfo.script.bytes().arr) snapshot.leaseStates.foreach { case (leaseId, details) => - addEntry(KeyType.LeaseStatus, leaseId.arr)(booleanToBytes(details.isActive)) + if (details.isActive) + addEntry(KeyType.LeaseStatus, leaseId.arr)( + booleanToBytes(true), + details.sender.arr, + details.recipient.bytes, + Longs.toByteArray(details.amount) + ) + else + addEntry(KeyType.LeaseStatus, leaseId.arr)( + booleanToBytes(false) + ) } snapshot.sponsorships.foreach { case (asset, sponsorship) => diff --git a/node/src/main/scala/com/wavesplatform/state/diffs/DiffsCommon.scala b/node/src/main/scala/com/wavesplatform/state/diffs/DiffsCommon.scala index bc5ccd8c90..5c83e03e36 100644 --- a/node/src/main/scala/com/wavesplatform/state/diffs/DiffsCommon.scala +++ b/node/src/main/scala/com/wavesplatform/state/diffs/DiffsCommon.scala @@ -16,7 +16,7 @@ import com.wavesplatform.lang.v1.estimator.v2.ScriptEstimatorV2 import com.wavesplatform.lang.v1.estimator.{ScriptEstimator, ScriptEstimatorV1} import com.wavesplatform.lang.v1.traits.domain.* import com.wavesplatform.state.reader.LeaseDetails -import com.wavesplatform.state.{AssetVolumeInfo, Blockchain, LeaseBalance, Portfolio, SponsorshipValue, StateSnapshot} +import com.wavesplatform.state.{AssetVolumeInfo, Blockchain, LeaseBalance, LeaseSnapshot, Portfolio, SponsorshipValue, StateSnapshot} import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.GenericError @@ -166,7 +166,7 @@ object DiffsCommon { senderAddress -> Portfolio(-fee, LeaseBalance(0, amount)), recipientAddress -> Portfolio(0, LeaseBalance(amount, 0)) ) - details = LeaseDetails(sender, recipient, amount, LeaseDetails.Status.Active, txId, blockchain.height) + details = LeaseSnapshot(sender, recipient, amount, LeaseDetails.Status.Active) snapshot <- StateSnapshot.build( blockchain, portfolios = portfolioDiff, @@ -207,7 +207,7 @@ object DiffsCommon { snapshot <- StateSnapshot.build( blockchain, portfolios = portfolios, - leaseStates = Map(leaseId -> actionInfo) + leaseStates = Map(leaseId -> LeaseSnapshot.fromDetails(actionInfo)) ) } yield snapshot } diff --git a/node/src/main/scala/com/wavesplatform/state/patch/CancelAllLeases.scala b/node/src/main/scala/com/wavesplatform/state/patch/CancelAllLeases.scala index 7a7462691e..15c83699c3 100644 --- a/node/src/main/scala/com/wavesplatform/state/patch/CancelAllLeases.scala +++ b/node/src/main/scala/com/wavesplatform/state/patch/CancelAllLeases.scala @@ -5,7 +5,7 @@ import com.wavesplatform.account.{Address, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.* import com.wavesplatform.state.reader.LeaseDetails -import com.wavesplatform.state.{Blockchain, LeaseBalance, StateSnapshot} +import com.wavesplatform.state.{Blockchain, LeaseBalance, LeaseSnapshot, StateSnapshot} import play.api.libs.json.{Json, OFormat} case object CancelAllLeases extends PatchAtHeight('W' -> 462000, 'T' -> 51500) { @@ -13,11 +13,11 @@ case object CancelAllLeases extends PatchAtHeight('W' -> 462000, 'T' -> 51500) { private[patch] case class CancelledLeases(balances: Map[Address, LeaseBalance], cancelledLeases: Seq[LeaseData]) { private[this] val height: Int = patchHeight.getOrElse(0) - val leaseStates: Map[ByteStr, LeaseDetails] = cancelledLeases.map { data => + val leaseStates: Map[ByteStr, LeaseSnapshot] = cancelledLeases.map { data => val sender = PublicKey(ByteStr.decodeBase58(data.senderPublicKey).get) val recipient = Address.fromString(data.recipient).explicitGet() val id = ByteStr.decodeBase58(data.id).get - (id, LeaseDetails(sender, recipient, data.amount, status = LeaseDetails.Status.Expired(height), id, height)) + (id, LeaseSnapshot(sender, recipient, data.amount, status = LeaseDetails.Status.Expired(height))) }.toMap } diff --git a/node/src/main/scala/com/wavesplatform/state/patch/CancelLeasesToDisabledAliases.scala b/node/src/main/scala/com/wavesplatform/state/patch/CancelLeasesToDisabledAliases.scala index 1dc9349c6e..e1e343e4bf 100644 --- a/node/src/main/scala/com/wavesplatform/state/patch/CancelLeasesToDisabledAliases.scala +++ b/node/src/main/scala/com/wavesplatform/state/patch/CancelLeasesToDisabledAliases.scala @@ -6,7 +6,7 @@ import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.{Base58, EitherExt2} import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.state.reader.LeaseDetails -import com.wavesplatform.state.{Blockchain, LeaseBalance, Portfolio, StateSnapshot} +import com.wavesplatform.state.{Blockchain, LeaseBalance, LeaseSnapshot, Portfolio, StateSnapshot} import play.api.libs.json.{Json, Reads} case object CancelLeasesToDisabledAliases extends PatchOnFeature(BlockchainFeatures.SynchronousCalls, Set('W')) { @@ -19,7 +19,7 @@ case object CancelLeasesToDisabledAliases extends PatchOnFeature(BlockchainFeatu height: Int ) - def patchData: Map[ByteStr, (LeaseDetails, Address)] = { + def patchData: Map[ByteStr, (LeaseSnapshot, Address)] = { implicit val cancelDetailsReads: Reads[CancelDetails] = Json.reads readPatchData[Seq[CancelDetails]]().map { cancelDetails => @@ -27,13 +27,11 @@ case object CancelLeasesToDisabledAliases extends PatchOnFeature(BlockchainFeatu val sender = PublicKey(Base58.decode(cancelDetails.senderPublicKey)) val recipientAlias = Alias.fromString(cancelDetails.recipientAlias).explicitGet() val recipientAddress = Address.fromString(cancelDetails.recipientAddress).explicitGet() - leaseId -> (LeaseDetails( + leaseId -> (LeaseSnapshot( sender, recipientAlias, cancelDetails.amount, - LeaseDetails.Status.Expired(0), - leaseId, - cancelDetails.height + LeaseDetails.Status.Expired(0) ) -> recipientAddress) }.toMap } diff --git a/node/src/main/scala/com/wavesplatform/state/reader/SnapshotBlockchain.scala b/node/src/main/scala/com/wavesplatform/state/reader/SnapshotBlockchain.scala index d8347f6c35..abc3ac79d5 100644 --- a/node/src/main/scala/com/wavesplatform/state/reader/SnapshotBlockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/reader/SnapshotBlockchain.scala @@ -84,8 +84,18 @@ case class SnapshotBlockchain( override def assetDescription(asset: IssuedAsset): Option[AssetDescription] = SnapshotBlockchain.assetDescription(asset, snapshot, height, inner) - override def leaseDetails(leaseId: ByteStr): Option[LeaseDetails] = - snapshot.leaseStates.get(leaseId).orElse(inner.leaseDetails(leaseId)) + override def leaseDetails(leaseId: ByteStr): Option[LeaseDetails] = { + lazy val innerDetails = inner.leaseDetails(leaseId) + val txOpt = + snapshot.transactions + .collectFirst { + case (_, txInfo) if txInfo.snapshot.leaseStates.contains(leaseId) => txInfo.transaction + } + snapshot.leaseStates + .get(leaseId) + .map(_.toDetails(this, txOpt, innerDetails)) + .orElse(innerDetails) + } override def transferById(id: ByteStr): Option[(Int, TransferTransactionLike)] = snapshot.transactions diff --git a/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala b/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala index d1d6195c51..185f5ad708 100644 --- a/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala +++ b/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala @@ -7,6 +7,7 @@ import com.wavesplatform.common.utils.EitherExt2 import com.wavesplatform.lang.ValidationError import com.wavesplatform.protobuf.ByteStringExt import com.wavesplatform.state.* +import com.wavesplatform.state.reader.LeaseDetails import com.wavesplatform.transaction.Asset.IssuedAsset import scala.collection.immutable.VectorMap @@ -21,7 +22,7 @@ object SnapshotOps { updatedAssets, s.aliases, orderFills(blockchain), - s.leaseStates, + s.leaseStates.map(l => (l._1, LeaseDetails(l._2.sender, l._2.recipient, l._2.amount, l._2.status, ByteStr.empty, 0))), s.accountScripts, s.assetScripts.view.mapValues(Some(_)).toMap, s.accountData, @@ -96,7 +97,7 @@ object SnapshotOps { diff.updatedAssets, diff.assetScripts.collect { case (asset, Some(info)) => (asset, info) }, diff.sponsorship, - diff.leaseState, + diff.leaseState.map(l => (l._1, LeaseSnapshot(l._2.sender, l._2.recipient, l._2.amount, l._2.status))), diff.aliases, diff.accountData, diff.scripts, diff --git a/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala b/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala index 52350e5698..2908946046 100644 --- a/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala +++ b/node/src/test/scala/com/wavesplatform/http/DebugApiRouteSpec.scala @@ -1,9 +1,8 @@ package com.wavesplatform.http -import java.util.concurrent.TimeUnit - import akka.http.scaladsl.model.{ContentTypes, HttpEntity, StatusCodes} import com.typesafe.config.ConfigObject +import com.wavesplatform.* import com.wavesplatform.account.{Alias, KeyPair} import com.wavesplatform.api.common.CommonTransactionsApi import com.wavesplatform.api.http.ApiError.ApiKeyNotValid @@ -42,12 +41,12 @@ import com.wavesplatform.transaction.smart.script.ScriptCompiler import com.wavesplatform.transaction.{ERC20Address, Transaction, TxHelpers, TxVersion} import com.wavesplatform.utils.SharedSchedulerMixin import com.wavesplatform.wallet.Wallet -import com.wavesplatform.* import monix.eval.Task import org.scalamock.scalatest.PathMockFactory import org.scalatest.{Assertion, OptionValues} import play.api.libs.json.{JsArray, JsObject, JsValue, Json} +import java.util.concurrent.TimeUnit import scala.concurrent.duration.* import scala.util.Random @@ -1619,10 +1618,10 @@ class DebugApiRouteSpec } "invoke tx returning leases" in { - val dAppPk = TxHelpers.defaultSigner.publicKey - val dAppAddress = dAppPk.toAddress - val invoke = TxHelpers.invoke(dAppPk.toAddress) - val leaseCancelId = ByteStr(bytes32gen.sample.get) + val dAppPk = TxHelpers.defaultSigner.publicKey + val dAppAddress = dAppPk.toAddress + val invoke = TxHelpers.invoke(dAppPk.toAddress) + val canceledLeaseId = ByteStr(bytes32gen.sample.get) val amount1 = 100 val recipient1 = Recipient.Address(ByteStr.decodeBase58("3NAgxLPGnw3RGv9JT6NTDaG5D1iLUehg2xd").get) @@ -1660,7 +1659,7 @@ class DebugApiRouteSpec | [ | Lease(Address(base58'${recipient1.bytes}'), $amount1, $nonce1), | Lease(Alias("${recipient2.name}"), $amount2, $nonce2), - | LeaseCancel(base58'$leaseCancelId') + | LeaseCancel(base58'$canceledLeaseId') | ] | else [] |} @@ -1685,13 +1684,13 @@ class DebugApiRouteSpec (blockchain.hasAccountScript _).when(*).returns(true) (blockchain.transactionMeta _) - .when(leaseCancelId) + .when(canceledLeaseId) .returns(Some(TxMeta(Height(1), Status.Succeeded, 0L))) .anyNumberOfTimes() (blockchain.leaseDetails _) - .when(leaseCancelId) - .returns(Some(LeaseDetails(dAppPk, TxHelpers.defaultAddress, leaseCancelAmount, LeaseDetails.Status.Active, leaseCancelId, 1))) + .when(canceledLeaseId) + .returns(Some(LeaseDetails(dAppPk, TxHelpers.defaultAddress, leaseCancelAmount, LeaseDetails.Status.Active, invoke.id(), 1))) .anyNumberOfTimes() (blockchain.leaseDetails _) @@ -1743,8 +1742,8 @@ class DebugApiRouteSpec | "cancelTransactionId" : null | } ], | "leaseCancels" : [ { - | "id" : "$leaseCancelId", - | "originTransactionId" : "$leaseCancelId", + | "id" : "$canceledLeaseId", + | "originTransactionId" : "${invoke.id()}", | "sender" : "$dAppAddress", | "recipient" : "${TxHelpers.defaultAddress}", | "amount" : $leaseCancelAmount, @@ -1797,8 +1796,8 @@ class DebugApiRouteSpec | "cancelTransactionId" : null | } ], | "leaseCancels" : [ { - | "id" : "$leaseCancelId", - | "originTransactionId" : "$leaseCancelId", + | "id" : "$canceledLeaseId", + | "originTransactionId" : "${invoke.id()}", | "sender" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", | "recipient" : "3MtGzgmNa5fMjGCcPi5nqMTdtZkfojyWHL9", | "amount" : $leaseCancelAmount, @@ -2007,7 +2006,7 @@ class DebugApiRouteSpec | "type" : "Array", | "value" : [ { | "type" : "ByteVector", - | "value" : "$leaseCancelId" + | "value" : "$canceledLeaseId" | } ] | }, { | "name" : "LeaseCancel.@complexity", @@ -2025,7 +2024,7 @@ class DebugApiRouteSpec | "value" : { | "leaseId" : { | "type" : "ByteVector", - | "value" : "$leaseCancelId" + | "value" : "$canceledLeaseId" | } | } | }, { @@ -2071,7 +2070,7 @@ class DebugApiRouteSpec | "value" : { | "leaseId" : { | "type" : "ByteVector", - | "value" : "$leaseCancelId" + | "value" : "$canceledLeaseId" | } | } | } ] @@ -2136,7 +2135,7 @@ class DebugApiRouteSpec | "value" : { | "leaseId" : { | "type" : "ByteVector", - | "value" : "$leaseCancelId" + | "value" : "$canceledLeaseId" | } | } | } ] diff --git a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotProtoTest.scala b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotProtoTest.scala index 7f2d5ae0a1..78c5df5722 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotProtoTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotProtoTest.scala @@ -1,14 +1,14 @@ package com.wavesplatform.state.snapshot import com.google.protobuf.ByteString -import com.wavesplatform.account.Alias +import com.wavesplatform.account.{Address, Alias, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 +import com.wavesplatform.crypto.KeyLength import com.wavesplatform.lang.directives.values.V6 import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot.AssetStatic import com.wavesplatform.state.* -import com.wavesplatform.state.reader.LeaseDetails import com.wavesplatform.state.reader.LeaseDetails.Status import com.wavesplatform.test.PropSpec import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} @@ -60,14 +60,12 @@ class StateSnapshotProtoTest extends PropSpec { IssuedAsset(ByteStr.fromBytes(2, 2, 2)) -> SponsorshipValue(0) ), Map( - ByteStr.fromBytes(4, 5, 6) -> LeaseDetails(defaultSigner.publicKey, secondAddress, 123, Status.Active, ByteStr.fromBytes(1, 2, 3), 4), - ByteStr.fromBytes(7, 8, 9) -> LeaseDetails( - secondSigner.publicKey, - defaultAddress, + ByteStr.fromBytes(4, 5, 6) -> LeaseSnapshot(defaultSigner.publicKey, secondAddress, 123, Status.Active), + ByteStr.fromBytes(7, 8, 9) -> LeaseSnapshot( + PublicKey(ByteStr.fill(KeyLength)(0)), + Address(Array.fill(Address.HashLength)(0)), 0, - Status.Cancelled(2, Some(ByteStr.fromBytes(5, 5, 5))), - ByteStr.fromBytes(1, 2, 3), - 777777777 + Status.Cancelled(0, None) ) ), Map( diff --git a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala index 34a4c6c90a..abe2f5495f 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala @@ -5,6 +5,7 @@ import com.wavesplatform.TestValues.fee import com.wavesplatform.account.{Address, Alias, PublicKey} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.EitherExt2 +import com.wavesplatform.crypto.KeyLength import com.wavesplatform.db.WithDomain import com.wavesplatform.lang.directives.values.V6 import com.wavesplatform.lang.v1.compiler.TestCompiler @@ -15,7 +16,6 @@ import com.wavesplatform.state.* import com.wavesplatform.state.TxMeta.Status.{Failed, Succeeded} import com.wavesplatform.state.diffs.BlockDiffer.CurrentBlockFeePart import com.wavesplatform.state.diffs.ENOUGH_AMT -import com.wavesplatform.state.reader.LeaseDetails import com.wavesplatform.state.reader.LeaseDetails.Status.{Active, Cancelled} import com.wavesplatform.test.DomainPresets.* import com.wavesplatform.test.{NumericExt, PropSpec} @@ -175,7 +175,7 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { recipient -> LeaseBalance(leaseTx.amount.value, 0) ), leaseStates = Map( - leaseTx.id() -> LeaseDetails(sender.publicKey, recipient, leaseTx.amount.value, Active, leaseTx.id(), d.solidStateHeight + 2) + leaseTx.id() -> LeaseSnapshot(sender.publicKey, recipient, leaseTx.amount.value, Active) ) ) ) @@ -193,13 +193,11 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { recipient -> LeaseBalance(0, 0) ), leaseStates = Map( - leaseTx.id() -> LeaseDetails( - sender.publicKey, - recipient, - leaseTx.amount.value, - Cancelled(d.solidStateHeight + 2, Some(leaseCancelTx.id())), - leaseTx.id(), - d.solidStateHeight + leaseTx.id() -> LeaseSnapshot( + PublicKey(ByteStr.fill(KeyLength)(0)), + Address(Array.fill(Address.HashLength)(0)), + 0, + Cancelled(0, None) ) ) ) @@ -333,7 +331,7 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { dAppAssetId -> AssetInfo("name", "description", Height(height)) ), leaseStates = Map( - leaseId -> LeaseDetails(dAppPk, senderAddress, 123, Active, invokeId, height) + leaseId -> LeaseSnapshot(dAppPk, senderAddress, 123, Active) ), accountData = Map( dAppPk.toAddress -> Map("key" -> StringDataEntry("key", "abc")) diff --git a/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala b/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala index 75ea473f46..9ed626dccd 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala @@ -35,8 +35,10 @@ class TxStateSnapshotHashSpec extends PropSpec with WithDomain { private val orderId = ByteStr.fill(DigestLength)(5) private val volumeAndFee = VolumeAndFee(11, 2) - private val leaseId = ByteStr.fill(DigestLength)(6) - private val leaseDetails = LeaseDetails(TxHelpers.signer(1).publicKey, address2, 1.waves, LeaseDetails.Status.Active, leaseId, 2) + private val leaseId1 = ByteStr.fill(DigestLength)(6) + private val leaseId2 = ByteStr.fill(DigestLength)(7) + private val leaseDetails1 = LeaseDetails(TxHelpers.signer(1).publicKey, address2, 1.waves, LeaseDetails.Status.Active, leaseId1, 2) + private val leaseDetails2 = LeaseDetails(TxHelpers.signer(1).publicKey, address2, 1.waves, LeaseDetails.Status.Cancelled(1, None), leaseId2, 2) private val addr1Balance = 10.waves private val addr2Balance = 20.waves @@ -100,7 +102,7 @@ class TxStateSnapshotHashSpec extends PropSpec with WithDomain { ), aliases = Map(addr1Alias1 -> address1, addr2Alias -> address2, addr1Alias2 -> address1), orderFills = Map(orderId -> volumeAndFee), - leaseState = Map(leaseId -> leaseDetails), + leaseState = Map(leaseId1 -> leaseDetails1, leaseId2 -> leaseDetails2), scripts = Map(TxHelpers.signer(2).publicKey -> Some(accountScriptInfo)), assetScripts = Map(assetId1 -> Some(assetScriptInfo)), accountData = Map(address1 -> Map(dataEntry.key -> dataEntry)), @@ -132,7 +134,15 @@ class TxStateSnapshotHashSpec extends PropSpec with WithDomain { Array(KeyType.LeaseBalance.id.toByte) ++ address1.bytes ++ Longs.toByteArray(addr1PortfolioDiff.lease.in) ++ Longs.toByteArray( addr1PortfolioDiff.lease.out ), - Array(KeyType.LeaseStatus.id.toByte) ++ leaseId.arr ++ (if (leaseDetails.isActive) Array(1: Byte) else Array(0: Byte)), + Array(KeyType.LeaseStatus.id.toByte) + ++ leaseId1.arr + ++ Array(1: Byte) + ++ leaseDetails1.sender.arr + ++ leaseDetails1.recipient.bytes + ++ Longs.toByteArray(leaseDetails1.amount), + Array(KeyType.LeaseStatus.id.toByte) + ++ leaseId2.arr + ++ Array(0: Byte), Array(KeyType.Sponsorship.id.toByte) ++ assetId1.id.arr ++ Longs.toByteArray(sponsorship.minFee), Array(KeyType.Alias.id.toByte) ++ address1.bytes ++ addr1Alias1.name.getBytes(StandardCharsets.UTF_8), Array(KeyType.Alias.id.toByte) ++ address1.bytes ++ addr1Alias2.name.getBytes(StandardCharsets.UTF_8), diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 9d5b08bc4a..c5ca5a3c52 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -6,7 +6,7 @@ import scalapb.compiler.Version.scalapbVersion object Dependencies { // Node protobuf schemas private[this] val protoSchemasLib = - "com.wavesplatform" % "protobuf-schemas" % "1.5.1-SNAPSHOT" classifier "protobuf-src" intransitive () + "com.wavesplatform" % "protobuf-schemas" % "1.5.1-84-SNAPSHOT" classifier "protobuf-src" intransitive () private def akkaModule(module: String) = "com.typesafe.akka" %% s"akka-$module" % "2.6.21"