Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
phearnot committed Nov 17, 2023
1 parent 899fd66 commit 121b5f0
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 61 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.wavesplatform.state

import cats.implicits.catsSyntaxSemigroup
import cats.syntax.either.*
import com.google.common.primitives.{Ints, Longs}
import com.google.common.primitives.{Ints, Longs, UnsignedBytes}
import com.wavesplatform.account.{Address, KeyPair}
import com.wavesplatform.common.state.ByteStr
import com.wavesplatform.common.utils.EitherExt2
Expand All @@ -15,123 +15,96 @@ import com.wavesplatform.state.reader.SnapshotBlockchain
import com.wavesplatform.transaction.Asset.{IssuedAsset, Waves}
import com.wavesplatform.transaction.smart.script.trace.TracedResult
import com.wavesplatform.transaction.{GenesisTransaction, Transaction}
import com.wavesplatform.utils.byteStrOrdering
import org.bouncycastle.crypto.digests.Blake2bDigest

import java.nio.charset.StandardCharsets
import scala.collection.mutable

object TxStateSnapshotHashBuilder {
object KeyType extends Enumeration {
val WavesBalance, AssetBalance, DataEntry, AccountScript, AssetScript, LeaseBalance, LeaseStatus, Sponsorship, Alias, VolumeAndFee, AssetStatic,
AssetVolume, AssetNameDescription, TransactionStatus = Value
}
private implicit val ByteArrayOrdering: Ordering[Array[Byte]] = (x, y) => UnsignedBytes.lexicographicalComparator().compare(x, y)

val InitStateHash: ByteStr = ByteStr(crypto.fastHash(""))

final case class Result(txStateSnapshotHash: ByteStr) {
def createHash(prevHash: ByteStr): ByteStr =
TxStateSnapshotHashBuilder.createHash(Seq(prevHash, txStateSnapshotHash))
TxStateSnapshotHashBuilder.createHash(Seq(prevHash.arr, txStateSnapshotHash.arr))
}

case class TxStatusInfo(id: ByteStr, status: TxMeta.Status)

def createHashFromSnapshot(snapshot: StateSnapshot, txStatusOpt: Option[TxStatusInfo]): Result = {
val changedKeys = mutable.Map.empty[ByteStr, Array[Byte]]

def addEntry(keyType: KeyType.Value, key: Array[Byte]*)(value: Array[Byte]*): Unit = {
val solidKey = ByteStr(key.fold(Array(keyType.id.toByte))(_ ++ _))
val solidValue = value.foldLeft(Array.emptyByteArray)(_ ++ _)
changedKeys(solidKey) = solidValue
}
val changedKeys = mutable.SortedSet.empty[Array[Byte]]

snapshot.balances.foreach { case ((address, asset), balance) =>
asset match {
case Waves => addEntry(KeyType.WavesBalance, address.bytes)(Longs.toByteArray(balance))
case asset: IssuedAsset => addEntry(KeyType.AssetBalance, address.bytes, asset.id.arr)(Longs.toByteArray(balance))
case Waves => changedKeys += address.bytes ++ Longs.toByteArray(balance)
case asset: IssuedAsset => changedKeys += address.bytes ++ asset.id.arr ++ Longs.toByteArray(balance)
}
}

snapshot.leaseBalances.foreach { case (address, balance) =>
addEntry(KeyType.LeaseBalance, address.bytes)(Longs.toByteArray(balance.in), Longs.toByteArray(balance.out))
changedKeys += address.bytes ++ Longs.toByteArray(balance.in) ++ Longs.toByteArray(balance.out)
}

for {
(address, data) <- snapshot.accountData
entry <- data.values
} addEntry(KeyType.DataEntry, address.bytes, entry.key.getBytes(StandardCharsets.UTF_8))(entry.valueBytes)
} changedKeys += address.bytes ++ entry.key.getBytes(StandardCharsets.UTF_8) ++ entry.valueBytes

snapshot.aliases.foreach { case (alias, address) =>
addEntry(KeyType.Alias, address.bytes, alias.name.getBytes(StandardCharsets.UTF_8))()
changedKeys += address.bytes ++ alias.name.getBytes(StandardCharsets.UTF_8)
}

snapshot.accountScriptsByAddress.foreach { case (address, sv) =>
addEntry(KeyType.AccountScript, address.bytes)(
sv.fold(Seq(Array.emptyByteArray))(s => Seq(s.script.bytes().arr, s.publicKey.arr, Longs.toByteArray(s.verifierComplexity)))*
)
changedKeys += address.bytes ++ (sv match {
case Some(s) => s.script.bytes().arr ++ s.publicKey.arr ++ Longs.toByteArray(s.verifierComplexity)
case None => Array.emptyByteArray
})
}

for {
(asset, scriptInfo) <- snapshot.assetScripts
} addEntry(KeyType.AssetScript, asset.id.arr)(scriptInfo.script.bytes().arr)
} changedKeys += asset.id.arr ++ scriptInfo.script.bytes().arr

