From d731e01281400874b412db788571310d44d923be Mon Sep 17 00:00:00 2001 From: Sergey Nazarov Date: Sun, 19 Nov 2023 12:02:43 +0400 Subject: [PATCH] wip --- .../scala/com/wavesplatform/Importer.scala | 9 +- .../wavesplatform/block/BlockSnapshot.scala | 4 +- .../block/MicroBlockSnapshot.scala | 4 +- .../database/RocksDBWriter.scala | 12 +- .../com/wavesplatform/database/package.scala | 22 +- .../com/wavesplatform/history/History.scala | 5 +- .../com/wavesplatform/mining/Miner.scala | 2 +- .../wavesplatform/protobuf/PBSnapshots.scala | 195 ++++++++ .../wavesplatform/state/StateSnapshot.scala | 184 +------ .../state/TxStateSnapshotHashBuilder.scala | 5 +- .../state/appender/package.scala | 2 +- .../state/reader/SnapshotBlockchain.scala | 5 +- .../transaction/TxValidationError.scala | 2 +- .../wavesplatform/history/SnapshotOps.scala | 5 +- .../mining/MiningFailuresSuite.scala | 2 +- .../snapshot/StateSnapshotProtoTest.scala | 22 +- .../snapshot/StateSnapshotStorageTest.scala | 9 +- .../snapshot/TxStateSnapshotHashSpec.scala | 453 +++++++++++------- project/Dependencies.scala | 2 +- 19 files changed, 527 insertions(+), 417 deletions(-) create mode 100644 node/src/main/scala/com/wavesplatform/protobuf/PBSnapshots.scala diff --git a/node/src/main/scala/com/wavesplatform/Importer.scala b/node/src/main/scala/com/wavesplatform/Importer.scala index b59eff7a73..4c98021480 100644 --- a/node/src/main/scala/com/wavesplatform/Importer.scala +++ b/node/src/main/scala/com/wavesplatform/Importer.scala @@ -17,6 +17,7 @@ import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.history.StorageFactory import com.wavesplatform.lang.ValidationError import com.wavesplatform.mining.Miner +import com.wavesplatform.protobuf.PBSnapshots import com.wavesplatform.protobuf.block.{PBBlocks, VanillaBlock} import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot import com.wavesplatform.settings.WavesSettings @@ -255,10 +256,12 @@ object Importer extends ScorexLogging { BlockSnapshot( block.id(), block.transactionData - .foldLeft((0, Seq.empty[(StateSnapshot, TxMeta.Status)])) { case ((offset, acc), _) => + .foldLeft((0, Seq.empty[(StateSnapshot, TxMeta.Status)])) { case ((offset, acc), tx) => val txSnapshotSize = Ints.fromByteArray(bytes.slice(offset, offset + Ints.BYTES)) - val txSnapshot = StateSnapshot.fromProtobuf( - TransactionStateSnapshot.parseFrom(bytes.slice(offset + Ints.BYTES, offset + Ints.BYTES + txSnapshotSize)) + val txSnapshot = PBSnapshots.fromProtobuf( + TransactionStateSnapshot.parseFrom(bytes.slice(offset + Ints.BYTES, offset + Ints.BYTES + txSnapshotSize)), + tx.id(), + ??? ) (offset + Ints.BYTES + txSnapshotSize, txSnapshot +: acc) } diff --git a/node/src/main/scala/com/wavesplatform/block/BlockSnapshot.scala b/node/src/main/scala/com/wavesplatform/block/BlockSnapshot.scala index 8ab14ac711..fbfa7eb333 100644 --- a/node/src/main/scala/com/wavesplatform/block/BlockSnapshot.scala +++ b/node/src/main/scala/com/wavesplatform/block/BlockSnapshot.scala @@ -7,6 +7,6 @@ import com.wavesplatform.state.{StateSnapshot, TxMeta} case class BlockSnapshot(blockId: BlockId, snapshots: Seq[(StateSnapshot, TxMeta.Status)]) object BlockSnapshot { - def fromResponse(response: BlockSnapshotResponse): BlockSnapshot = - BlockSnapshot(response.blockId, response.snapshots.map(StateSnapshot.fromProtobuf)) + def fromResponse(response: BlockSnapshotResponse): BlockSnapshot = ??? +// BlockSnapshot(response.blockId, response.snapshots.map(StateSnapshot.fromProtobuf)) } diff --git a/node/src/main/scala/com/wavesplatform/block/MicroBlockSnapshot.scala b/node/src/main/scala/com/wavesplatform/block/MicroBlockSnapshot.scala index 34f43c87fe..6388f6ad2a 100644 --- a/node/src/main/scala/com/wavesplatform/block/MicroBlockSnapshot.scala +++ b/node/src/main/scala/com/wavesplatform/block/MicroBlockSnapshot.scala @@ -7,6 +7,6 @@ import com.wavesplatform.state.{StateSnapshot, TxMeta} case class MicroBlockSnapshot(totalBlockId: BlockId, snapshots: Seq[(StateSnapshot, TxMeta.Status)]) object MicroBlockSnapshot { - def fromResponse(response: MicroBlockSnapshotResponse): MicroBlockSnapshot = - MicroBlockSnapshot(response.totalBlockId, response.snapshots.map(StateSnapshot.fromProtobuf)) + def fromResponse(response: MicroBlockSnapshotResponse): MicroBlockSnapshot = ??? +// MicroBlockSnapshot(response.totalBlockId, response.snapshots.map(StateSnapshot.fromProtobuf)) } diff --git a/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala b/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala index 4b11f41289..4da53d6a2a 100644 --- a/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala +++ b/node/src/main/scala/com/wavesplatform/database/RocksDBWriter.scala @@ -18,7 +18,7 @@ import com.wavesplatform.features.BlockchainFeatures import com.wavesplatform.lang.ValidationError import com.wavesplatform.protobuf.block.PBBlocks import com.wavesplatform.protobuf.snapshot.{TransactionStateSnapshot, TransactionStatus as PBStatus} -import com.wavesplatform.protobuf.{ByteStrExt, ByteStringExt} +import com.wavesplatform.protobuf.{ByteStrExt, ByteStringExt, PBSnapshots} import com.wavesplatform.settings.{BlockchainSettings, DBSettings} import com.wavesplatform.state.* import com.wavesplatform.state.reader.LeaseDetails @@ -298,7 +298,7 @@ class RocksDBWriter( private def appendBalances( balances: Map[(AddressId, Asset), (CurrentBalance, BalanceNode)], - assetStatics: Map[IssuedAsset, TransactionStateSnapshot.AssetStatic], + assetStatics: Map[IssuedAsset, AssetStaticInfo], rw: RW ): Unit = { val changedAssetBalances = MultimapBuilder.hashKeys().hashSetValues().build[IssuedAsset, java.lang.Long]() @@ -452,8 +452,8 @@ class RocksDBWriter( for ((asset, (assetStatic, assetNum)) <- snapshot.indexedAssetStatics) { val pbAssetStatic = StaticAssetInfo( - assetStatic.sourceTransactionId, - assetStatic.issuerPublicKey, + assetStatic.source.toByteString, + assetStatic.issuer.toByteString, assetStatic.decimals, assetStatic.nft, assetNum, @@ -523,7 +523,7 @@ class RocksDBWriter( val txId = TransactionId(id) val size = rw.put(Keys.transactionAt(Height(height), num, rdb.txHandle), Some((meta, tx))) - rw.put(Keys.transactionStateSnapshotAt(Height(height), num, rdb.txSnapshotHandle), Some(txInfo.snapshot.toProtobuf(txInfo.status))) + rw.put(Keys.transactionStateSnapshotAt(Height(height), num, rdb.txSnapshotHandle), Some(PBSnapshots.toProtobuf(txInfo.snapshot, txInfo.status))) rw.put(Keys.transactionMetaById(txId, rdb.txMetaHandle), Some(TransactionMeta(height, num, tx.tpe.id, meta.status.protobuf, 0, size))) targetBf.put(id.arr) @@ -798,7 +798,7 @@ class RocksDBWriter( ).explicitGet() val snapshot = if (isLightMode) { - Some(BlockSnapshot(block.id(), loadTxStateSnapshotsWithStatus(currentHeight, rdb))) + Some(BlockSnapshot(block.id(), loadTxStateSnapshotsWithStatus(currentHeight, rdb, block.transactionData))) } else None (block, Caches.toHitSource(discardedMeta), snapshot) diff --git a/node/src/main/scala/com/wavesplatform/database/package.scala b/node/src/main/scala/com/wavesplatform/database/package.scala index 30ea8448aa..a450c64d05 100644 --- a/node/src/main/scala/com/wavesplatform/database/package.scala +++ b/node/src/main/scala/com/wavesplatform/database/package.scala @@ -1,8 +1,5 @@ package com.wavesplatform -import java.nio.ByteBuffer -import java.util -import java.util.Map as JMap import com.google.common.base.Charsets.UTF_8 import com.google.common.collect.{Interners, Maps} import com.google.common.io.ByteStreams.{newDataInput, newDataOutput} @@ -19,24 +16,16 @@ import com.wavesplatform.database.protobuf as pb import com.wavesplatform.database.protobuf.DataEntry.Value import com.wavesplatform.database.protobuf.TransactionData.Transaction as TD import com.wavesplatform.lang.script.ScriptReader -import com.wavesplatform.protobuf.ByteStringExt import com.wavesplatform.protobuf.block.PBBlocks import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot import com.wavesplatform.protobuf.transaction.{PBRecipients, PBTransactions} +import com.wavesplatform.protobuf.{ByteStringExt, PBSnapshots} import com.wavesplatform.state.* import com.wavesplatform.state.StateHash.SectionId import com.wavesplatform.state.reader.LeaseDetails import com.wavesplatform.transaction.Asset.IssuedAsset import com.wavesplatform.transaction.lease.LeaseTransaction -import com.wavesplatform.transaction.{ - EthereumTransaction, - GenesisTransaction, - PBSince, - PaymentTransaction, - Transaction, - TransactionParsers, - TxValidationError -} +import com.wavesplatform.transaction.{EthereumTransaction, GenesisTransaction, PBSince, PaymentTransaction, Transaction, TransactionParsers, TxValidationError} import com.wavesplatform.utils.* import monix.eval.Task import monix.reactive.Observable @@ -44,6 +33,9 @@ import org.rocksdb.* import sun.nio.ch.Util import supertagged.TaggedType +import java.nio.ByteBuffer +import java.util +import java.util.Map as JMap import scala.annotation.tailrec import scala.collection.mutable.ArrayBuffer import scala.collection.{View, mutable} @@ -672,8 +664,8 @@ package object database { txSnapshots.result() } - def loadTxStateSnapshotsWithStatus(height: Height, rdb: RDB): Seq[(StateSnapshot, TxMeta.Status)] = - loadTxStateSnapshots(height, rdb).map(StateSnapshot.fromProtobuf) + def loadTxStateSnapshotsWithStatus(height: Height, rdb: RDB, transactions: Seq[Transaction]): Seq[(StateSnapshot, TxMeta.Status)] = + loadTxStateSnapshots(height, rdb).zip(transactions).map { case (s, tx) => PBSnapshots.fromProtobuf(s, tx.id(), height) } def loadBlock(height: Height, rdb: RDB): Option[Block] = for { diff --git a/node/src/main/scala/com/wavesplatform/history/History.scala b/node/src/main/scala/com/wavesplatform/history/History.scala index 4e86d87e38..47098d2b57 100644 --- a/node/src/main/scala/com/wavesplatform/history/History.scala +++ b/node/src/main/scala/com/wavesplatform/history/History.scala @@ -4,6 +4,7 @@ import com.wavesplatform.block.{Block, MicroBlock} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.database import com.wavesplatform.database.RDB +import com.wavesplatform.protobuf.PBSnapshots import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot import com.wavesplatform.state.{Blockchain, Height, StateSnapshot} @@ -43,7 +44,7 @@ object History { override def loadBlockSnapshots(id: ByteStr): Option[Seq[TransactionStateSnapshot]] = liquidBlockSnapshot(id) - .map(_.transactions.values.toSeq.map(txInfo => txInfo.snapshot.toProtobuf(txInfo.status))) + .map(_.transactions.values.toSeq.map(txInfo => PBSnapshots.toProtobuf(txInfo.snapshot, txInfo.status))) .orElse(blockchain.heightOf(id).map { h => database.loadTxStateSnapshots(Height(h), rdb) }) @@ -51,7 +52,7 @@ object History { override def loadMicroBlockSnapshots(id: ByteStr): Option[Seq[TransactionStateSnapshot]] = microBlockSnapshot(id) .map(_.transactions.values.toSeq.map { txInfo => - txInfo.snapshot.toProtobuf(txInfo.status) + PBSnapshots.toProtobuf(txInfo.snapshot, txInfo.status) }) } } diff --git a/node/src/main/scala/com/wavesplatform/mining/Miner.scala b/node/src/main/scala/com/wavesplatform/mining/Miner.scala index ec84c72047..239fdb8d06 100644 --- a/node/src/main/scala/com/wavesplatform/mining/Miner.scala +++ b/node/src/main/scala/com/wavesplatform/mining/Miner.scala @@ -289,7 +289,7 @@ class MinerImpl( def appendTask(block: Block, totalConstraint: MiningConstraint) = BlockAppender(blockchainUpdater, timeService, utx, pos, appenderScheduler)(block, None).flatMap { - case Left(BlockFromFuture(_)) => // Time was corrected, retry + case Left(BlockFromFuture(_, _)) => // Time was corrected, retry generateBlockTask(account, None) case Left(err) => diff --git a/node/src/main/scala/com/wavesplatform/protobuf/PBSnapshots.scala b/node/src/main/scala/com/wavesplatform/protobuf/PBSnapshots.scala new file mode 100644 index 0000000000..3002c82201 --- /dev/null +++ b/node/src/main/scala/com/wavesplatform/protobuf/PBSnapshots.scala @@ -0,0 +1,195 @@ +package com.wavesplatform.protobuf + +import com.google.protobuf.ByteString +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.script.ScriptReader +import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot +import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot.AssetStatic +import com.wavesplatform.protobuf.transaction.{PBAmounts, PBTransactions} +import com.wavesplatform.state.* +import com.wavesplatform.state.reader.LeaseDetails +import com.wavesplatform.state.reader.LeaseDetails.Status +import com.wavesplatform.transaction.Asset +import com.wavesplatform.transaction.Asset.IssuedAsset + +import scala.collection.immutable.VectorMap + +object PBSnapshots { + + import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot as S + + def toProtobuf(snapshot: StateSnapshot, txStatus: TxMeta.Status): TransactionStateSnapshot = { + import snapshot.* + TransactionStateSnapshot( + balances.map { case ((address, asset), balance) => + S.Balance(address.toByteString, Some(PBAmounts.fromAssetAndAmount(asset, balance))) + }.toSeq, + leaseBalances.map { case (address, balance) => + S.LeaseBalance(address.toByteString, balance.in, balance.out) + }.toSeq, + assetStatics.map { case (id, st) => + AssetStatic(id.id.toByteString, st.issuer.toByteString, st.decimals, st.nft) + }.toSeq, + assetVolumes.map { case (asset, info) => + S.AssetVolume(asset.id.toByteString, info.isReissuable, ByteString.copyFrom(info.volume.toByteArray)) + }.toSeq, + assetNamesAndDescriptions.map { case (asset, info) => + S.AssetNameAndDescription(asset.id.toByteString, info.name.toStringUtf8, info.description.toStringUtf8) + }.toSeq, + assetScripts.map { case (asset, script) => + S.AssetScript(asset.id.toByteString, script.script.bytes().toByteString) + }.headOption, + aliases.map { case (alias, address) => S.Alias(address.toByteString, alias.name) }.headOption, + orderFills.map { case (orderId, VolumeAndFee(volume, fee)) => + S.OrderFill(orderId.toByteString, volume, fee) + }.toSeq, + leaseStates.map { case (leaseId, LeaseSnapshot(sender, recipient, amount, status)) => + val pbStatus = status match { + case Status.Active => + 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) + }.toSeq, + accountScripts.map { case (publicKey, scriptOpt) => + scriptOpt.fold( + S.AccountScript(publicKey.toByteString) + )(script => + S.AccountScript( + publicKey.toByteString, + script.script.bytes().toByteString, + script.verifierComplexity + ) + ) + }.headOption, + accountData.map { case (address, data) => + S.AccountData(address.toByteString, data.values.map(PBTransactions.toPBDataEntry).toSeq) + }.toSeq, + sponsorships.collect { case (asset, SponsorshipValue(minFee)) => + S.Sponsorship(asset.id.toByteString, minFee) + }.toSeq, + txStatus.protobuf + ) + } + + def fromProtobuf(pbSnapshot: TransactionStateSnapshot, txId: ByteStr, height: Int): (StateSnapshot, TxMeta.Status) = { + val balances: VectorMap[(Address, Asset), Long] = + VectorMap() ++ pbSnapshot.balances.map(b => (b.address.toAddress(), b.getAmount.assetId.toAssetId) -> b.getAmount.amount) + + val leaseBalances: Map[Address, LeaseBalance] = + pbSnapshot.leaseBalances + .map(b => b.address.toAddress() -> LeaseBalance(b.in, b.out)) + .toMap + + val assetScripts: Map[IssuedAsset, AssetScriptInfo] = + pbSnapshot.assetScripts.map { s => + s.assetId.toIssuedAssetId -> AssetScriptInfo(ScriptReader.fromBytes(s.script.toByteArray).explicitGet(), 0) + }.toMap + + val assetStatics: VectorMap[IssuedAsset, AssetStaticInfo] = + VectorMap() ++ pbSnapshot.assetStatics.map(info => + info.assetId.toIssuedAssetId -> AssetStaticInfo( + info.assetId.toByteStr, + TransactionId(txId), + PublicKey(info.issuerPublicKey.toByteStr), + info.decimals, + info.nft + ) + ) + + val assetVolumes: Map[IssuedAsset, AssetVolumeInfo] = + pbSnapshot.assetVolumes + .map(v => v.assetId.toIssuedAssetId -> AssetVolumeInfo(v.reissuable, BigInt(v.volume.toByteArray))) + .toMap + + val assetNamesAndDescriptions: Map[IssuedAsset, AssetInfo] = + pbSnapshot.assetNamesAndDescriptions + .map(i => i.assetId.toIssuedAssetId -> AssetInfo(i.name, i.description, Height @@ height)) + .toMap + + val sponsorships: Map[IssuedAsset, SponsorshipValue] = + pbSnapshot.sponsorships + .map(s => s.assetId.toIssuedAssetId -> SponsorshipValue(s.minFee)) + .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 + .map(a => Alias.create(a.alias).explicitGet() -> a.address.toAddress()) + .toMap + + val orderFills: Map[ByteStr, VolumeAndFee] = + pbSnapshot.orderFills + .map(of => of.orderId.toByteStr -> VolumeAndFee(of.volume, of.fee)) + .toMap + + val accountScripts: Map[PublicKey, Option[AccountScriptInfo]] = + pbSnapshot.accountScripts.map { pbInfo => + val info = + if (pbInfo.script.isEmpty) + None + else + Some( + AccountScriptInfo( + pbInfo.senderPublicKey.toPublicKey, + ScriptReader.fromBytes(pbInfo.script.toByteArray).explicitGet(), + pbInfo.verifierComplexity + ) + ) + pbInfo.senderPublicKey.toPublicKey -> info + }.toMap + + val accountData: Map[Address, Map[String, DataEntry[?]]] = + pbSnapshot.accountData.map { data => + val entries = + data.entries.map { pbEntry => + val entry = PBTransactions.toVanillaDataEntry(pbEntry) + entry.key -> entry + }.toMap + data.address.toAddress() -> entries + }.toMap + + ( + StateSnapshot( + VectorMap(), + balances, + leaseBalances, + assetStatics, + assetVolumes, + assetNamesAndDescriptions, + assetScripts, + sponsorships, + leaseStates, + aliases, + orderFills, + accountScripts, + accountData + ), + TxMeta.Status.fromProtobuf(pbSnapshot.transactionStatus) + ) + } + +} diff --git a/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala b/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala index f3782f2707..7cb0a8ab49 100644 --- a/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala +++ b/node/src/main/scala/com/wavesplatform/state/StateSnapshot.scala @@ -2,20 +2,12 @@ package com.wavesplatform.state 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, 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, PBTransactions} -import com.wavesplatform.protobuf.{AddressExt, ByteStrExt, ByteStringExt} -import com.wavesplatform.state.reader.LeaseDetails.Status -import com.wavesplatform.state.reader.{LeaseDetails, SnapshotBlockchain} +import com.wavesplatform.state.reader.SnapshotBlockchain import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxValidationError.GenericError import com.wavesplatform.transaction.{Asset, Transaction} @@ -26,7 +18,7 @@ case class StateSnapshot( transactions: VectorMap[ByteStr, NewTransactionInfo] = VectorMap(), balances: VectorMap[(Address, Asset), Long] = VectorMap(), leaseBalances: Map[Address, LeaseBalance] = Map(), - assetStatics: VectorMap[IssuedAsset, AssetStatic] = VectorMap(), + assetStatics: VectorMap[IssuedAsset, AssetStaticInfo] = VectorMap(), assetVolumes: Map[IssuedAsset, AssetVolumeInfo] = Map(), assetNamesAndDescriptions: Map[IssuedAsset, AssetInfo] = Map(), assetScripts: Map[IssuedAsset, AssetScriptInfo] = Map(), @@ -40,58 +32,6 @@ case class StateSnapshot( ethereumTransactionMeta: Map[ByteStr, EthereumTransactionMeta] = Map(), scriptsComplexity: Long = 0 ) { - import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot as S - - def toProtobuf(txStatus: TxMeta.Status): TransactionStateSnapshot = - TransactionStateSnapshot( - balances.map { case ((address, asset), balance) => - S.Balance(address.toByteString, Some(PBAmounts.fromAssetAndAmount(asset, balance))) - }.toSeq, - leaseBalances.map { case (address, balance) => - S.LeaseBalance(address.toByteString, balance.in, balance.out) - }.toSeq, - assetStatics.values.toSeq, - assetVolumes.map { case (asset, info) => - S.AssetVolume(asset.id.toByteString, info.isReissuable, ByteString.copyFrom(info.volume.toByteArray)) - }.toSeq, - assetNamesAndDescriptions.map { case (asset, info) => - S.AssetNameAndDescription(asset.id.toByteString, info.name.toStringUtf8, info.description.toStringUtf8, info.lastUpdatedAt) - }.toSeq, - assetScripts.map { case (asset, script) => - S.AssetScript(asset.id.toByteString, script.script.bytes().toByteString) - }.toSeq, - aliases.map { case (alias, address) => S.Alias(address.toByteString, alias.name) }.toSeq, - orderFills.map { case (orderId, VolumeAndFee(volume, fee)) => - S.OrderFill(orderId.toByteString, volume, fee) - }.toSeq, - leaseStates.map { case (leaseId, LeaseSnapshot(sender, recipient, amount, status)) => - val pbStatus = status match { - case Status.Active => - 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) - }.toSeq, - accountScripts.map { case (publicKey, scriptOpt) => - scriptOpt.fold( - S.AccountScript(publicKey.toByteString) - )(script => - S.AccountScript( - publicKey.toByteString, - script.script.bytes().toByteString, - script.verifierComplexity - ) - ) - }.toSeq, - accountData.map { case (address, data) => - S.AccountData(address.toByteString, data.values.map(PBTransactions.toPBDataEntry).toSeq) - }.toSeq, - sponsorships.collect { case (asset, SponsorshipValue(minFee)) => - S.Sponsorship(asset.id.toByteString, minFee) - }.toSeq, - txStatus.protobuf - ) // ignores lease balances from portfolios def addBalances(portfolios: Map[Address, Portfolio], blockchain: Blockchain): Either[String, StateSnapshot] = @@ -119,7 +59,7 @@ case class StateSnapshot( transactions = transactions + (tx.id() -> NewTransactionInfo.create(tx, TxMeta.Status.Elided, StateSnapshot.empty, blockchain)) ) - lazy val indexedAssetStatics: Map[IssuedAsset, (AssetStatic, Int)] = + lazy val indexedAssetStatics: Map[IssuedAsset, (AssetStaticInfo, Int)] = assetStatics.zipWithIndex.map { case ((asset, static), i) => asset -> (static, i + 1) }.toMap lazy val accountScriptsByAddress: Map[Address, Option[AccountScriptInfo]] = @@ -130,113 +70,6 @@ case class StateSnapshot( } object StateSnapshot { - def fromProtobuf(pbSnapshot: TransactionStateSnapshot): (StateSnapshot, TxMeta.Status) = { - val balances: VectorMap[(Address, Asset), Long] = - VectorMap() ++ pbSnapshot.balances.map(b => (b.address.toAddress(), b.getAmount.assetId.toAssetId) -> b.getAmount.amount) - - val leaseBalances: Map[Address, LeaseBalance] = - pbSnapshot.leaseBalances - .map(b => b.address.toAddress() -> LeaseBalance(b.in, b.out)) - .toMap - - val assetScripts: Map[IssuedAsset, AssetScriptInfo] = - pbSnapshot.assetScripts.map { s => - s.assetId.toIssuedAssetId -> AssetScriptInfo(ScriptReader.fromBytes(s.script.toByteArray).explicitGet(), 0) - }.toMap - - val assetStatics: VectorMap[IssuedAsset, AssetStatic] = - VectorMap() ++ pbSnapshot.assetStatics.map(info => info.assetId.toIssuedAssetId -> info) - - val assetVolumes: Map[IssuedAsset, AssetVolumeInfo] = - pbSnapshot.assetVolumes - .map(v => v.assetId.toIssuedAssetId -> AssetVolumeInfo(v.reissuable, BigInt(v.volume.toByteArray))) - .toMap - - val assetNamesAndDescriptions: Map[IssuedAsset, AssetInfo] = - pbSnapshot.assetNamesAndDescriptions - .map(i => i.assetId.toIssuedAssetId -> AssetInfo(i.name, i.description, Height @@ i.lastUpdated)) - .toMap - - val sponsorships: Map[IssuedAsset, SponsorshipValue] = - pbSnapshot.sponsorships - .map(s => s.assetId.toIssuedAssetId -> SponsorshipValue(s.minFee)) - .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 - .map(a => Alias.create(a.alias).explicitGet() -> a.address.toAddress()) - .toMap - - val orderFills: Map[ByteStr, VolumeAndFee] = - pbSnapshot.orderFills - .map(of => of.orderId.toByteStr -> VolumeAndFee(of.volume, of.fee)) - .toMap - - val accountScripts: Map[PublicKey, Option[AccountScriptInfo]] = - pbSnapshot.accountScripts.map { pbInfo => - val info = - if (pbInfo.script.isEmpty) - None - else - Some( - AccountScriptInfo( - pbInfo.senderPublicKey.toPublicKey, - ScriptReader.fromBytes(pbInfo.script.toByteArray).explicitGet(), - pbInfo.verifierComplexity - ) - ) - pbInfo.senderPublicKey.toPublicKey -> info - }.toMap - - val accountData: Map[Address, Map[String, DataEntry[?]]] = - pbSnapshot.accountData.map { data => - val entries = - data.entries.map { pbEntry => - val entry = PBTransactions.toVanillaDataEntry(pbEntry) - entry.key -> entry - }.toMap - data.address.toAddress() -> entries - }.toMap - - ( - StateSnapshot( - VectorMap(), - balances, - leaseBalances, - assetStatics, - assetVolumes, - assetNamesAndDescriptions, - assetScripts, - sponsorships, - leaseStates, - aliases, - orderFills, - accountScripts, - accountData - ), - TxMeta.Status.fromProtobuf(pbSnapshot.transactionStatus) - ) - } def build( blockchain: Blockchain, @@ -329,16 +162,9 @@ object StateSnapshot { } .map(_.toMap) - private def assetStatics(issuedAssets: VectorMap[IssuedAsset, NewAssetInfo]): VectorMap[IssuedAsset, AssetStatic] = + private def assetStatics(issuedAssets: VectorMap[IssuedAsset, NewAssetInfo]): VectorMap[IssuedAsset, AssetStaticInfo] = issuedAssets.map { case (asset, info) => - asset -> - AssetStatic( - asset.id.toByteString, - info.static.source.toByteString, - info.static.issuer.toByteString, - info.static.decimals, - info.static.nft - ) + asset -> info.static } private def assetVolumes( diff --git a/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala b/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala index 1989ea5649..5c3d9caf5e 100644 --- a/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala +++ b/node/src/main/scala/com/wavesplatform/state/TxStateSnapshotHashBuilder.scala @@ -82,7 +82,7 @@ object TxStateSnapshotHashBuilder { } snapshot.assetStatics.foreach { case (asset, assetInfo) => - changedKeys += asset.id.arr ++ assetInfo.issuerPublicKey.toByteArray ++ Array(assetInfo.decimals.toByte) ++ booleanToBytes(assetInfo.nft) + changedKeys += asset.id.arr ++ assetInfo.issuer.arr ++ Array(assetInfo.decimals.toByte) ++ booleanToBytes(assetInfo.nft) } snapshot.assetVolumes.foreach { case (asset, volume) => @@ -174,12 +174,11 @@ object TxStateSnapshotHashBuilder { if (flag) Array(1: Byte) else Array(0: Byte) private def createHash(bs: Iterable[Array[Byte]]): ByteStr = { - val digestFn: Blake2bDigest = newDigestInstance() + val digestFn: Blake2bDigest = new Blake2bDigest(crypto.DigestLength * 8) bs.foreach(bs => digestFn.update(bs, 0, bs.length)) val result = new Array[Byte](crypto.DigestLength) digestFn.doFinal(result, 0) ByteStr(result) } - private def newDigestInstance(): Blake2bDigest = new Blake2bDigest(crypto.DigestLength * 8) } diff --git a/node/src/main/scala/com/wavesplatform/state/appender/package.scala b/node/src/main/scala/com/wavesplatform/state/appender/package.scala index 9d61ba9312..cf6f8c9d81 100644 --- a/node/src/main/scala/com/wavesplatform/state/appender/package.scala +++ b/node/src/main/scala/com/wavesplatform/state/appender/package.scala @@ -146,7 +146,7 @@ package object appender { grandParent = blockchain.parentHeader(parent, 2) effectiveBalance <- genBalance(height, block.header.reference).left.map(GenericError(_)) _ <- validateBlockVersion(height, block, blockchain) - _ <- Either.cond(blockTime - currentTs < MaxTimeDrift, (), BlockFromFuture(blockTime)) + _ <- Either.cond(blockTime - currentTs < MaxTimeDrift, (), BlockFromFuture(blockTime, currentTs)) _ <- pos.validateBaseTarget(height, block, parent, grandParent) hitSource <- pos.validateGenerationSignature(block) _ <- pos 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 abc3ac79d5..c757ff5140 100644 --- a/node/src/main/scala/com/wavesplatform/state/reader/SnapshotBlockchain.scala +++ b/node/src/main/scala/com/wavesplatform/state/reader/SnapshotBlockchain.scala @@ -7,7 +7,6 @@ import com.wavesplatform.block.{Block, SignedBlockHeader} import com.wavesplatform.common.state.ByteStr import com.wavesplatform.features.BlockchainFeatures.RideV6 import com.wavesplatform.lang.ValidationError -import com.wavesplatform.protobuf.ByteStringExt import com.wavesplatform.settings.BlockchainSettings import com.wavesplatform.state.* import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} @@ -272,8 +271,8 @@ object SnapshotBlockchain { .get(asset) .map { case (static, assetNum) => AssetDescription( - static.sourceTransactionId.toByteStr, - static.issuerPublicKey.toPublicKey, + static.source, + static.issuer, info.get.name, info.get.description, static.decimals, diff --git a/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala b/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala index 5d4f1cabc5..bd950a11fe 100644 --- a/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala +++ b/node/src/main/scala/com/wavesplatform/transaction/TxValidationError.scala @@ -29,7 +29,7 @@ object TxValidationError { case object MissingSenderPrivateKey extends ValidationError case object UnsupportedTransactionType extends ValidationError case object InvalidRequestSignature extends ValidationError - case class BlockFromFuture(ts: Long) extends ValidationError + case class BlockFromFuture(blockTs: Long, localTs: Long) extends ValidationError case class AlreadyInTheState(txId: ByteStr, txHeight: Int) extends ValidationError case class AccountBalanceError(errs: Map[Address, String]) extends ValidationError case class AliasDoesNotExist(a: Alias) extends ValidationError { override def toString: String = s"Alias '$a' does not exists." } diff --git a/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala b/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala index 185f5ad708..aed67685d6 100644 --- a/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala +++ b/node/src/test/scala/com/wavesplatform/history/SnapshotOps.scala @@ -5,7 +5,6 @@ import com.wavesplatform.account.Address import com.wavesplatform.common.state.ByteStr 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 @@ -58,8 +57,8 @@ object SnapshotOps { VectorMap[IssuedAsset, NewAssetInfo]() ++ s.assetStatics.map { case (asset, pbStatic) => val static = AssetStaticInfo( asset.id, - pbStatic.sourceTransactionId.toTxId, - pbStatic.issuerPublicKey.toPublicKey, + pbStatic.source, + pbStatic.issuer, pbStatic.decimals, pbStatic.nft ) diff --git a/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala b/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala index 9c5fb734dd..9aa7ad379a 100644 --- a/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala +++ b/node/src/test/scala/com/wavesplatform/mining/MiningFailuresSuite.scala @@ -99,7 +99,7 @@ class MiningFailuresSuite extends FlatSpec with PathMockFactory with WithNewDBFo ) var minedBlock: Block = null - (blockchainUpdater.processBlock _).when(*, *, *, *, *, *).returning(Left(BlockFromFuture(100))).repeated(10) + (blockchainUpdater.processBlock _).when(*, *, *, *, *, *).returning(Left(BlockFromFuture(100, 100))).repeated(10) (blockchainUpdater.processBlock _) .when(*, *, *, *, *, *) .onCall { (block, _, _, _, _, _) => 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 78c5df5722..0cb4d8a659 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotProtoTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotProtoTest.scala @@ -1,18 +1,18 @@ package com.wavesplatform.state.snapshot -import com.google.protobuf.ByteString 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.protobuf.PBSnapshots import com.wavesplatform.state.* import com.wavesplatform.state.reader.LeaseDetails.Status import com.wavesplatform.test.PropSpec import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves} import com.wavesplatform.transaction.TxHelpers.{defaultAddress, defaultSigner, secondAddress, secondSigner} +import com.wavesplatform.utils.StringBytes import scala.collection.immutable.VectorMap @@ -29,17 +29,17 @@ class StateSnapshotProtoTest extends PropSpec { secondAddress -> LeaseBalance.empty ), VectorMap( - IssuedAsset(ByteStr.fromBytes(1, 1, 1)) -> AssetStatic( - ByteString.copyFrom(Array[Byte](1, 1, 1)), - ByteString.copyFromUtf8("txId"), - ByteString.copyFromUtf8("pk"), + IssuedAsset(ByteStr.fromBytes(1, 1, 1)) -> AssetStaticInfo( + ByteStr(Array[Byte](1, 1, 1)), + TransactionId(ByteStr("txId".utf8Bytes)), + PublicKey(ByteStr("pk".utf8Bytes)), 5, nft = true ), - IssuedAsset(ByteStr.fromBytes(2, 2, 2)) -> AssetStatic( - ByteString.copyFrom(Array[Byte](2, 2, 2)), - ByteString.copyFromUtf8("txId"), - ByteString.copyFromUtf8("pk"), + IssuedAsset(ByteStr.fromBytes(2, 2, 2)) -> AssetStaticInfo( + ByteStr(Array[Byte](2, 2, 2)), + TransactionId(ByteStr("txId".utf8Bytes)), + PublicKey(ByteStr("pk".utf8Bytes)), 5, nft = false ) @@ -95,6 +95,6 @@ class StateSnapshotProtoTest extends PropSpec { ) ) Seq(TxMeta.Status.Succeeded, TxMeta.Status.Failed, TxMeta.Status.Elided) - .foreach(txStatus => StateSnapshot.fromProtobuf(snapshot.toProtobuf(txStatus)) shouldBe (snapshot, txStatus)) + .foreach(txStatus => PBSnapshots.fromProtobuf(PBSnapshots.toProtobuf(snapshot, txStatus), ???, ???) shouldBe (snapshot, txStatus)) } } 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 abe2f5495f..8d3c115a9d 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/StateSnapshotStorageTest.scala @@ -10,8 +10,7 @@ import com.wavesplatform.db.WithDomain import com.wavesplatform.lang.directives.values.V6 import com.wavesplatform.lang.v1.compiler.TestCompiler import com.wavesplatform.lang.v1.traits.domain.{Issue, Lease, Recipient} -import com.wavesplatform.protobuf.ByteStrExt -import com.wavesplatform.protobuf.snapshot.TransactionStateSnapshot.AssetStatic +import com.wavesplatform.protobuf.PBSnapshots import com.wavesplatform.state.* import com.wavesplatform.state.TxMeta.Status.{Failed, Succeeded} import com.wavesplatform.state.diffs.BlockDiffer.CurrentBlockFeePart @@ -50,7 +49,7 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { if (failed) d.appendAndAssertFailed(tx) else d.appendAndAssertSucceed(tx) d.appendBlock() val status = if (failed) Failed else Succeeded - StateSnapshot.fromProtobuf(d.rocksDBWriter.transactionSnapshot(tx.id()).get) shouldBe (expectedSnapshotWithMiner, status) + PBSnapshots.fromProtobuf(d.rocksDBWriter.transactionSnapshot(tx.id()).get, tx.id(), d.blockchain.height) shouldBe (expectedSnapshotWithMiner, status) } // Genesis @@ -95,7 +94,7 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { (senderAddress, Waves) -> (d.balance(senderAddress) - 1.waves) ), assetStatics = VectorMap( - asset -> AssetStatic(asset.id.toByteString, issueTx.id().toByteString, sender.publicKey.toByteString, issueTx.decimals.value) + asset -> AssetStaticInfo(asset.id, TransactionId(issueTx.id()), sender.publicKey, issueTx.decimals.value, false) ), assetVolumes = Map( asset -> AssetVolumeInfo(isReissuable = true, BigInt(issueTx.quantity.value)) @@ -322,7 +321,7 @@ class StateSnapshotStorageTest extends PropSpec with WithDomain { senderAddress -> LeaseBalance(123, 0) ), assetStatics = VectorMap( - dAppAssetId -> AssetStatic(dAppAssetId.id.toByteString, invokeId.toByteString, dAppPk.toByteString, 4) + dAppAssetId -> AssetStaticInfo(dAppAssetId.id, TransactionId(invokeId), dAppPk, 4, false) ), assetVolumes = Map( dAppAssetId -> AssetVolumeInfo(true, 1000) 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 9ed626dccd..bda3701f1a 100644 --- a/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala +++ b/node/src/test/scala/com/wavesplatform/state/snapshot/TxStateSnapshotHashSpec.scala @@ -1,197 +1,294 @@ package com.wavesplatform.state.snapshot -import cats.data.Ior -import com.google.common.primitives.{Ints, Longs, UnsignedBytes} -import com.wavesplatform.account.{AddressScheme, Alias} +import com.google.common.primitives.Ints +import com.google.protobuf.ByteString.copyFrom as bs import com.wavesplatform.common.state.ByteStr import com.wavesplatform.common.utils.* -import com.wavesplatform.crypto.DigestLength -import com.wavesplatform.db.WithDomain -import com.wavesplatform.db.WithState.AddrWithBalance -import com.wavesplatform.history.SnapshotOps -import com.wavesplatform.lang.v1.estimator.ScriptEstimatorV1 +import com.wavesplatform.crypto.fastHash +import com.wavesplatform.lang.directives.values.V6 +import com.wavesplatform.lang.v1.compiler.TestCompiler +import com.wavesplatform.protobuf.snapshot.{TransactionStatus, TransactionStateSnapshot as TSS} +import com.wavesplatform.protobuf.transaction.DataTransactionData.DataEntry +import com.wavesplatform.protobuf.{Amount, PBSnapshots} import com.wavesplatform.state.* -import com.wavesplatform.state.TxMeta.Status.* -import com.wavesplatform.state.TxStateSnapshotHashBuilder.{KeyType, TxStatusInfo} -import com.wavesplatform.state.reader.LeaseDetails import com.wavesplatform.test.* -import com.wavesplatform.transaction.Asset.IssuedAsset -import com.wavesplatform.transaction.TxHelpers.invoke -import com.wavesplatform.transaction.smart.script.ScriptCompiler -import com.wavesplatform.transaction.{AssetIdLength, TxHelpers} - -import java.nio.charset.StandardCharsets -import scala.collection.immutable.VectorMap - -class TxStateSnapshotHashSpec extends PropSpec with WithDomain { - val stateHash = new StateHashBuilder - private val address1 = TxHelpers.address(1) - private val address2 = TxHelpers.address(2) - private val assetId1 = IssuedAsset(ByteStr.fill(AssetIdLength)(1)) - private val assetId2 = IssuedAsset(ByteStr.fill(AssetIdLength)(2)) - private val assetId3 = IssuedAsset(ByteStr.fill(AssetIdLength)(3)) - private val assetId4 = IssuedAsset(ByteStr.fill(AssetIdLength)(4)) - - private val orderId = ByteStr.fill(DigestLength)(5) - private val volumeAndFee = VolumeAndFee(11, 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 - private val addr1PortfolioDiff = Portfolio(balance = 2.waves, lease = LeaseBalance(3.waves, 1.waves)) - private val addr2PortfolioDiff = Portfolio(assets = VectorMap(assetId1 -> 123)) - - private val addr1Alias1 = Alias(AddressScheme.current.chainId, "addr1Alias1") - private val addr1Alias2 = Alias(AddressScheme.current.chainId, "addr1Alias2") - private val addr2Alias = Alias(AddressScheme.current.chainId, "addr2") - - private val assetInfo1 = NewAssetInfo( - AssetStaticInfo(assetId1.id, TransactionId(assetId1.id), TxHelpers.signer(1).publicKey, 8, false), - AssetInfo("test1", "desc1", Height(2)), - AssetVolumeInfo(true, BigInt(123)) - ) - private val assetInfo2 = NewAssetInfo( - AssetStaticInfo(assetId2.id, TransactionId(assetId2.id), TxHelpers.signer(1).publicKey, 8, false), - AssetInfo("test2", "desc2", Height(2)), - AssetVolumeInfo(true, BigInt(123)) - ) - private val assetInfo3 = NewAssetInfo( - AssetStaticInfo(assetId3.id, TransactionId(assetId3.id), TxHelpers.signer(1).publicKey, 8, false), - AssetInfo("test3", "desc3", Height(2)), - AssetVolumeInfo(true, BigInt(123)) - ) - private val assetInfo4 = NewAssetInfo( - AssetStaticInfo(assetId4.id, TransactionId(assetId4.id), TxHelpers.signer(1).publicKey, 8, false), - AssetInfo("test4", "desc4", Height(2)), - AssetVolumeInfo(true, BigInt(123)) - ) - private val updatedAssetInfo1 = AssetInfo("updTest1", "updDesc1", Height(2)) - private val updatedAssetVolumeInfo1 = AssetVolumeInfo(false, 124) - private val updatedAssetInfo2 = AssetInfo("updTest2", "updDesc2", Height(2)) - private val updatedAssetVolumeInfo3 = AssetVolumeInfo(false, 125) - private val sponsorship = SponsorshipValue(12) - - private val testScript = ScriptCompiler - .compile( - """ - |{-# STDLIB_VERSION 2 #-} - |{-# CONTENT_TYPE EXPRESSION #-} - |{-# SCRIPT_TYPE ACCOUNT #-} - |true - |""".stripMargin, - ScriptEstimatorV1 +import com.wavesplatform.transaction.TxHelpers +import org.bouncycastle.util.encoders.Hex + +class TxStateSnapshotHashSpec extends PropSpec { + private def hashInt(i: Int) = bs(fastHash(Ints.toByteArray(i))) + + val stateHash = new StateHashBuilder + private val signer101 = TxHelpers.signer(101) + private val signer102 = TxHelpers.signer(102) + + private val address1 = signer101.toAddress + private val address2 = signer102.toAddress + + private val assetId1 = hashInt(0xaa22aa44) + private val assetId2 = hashInt(0xbb22aa44) + + private val leaseId = hashInt(0x11aaef22) + private val orderId1 = hashInt(0xee23ef22) + private val orderId2 = hashInt(0xbb77ef29) + + private val testScript = bs(TestCompiler(V6).compileExpression("true").bytes().arr) + + private val wavesBalances = TSS(balances = + Seq( + TSS.Balance(bs(address1.bytes), Some(Amount(amount = 10.waves))), + TSS.Balance(bs(address2.bytes), Some(Amount(amount = 20.waves))) + ) + ) + + private val assetBalances = TSS(balances = + Seq( + TSS.Balance(bs(address1.bytes), Some(Amount(assetId1, 10_000))), + TSS.Balance(bs(address2.bytes), Some(Amount(assetId2, 20_000))) + ) + ) + + private val dataEntries = TSS(accountData = + Seq( + TSS.AccountData( + bs(address1.bytes), + Seq( + DataEntry("foo", DataEntry.Value.Empty), + DataEntry("baz", DataEntry.Value.StringValue("StringValue")), + DataEntry("baz", DataEntry.Value.BinaryValue(bs(address1.bytes))) + ) + ), + TSS.AccountData( + bs(address2.bytes), + Seq( + DataEntry("foo", DataEntry.Value.IntValue(1200)), + DataEntry("bar", DataEntry.Value.BoolValue(true)) + ) + ) ) - .explicitGet() - ._1 - private val accountScriptInfo = AccountScriptInfo(TxHelpers.signer(2).publicKey, testScript, 1) - private val assetScriptInfo = AssetScriptInfo(testScript, 1) + ) - private val dataEntry = StringDataEntry("key", "value") + private val accountScript = TSS(accountScripts = + Some( + TSS.AccountScript( + bs(signer101.publicKey.arr), + testScript, + 250 + ) + ) + ) - private val diff = Diff( - portfolios = Map(address1 -> addr1PortfolioDiff, address2 -> addr2PortfolioDiff), - issuedAssets = VectorMap(assetId1 -> assetInfo1, assetId2 -> assetInfo2, assetId3 -> assetInfo3, assetId4 -> assetInfo4), - updatedAssets = Map( - assetId1 -> Ior.Both(updatedAssetInfo1, updatedAssetVolumeInfo1), - assetId2 -> Ior.Left(updatedAssetInfo2), - assetId3 -> Ior.Right(updatedAssetVolumeInfo3) + private val assetScript = TSS(assetScripts = Some(TSS.AssetScript(assetId2, testScript))) + + private val newLease = TSS( + leaseBalances = Seq( + TSS.LeaseBalance(bs(address1.bytes), out = 45.waves), + TSS.LeaseBalance(bs(address2.bytes), in = 55.waves) ), - aliases = Map(addr1Alias1 -> address1, addr2Alias -> address2, addr1Alias2 -> address1), - orderFills = Map(orderId -> volumeAndFee), - 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)), - sponsorship = Map(assetId1 -> sponsorship) + leaseStates = Seq( + TSS.LeaseState(leaseId, TSS.LeaseState.Status.Active(TSS.LeaseState.Active(25.waves, bs(signer101.publicKey.arr), bs(address2.bytes)))) + ) ) - def hash(bs: Seq[Array[Byte]]): ByteStr = ByteStr(com.wavesplatform.crypto.fastHash(bs.reduce(_ ++ _))) + private val cancelledLease = TSS( + leaseBalances = Seq(TSS.LeaseBalance(bs(address1.bytes), out = 20.waves), TSS.LeaseBalance(bs(address2.bytes), in = 0.waves)), + leaseStates = Seq( + TSS.LeaseState(leaseId, TSS.LeaseState.Status.Cancelled(TSS.LeaseState.Cancelled())) + ) + ) - property("correctly create transaction state snapshot hash from snapshot") { - withDomain(DomainPresets.RideV6, balances = Seq(AddrWithBalance(address1, addr1Balance), AddrWithBalance(address2, addr2Balance))) { d => - val snapshot = SnapshotOps.fromDiff(diff, d.blockchain).explicitGet() - val tx = invoke() - testHash(snapshot, Some(TxStatusInfo(tx.id(), Succeeded)), Array()) - testHash(snapshot, Some(TxStatusInfo(tx.id(), Failed)), tx.id().arr :+ 1) - testHash(snapshot, Some(TxStatusInfo(tx.id(), Elided)), tx.id().arr :+ 2) - testHash(snapshot, None, Array()) - } - } + private val sponsorship = TSS( + sponsorships = Seq(TSS.Sponsorship(assetId2, 5500)) + ) - private def testHash(snapshot: StateSnapshot, txInfoOpt: Option[TxStatusInfo], txStatusBytes: Array[Byte]) = - TxStateSnapshotHashBuilder.createHashFromSnapshot(snapshot, txInfoOpt).txStateSnapshotHash shouldBe hash( - Seq( - Array(KeyType.WavesBalance.id.toByte) ++ address1.bytes ++ Longs.toByteArray(addr1PortfolioDiff.balance + addr1Balance), - Array(KeyType.AssetBalance.id.toByte) ++ address2.bytes ++ assetId1.id.arr ++ Longs.toByteArray(addr2PortfolioDiff.assets.head._2), - Array(KeyType.DataEntry.id.toByte) ++ address1.bytes ++ dataEntry.key.getBytes(StandardCharsets.UTF_8) ++ dataEntry.valueBytes, - Array(KeyType.AccountScript.id.toByte) ++ address2.bytes ++ accountScriptInfo.script.bytes().arr ++ accountScriptInfo.publicKey.arr ++ Longs - .toByteArray(accountScriptInfo.verifierComplexity), - Array(KeyType.AssetScript.id.toByte) ++ assetId1.id.arr ++ testScript.bytes().arr, - Array(KeyType.LeaseBalance.id.toByte) ++ address1.bytes ++ Longs.toByteArray(addr1PortfolioDiff.lease.in) ++ Longs.toByteArray( - addr1PortfolioDiff.lease.out - ), - 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), - Array(KeyType.Alias.id.toByte) ++ address2.bytes ++ addr2Alias.name.getBytes(StandardCharsets.UTF_8), - Array(KeyType.VolumeAndFee.id.toByte) ++ orderId.arr ++ Longs.toByteArray(volumeAndFee.volume) ++ Longs.toByteArray(volumeAndFee.fee), - Array(KeyType.AssetStatic.id.toByte) ++ assetId1.id.arr ++ assetInfo1.static.issuer.arr ++ - Array(assetInfo1.static.decimals.toByte) ++ (if (assetInfo1.static.nft) Array(1: Byte) else Array(0: Byte)), - Array(KeyType.AssetStatic.id.toByte) ++ assetId2.id.arr ++ assetInfo2.static.issuer.arr ++ - Array(assetInfo2.static.decimals.toByte) ++ (if (assetInfo2.static.nft) Array(1: Byte) else Array(0: Byte)), - Array(KeyType.AssetStatic.id.toByte) ++ assetId3.id.arr ++ assetInfo3.static.issuer.arr ++ - Array(assetInfo3.static.decimals.toByte) ++ (if (assetInfo3.static.nft) Array(1: Byte) else Array(0: Byte)), - Array(KeyType.AssetStatic.id.toByte) ++ assetId4.id.arr ++ assetInfo4.static.issuer.arr ++ - Array(assetInfo4.static.decimals.toByte) ++ (if (assetInfo4.static.nft) Array(1: Byte) else Array(0: Byte)), - Array(KeyType.AssetVolume.id.toByte) ++ assetId1.id.arr ++ - (if (updatedAssetVolumeInfo1.isReissuable) Array(1: Byte) else Array(0: Byte)) ++ snapshot.assetVolumes(assetId1).volume.toByteArray, - Array(KeyType.AssetVolume.id.toByte) ++ assetId2.id.arr ++ - (if (assetInfo2.volume.isReissuable) Array(1: Byte) else Array(0: Byte)) ++ snapshot.assetVolumes(assetId2).volume.toByteArray, - Array(KeyType.AssetVolume.id.toByte) ++ assetId3.id.arr ++ - (if (updatedAssetVolumeInfo3.isReissuable) Array(1: Byte) else Array(0: Byte)) ++ snapshot.assetVolumes(assetId3).volume.toByteArray, - Array(KeyType.AssetVolume.id.toByte) ++ assetId4.id.arr ++ - (if (assetInfo4.volume.isReissuable) Array(1: Byte) else Array(0: Byte)) ++ snapshot.assetVolumes(assetId4).volume.toByteArray, - Array( - KeyType.AssetNameDescription.id.toByte - ) ++ assetId1.id.arr ++ updatedAssetInfo1.name.toByteArray ++ updatedAssetInfo1.description.toByteArray ++ Ints.toByteArray( - updatedAssetInfo1.lastUpdatedAt - ), - Array( - KeyType.AssetNameDescription.id.toByte - ) ++ assetId2.id.arr ++ updatedAssetInfo2.name.toByteArray ++ updatedAssetInfo2.description.toByteArray ++ Ints.toByteArray( - updatedAssetInfo2.lastUpdatedAt - ), - Array( - KeyType.AssetNameDescription.id.toByte - ) ++ assetId3.id.arr ++ assetInfo3.dynamic.name.toByteArray ++ assetInfo3.dynamic.description.toByteArray ++ Ints.toByteArray( - assetInfo3.dynamic.lastUpdatedAt - ), - Array( - KeyType.AssetNameDescription.id.toByte - ) ++ assetId4.id.arr ++ assetInfo4.dynamic.name.toByteArray ++ assetInfo4.dynamic.description.toByteArray ++ Ints.toByteArray( - assetInfo4.dynamic.lastUpdatedAt - ), - if (txStatusBytes.nonEmpty) Array(KeyType.TransactionStatus.id.toByte) ++ txStatusBytes else Array[Byte]() - ).sorted((x: Array[Byte], y: Array[Byte]) => UnsignedBytes.lexicographicalComparator().compare(x, y)) + private val alias = TSS( + aliases = Some(TSS.Alias(bs(address2.bytes), "wavesevo")) + ) + + private val volumeAndFee = TSS( + orderFills = Seq( + TSS.OrderFill(orderId1, 10.waves, 2000), + TSS.OrderFill(orderId2, 10.waves, 2000) ) + ) - property("correctly compute hash using previous value") { - val txStateHash = TxStateSnapshotHashBuilder.Result(ByteStr.fill(DigestLength)(1)) - val prevHash = ByteStr.fill(DigestLength)(2) + private val newAsset = TSS( + assetStatics = Seq( + TSS.AssetStatic(assetId1, hashInt(0x88aadd55), nft = true), + TSS.AssetStatic(assetId2, hashInt(0x88aadd55), decimals = 8) + ), + assetVolumes = Seq( + TSS.AssetVolume(assetId2, true, bs((BigInt(Long.MaxValue) * 10).toByteArray)), + TSS.AssetVolume(assetId1, false, bs(BigInt(1).toByteArray)) + ), + assetNamesAndDescriptions = Seq() + ) - txStateHash.createHash(prevHash) shouldBe hash(Seq(prevHash.arr ++ txStateHash.txStateSnapshotHash.arr)) + private val reissuedAsset = TSS( + assetVolumes = Seq( + TSS.AssetVolume(assetId2, false, bs((BigInt(10_00000000L)).toByteArray)), + ) + ) + private val renamedAsset = TSS( + assetNamesAndDescriptions = Seq( + TSS.AssetNameAndDescription() + ) + ) + private val failedTransaction = TSS( + balances = Seq(), + transactionStatus = TransactionStatus.FAILED + ) + private val elidedTransaction = TSS( + transactionStatus = TransactionStatus.ELIDED + ) + + private val all = TSS( + assetBalances.balances ++ wavesBalances.balances, + newLease.leaseBalances ++ cancelledLease.leaseBalances, + newAsset.assetStatics, + newAsset.assetVolumes ++ reissuedAsset.assetVolumes, + newAsset.assetNamesAndDescriptions ++ renamedAsset.assetNamesAndDescriptions, + newAsset.assetScripts, + alias.aliases, + volumeAndFee.orderFills, + newLease.leaseStates ++ cancelledLease.leaseStates, + accountScript.accountScripts, + dataEntries.accountData, + sponsorship.sponsorships, + failedTransaction.transactionStatus + ) + + private val testData = Table( + ("state snapshot", "base64 bytes", "tx id", "previous state hash", "expected result"), + ( + wavesBalances, + "CiQKGgFUYP1Q7yDeRXEgffuciL58HC+KIscK2I+1EgYQgJTr3AMKJAoaAVRCxcljc/UP2BNQYE8cFPKmySVq2v0ZsCoSBhCAqNa5Bw==", + ByteStr.empty, + "", + "954bf440a83542e528fe1e650471033e42d97c5896cc571aec39fccc912d7db0" + ), + ( + assetBalances, + "CkMKGgFUYP1Q7yDeRXEgffuciL58HC+KIscK2I+1EiUKIF5mn4IKZ9CIbYdHjPBDoqx4XMevVdwxzhB1OUvTUKJbEJBOCkQKGgFUQsXJY3P1D9gTUGBPHBTypsklatr9GbAqEiYKIHidwBEj1TYPcIKv1LRquL/otRYLv7UmwEPl/Hg6T4lOEKCcAQ==", + ByteStr.empty, + "954bf440a83542e528fe1e650471033e42d97c5896cc571aec39fccc912d7db0", + "534e27c3a787536e18faf844ff217a8f14e5323dfcd3cc5b9ab3a8e261f60cf7" + ), + ( + dataEntries, + "WloKGgFUYP1Q7yDeRXEgffuciL58HC+KIscK2I+1EgUKA2ZvbxISCgNiYXpqC1N0cmluZ1ZhbHVlEiEKA2JhemIaAVRg/VDvIN5FcSB9+5yIvnwcL4oixwrYj7VaLwoaAVRCxcljc/UP2BNQYE8cFPKmySVq2v0ZsCoSCAoDZm9vULAJEgcKA2JhclgB", + ByteStr.empty, + "534e27c3a787536e18faf844ff217a8f14e5323dfcd3cc5b9ab3a8e261f60cf7", + "e24952f7e5ee51450ffedbddb03cbbf116964f207c9621d9c67353a75030ba01" + ), + ( + accountScript, + "Ui4KIFDHWa9Cd6VU8M20LLFHzbBTveERf1sEOw19SUS40GBoEgcGAQaw0U/PGPoB", + ByteStr.empty, + "e24952f7e5ee51450ffedbddb03cbbf116964f207c9621d9c67353a75030ba01", + "976df303cdd90d3e89b1a9fd11b0b93cfec47107ee51a9c00746b8dc4d4ccf4d" + ), + ( + assetScript, + "MisKIHidwBEj1TYPcIKv1LRquL/otRYLv7UmwEPl/Hg6T4lOEgcGAQaw0U/P", + ByteStr.empty, + "976df303cdd90d3e89b1a9fd11b0b93cfec47107ee51a9c00746b8dc4d4ccf4d", + "08c68eb3bf3494a0725de020f353b530a4ac5a9db059e23b54f0b98b2a2a4d24" + ), + ( + newLease, + "EiIKGgFUYP1Q7yDeRXEgffuciL58HC+KIscK2I+1GICa4uEQEiIKGgFUQsXJY3P1D9gTUGBPHBTypsklatr9GbAqEICuzb4USmkKILiCMyyFggW8Zd2LGt/AtMr7WWp+kfWbzlN93pXZqzqNqgFECIDyi6gJEiBQx1mvQnelVPDNtCyxR82wU73hEX9bBDsNfUlEuNBgaBoaAVRCxcljc/UP2BNQYE8cFPKmySVq2v0ZsCo=", + ByteStr.empty, + "08c68eb3bf3494a0725de020f353b530a4ac5a9db059e23b54f0b98b2a2a4d24", + "77bcff83d90592e950527d35e70886eceaab04648e8b0e30dd74c6ec2f722e53" + ), + ( + cancelledLease, + "EiIKGgFUYP1Q7yDeRXEgffuciL58HC+KIscK2I+1GICo1rkHEhwKGgFUQsXJY3P1D9gTUGBPHBTypsklatr9GbAqSiUKILiCMyyFggW8Zd2LGt/AtMr7WWp+kfWbzlN93pXZqzqNsgEA", + ByteStr.empty, + "77bcff83d90592e950527d35e70886eceaab04648e8b0e30dd74c6ec2f722e53", + "3c115c2cf9e73ea9b9b97c2c3ac672227814c313a28ee1f3c226501f50ede2db" + ), + ( + sponsorship, + "YiUKIHidwBEj1TYPcIKv1LRquL/otRYLv7UmwEPl/Hg6T4lOEPwq", + ByteStr.empty, + "3c115c2cf9e73ea9b9b97c2c3ac672227814c313a28ee1f3c226501f50ede2db", + "192e86fa9ebb24b5780a053a9b9864518705795647be47523dcb6cba9a67345d" + ), + ( + alias, + "OiYKGgFUQsXJY3P1D9gTUGBPHBTypsklatr9GbAqEgh3YXZlc2V2bw==", + ByteStr.empty, + "192e86fa9ebb24b5780a053a9b9864518705795647be47523dcb6cba9a67345d", + "8a64e138c63c6f23b657316b9cecbbbb163a30c77caf1111ddecf70428b585b9" + ), + ( + volumeAndFee, + "QisKIMkknO8yHpMUT/XKkkdlrbYCG0Dt+qvVgphfgtRbyRDMEICU69wDGNAPQisKIJZ9YwvJObbWItHAD2zhbaFOTFx2zQ4p0Xbo81GXHKeEEICU69wDGNAP", + ByteStr.empty, + "8a64e138c63c6f23b657316b9cecbbbb163a30c77caf1111ddecf70428b585b9", + "2cd2b9115eb62e30904c0bee93d0051c11ab7df99f6ce403bb30dbc8314d8f68" + ), + ( + newAsset, + "GkYKIF5mn4IKZ9CIbYdHjPBDoqx4XMevVdwxzhB1OUvTUKJbEiDcYGFqY9MotHTpDpskoycN/Mt62bZfPxIC4fpU0ZTBniABGkYKIHidwBEj1TYPcIKv1LRquL/otRYLv7UmwEPl/Hg6T4lOEiDcYGFqY9MotHTpDpskoycN/Mt62bZfPxIC4fpU0ZTBnhgK", + ByteStr.empty, + "2cd2b9115eb62e30904c0bee93d0051c11ab7df99f6ce403bb30dbc8314d8f68", + "3724dfd7fc0a9d756b64a956829a2d2377bb90c852b388a9f85e00fe999c050d" + ), + ( + reissuedAsset, + "", + ByteStr.empty, + "3724dfd7fc0a9d756b64a956829a2d2377bb90c852b388a9f85e00fe999c050d", + "dd2971ec2c572c5ffea5a6870eea0a5baec8ded08a6ab1e3bb132271646c5924" + ), + ( + renamedAsset, + "", + ByteStr.empty, + "dd2971ec2c572c5ffea5a6870eea0a5baec8ded08a6ab1e3bb132271646c5924", + "048fb3f170306f8f34c2e0c2841604333529540160d235c11852f5169d1b9a08" + ), + ( + failedTransaction, + "", + ByteStr.empty, + "048fb3f170306f8f34c2e0c2841604333529540160d235c11852f5169d1b9a08", + "684262eae7aa1ec68e6adbf0abe528b1d77cb9389b334193236fb2927c4287f1" + ), + ( + elidedTransaction, + "", + ByteStr.empty, + "684262eae7aa1ec68e6adbf0abe528b1d77cb9389b334193236fb2927c4287f1", + "6fbd5cca3707185cd6004e31521c6f94fab3c75474bda7926862aa3385205200" + ), + ( + all, + "CkMKGgFUYP1Q7yDeRXEgffuciL58HC+KIscK2I+1EiUKIF5mn4IKZ9CIbYdHjPBDoqx4XMevVdwxzhB1OUvTUKJbEJBOCkQKGgFUQsXJY3P1D9gTUGBPHBTypsklatr9GbAqEiYKIHidwBEj1TYPcIKv1LRquL/otRYLv7UmwEPl/Hg6T4lOEKCcAQokChoBVGD9UO8g3kVxIH37nIi+fBwviiLHCtiPtRIGEICU69wDCiQKGgFUQsXJY3P1D9gTUGBPHBTypsklatr9GbAqEgYQgKjWuQcSIgoaAVRg/VDvIN5FcSB9+5yIvnwcL4oixwrYj7UYgJri4RASIgoaAVRCxcljc/UP2BNQYE8cFPKmySVq2v0ZsCoQgK7NvhQSIgoaAVRg/VDvIN5FcSB9+5yIvnwcL4oixwrYj7UYgKjWuQcSHAoaAVRCxcljc/UP2BNQYE8cFPKmySVq2v0ZsCoaRgogXmafggpn0Ihth0eM8EOirHhcx69V3DHOEHU5S9NQolsSINxgYWpj0yi0dOkOmySjJw38y3rZtl8/EgLh+lTRlMGeIAEaRgogeJ3AESPVNg9wgq/UtGq4v+i1Fgu/tSbAQ+X8eDpPiU4SINxgYWpj0yi0dOkOmySjJw38y3rZtl8/EgLh+lTRlMGeGAo6JgoaAVRCxcljc/UP2BNQYE8cFPKmySVq2v0ZsCoSCHdhdmVzZXZvQisKIMkknO8yHpMUT/XKkkdlrbYCG0Dt+qvVgphfgtRbyRDMEICU69wDGNAPQisKIJZ9YwvJObbWItHAD2zhbaFOTFx2zQ4p0Xbo81GXHKeEEICU69wDGNAPSmkKILiCMyyFggW8Zd2LGt/AtMr7WWp+kfWbzlN93pXZqzqNqgFECIDyi6gJEiBQx1mvQnelVPDNtCyxR82wU73hEX9bBDsNfUlEuNBgaBoaAVRCxcljc/UP2BNQYE8cFPKmySVq2v0ZsCpKJQoguIIzLIWCBbxl3Ysa38C0yvtZan6R9ZvOU33eldmrOo2yAQBSLgogUMdZr0J3pVTwzbQssUfNsFO94RF/WwQ7DX1JRLjQYGgSBwYBBrDRT88Y+gFaWgoaAVRg/VDvIN5FcSB9+5yIvnwcL4oixwrYj7USBQoDZm9vEhIKA2JhemoLU3RyaW5nVmFsdWUSIQoDYmF6YhoBVGD9UO8g3kVxIH37nIi+fBwviiLHCtiPtVovChoBVELFyWNz9Q/YE1BgTxwU8qbJJWra/RmwKhIICgNmb29QsAkSBwoDYmFyWAFiJQogeJ3AESPVNg9wgq/UtGq4v+i1Fgu/tSbAQ+X8eDpPiU4Q/Co=", + ByteStr.empty, + "6fbd5cca3707185cd6004e31521c6f94fab3c75474bda7926862aa3385205200", + "f6b2f644c24466b27b05aa187b4a339e732d37b488635403c03f437686f07e46" + ) + ) + + property("correctly create transaction state snapshot hash from snapshot") { + forAll(testData) { case (pbSnapshot, b64str, txId, prev, expectedResult) => + val parsedSnapshot = TSS.parseFrom(Base64.decode(b64str)) + parsedSnapshot shouldEqual pbSnapshot + val (snapshot, meta) = PBSnapshots.fromProtobuf(pbSnapshot, txId, 10) + val raw = Hex.toHexString( + TxStateSnapshotHashBuilder + .createHashFromSnapshot(snapshot, Some(TxStateSnapshotHashBuilder.TxStatusInfo(txId, meta))) + .createHash(ByteStr.decodeBase64(prev).get) + .arr + ) + + println(s"\n\t${Base64.encode(pbSnapshot.toByteArray)}\n\t$raw") + + raw shouldEqual expectedResult + } } } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 9d5b08bc4a..8715294422 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-85-SNAPSHOT" classifier "protobuf-src" intransitive () private def akkaModule(module: String) = "com.typesafe.akka" %% s"akka-$module" % "2.6.21"