snapshot.leaseStates.foreach { case (leaseId, details) =>
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)
)
changedKeys += leaseId.arr ++ booleanToBytes(details.isActive)
if (details.isActive) {
changedKeys += leaseId.arr ++ details.sender.arr ++ details.recipient.bytes ++ Longs.toByteArray(details.amount)
}
}

snapshot.sponsorships.foreach { case (asset, sponsorship) =>
addEntry(KeyType.Sponsorship, asset.id.arr)(Longs.toByteArray(sponsorship.minFee))
changedKeys += asset.id.arr ++ Longs.toByteArray(sponsorship.minFee)
}

snapshot.orderFills.foreach { case (orderId, fillInfo) =>
addEntry(KeyType.VolumeAndFee, orderId.arr)(
Longs.toByteArray(fillInfo.volume),
Longs.toByteArray(fillInfo.fee)
)
changedKeys += orderId.arr ++ Longs.toByteArray(fillInfo.volume) ++ Longs.toByteArray(fillInfo.fee)
}

snapshot.assetStatics.foreach { case (asset, assetInfo) =>
addEntry(KeyType.AssetStatic, asset.id.arr)(
assetInfo.issuerPublicKey.toByteArray,
Array(assetInfo.decimals.toByte),
booleanToBytes(assetInfo.nft)
)
changedKeys += asset.id.arr ++ assetInfo.issuerPublicKey.toByteArray ++ Array(assetInfo.decimals.toByte) ++ booleanToBytes(assetInfo.nft)
}

snapshot.assetVolumes.foreach { case (asset, volume) =>
addEntry(KeyType.AssetVolume, asset.id.arr)(
booleanToBytes(volume.isReissuable),
volume.volume.toByteArray
)
changedKeys += asset.id.arr ++ booleanToBytes(volume.isReissuable) ++ volume.volume.toByteArray
}

snapshot.assetNamesAndDescriptions.foreach { case (asset, assetInfo) =>
addEntry(KeyType.AssetNameDescription, asset.id.arr)(
assetInfo.name.toByteArray,
assetInfo.description.toByteArray,
changedKeys += asset.id.arr ++
assetInfo.name.toByteArray ++
assetInfo.description.toByteArray ++
Ints.toByteArray(assetInfo.lastUpdatedAt.toInt)
)
}

txStatusOpt.foreach(txInfo =>
txInfo.status match {
case Status.Failed => addEntry(KeyType.TransactionStatus, txInfo.id.arr)(Array(1: Byte))
case Status.Elided => addEntry(KeyType.TransactionStatus, txInfo.id.arr)(Array(2: Byte))
case Status.Failed => changedKeys += txInfo.id.arr ++ Array(1: Byte)
case Status.Elided => changedKeys += txInfo.id.arr ++ Array(2: Byte)
case Status.Succeeded =>
}
)

Result(createHash(changedKeys.toSeq.sortBy(_._1).flatMap { case (k, v) => Seq(k, ByteStr(v)) }))
Result(createHash(changedKeys))
}

def createGenesisStateHash(txs: Seq[GenesisTransaction]): ByteStr =
Expand All @@ -140,9 +113,7 @@ object TxStateSnapshotHashBuilder {
.foldLeft(InitStateHash.arr -> Map.empty[Address, Long]) { case ((prevStateHash, balances), tx) =>
val newBalance = balances.getOrElse(tx.recipient, 0L) + tx.amount.value
val tsh =
crypto.fastHash(
Array(TxStateSnapshotHashBuilder.KeyType.WavesBalance.id.toByte) ++ tx.recipient.bytes ++ Longs.toByteArray(newBalance)
)
crypto.fastHash(tx.recipient.bytes ++ Longs.toByteArray(newBalance))
val newStateHash = crypto.fastHash(prevStateHash ++ tsh)
newStateHash -> balances.updated(tx.recipient, newBalance)
}
Expand Down Expand Up @@ -202,8 +173,9 @@ object TxStateSnapshotHashBuilder {
private def booleanToBytes(flag: Boolean): Array[Byte] =
if (flag) Array(1: Byte) else Array(0: Byte)

private def createHash(bs: Iterable[ByteStr], digestFn: Blake2bDigest = newDigestInstance()): ByteStr = {
bs.foreach(bs => digestFn.update(bs.arr, 0, bs.arr.length))
private def createHash(bs: Iterable[Array[Byte]]): ByteStr = {
val digestFn: Blake2bDigest = newDigestInstance()
bs.foreach(bs => digestFn.update(bs, 0, bs.length))
val result = new Array[Byte](crypto.DigestLength)
digestFn.doFinal(result, 0)
ByteStr(result)
Expand Down
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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-84-SNAPSHOT" classifier "protobuf-src" intransitive ()
"com.wavesplatform" % "protobuf-schemas" % "1.5.1-SNAPSHOT" classifier "protobuf-src" intransitive ()

private def akkaModule(module: String) = "com.typesafe.akka" %% s"akka-$module" % "2.6.21"

Expand Down

0 comments on commit 121b5f0

Please sign in to comment.