From efd5c710612241ccde8f1e7b2cad112d62e180be Mon Sep 17 00:00:00 2001 From: Lior Date: Mon, 10 Feb 2020 13:34:10 +0300 Subject: [PATCH 01/36] getBalance type change --- .../encry/api/http/DataHolderForApi.scala | 15 ++-- .../encry/api/http/routes/WalletRoute.scala | 89 ++++++++++--------- .../WalletVersionalLevelDB.scala | 33 ++++--- .../scala/encry/utils/BalanceCalculator.scala | 8 +- .../scala/encry/view/wallet/EncryWallet.scala | 34 ++++--- 5 files changed, 99 insertions(+), 80 deletions(-) diff --git a/src/main/scala/encry/api/http/DataHolderForApi.scala b/src/main/scala/encry/api/http/DataHolderForApi.scala index 72adcec939..16ba86cecb 100644 --- a/src/main/scala/encry/api/http/DataHolderForApi.scala +++ b/src/main/scala/encry/api/http/DataHolderForApi.scala @@ -1,6 +1,7 @@ package encry.api.http import java.net.{InetAddress, InetSocketAddress} + import akka.actor.{Actor, ActorRef, Props, Stash} import akka.pattern._ import akka.util.Timeout @@ -26,11 +27,13 @@ import encry.view.NodeViewHolder.ReceivableMessages.{CreateAccountManagerFromSee import encry.view.history.History import encry.view.state.{UtxoState, UtxoStateReader} import encry.view.wallet.EncryWallet -import org.encryfoundation.common.crypto.PrivateKey25519 +import org.encryfoundation.common.crypto.{PrivateKey25519, PublicKey25519} import org.encryfoundation.common.modifiers.history.{Block, Header} import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.ModifierId + import scala.concurrent.Future class DataHolderForApi(settings: EncryAppSettings, ntp: NetworkTimeProvider) @@ -197,11 +200,11 @@ class DataHolderForApi(settings: EncryAppSettings, ntp: NetworkTimeProvider) }).pipeTo(sender) case GetViewGetBalance => - (nvhRef ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Map[String, List[(String, Amount)]]] { view => - val balance: Map[String, List[(String, Amount)]] = view.vault.getBalances.map { - case ((key, token), amount) => Map(key -> List((token, amount))) - }.foldLeft(Map.empty[String, List[(String, Amount)]]) { case (el1, el2) => el1 |+| el2 } - if (balance.isEmpty) Map.empty[String, List[(String, Amount)]] else balance + (nvhRef ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Map[(PublicKey25519, TokenId), Amount]] { view => + val balance: Map[(PublicKey25519, TokenId), Amount] = view.vault.getBalances.map { + case ((key, token), amount) => Map((key -> token) -> amount) + }.foldLeft(Map.empty[(PublicKey25519, TokenId), Amount]) { case (el1, el2) => el1 ++ el2 } + if (balance.isEmpty) Map.empty[(PublicKey25519, TokenId), Amount] else balance }).pipeTo(sender) case GetViewPrintPrivKeys => diff --git a/src/main/scala/encry/api/http/routes/WalletRoute.scala b/src/main/scala/encry/api/http/routes/WalletRoute.scala index 1dde32a658..8a2858354d 100644 --- a/src/main/scala/encry/api/http/routes/WalletRoute.scala +++ b/src/main/scala/encry/api/http/routes/WalletRoute.scala @@ -7,11 +7,14 @@ import akka.pattern._ import com.typesafe.scalalogging.StrictLogging import encry.api.http.DataHolderForApi.{GetViewGetBalance, GetViewPrintPubKeys} import encry.settings.{EncryAppSettings, RESTApiSettings} +import org.encryfoundation.common.crypto.PublicKey25519 import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.ADKey import scalatags.Text import scalatags.Text.all.{div, span, _} + import scala.concurrent.Future import scala.language.implicitConversions import scala.util.Success @@ -24,19 +27,18 @@ case class WalletRoute(settings: RESTApiSettings, val EttTokenId: String = Algos.encode(encrySettings.constants.IntrinsicTokenId) - def walletF: Future[Map[String, List[(String, Amount)]]] = + def walletF: Future[Map[(PublicKey25519, TokenId), Amount]] = (dataHolder ? GetViewGetBalance) - .mapTo[Map[String, List[(String, Amount)]]] + .mapTo[Map[(PublicKey25519, TokenId), Amount]] def pubKeysF: Future[List[String]] = (dataHolder ? GetViewPrintPubKeys).mapTo[List[String]] - def info: Future[(Map[String, List[(String, Amount)]], List[String])] = for { + def info: Future[(Map[(PublicKey25519, TokenId), Amount], List[String])] = for { wallet <- walletF pubKeys <- pubKeysF } yield (wallet, pubKeys) - def walletScript(balances: Map[String, List[(String, Amount)]]): Text.TypedTag[String] = { - + def walletScript(balances: Map[(PublicKey25519, TokenId), Amount]): Text.TypedTag[String] = { html( scalatags.Text.all.head( meta(charset := "utf-8"), @@ -427,9 +429,8 @@ case class WalletRoute(settings: RESTApiSettings, select(cls := "form-control", id :="coin", name:="coin", for { coinI <- balances.toList - coinIds <- coinI._2 } yield { - option(value := coinIds._1, if (coinIds._1 == EttTokenId) s"ETT (${coinIds._2/100000000})" else coinIds._1) + option(value := coinI._1._2.toString, if (Algos.encode(coinI._1._2) == EttTokenId) s"ETT (${coinI._2/100000000})" else "something else") } ) ), @@ -683,17 +684,17 @@ case class WalletRoute(settings: RESTApiSettings, ), ) ), - div(cls := "form-group", - select(cls := "form-control", id :="coin", name:="coin", - if (balances.nonEmpty) { - balances.values.flatten.toList.map( coinIds => - option(value := coinIds._1, if (coinIds._1 == EttTokenId) s"ETT (${coinIds._2/100000000})" else coinIds._1) - ) - } else { - option(value := "", "") - } - ) - ), +// div(cls := "form-group", +// select(cls := "form-control", id :="coin", name:="coin", +// if (balances.nonEmpty) { +// balances.values.flatten.toList.map( coinIds => +// option(value := coinIds._1, if (coinIds._1 == EttTokenId) s"ETT (${coinIds._2/100000000})" else coinIds._1) +// ) +// } else { +// option(value := "", "") +// } +// ) +// ), div(cls := "text-center", button(tpe := "button", onclick := "wallet()", cls := "btn btn-primary mt-4", "Send Money") ) @@ -721,17 +722,17 @@ case class WalletRoute(settings: RESTApiSettings, tbody( if (balances.nonEmpty) { (for { - mapKeyValue <- balances - tokenAmount <- mapKeyValue._2 + mapKeyValue <- balances.toList +// tokenAmount <- mapKeyValue._1 } yield { - val tknStr = tokenAmount._1 match { - case tokenId if tokenId == EttTokenId => "ETT" + val tknStr = mapKeyValue._1._2 match { + case tokenId if Algos.encode(tokenId) == EttTokenId => "ETT" case tokenId => tokenId } tr( - th(mapKeyValue._1), - th(tknStr), - if (tokenAmount._1 == EttTokenId ) th(tokenAmount._2/100000000) else th(tokenAmount._2) + th(mapKeyValue._1._1.toString()), + th(tknStr.toString), + if (Algos.encode(mapKeyValue._1._2) == EttTokenId ) th(mapKeyValue._2/100000000) else th(mapKeyValue._2) ) }).toList } else { @@ -754,25 +755,25 @@ case class WalletRoute(settings: RESTApiSettings, ) ) ), - div(cls := "table-responsive", - // Projects table - table(cls := "table align-items-center table-flush", - thead(cls := "thead-light", - tr( - th(attr("scope") := "col", "Key") - ) - ), - tbody( - if(balances.keys.nonEmpty) { - for (p <- balances.keys.toList) yield { - tr(th(attr("scope") := "row", p)) - } - } else { - tr() - } - ) - ) - ) +// div(cls := "table-responsive", +// // Projects table +// table(cls := "table align-items-center table-flush", +// thead(cls := "thead-light", +// tr( +// th(attr("scope") := "col", "Key") +// ) +// ), +// tbody( +// if(balances.keys.nonEmpty) { +// for (p <- balances.keys.toList) yield { +// tr(th(attr("scope") := "row", p)) +// } +// } else { +// tr() +// } +// ) +// ) +// ) ) ) ) diff --git a/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala b/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala index 9991ea0933..bce12f4cc6 100644 --- a/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala +++ b/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala @@ -7,6 +7,8 @@ import com.typesafe.scalalogging.StrictLogging import encry.settings.LevelDBSettings import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion._ import encry.utils.{BalanceCalculator, ByteStr} +import org.encryfoundation.common.crypto.PublicKey25519 +import org.encryfoundation.common.modifiers.mempool.transaction.PubKeyLockedContract import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.EncryBaseBox @@ -15,6 +17,8 @@ import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} import org.iq80.leveldb.DB import scorex.crypto.hash.Digest32 +import scorex.crypto.signatures.PublicKey + import scala.util.Success case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends StrictLogging with AutoCloseable { @@ -32,9 +36,11 @@ case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends Str def getBoxById(id: ADKey): Option[EncryBaseBox] = levelDb.get(VersionalLevelDbKey @@ id.untag(ADKey)) .flatMap(wrappedBx => StateModifierSerializer.parseBytes(wrappedBx, id.head).toOption) - def getTokenBalanceById(id: TokenId): Option[Amount] = getBalances - .find(_._1._2 == Algos.encode(id)) - .map(_._2) + def getTokenBalanceById(id: TokenId): Option[Amount] = { + getBalances + .find(_._1._2 sameElements id) + .map(_._2) + } def containsBox(id: ADKey): Boolean = getBoxById(id).isDefined @@ -43,17 +49,17 @@ case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends Str def updateWallet(modifierId: ModifierId, newBxs: Seq[EncryBaseBox], spentBxs: Seq[EncryBaseBox], intrinsicTokenId: ADKey): Unit = { val bxsToInsert: Seq[EncryBaseBox] = newBxs.filter(bx => !spentBxs.contains(bx)) - val newBalances: Map[(String, String), Amount] = { - val toRemoveFromBalance = BalanceCalculator.balanceSheet(spentBxs, intrinsicTokenId) - .map { case ((hash, key), value) => (hash, ByteStr(key)) -> value * -1 } - val toAddToBalance = BalanceCalculator.balanceSheet(newBxs, intrinsicTokenId) - .map { case ((hash, key), value) => (hash, ByteStr(key)) -> value } - val prevBalance = getBalances.map { case ((hash, id), value) => (hash, ByteStr(Algos.decode(id).get)) -> value } - (toAddToBalance |+| toRemoveFromBalance |+| prevBalance).map { case ((hash, tokenId), value) => (hash, tokenId.toString) -> value } + val newBalances: Map[(String, TokenId), Amount] = { + val toRemoveFromBalance: Map[(String, TokenId), Amount] = BalanceCalculator.balanceSheet(spentBxs, intrinsicTokenId) + .map { case ((publickKey, key), value) => (publickKey, key) -> value * -1 } + val toAddToBalance: Map[(String, TokenId), Amount] = BalanceCalculator.balanceSheet(newBxs, intrinsicTokenId) + .map { case ((publickKey, key), value) => (publickKey, key) -> value } + val prevBalance: Map[(String, TokenId), Amount] = getBalances.map { case ((publicKey, id), value) => (Algos.encode(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash), id) -> value } + (toAddToBalance |+| toRemoveFromBalance |+| prevBalance).map { case ((hash, tokenId), value) => (hash, tokenId) -> value } } val newBalanceKeyValue = BALANCE_KEY -> VersionalLevelDbValue @@ newBalances.foldLeft(Array.emptyByteArray) { case (acc, ((hash, tokenId), balance)) => - acc ++ Algos.decode(hash).get ++ Algos.decode(tokenId).get ++ Longs.toByteArray(balance) + acc ++ Algos.decode(hash).get ++ tokenId ++ Longs.toByteArray(balance) } levelDb.insert(LevelDbDiff(LevelDBVersion @@ modifierId.untag(ModifierId), newBalanceKeyValue :: bxsToInsert.map(bx => (VersionalLevelDbKey @@ bx.id.untag(ADKey), @@ -62,11 +68,12 @@ case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends Str ) } - def getBalances: Map[(String, String), Amount] = + def getBalances: Map[(PublicKey25519, TokenId), Amount] = { levelDb.get(BALANCE_KEY) .map(_.sliding(72, 72) - .map(ch => (Algos.encode(ch.take(32)), Algos.encode(ch.slice(32, 64))) -> Longs.fromByteArray(ch.takeRight(8))) + .map(ch => (PublicKey25519(PublicKey @@ ch.take(32)), ch.slice(32, 64)) -> Longs.fromByteArray(ch.takeRight(8))) .toMap).getOrElse(Map.empty) + } override def close(): Unit = levelDb.close() } diff --git a/src/main/scala/encry/utils/BalanceCalculator.scala b/src/main/scala/encry/utils/BalanceCalculator.scala index e15f30ba99..ec1b654528 100644 --- a/src/main/scala/encry/utils/BalanceCalculator.scala +++ b/src/main/scala/encry/utils/BalanceCalculator.scala @@ -10,19 +10,19 @@ object BalanceCalculator { def balanceSheet(bxs: Traversable[EncryBaseBox], defaultTokenId: TokenId, excludeTokenIssuance: Boolean = false): Map[(String, TokenId), Amount] = - bxs.foldLeft(Map.empty[(String, ByteStr), Amount]) { + bxs.foldLeft(Map.empty[(String, TokenId), Amount]) { case (cache, bx: AssetBox) => - val tokenId: ByteStr = ByteStr(bx.tokenIdOpt.getOrElse(defaultTokenId)) + val tokenId: TokenId = bx.tokenIdOpt.getOrElse(defaultTokenId) val contractHash = Algos.encode(bx.proposition.contractHash) cache.get(contractHash -> tokenId).map { amount => cache.updated(contractHash -> tokenId, amount + bx.amount) }.getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) case (cache, bx: TokenIssuingBox) if !excludeTokenIssuance => val contractHash = Algos.encode(bx.proposition.contractHash) - val tokenId: ByteStr = ByteStr(bx.tokenId) + val tokenId: TokenId = bx.tokenId cache.get(contractHash -> tokenId).map { amount => cache.updated(contractHash -> tokenId, amount + bx.amount) }.getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) case (cache, _) => cache - }.map { case ((hash, id), am) => (hash -> id.arr) -> am } + }.map { case ((hash, id), am) => (hash -> id) -> am } } diff --git a/src/main/scala/encry/view/wallet/EncryWallet.scala b/src/main/scala/encry/view/wallet/EncryWallet.scala index 604a3a187e..dbc9f283be 100644 --- a/src/main/scala/encry/view/wallet/EncryWallet.scala +++ b/src/main/scala/encry/view/wallet/EncryWallet.scala @@ -1,6 +1,7 @@ package encry.view.wallet import java.io.File + import cats.data.NonEmptyChain._ import cats.data.{NonEmptyChain, Validated} import cats.instances.string._ @@ -23,10 +24,14 @@ import org.encryfoundation.common.modifiers.PersistentModifier import org.encryfoundation.common.modifiers.history.Block import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.StateModifierSerializer +import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{EncryBaseBox, EncryProposition, MonetaryBox} -import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.ModifierId +import org.encryfoundation.common.utils.{Algos, TaggedTypes} +import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} import org.iq80.leveldb.{DB, Options} +import scorex.crypto.signatures.PublicKey + import scala.util.{Failure, Success, Try} case class EncryWallet(walletStorage: WalletVersionalLevelDB, accountManagers: Seq[AccountManager], private val accountStore: Store) @@ -87,15 +92,17 @@ case class EncryWallet(walletStorage: WalletVersionalLevelDB, accountManagers: S def rollback(to: VersionTag): Try[Unit] = Try(walletStorage.rollback(ModifierId @@ to.untag(VersionTag))) - def getBalances: Seq[((String, String), Long)] = { - val pubKeys = publicKeys - val contractHashToKey = contractHashesToKeys(pubKeys) - val positiveBalance = walletStorage.getBalances.map { case ((hash, tokenId), amount) => - (contractHashToKey(hash), tokenId) -> amount + def getBalances: Seq[((PublicKey25519, TokenId), Amount)] = { + val pubKeys = publicKeys + val contractHashToKey: Map[String, String] = contractHashesToKeys(pubKeys) + val positiveBalance: Map[(PublicKey25519, TokenId), Amount] = walletStorage.getBalances.map { + case ((hash, tokenId), amount) => + (hash, tokenId) -> amount } - (pubKeys.map(k => Algos.encode(k.pubKeyBytes)) -- positiveBalance.keys.map(_._1)) - .map(_ -> Algos.encode(settings.constants.IntrinsicTokenId) -> 0L).toSeq ++ positiveBalance - }.sortBy(_._1._1 != Algos.encode(accountManagers.head.publicAccounts.head.pubKeyBytes)) + (pubKeys -- positiveBalance.keys.map(_._1)) + .map(l => (l -> settings.constants.IntrinsicTokenId) -> 0L) + .toSeq ++ positiveBalance + }.sortBy(l => !(l._1._1.pubKeyBytes sameElements accountManagers.head.publicAccounts.head.pubKeyBytes)) def contractHashesToKeys(pubKeys: Set[PublicKey25519]): Map[String, String] = pubKeys .map(key => Algos.encode(key.pubKeyBytes) -> key.address.address) @@ -154,7 +161,7 @@ object EncryWallet extends StrictLogging { } case leafNode: LeafNode[StorageKey, StorageValue] => StateModifierSerializer.parseBytes(leafNode.value, leafNode.key.head) match { - case Success(bx) => collectBx(bx, accounts) + case Success(bx) => collectBx(bx, accounts) case Failure(exception) => throw exception //??????? } case shadowNode: ShadowNode[StorageKey, StorageValue] => List.empty @@ -172,9 +179,10 @@ object EncryWallet extends StrictLogging { val db: DB = LevelDbFactory.factory.open(walletDir, new Options) val accountManagerStore: LSMStore = new LSMStore(keysDir, keepVersions = 0, keySize = 34) // 34 = 1 prefix byte + 1 account number byte + 32 key bytes val walletStorage: WalletVersionalLevelDB = WalletVersionalLevelDBCompanion(db, settings.levelDB) - val password: String = settings.wallet.map(_.password).getOrElse(throw new RuntimeException("Password not specified")) + val password: String = + settings.wallet.map(_.password).getOrElse(throw new RuntimeException("Password not specified")) val restoredAccounts: Seq[AccountManager] = AccountManager.restoreAccounts(accountManagerStore, password) //init keys EncryWallet(walletStorage, restoredAccounts, accountManagerStore) } -} \ No newline at end of file +} From b2a00b059ed1be0d674ca27c0152b3083fb5176f Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Mon, 10 Feb 2020 15:48:12 +0300 Subject: [PATCH 02/36] wallet types were clarified --- .../encry/api/http/DataHolderForApi.scala | 9 +++-- .../encry/api/http/routes/WalletRoute.scala | 15 ++++---- .../WalletVersionalLevelDB.scala | 35 +++++++++++-------- .../scala/encry/utils/BalanceCalculator.scala | 18 +++++----- .../scala/encry/view/wallet/EncryWallet.scala | 35 +++++++++---------- 5 files changed, 58 insertions(+), 54 deletions(-) diff --git a/src/main/scala/encry/api/http/DataHolderForApi.scala b/src/main/scala/encry/api/http/DataHolderForApi.scala index 16ba86cecb..1eb76b76e0 100644 --- a/src/main/scala/encry/api/http/DataHolderForApi.scala +++ b/src/main/scala/encry/api/http/DataHolderForApi.scala @@ -33,6 +33,7 @@ import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.ModifierId +import scorex.crypto.signatures.PublicKey import scala.concurrent.Future @@ -200,11 +201,9 @@ class DataHolderForApi(settings: EncryAppSettings, ntp: NetworkTimeProvider) }).pipeTo(sender) case GetViewGetBalance => - (nvhRef ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Map[(PublicKey25519, TokenId), Amount]] { view => - val balance: Map[(PublicKey25519, TokenId), Amount] = view.vault.getBalances.map { - case ((key, token), amount) => Map((key -> token) -> amount) - }.foldLeft(Map.empty[(PublicKey25519, TokenId), Amount]) { case (el1, el2) => el1 ++ el2 } - if (balance.isEmpty) Map.empty[(PublicKey25519, TokenId), Amount] else balance + (nvhRef ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Map[(PublicKey, TokenId), Amount]] { view => + val balance: Map[(PublicKey, TokenId), Amount] = view.vault.getBalances.toMap + if (balance.isEmpty) Map.empty[(PublicKey, TokenId), Amount] else balance }).pipeTo(sender) case GetViewPrintPrivKeys => diff --git a/src/main/scala/encry/api/http/routes/WalletRoute.scala b/src/main/scala/encry/api/http/routes/WalletRoute.scala index 8a2858354d..e4c9c21848 100644 --- a/src/main/scala/encry/api/http/routes/WalletRoute.scala +++ b/src/main/scala/encry/api/http/routes/WalletRoute.scala @@ -14,6 +14,7 @@ import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.ADKey import scalatags.Text import scalatags.Text.all.{div, span, _} +import scorex.crypto.signatures.PublicKey import scala.concurrent.Future import scala.language.implicitConversions @@ -27,18 +28,18 @@ case class WalletRoute(settings: RESTApiSettings, val EttTokenId: String = Algos.encode(encrySettings.constants.IntrinsicTokenId) - def walletF: Future[Map[(PublicKey25519, TokenId), Amount]] = + def walletF: Future[Map[(PublicKey, TokenId), Amount]] = (dataHolder ? GetViewGetBalance) - .mapTo[Map[(PublicKey25519, TokenId), Amount]] + .mapTo[Map[(PublicKey, TokenId), Amount]] def pubKeysF: Future[List[String]] = (dataHolder ? GetViewPrintPubKeys).mapTo[List[String]] - def info: Future[(Map[(PublicKey25519, TokenId), Amount], List[String])] = for { + def info: Future[(Map[(PublicKey, TokenId), Amount], List[String])] = for { wallet <- walletF pubKeys <- pubKeysF } yield (wallet, pubKeys) - def walletScript(balances: Map[(PublicKey25519, TokenId), Amount]): Text.TypedTag[String] = { + def walletScript(balances: Map[(PublicKey, TokenId), Amount]): Text.TypedTag[String] = { html( scalatags.Text.all.head( meta(charset := "utf-8"), @@ -727,11 +728,11 @@ case class WalletRoute(settings: RESTApiSettings, } yield { val tknStr = mapKeyValue._1._2 match { case tokenId if Algos.encode(tokenId) == EttTokenId => "ETT" - case tokenId => tokenId + case tokenId => Algos.encode(tokenId) } tr( - th(mapKeyValue._1._1.toString()), - th(tknStr.toString), + th(Algos.encode(mapKeyValue._1._1)), + th(tknStr), if (Algos.encode(mapKeyValue._1._2) == EttTokenId ) th(mapKeyValue._2/100000000) else th(mapKeyValue._2) ) }).toList diff --git a/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala b/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala index bce12f4cc6..8badba6c3e 100644 --- a/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala +++ b/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala @@ -1,5 +1,6 @@ package encry.storage.levelDb.versionalLevelDB +import cats.Semigroup import cats.instances.all._ import cats.syntax.semigroup._ import com.google.common.primitives.Longs @@ -7,17 +8,15 @@ import com.typesafe.scalalogging.StrictLogging import encry.settings.LevelDBSettings import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion._ import encry.utils.{BalanceCalculator, ByteStr} -import org.encryfoundation.common.crypto.PublicKey25519 -import org.encryfoundation.common.modifiers.mempool.transaction.PubKeyLockedContract import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.EncryBaseBox import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import org.iq80.leveldb.DB import scorex.crypto.hash.Digest32 -import scorex.crypto.signatures.PublicKey import scala.util.Success @@ -49,17 +48,26 @@ case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends Str def updateWallet(modifierId: ModifierId, newBxs: Seq[EncryBaseBox], spentBxs: Seq[EncryBaseBox], intrinsicTokenId: ADKey): Unit = { val bxsToInsert: Seq[EncryBaseBox] = newBxs.filter(bx => !spentBxs.contains(bx)) - val newBalances: Map[(String, TokenId), Amount] = { - val toRemoveFromBalance: Map[(String, TokenId), Amount] = BalanceCalculator.balanceSheet(spentBxs, intrinsicTokenId) - .map { case ((publickKey, key), value) => (publickKey, key) -> value * -1 } - val toAddToBalance: Map[(String, TokenId), Amount] = BalanceCalculator.balanceSheet(newBxs, intrinsicTokenId) - .map { case ((publickKey, key), value) => (publickKey, key) -> value } - val prevBalance: Map[(String, TokenId), Amount] = getBalances.map { case ((publicKey, id), value) => (Algos.encode(PubKeyLockedContract(publicKey.pubKeyBytes).contract.hash), id) -> value } - (toAddToBalance |+| toRemoveFromBalance |+| prevBalance).map { case ((hash, tokenId), value) => (hash, tokenId) -> value } + val newBalances: Map[(ContractHash, TokenId), Amount] = { + // (String, String) is a (ContractHash, TokenId) + val toRemoveFromBalance: Map[(String, String), Amount] = + BalanceCalculator + .balanceSheet(spentBxs, intrinsicTokenId) + .map { case ((hash, key), value) => (Algos.encode(hash), Algos.encode(key)) -> value * -1 } + val toAddToBalance: Map[(String, String), Amount] = + BalanceCalculator + .balanceSheet(newBxs, intrinsicTokenId) + .map { case ((hash, key), value) => (Algos.encode(hash), Algos.encode(key)) -> value } + val prevBalance: Map[(String, String), Amount] = getBalances.map { + case ((hash, id), value) => (Algos.encode(hash), Algos.encode(id)) -> value + } + (toRemoveFromBalance |+| toAddToBalance |+| prevBalance).map { + case ((hash, tokenId), value) => (Algos.decode(hash).get, Algos.decode(tokenId).get) -> value + } } val newBalanceKeyValue = BALANCE_KEY -> VersionalLevelDbValue @@ newBalances.foldLeft(Array.emptyByteArray) { case (acc, ((hash, tokenId), balance)) => - acc ++ Algos.decode(hash).get ++ tokenId ++ Longs.toByteArray(balance) + acc ++ hash ++ tokenId ++ Longs.toByteArray(balance) } levelDb.insert(LevelDbDiff(LevelDBVersion @@ modifierId.untag(ModifierId), newBalanceKeyValue :: bxsToInsert.map(bx => (VersionalLevelDbKey @@ bx.id.untag(ADKey), @@ -68,12 +76,11 @@ case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends Str ) } - def getBalances: Map[(PublicKey25519, TokenId), Amount] = { + def getBalances: Map[(ContractHash, TokenId), Amount] = levelDb.get(BALANCE_KEY) .map(_.sliding(72, 72) - .map(ch => (PublicKey25519(PublicKey @@ ch.take(32)), ch.slice(32, 64)) -> Longs.fromByteArray(ch.takeRight(8))) + .map(ch => (ch.take(32), ch.slice(32, 64)) -> Longs.fromByteArray(ch.takeRight(8))) .toMap).getOrElse(Map.empty) - } override def close(): Unit = levelDb.close() } diff --git a/src/main/scala/encry/utils/BalanceCalculator.scala b/src/main/scala/encry/utils/BalanceCalculator.scala index ec1b654528..b8d6f8a195 100644 --- a/src/main/scala/encry/utils/BalanceCalculator.scala +++ b/src/main/scala/encry/utils/BalanceCalculator.scala @@ -3,26 +3,26 @@ package encry.utils import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryBaseBox, TokenIssuingBox} -import org.encryfoundation.common.utils.Algos +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash object BalanceCalculator { def balanceSheet(bxs: Traversable[EncryBaseBox], defaultTokenId: TokenId, - excludeTokenIssuance: Boolean = false): Map[(String, TokenId), Amount] = - bxs.foldLeft(Map.empty[(String, TokenId), Amount]) { + excludeTokenIssuance: Boolean = false): Map[(ContractHash, TokenId), Amount] = + bxs.foldLeft(Map.empty[(ByteStr, ByteStr), Amount]) { case (cache, bx: AssetBox) => - val tokenId: TokenId = bx.tokenIdOpt.getOrElse(defaultTokenId) - val contractHash = Algos.encode(bx.proposition.contractHash) + val tokenId: ByteStr = ByteStr(bx.tokenIdOpt.getOrElse(defaultTokenId)) + val contractHash: ByteStr = ByteStr(bx.proposition.contractHash) cache.get(contractHash -> tokenId).map { amount => cache.updated(contractHash -> tokenId, amount + bx.amount) }.getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) case (cache, bx: TokenIssuingBox) if !excludeTokenIssuance => - val contractHash = Algos.encode(bx.proposition.contractHash) - val tokenId: TokenId = bx.tokenId + val contractHash: ByteStr = ByteStr(bx.proposition.contractHash) + val tokenId: ByteStr = ByteStr(bx.tokenId) cache.get(contractHash -> tokenId).map { amount => cache.updated(contractHash -> tokenId, amount + bx.amount) }.getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) case (cache, _) => cache - }.map { case ((hash, id), am) => (hash -> id) -> am } -} + }.map { case ((hash, id), am) => (hash.arr -> id.arr) -> am } +} \ No newline at end of file diff --git a/src/main/scala/encry/view/wallet/EncryWallet.scala b/src/main/scala/encry/view/wallet/EncryWallet.scala index dbc9f283be..5dac5f2c95 100644 --- a/src/main/scala/encry/view/wallet/EncryWallet.scala +++ b/src/main/scala/encry/view/wallet/EncryWallet.scala @@ -15,7 +15,7 @@ import encry.storage.VersionalStorage import encry.storage.VersionalStorage.{StorageKey, StorageValue} import encry.storage.levelDb.versionalLevelDB.{LevelDbFactory, WalletVersionalLevelDB, WalletVersionalLevelDBCompanion} import encry.utils.CoreTaggedTypes.VersionTag -import encry.utils.Mnemonic +import encry.utils.{ByteStr, Mnemonic} import encry.view.state.UtxoStateReader import encry.view.state.avlTree.{InternalNode, LeafNode, Node, ShadowNode} import io.iohk.iodb.{LSMStore, Store} @@ -27,8 +27,9 @@ import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{EncryBaseBox, EncryProposition, MonetaryBox} -import org.encryfoundation.common.utils.{Algos, TaggedTypes} -import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.TaggedTypes.ModifierId +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import org.iq80.leveldb.{DB, Options} import scorex.crypto.signatures.PublicKey @@ -92,23 +93,19 @@ case class EncryWallet(walletStorage: WalletVersionalLevelDB, accountManagers: S def rollback(to: VersionTag): Try[Unit] = Try(walletStorage.rollback(ModifierId @@ to.untag(VersionTag))) - def getBalances: Seq[((PublicKey25519, TokenId), Amount)] = { - val pubKeys = publicKeys - val contractHashToKey: Map[String, String] = contractHashesToKeys(pubKeys) - val positiveBalance: Map[(PublicKey25519, TokenId), Amount] = walletStorage.getBalances.map { - case ((hash, tokenId), amount) => - (hash, tokenId) -> amount + def getBalances: Seq[((PublicKey, TokenId), Amount)] = { + val pubKeys: Set[PublicKey25519] = publicKeys + val contractHashToKey: Map[ByteStr, PublicKey] = pubKeysToContractHashes(pubKeys) + val positiveBalance: Map[(PublicKey, TokenId), Amount] = walletStorage.getBalances.map { + case ((hash, tokenId), amount) => (contractHashToKey(ByteStr(hash)), tokenId) -> amount } - (pubKeys -- positiveBalance.keys.map(_._1)) - .map(l => (l -> settings.constants.IntrinsicTokenId) -> 0L) - .toSeq ++ positiveBalance - }.sortBy(l => !(l._1._1.pubKeyBytes sameElements accountManagers.head.publicAccounts.head.pubKeyBytes)) - - def contractHashesToKeys(pubKeys: Set[PublicKey25519]): Map[String, String] = pubKeys - .map(key => Algos.encode(key.pubKeyBytes) -> key.address.address) - .map { case (key, addr) => - Algos.encode(EncryProposition.addressLocked(addr).contractHash) -> key - }.toMap + (pubKeys.map(k => ByteStr(k.pubKeyBytes)) -- positiveBalance.keys.map(l => ByteStr(l._1))) + .map(l => (PublicKey @@ l.arr) -> settings.constants.IntrinsicTokenId -> 0L).toSeq ++ positiveBalance + }.sortBy(l => !(l._1._1 sameElements accountManagers.head.publicAccounts.head.pubKeyBytes)) + + def pubKeysToContractHashes(pubKeys: Set[PublicKey25519]): Map[ByteStr, PublicKey] = pubKeys + .map(key => ByteStr(EncryProposition.addressLocked(key.address.address).contractHash) -> key.pubKeyBytes) + .toMap private def validateMnemonicKey(mnemonic: String): Either[NonEmptyChain[String], String] = { val words: Array[String] = mnemonic.split(" ") From 8404eea7a46845fefdcadd9a85a5b4288b5bc807 Mon Sep 17 00:00:00 2001 From: Lior Date: Mon, 10 Feb 2020 17:51:51 +0300 Subject: [PATCH 03/36] taking as many boxes as we need --- .../api/http/routes/WalletInfoApiRoute.scala | 258 ++++++++++-------- 1 file changed, 146 insertions(+), 112 deletions(-) diff --git a/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala b/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala index 17e4cc51d5..49154e23b9 100644 --- a/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala +++ b/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala @@ -1,7 +1,7 @@ package encry.api.http.routes -import akka.actor.{ActorRef, ActorRefFactory} -import akka.http.scaladsl.server.{Route, ValidationRejection} +import akka.actor.{ ActorRef, ActorRefFactory } +import akka.http.scaladsl.server.{ Route, ValidationRejection } import akka.pattern._ import cats.syntax.eq._ import cats.kernel.Eq @@ -18,22 +18,23 @@ import encry.view.state.UtxoState import encry.view.wallet.EncryWallet import io.circe.syntax._ import org.encryfoundation.common.crypto.PrivateKey25519 -import org.encryfoundation.common.modifiers.mempool.transaction.{PubKeyLockedContract, Transaction} -import org.encryfoundation.common.modifiers.state.box.{AssetBox, MonetaryBox, TokenIssuingBox} +import org.encryfoundation.common.modifiers.mempool.transaction.{ PubKeyLockedContract, Transaction } +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, MonetaryBox, TokenIssuingBox } import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.ADKey + import scala.concurrent.Future -import scala.util.{Failure, Success, Try} +import scala.util.{ Failure, Success, Try } -case class WalletInfoApiRoute(dataHolder: ActorRef, - settings: RESTApiSettings, - intrinsicTokenId: String)(implicit val context: ActorRefFactory) - extends EncryBaseApiRoute with FailFastCirceSupport with StrictLogging { +case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, intrinsicTokenId: String)( + implicit val context: ActorRefFactory +) extends EncryBaseApiRoute + with FailFastCirceSupport + with StrictLogging { implicit val eqOptBytes = new Eq[Option[Array[Byte]]] { - override def eqv(x: Option[Array[Byte]], y: Option[Array[Byte]]) = { + override def eqv(x: Option[Array[Byte]], y: Option[Array[Byte]]) = x.exists(l => y.exists(_.sameElements(l))) - } } override val route: Route = pathPrefix("wallet") { @@ -42,13 +43,14 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, printAddressR ~ printPubKeysR ~ getBalanceR ~ - WebRoute.authRoute( + WebRoute.authRoute( createKeyR ~ - transferR ~ - transferContractR ~ - createTokenR ~ - dataTransactionR, settings - ) + transferR ~ + transferContractR ~ + createTokenR ~ + dataTransactionR, + settings + ) } private def getWallet: Future[EncryWallet] = @@ -59,7 +61,7 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, def infoR: Route = (path("info") & get) { getWallet.map { w => Map( - "balances" -> w.getBalances.map{ i => + "balances" -> w.getBalances.map { i => if (i._1._2 != intrinsicTokenId) s"TokenID(${i._1._2}) for contractHash ${i._1._1} : ${i._2}" else @@ -72,26 +74,42 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, def printAddressR: Route = (path("addr") & get) { (dataHolder ? GetViewPrintAddress) - .mapTo[String].map(_.asJson).okJson() + .mapTo[String] + .map(_.asJson) + .okJson() } def createTokenR: Route = (path("createToken") & get) { - parameters('fee.as[Int], 'amount.as[Long]) { (fee, amount) => - (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { - wallet => - Try { - val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount - val boxes = wallet.vault.walletStorage - .getAllBoxes().collectFirst { case ab: AssetBox => ab }.toList.take(1) - TransactionFactory.assetIssuingTransactionScratch( - secret, - fee, - System.currentTimeMillis(), - boxes.map(_ -> None), - PubKeyLockedContract(wallet.vault.accountManagers.head.mandatoryAccount.publicImage.pubKeyBytes).contract, - amount) - }.toOption - }).flatMap { + parameters('fee.as[Long], 'amount.as[Long]) { (fee, amount) => + (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { wallet => + Try { + val actualFee: Long = fee * 100000000 + val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount + def loopBoxes(input: List[EncryBaseBox], + acc: List[AssetBox], + totalAmount: Long, + requiredAmount: Long): List[AssetBox] = + input.headOption match { + case Some(value: AssetBox) => + val newAmount = totalAmount + value.amount + if (requiredAmount > newAmount) { + loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) + } else value :: acc + case Some(value) => + loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) + case None => acc + } + val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, List.empty[AssetBox], 0L, actualFee) + TransactionFactory.assetIssuingTransactionScratch( + secret, + actualFee, + System.currentTimeMillis(), + boxes.map(_ -> None), + PubKeyLockedContract(wallet.vault.accountManagers.head.mandatoryAccount.publicImage.pubKeyBytes).contract, + amount + ) + }.toOption + }).flatMap { case Some(tx: Transaction) => EncryApp.system.eventStream.publish(NewTransaction(tx)) Future.unit @@ -99,25 +117,28 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, } complete("Token was created") } - } - + } def dataTransactionR: Route = (path("data") & get) { parameters('fee.as[Int], 'data) { (fee, data) => - (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { - wallet => - Try { - val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount - val boxes = wallet.vault.walletStorage - .getAllBoxes().collectFirst { case ab: AssetBox => ab }.toList.take(1) - TransactionFactory.dataTransactionScratch(secret, - fee, - System.currentTimeMillis(), - boxes.map(_ -> None), - PubKeyLockedContract(wallet.vault.accountManagers.head.mandatoryAccount.publicImage.pubKeyBytes).contract, - data.getBytes) - }.toOption - }).flatMap { + (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { wallet => + Try { + val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount + val boxes = wallet.vault.walletStorage + .getAllBoxes() + .collectFirst { case ab: AssetBox => ab } + .toList + .take(1) + TransactionFactory.dataTransactionScratch( + secret, + fee, + System.currentTimeMillis(), + boxes.map(_ -> None), + PubKeyLockedContract(wallet.vault.accountManagers.head.mandatoryAccount.publicImage.pubKeyBytes).contract, + data.getBytes + ) + }.toOption + }).flatMap { case Some(tx: Transaction) => EncryApp.system.eventStream.publish(NewTransaction(tx)) Future.unit @@ -129,44 +150,48 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, def transferR: Route = (path("transfer") & get) { parameters('addr, 'fee.as[Int], 'amount.as[Long], 'token.?) { (addr, fee, amount, token) => - (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { - wallet => - Try { - val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount - val decodedTokenOpt = token.map(s => Algos.decode(s) match { - case Success(value) => ADKey @@ value - case Failure(e) => throw new RuntimeException(s"Failed to decode tokeId $s. Cause: $e") - }) - val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage - .getAllBoxes() - .collect { - case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab - case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib - }.foldLeft(List.empty[MonetaryBox]) { + (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { wallet => + Try { + val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount + val decodedTokenOpt = token.map( + s => + Algos.decode(s) match { + case Success(value) => ADKey @@ value + case Failure(e) => throw new RuntimeException(s"Failed to decode tokeId $s. Cause: $e") + } + ) + val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage + .getAllBoxes() + .collect { + case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab + case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib + } + .foldLeft(List.empty[MonetaryBox]) { case (seq, box) if decodedTokenOpt.isEmpty && seq.map(_.amount).sum < (amount + fee) => seq :+ box - case (seq, _) if decodedTokenOpt.isEmpty => seq + case (seq, _) if decodedTokenOpt.isEmpty => seq case (seq, box: AssetBox) if box.tokenIdOpt.isEmpty => - if (seq.collect { case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < fee) seq :+ box else seq + if (seq.collect { case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < fee) seq :+ box + else seq case (seq, box: AssetBox) => val totalAmount = seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + - seq.collect { case tib: TokenIssuingBox => tib.amount }.sum + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum if (totalAmount < amount) seq :+ box else seq case (seq, box: TokenIssuingBox) => val totalAmount = seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + - seq.collect { case tib: TokenIssuingBox => tib.amount }.sum + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum if (totalAmount < amount) seq :+ box else seq } - .toIndexedSeq - TransactionFactory.defaultPaymentTransaction(secret, - fee, - System.currentTimeMillis(), - boxes.map(_ -> None), - addr, - amount, - decodedTokenOpt) - }.toOption + .toIndexedSeq + TransactionFactory.defaultPaymentTransaction(secret, + fee, + System.currentTimeMillis(), + boxes.map(_ -> None), + addr, + amount, + decodedTokenOpt) + }.toOption }).flatMap { case Some(tx: Transaction) => EncryApp.system.eventStream.publish(NewTransaction(tx)) @@ -178,47 +203,50 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, } def transferContractR: Route = (path("transferContract") & get) { - parameters('contract, 'fee.as[Int], 'amount.as[Long], 'token.?) { - (contract, fee, amount, token) => - (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { - wallet => - Try { - val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount - val decodedTokenOpt = token.map(s => Algos.decode(s) match { - case Success(value) => ADKey @@ value - case Failure(_) => throw new RuntimeException(s"Failed to decode tokeId $s") - }) - val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage - .getAllBoxes() - .collect { - case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab - case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib - }.foldLeft(List.empty[MonetaryBox]) { + parameters('contract, 'fee.as[Int], 'amount.as[Long], 'token.?) { (contract, fee, amount, token) => + (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { wallet => + Try { + val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount + val decodedTokenOpt = token.map( + s => + Algos.decode(s) match { + case Success(value) => ADKey @@ value + case Failure(_) => throw new RuntimeException(s"Failed to decode tokeId $s") + } + ) + val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage + .getAllBoxes() + .collect { + case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab + case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib + } + .foldLeft(List.empty[MonetaryBox]) { case (seq, box) if decodedTokenOpt.isEmpty => if (seq.map(_.amount).sum < (amount + fee)) seq :+ box else seq case (seq, box: AssetBox) if box.tokenIdOpt.isEmpty => - if (seq.collect{ case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < fee) seq :+ box else seq + if (seq.collect { case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < fee) seq :+ box + else seq case (seq, box: AssetBox) => val totalAmount = seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + - seq.collect{ case tib: TokenIssuingBox => tib.amount }.sum + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum if (totalAmount < amount) seq :+ box else seq case (seq, box: TokenIssuingBox) => val totalAmount = - seq.collect{ case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount}.sum + - seq.collect{ case tib: TokenIssuingBox => tib.amount }.sum + seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum if (totalAmount < amount) seq :+ box else seq } - .toIndexedSeq - TransactionFactory.defaultContractTransaction(secret, - fee, - System.currentTimeMillis(), - boxes.map(_ -> None), - contract, - amount, - decodedTokenOpt) - }.toOption - }).flatMap { + .toIndexedSeq + TransactionFactory.defaultContractTransaction(secret, + fee, + System.currentTimeMillis(), + boxes.map(_ -> None), + contract, + amount, + decodedTokenOpt) + }.toOption + }).flatMap { case Some(tx: Transaction) => EncryApp.system.eventStream.publish(NewTransaction(tx)) Future.unit @@ -230,20 +258,26 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, def createKeyR: Route = (path("createKey") & get) { (dataHolder ? GetViewCreateKey) - .mapTo[PrivateKey25519].map(key => Algos.encode(key.privKeyBytes).asJson).okJson() + .mapTo[PrivateKey25519] + .map(key => Algos.encode(key.privKeyBytes).asJson) + .okJson() } def printPubKeysR: Route = (path("pubKeys") & get) { (dataHolder ? GetViewPrintPubKeys) - .mapTo[List[String]].map(_.asJson).okJson() + .mapTo[List[String]] + .map(_.asJson) + .okJson() } def getBalanceR: Route = (path("balance") & get) { (dataHolder ? GetViewGetBalance) - .mapTo[String].map(_.asJson).okJson() + .mapTo[String] + .map(_.asJson) + .okJson() } def getUtxosR: Route = (path("utxos") & get) { getWallet.map(wallet => wallet.walletStorage.getAllBoxes().asJson).okJson() } -} \ No newline at end of file +} From b6c0aea669000f3dd762ebdc504e1a6bd117199f Mon Sep 17 00:00:00 2001 From: Lior Date: Tue, 11 Feb 2020 11:49:33 +0300 Subject: [PATCH 04/36] code cleanup --- .../api/http/routes/WalletInfoApiRoute.scala | 111 +++++++++--------- .../encry/api/http/routes/WalletRoute.scala | 64 +++++----- .../WalletVersionalLevelDB.scala | 3 +- 3 files changed, 90 insertions(+), 88 deletions(-) diff --git a/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala b/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala index 49154e23b9..fbf6bc8f3a 100644 --- a/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala +++ b/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala @@ -95,7 +95,7 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i if (requiredAmount > newAmount) { loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) } else value :: acc - case Some(value) => + case Some(_) => loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) case None => acc } @@ -124,11 +124,22 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { wallet => Try { val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount - val boxes = wallet.vault.walletStorage - .getAllBoxes() - .collectFirst { case ab: AssetBox => ab } - .toList - .take(1) + val actualFee: Long = fee * 100000000 + def loopBoxes(input: List[EncryBaseBox], + acc: List[AssetBox], + totalAmount: Long, + requiredAmount: Long): List[AssetBox] = + input.headOption match { + case Some(value: AssetBox) => + val newAmount = totalAmount + value.amount + if (requiredAmount > newAmount) { + loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) + } else value :: acc + case Some(_) => + loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) + case None => acc + } + val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, List.empty[AssetBox], 0L, actualFee) TransactionFactory.dataTransactionScratch( secret, fee, @@ -153,6 +164,8 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i (dataHolder ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Option[Transaction]] { wallet => Try { val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount + val actualFee = fee * 100000000 + val actualAmount = amount * 100000000 val decodedTokenOpt = token.map( s => Algos.decode(s) match { @@ -160,36 +173,31 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i case Failure(e) => throw new RuntimeException(s"Failed to decode tokeId $s. Cause: $e") } ) - val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage - .getAllBoxes() - .collect { - case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab - case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib - } - .foldLeft(List.empty[MonetaryBox]) { - case (seq, box) if decodedTokenOpt.isEmpty && seq.map(_.amount).sum < (amount + fee) => seq :+ box - case (seq, _) if decodedTokenOpt.isEmpty => seq - case (seq, box: AssetBox) if box.tokenIdOpt.isEmpty => - if (seq.collect { case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < fee) seq :+ box - else seq - case (seq, box: AssetBox) => - val totalAmount = - seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + - seq.collect { case tib: TokenIssuingBox => tib.amount }.sum - if (totalAmount < amount) seq :+ box else seq - case (seq, box: TokenIssuingBox) => - val totalAmount = - seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + - seq.collect { case tib: TokenIssuingBox => tib.amount }.sum - if (totalAmount < amount) seq :+ box else seq + def loopBoxes(input: List[EncryBaseBox], + acc: List[AssetBox], + totalAmount: Long, + requiredAmount: Long): List[AssetBox] = + input.headOption match { + case Some(value: AssetBox) => + val newAmount = totalAmount + value.amount + if (requiredAmount > newAmount) { + loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) + } else value :: acc + case Some(_) => + loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) + case None => acc } - .toIndexedSeq + + val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, + List.empty[AssetBox], + 0L, + actualAmount + actualFee) TransactionFactory.defaultPaymentTransaction(secret, fee, System.currentTimeMillis(), boxes.map(_ -> None), addr, - amount, + actualAmount, decodedTokenOpt) }.toOption }).flatMap { @@ -214,30 +222,27 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i case Failure(_) => throw new RuntimeException(s"Failed to decode tokeId $s") } ) - val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage - .getAllBoxes() - .collect { - case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab - case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib - } - .foldLeft(List.empty[MonetaryBox]) { - case (seq, box) if decodedTokenOpt.isEmpty => - if (seq.map(_.amount).sum < (amount + fee)) seq :+ box else seq - case (seq, box: AssetBox) if box.tokenIdOpt.isEmpty => - if (seq.collect { case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < fee) seq :+ box - else seq - case (seq, box: AssetBox) => - val totalAmount = - seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + - seq.collect { case tib: TokenIssuingBox => tib.amount }.sum - if (totalAmount < amount) seq :+ box else seq - case (seq, box: TokenIssuingBox) => - val totalAmount = - seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + - seq.collect { case tib: TokenIssuingBox => tib.amount }.sum - if (totalAmount < amount) seq :+ box else seq + def loopBoxes(input: List[EncryBaseBox], + acc: List[AssetBox], + totalAmount: Long, + requiredAmount: Long): List[AssetBox] = + input.headOption match { + case Some(value: AssetBox) => + val newAmount = totalAmount + value.amount + if (requiredAmount > newAmount) { + loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) + } else value :: acc + case Some(_) => + loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) + case None => acc } - .toIndexedSeq + val actualFee = fee * 100000000 + val actualAmount = amount * 100000000 + val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, + List.empty[AssetBox], + 0L, + actualAmount + actualFee) + TransactionFactory.defaultContractTransaction(secret, fee, System.currentTimeMillis(), diff --git a/src/main/scala/encry/api/http/routes/WalletRoute.scala b/src/main/scala/encry/api/http/routes/WalletRoute.scala index e4c9c21848..3ca18463bb 100644 --- a/src/main/scala/encry/api/http/routes/WalletRoute.scala +++ b/src/main/scala/encry/api/http/routes/WalletRoute.scala @@ -7,15 +7,12 @@ import akka.pattern._ import com.typesafe.scalalogging.StrictLogging import encry.api.http.DataHolderForApi.{GetViewGetBalance, GetViewPrintPubKeys} import encry.settings.{EncryAppSettings, RESTApiSettings} -import org.encryfoundation.common.crypto.PublicKey25519 import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.ADKey import scalatags.Text import scalatags.Text.all.{div, span, _} import scorex.crypto.signatures.PublicKey - import scala.concurrent.Future import scala.language.implicitConversions import scala.util.Success @@ -685,17 +682,17 @@ case class WalletRoute(settings: RESTApiSettings, ), ) ), -// div(cls := "form-group", -// select(cls := "form-control", id :="coin", name:="coin", -// if (balances.nonEmpty) { -// balances.values.flatten.toList.map( coinIds => -// option(value := coinIds._1, if (coinIds._1 == EttTokenId) s"ETT (${coinIds._2/100000000})" else coinIds._1) -// ) -// } else { -// option(value := "", "") -// } -// ) -// ), + div(cls := "form-group", + select(cls := "form-control", id :="coin", name:="coin", + if (balances.nonEmpty) { + balances.toList.map { coinIds => + option(value := Algos.encode(coinIds._1._2), if (Algos.encode(coinIds._1._2) == EttTokenId) s"ETT (${coinIds._2 / 100000000})" else Algos.encode(coinIds._1._2)) + } + } else { + option(value := "", "") + } + ) + ), div(cls := "text-center", button(tpe := "button", onclick := "wallet()", cls := "btn btn-primary mt-4", "Send Money") ) @@ -724,7 +721,6 @@ case class WalletRoute(settings: RESTApiSettings, if (balances.nonEmpty) { (for { mapKeyValue <- balances.toList -// tokenAmount <- mapKeyValue._1 } yield { val tknStr = mapKeyValue._1._2 match { case tokenId if Algos.encode(tokenId) == EttTokenId => "ETT" @@ -756,25 +752,25 @@ case class WalletRoute(settings: RESTApiSettings, ) ) ), -// div(cls := "table-responsive", -// // Projects table -// table(cls := "table align-items-center table-flush", -// thead(cls := "thead-light", -// tr( -// th(attr("scope") := "col", "Key") -// ) -// ), -// tbody( -// if(balances.keys.nonEmpty) { -// for (p <- balances.keys.toList) yield { -// tr(th(attr("scope") := "row", p)) -// } -// } else { -// tr() -// } -// ) -// ) -// ) + div(cls := "table-responsive", + // Projects table + table(cls := "table align-items-center table-flush", + thead(cls := "thead-light", + tr( + th(attr("scope") := "col", "Key") + ) + ), + tbody( + if(balances.keys.nonEmpty) { + for (p <- balances.toList.map(x => Algos.encode(x._1._1))) yield { + tr(th(attr("scope") := "row", p)) + } + } else { + tr() + } + ) + ) + ) ) ) ) diff --git a/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala b/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala index 8badba6c3e..ed96feb130 100644 --- a/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala +++ b/src/main/scala/encry/storage/levelDb/versionalLevelDB/WalletVersionalLevelDB.scala @@ -61,7 +61,8 @@ case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends Str val prevBalance: Map[(String, String), Amount] = getBalances.map { case ((hash, id), value) => (Algos.encode(hash), Algos.encode(id)) -> value } - (toRemoveFromBalance |+| toAddToBalance |+| prevBalance).map { + val res = toRemoveFromBalance |+| toAddToBalance |+| prevBalance + res.map { case ((hash, tokenId), value) => (Algos.decode(hash).get, Algos.decode(tokenId).get) -> value } } From e41d901741b4cbc30ccc7fbab78cf154dd1af9b6 Mon Sep 17 00:00:00 2001 From: Lior Date: Tue, 11 Feb 2020 12:51:43 +0300 Subject: [PATCH 05/36] code cleanup --- .../encry/api/http/DataHolderForApi.scala | 6 +- .../api/http/routes/WalletInfoApiRoute.scala | 157 +++++++++--------- .../encry/api/http/routes/WalletRoute.scala | 4 +- .../scala/encry/view/wallet/EncryWallet.scala | 4 +- 4 files changed, 82 insertions(+), 89 deletions(-) diff --git a/src/main/scala/encry/api/http/DataHolderForApi.scala b/src/main/scala/encry/api/http/DataHolderForApi.scala index 1eb76b76e0..3a046f5b1b 100644 --- a/src/main/scala/encry/api/http/DataHolderForApi.scala +++ b/src/main/scala/encry/api/http/DataHolderForApi.scala @@ -201,10 +201,8 @@ class DataHolderForApi(settings: EncryAppSettings, ntp: NetworkTimeProvider) }).pipeTo(sender) case GetViewGetBalance => - (nvhRef ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Map[(PublicKey, TokenId), Amount]] { view => - val balance: Map[(PublicKey, TokenId), Amount] = view.vault.getBalances.toMap - if (balance.isEmpty) Map.empty[(PublicKey, TokenId), Amount] else balance - }).pipeTo(sender) + (nvhRef ? GetDataFromCurrentView[History, UtxoState, EncryWallet, Map[(PublicKey, TokenId), Amount]] + (_.vault.getBalances.toMap)).pipeTo(sender) case GetViewPrintPrivKeys => (nvhRef ? GetDataFromCurrentView[History, UtxoState, EncryWallet, String] { view => diff --git a/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala b/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala index fbf6bc8f3a..17e332fa39 100644 --- a/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala +++ b/src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala @@ -58,11 +58,26 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i GetDataFromCurrentView[History, UtxoState, EncryWallet, EncryWallet](_.vault)) .mapTo[EncryWallet] + private def loopBoxes(input: List[EncryBaseBox], + acc: List[AssetBox], + totalAmount: Long, + requiredAmount: Long): List[AssetBox] = + input.headOption match { + case Some(value: AssetBox) => + val newAmount = totalAmount + value.amount + if (requiredAmount > newAmount) { + loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) + } else value :: acc + case Some(_) => + loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) + case None => acc + } + def infoR: Route = (path("info") & get) { getWallet.map { w => Map( "balances" -> w.getBalances.map { i => - if (i._1._2 != intrinsicTokenId) + if (Algos.encode(i._1._2) != intrinsicTokenId) s"TokenID(${i._1._2}) for contractHash ${i._1._1} : ${i._2}" else s"TokenID(${i._1._2}) for contractHash ${i._1._1} : ${BigDecimal(i._2) / 100000000}" @@ -85,20 +100,6 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i Try { val actualFee: Long = fee * 100000000 val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount - def loopBoxes(input: List[EncryBaseBox], - acc: List[AssetBox], - totalAmount: Long, - requiredAmount: Long): List[AssetBox] = - input.headOption match { - case Some(value: AssetBox) => - val newAmount = totalAmount + value.amount - if (requiredAmount > newAmount) { - loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) - } else value :: acc - case Some(_) => - loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) - case None => acc - } val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, List.empty[AssetBox], 0L, actualFee) TransactionFactory.assetIssuingTransactionScratch( secret, @@ -125,20 +126,6 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i Try { val secret: PrivateKey25519 = wallet.vault.accountManagers.head.mandatoryAccount val actualFee: Long = fee * 100000000 - def loopBoxes(input: List[EncryBaseBox], - acc: List[AssetBox], - totalAmount: Long, - requiredAmount: Long): List[AssetBox] = - input.headOption match { - case Some(value: AssetBox) => - val newAmount = totalAmount + value.amount - if (requiredAmount > newAmount) { - loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) - } else value :: acc - case Some(_) => - loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) - case None => acc - } val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, List.empty[AssetBox], 0L, actualFee) TransactionFactory.dataTransactionScratch( secret, @@ -173,32 +160,36 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i case Failure(e) => throw new RuntimeException(s"Failed to decode tokeId $s. Cause: $e") } ) - def loopBoxes(input: List[EncryBaseBox], - acc: List[AssetBox], - totalAmount: Long, - requiredAmount: Long): List[AssetBox] = - input.headOption match { - case Some(value: AssetBox) => - val newAmount = totalAmount + value.amount - if (requiredAmount > newAmount) { - loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) - } else value :: acc - case Some(_) => - loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) - case None => acc - } - - val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, - List.empty[AssetBox], - 0L, - actualAmount + actualFee) - TransactionFactory.defaultPaymentTransaction(secret, - fee, - System.currentTimeMillis(), - boxes.map(_ -> None), - addr, - actualAmount, - decodedTokenOpt) + val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage + .getAllBoxes() + .collect { + case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab + case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib + }.foldLeft(List.empty[MonetaryBox]) { + case (seq, box) if decodedTokenOpt.isEmpty && seq.map(_.amount).sum < (actualAmount + actualFee) => seq :+ box + case (seq, _) if decodedTokenOpt.isEmpty => seq + case (seq, box: AssetBox) if box.tokenIdOpt.isEmpty => + if (seq.collect { case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < actualFee) seq :+ box else seq + case (seq, box: AssetBox) => + val totalAmount = + seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum + if (totalAmount < actualAmount) seq :+ box else seq + case (seq, box: TokenIssuingBox) => + val totalAmount = + seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum + if (totalAmount < actualAmount) seq :+ box else seq + }.toIndexedSeq + TransactionFactory.defaultPaymentTransaction( + secret, + actualFee, + System.currentTimeMillis(), + boxes.map(_ -> None), + addr, + actualAmount, + decodedTokenOpt + ) }.toOption }).flatMap { case Some(tx: Transaction) => @@ -222,34 +213,38 @@ case class WalletInfoApiRoute(dataHolder: ActorRef, settings: RESTApiSettings, i case Failure(_) => throw new RuntimeException(s"Failed to decode tokeId $s") } ) - def loopBoxes(input: List[EncryBaseBox], - acc: List[AssetBox], - totalAmount: Long, - requiredAmount: Long): List[AssetBox] = - input.headOption match { - case Some(value: AssetBox) => - val newAmount = totalAmount + value.amount - if (requiredAmount > newAmount) { - loopBoxes(input.drop(1), value :: acc, newAmount, requiredAmount) - } else value :: acc - case Some(_) => - loopBoxes(input.drop(1), acc, totalAmount, requiredAmount) - case None => acc - } val actualFee = fee * 100000000 val actualAmount = amount * 100000000 - val boxes = loopBoxes(wallet.vault.walletStorage.getAllBoxes().toList, - List.empty[AssetBox], - 0L, - actualAmount + actualFee) - - TransactionFactory.defaultContractTransaction(secret, - fee, - System.currentTimeMillis(), - boxes.map(_ -> None), - contract, - amount, - decodedTokenOpt) + val boxes: IndexedSeq[MonetaryBox] = wallet.vault.walletStorage + .getAllBoxes() + .collect { + case ab: AssetBox if ab.tokenIdOpt.isEmpty || ab.tokenIdOpt === decodedTokenOpt => ab + case tib: TokenIssuingBox if decodedTokenOpt.exists(_.sameElements(tib.tokenId)) => tib + }.foldLeft(List.empty[MonetaryBox]) { + case (seq, box) if decodedTokenOpt.isEmpty => + if (seq.map(_.amount).sum < (actualAmount + actualFee)) seq :+ box else seq + case (seq, box: AssetBox) if box.tokenIdOpt.isEmpty => + if (seq.collect { case ab: AssetBox if ab.tokenIdOpt.isEmpty => ab.amount }.sum < actualFee) seq :+ box else seq + case (seq, box: AssetBox) => + val totalAmount = + seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum + if (totalAmount < actualAmount) seq :+ box else seq + case (seq, box: TokenIssuingBox) => + val totalAmount = + seq.collect { case ab: AssetBox if ab.tokenIdOpt.nonEmpty => ab.amount }.sum + + seq.collect { case tib: TokenIssuingBox => tib.amount }.sum + if (totalAmount < actualAmount) seq :+ box else seq + }.toIndexedSeq + TransactionFactory.defaultContractTransaction( + secret, + actualFee, + System.currentTimeMillis(), + boxes.map(_ -> None), + contract, + actualAmount, + decodedTokenOpt + ) }.toOption }).flatMap { case Some(tx: Transaction) => diff --git a/src/main/scala/encry/api/http/routes/WalletRoute.scala b/src/main/scala/encry/api/http/routes/WalletRoute.scala index 3ca18463bb..c397d25989 100644 --- a/src/main/scala/encry/api/http/routes/WalletRoute.scala +++ b/src/main/scala/encry/api/http/routes/WalletRoute.scala @@ -426,9 +426,9 @@ case class WalletRoute(settings: RESTApiSettings, div(cls := "form-group", select(cls := "form-control", id :="coin", name:="coin", for { - coinI <- balances.toList + walletMap <- balances.toList } yield { - option(value := coinI._1._2.toString, if (Algos.encode(coinI._1._2) == EttTokenId) s"ETT (${coinI._2/100000000})" else "something else") + option(value := walletMap._1._2.toString, if (Algos.encode(walletMap._1._2) == EttTokenId) s"ETT (${walletMap._2/100000000})" else Algos.encode(walletMap._1._2)) } ) ), diff --git a/src/main/scala/encry/view/wallet/EncryWallet.scala b/src/main/scala/encry/view/wallet/EncryWallet.scala index 5dac5f2c95..9f66f6fac4 100644 --- a/src/main/scala/encry/view/wallet/EncryWallet.scala +++ b/src/main/scala/encry/view/wallet/EncryWallet.scala @@ -93,7 +93,7 @@ case class EncryWallet(walletStorage: WalletVersionalLevelDB, accountManagers: S def rollback(to: VersionTag): Try[Unit] = Try(walletStorage.rollback(ModifierId @@ to.untag(VersionTag))) - def getBalances: Seq[((PublicKey, TokenId), Amount)] = { + def getBalances: List[((PublicKey, TokenId), Amount)] = { val pubKeys: Set[PublicKey25519] = publicKeys val contractHashToKey: Map[ByteStr, PublicKey] = pubKeysToContractHashes(pubKeys) val positiveBalance: Map[(PublicKey, TokenId), Amount] = walletStorage.getBalances.map { @@ -101,7 +101,7 @@ case class EncryWallet(walletStorage: WalletVersionalLevelDB, accountManagers: S } (pubKeys.map(k => ByteStr(k.pubKeyBytes)) -- positiveBalance.keys.map(l => ByteStr(l._1))) .map(l => (PublicKey @@ l.arr) -> settings.constants.IntrinsicTokenId -> 0L).toSeq ++ positiveBalance - }.sortBy(l => !(l._1._1 sameElements accountManagers.head.publicAccounts.head.pubKeyBytes)) + }.sortBy(l => !(l._1._1 sameElements accountManagers.head.publicAccounts.head.pubKeyBytes)).toList def pubKeysToContractHashes(pubKeys: Set[PublicKey25519]): Map[ByteStr, PublicKey] = pubKeys .map(key => ByteStr(EncryProposition.addressLocked(key.address.address).contractHash) -> key.pubKeyBytes) From 9b28641bc65edb2533e28aae9f0fae64a9e1f335 Mon Sep 17 00:00:00 2001 From: Lior Date: Tue, 11 Feb 2020 15:47:42 +0300 Subject: [PATCH 06/36] test changed --- src/test/scala/encry/view/wallet/WalletSpec.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/scala/encry/view/wallet/WalletSpec.scala b/src/test/scala/encry/view/wallet/WalletSpec.scala index 97a0ea31d0..e968ea612f 100644 --- a/src/test/scala/encry/view/wallet/WalletSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletSpec.scala @@ -164,12 +164,12 @@ class WalletSpec extends PropSpec with Matchers with InstanceFactory with EncryG wallet.scanPersistent(block) - val addr1 = Algos.encode(keyManagerOne.mandatoryAccount.publicKeyBytes) - val addr2 = Algos.encode(keyManagerTwo.mandatoryAccount.publicKeyBytes) - val addr3 = Algos.encode(extraAcc.publicKeyBytes) + val addr1: String = Algos.encode(keyManagerOne.mandatoryAccount.publicKeyBytes) + val addr2: String = Algos.encode(keyManagerTwo.mandatoryAccount.publicKeyBytes) + val addr3: String = Algos.encode(extraAcc.publicKeyBytes) - wallet.getBalances.filter(_._1._1 == addr1).map(_._2).sum shouldEqual txsQty * Props.boxValue - wallet.getBalances.filter(_._1._1 == addr2).map(_._2).sum shouldEqual (txsQty - 1) * Props.boxValue - wallet.getBalances.filter(_._1._1 == addr3).map(_._2).sum shouldEqual (txsQty - 2) * Props.boxValue + wallet.getBalances.filter(x => Algos.encode(x._1._1) == addr1).map(_._2).sum shouldEqual txsQty * Props.boxValue + wallet.getBalances.filter(x => Algos.encode(x._1._1) == addr2).map(_._2).sum shouldEqual (txsQty - 1) * Props.boxValue + wallet.getBalances.filter(x => Algos.encode(x._1._1) == addr3).map(_._2).sum shouldEqual (txsQty - 2) * Props.boxValue } } \ No newline at end of file From 9bd38ef626719bcd1be16f60b7fb718e60401011 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Wed, 12 Feb 2020 14:35:11 +0300 Subject: [PATCH 07/36] added frame for new wallet db impl --- .../scala/encry/view/wallet/WalletDB.scala | 37 +++++++++ .../encry/view/wallet/WalletDBImpl.scala | 79 +++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 src/main/scala/encry/view/wallet/WalletDB.scala create mode 100644 src/main/scala/encry/view/wallet/WalletDBImpl.scala diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala new file mode 100644 index 0000000000..6b20b6f345 --- /dev/null +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -0,0 +1,37 @@ +package encry.view.wallet + +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.VersionalLevelDbKey +import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash + +trait WalletDB { + + def getAllWallets: List[ContractHash] + + def getBoxById(id: ADKey): Option[EncryBaseBox] + + def getAssetBoxesByPredicate(contractHash: ContractHash, f: List[AssetBox] => Boolean): List[AssetBox] + + def getTokenIssuingBoxes(contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean): List[TokenIssuingBox] + + def getDataBoxes(contractHash: ContractHash, f: List[DataBox] => Boolean): List[DataBox] + + def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] + + def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount + + def contains(id: ADKey): Boolean + + def updateWallet( + modifierId: ModifierId, + newBxs: Seq[EncryBaseBox], + spentBxs: Seq[EncryBaseBox], + intrinsicTokenId: ADKey + ): Unit + + def rollback(modId: ModifierId): Unit +} diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala new file mode 100644 index 0000000000..8031318db8 --- /dev/null +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -0,0 +1,79 @@ +package encry.view.wallet + +import com.typesafe.scalalogging.StrictLogging +import encry.settings.EncryAppSettings +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDB +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.VersionalLevelDbKey +import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId +import org.encryfoundation.common.modifiers.state.box.{AssetBox, DataBox, EncryBaseBox, TokenIssuingBox} +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash + +class WalletDBImpl private ( + levelDb: VersionalLevelDB, + settings: EncryAppSettings +) extends WalletDB + with StrictLogging with AutoCloseable { + + override def getAllWallets: List[ContractHash] = ??? + + override def getBoxById(id: ADKey): Option[EncryBaseBox] = ??? + + override def getAssetBoxesByPredicate( + contractHash: ContractHash, + f: List[AssetBox] => Boolean + ): List[AssetBox] = ??? + + override def getTokenIssuingBoxes( + contractHash: ContractHash, + f: List[TokenIssuingBox] => Boolean + ): List[TokenIssuingBox] = ??? + + override def getDataBoxes( + contractHash: ContractHash, + f: List[DataBox] => Boolean + ): List[DataBox] = ??? + + override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = ??? + + override def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = ??? + + override def contains(id: ADKey): Boolean = ??? + + override def updateWallet( + modifierId: ModifierId, + newBxs: Seq[EncryBaseBox], + spentBxs: Seq[EncryBaseBox], + intrinsicTokenId: ADKey + ): Unit = ??? + + override def rollback(modId: ModifierId): Unit = ??? + + override def close(): Unit = ??? + + private val ASSET_BOXES_BYTES: Array[Byte] = "ASSET_BOXES_BY_USER_KEY".getBytes() + + private val TOKEN_ISSUING_BOXES_BYTES: Array[Byte] = "TOKEN_ISSUING_BOXES_BYTES".getBytes() + + private val DATA_BOXES_BYTES: Array[Byte] = "DATA_BOXES_BYTES".getBytes() + + private val CONTRACT_HASH_ACCOUNTS: VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash("CONTRACT_HASH_ACCOUNTS") + + private def assetBoxesByContractHashKey(userHash: ContractHash): VersionalLevelDbKey = + VersionalLevelDbKey @@ Algos.hash(userHash ++ ASSET_BOXES_BYTES) + + private def tokenIssuingBoxesByContractHashKey(userHash: ContractHash): VersionalLevelDbKey = + VersionalLevelDbKey @@ Algos.hash(userHash ++ TOKEN_ISSUING_BOXES_BYTES) + + private def dataBoxesByContractHashKey(userHash: ContractHash): VersionalLevelDbKey = + VersionalLevelDbKey @@ Algos.hash(userHash ++ DATA_BOXES_BYTES) + + private def tokenKeyByContractHash(userHash: ContractHash, tokenId: TokenId): VersionalLevelDbKey = + VersionalLevelDbKey @@ Algos.hash(userHash ++ tokenId) +} + +object WalletDBImpl { + +} \ No newline at end of file From b9406c51e2bf73bacac245377ba0816a3448c676 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Mon, 17 Feb 2020 14:10:24 +0300 Subject: [PATCH 08/36] first impl of updateWallet function --- .../scala/encry/utils/BalanceCalculator.scala | 26 +++ .../scala/encry/view/wallet/WalletDB.scala | 4 +- .../encry/view/wallet/WalletDBImpl.scala | 183 ++++++++++++++++-- 3 files changed, 200 insertions(+), 13 deletions(-) diff --git a/src/main/scala/encry/utils/BalanceCalculator.scala b/src/main/scala/encry/utils/BalanceCalculator.scala index b8d6f8a195..82a17d0e82 100644 --- a/src/main/scala/encry/utils/BalanceCalculator.scala +++ b/src/main/scala/encry/utils/BalanceCalculator.scala @@ -1,8 +1,12 @@ package encry.utils +import cats.syntax.semigroup._ +import cats.instances.long._ +import cats.instances.map._ import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryBaseBox, TokenIssuingBox} +import org.encryfoundation.common.utils.Algos import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash object BalanceCalculator { @@ -25,4 +29,26 @@ object BalanceCalculator { }.getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) case (cache, _) => cache }.map { case ((hash, id), am) => (hash.arr -> id.arr) -> am } + + def balanceSheet1( + boxes: List[EncryBaseBox], + defaultTokenId: TokenId, + excludeTokenIssuing: Boolean = false + ): Map[ContractHash, Map[TokenId, Amount]] = { + boxes.foldLeft(Map.empty[String, Map[String, Amount]]) { + case (hashToValue, box: AssetBox) => + val tokenId = Algos.encode(box.tokenIdOpt.getOrElse(defaultTokenId)) + val toAdd = Map(Algos.encode(box.proposition.contractHash) -> Map(tokenId -> box.amount)) + hashToValue |+| toAdd + case (hashToValue, box: TokenIssuingBox) => + val tokenId = Algos.encode(box.tokenId) + val toAdd = Map(Algos.encode(box.proposition.contractHash) -> Map(tokenId -> box.amount)) + hashToValue |+| toAdd + case (hashToValue, _) => hashToValue + }.map { + case (str, stringToAmount) => Algos.decode(str).get -> stringToAmount.map { + case (str, amount) => Algos.decode(str).get -> amount + } + } + } } \ No newline at end of file diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index 6b20b6f345..9c1693047a 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -28,8 +28,8 @@ trait WalletDB { def updateWallet( modifierId: ModifierId, - newBxs: Seq[EncryBaseBox], - spentBxs: Seq[EncryBaseBox], + newBxs: List[EncryBaseBox], + spentBxs: List[EncryBaseBox], intrinsicTokenId: ADKey ): Unit diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 8031318db8..914183f612 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -1,26 +1,56 @@ package encry.view.wallet +import cats.syntax.semigroup._ +import cats.instances.long._ +import cats.instances.map._ +import cats.instances.set._ +import cats.instances.list._ +import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDB -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.VersionalLevelDbKey +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ + LevelDBVersion, + VersionalLevelDbKey, + VersionalLevelDbValue +} +import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{AssetBox, DataBox, EncryBaseBox, TokenIssuingBox} +import org.encryfoundation.common.modifiers.state.box.{ + AssetBox, + DataBox, + EncryBaseBox, + EncryBox, + MonetaryBox, + TokenIssuingBox +} import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} +import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import supertagged.@@ + +import scala.collection.immutable class WalletDBImpl private ( levelDb: VersionalLevelDB, settings: EncryAppSettings ) extends WalletDB - with StrictLogging with AutoCloseable { + with StrictLogging + with AutoCloseable { override def getAllWallets: List[ContractHash] = ??? + def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? + override def getBoxById(id: ADKey): Option[EncryBaseBox] = ??? + def getTokenIds(hash: ContractHash): List[TokenId] = ??? + + def assetKeysByHash(hash: ContractHash): List[ADKey] = ??? + def tokenKeysByHash(hash: ContractHash): List[ADKey] = ??? + def dataKeysByHash(hash: ContractHash): List[ADKey] = ??? + override def getAssetBoxesByPredicate( contractHash: ContractHash, f: List[AssetBox] => Boolean @@ -44,10 +74,138 @@ class WalletDBImpl private ( override def updateWallet( modifierId: ModifierId, - newBxs: Seq[EncryBaseBox], - spentBxs: Seq[EncryBaseBox], + newBxs: List[EncryBaseBox], + spentBxs: List[EncryBaseBox], intrinsicTokenId: ADKey - ): Unit = ??? + ): Unit = { + val boxesToInsert = newBxs.filterNot(spentBxs.contains) + val newBalances: Map[ContractHash, Map[TokenId, Amount]] = { + val toAdd = BalanceCalculator.balanceSheet1(newBxs, intrinsicTokenId).map { + case (hash, idToAmount) => + Algos.encode(hash) -> idToAmount.map { + case (id, amount) => Algos.encode(id) -> amount + } + } + val toRemove = BalanceCalculator.balanceSheet1(spentBxs, intrinsicTokenId).map { + case (hash, idToAmount) => + Algos.encode(hash) -> idToAmount.map { + case (id, amount) => Algos.encode(id) -> amount + } + } + val current = getBalances.map { + case (hash, idToAmount) => + Algos.encode(hash) -> idToAmount.map { + case (id, amount) => Algos.encode(id) -> amount + } + } + (toAdd |+| toRemove |+| current).map { + case (str, stringToAmount) => + Algos.decode(str).get -> stringToAmount.map { + case (str, amount) => Algos.decode(str).get -> amount + } + } + } + val accs: List[ContractHash] = getAllWallets + + val a: (List[AssetBox], List[DataBox], List[TokenIssuingBox]) = + boxesToInsert.foldLeft(List.empty[AssetBox], List.empty[DataBox], List.empty[TokenIssuingBox]) { + case ((asset, data, token), nextBox: AssetBox) => + (nextBox :: asset, data, token) + case ((asset, data, token), nextBox: DataBox) => + (asset, nextBox :: data, token) + case ((asset, data, token), nextBox: TokenIssuingBox) => + (asset, data, nextBox :: token) + case ((asset, data, token), _) => + (asset, data, token) + } + + val assetByAcc: Map[String, List[AssetBox]] = a._1.foldLeft(Map.empty[String, List[AssetBox]]) { + case (hashToBoxes, nextBox) => + val hash = Algos.encode(nextBox.proposition.contractHash) + hashToBoxes |+| Map(hash -> List(nextBox)) + } + val dataByAcc: Map[String, List[DataBox]] = a._2.foldLeft(Map.empty[String, List[DataBox]]) { + case (hashToBoxes, nextBox) => + val hash = Algos.encode(nextBox.proposition.contractHash) + hashToBoxes |+| Map(hash -> List(nextBox)) + } + val tokenByAcc: Map[String, List[TokenIssuingBox]] = a._3.foldLeft(Map.empty[String, List[TokenIssuingBox]]) { + case (hashToBoxes, nextBox) => + val hash = Algos.encode(nextBox.proposition.contractHash) + hashToBoxes |+| Map(hash -> List(nextBox)) + } + + val assetFromDbCleaned: Map[String, Set[String]] = accs.map { hash => + Algos.encode(hash) -> assetKeysByHash(hash) + .filterNot(l => spentBxs.exists(_.id sameElements (l))) + .map(Algos.encode) + .toSet + }.toMap + val tokenFromDbCleaned: Map[String, Set[String]] = accs.map { hash => + Algos.encode(hash) -> tokenKeysByHash(hash) + .filterNot(l => spentBxs.exists(_.id sameElements (l))) + .map(Algos.encode) + .toSet + }.toMap + val dataFromDbCleaned: Map[String, Set[String]] = accs.map { hash => + Algos.encode(hash) -> dataKeysByHash(hash) + .filterNot(l => spentBxs.exists(_.id sameElements (l))) + .map(Algos.encode) + .toSet + }.toMap + + val newAsset: Map[String, Set[String]] = assetFromDbCleaned |+| assetByAcc.map { + case (str, boxes) => str -> boxes.map(k => Algos.encode(k.id)).toSet + } + val newToken: Map[String, Set[String]] = tokenFromDbCleaned |+| tokenByAcc.map { + case (str, boxes) => str -> boxes.map(k => Algos.encode(k.id)).toSet + } + val newData: Map[String, Set[String]] = dataFromDbCleaned |+| dataByAcc.map { + case (str, boxes) => str -> boxes.map(k => Algos.encode(k.id)).toSet + } + + val newAssetKeys: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newAsset.map { + case (hash, value) => + assetBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + .flatMap(k => Algos.decode(k).get) + .toArray + }.toList + val newDataKeys: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newData.map { + case (hash, value) => + dataBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + .flatMap(k => Algos.decode(k).get) + .toArray + }.toList + val newTokenKeys: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newToken.map { + case (hash, value) => + tokenIssuingBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + .flatMap(k => Algos.decode(k).get) + .toArray + }.toList + + val balancesToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newBalances.flatMap { + case (hash, idToAmount) => + val keys = idToAmount.keys.map(Algos.encode).toSet + val curK: Set[String] = getTokenIds(hash).map(Algos.encode).toSet + val newKeys: Set[String] = (keys ++ curK) + val upd: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = idToAmount.map { + case (id, amount) => + tokenKeyByContractHash(hash, id) -> VersionalLevelDbValue @@ Longs.toByteArray(amount) + }.toList + val newKeysupd = hashToTokens(hash) -> VersionalLevelDbValue @@ newKeys + .flatMap(j => Algos.decode(j).get) + .toArray + newKeysupd :: upd + }.toList + val toIns = LevelDbDiff( + LevelDBVersion @@ modifierId.untag(ModifierId), + balancesToInsert ::: newTokenKeys ::: newDataKeys ::: newAssetKeys ::: boxesToInsert.map { box => + (VersionalLevelDbKey @@ box.id.untag(ADKey)) -> VersionalLevelDbValue @@ box.bytes + }, + spentBxs.map(l => VersionalLevelDbKey @@ l.id.untag(ADKey)) + ) + levelDb.insert(toIns) + } override def rollback(modId: ModifierId): Unit = ??? @@ -59,8 +217,13 @@ class WalletDBImpl private ( private val DATA_BOXES_BYTES: Array[Byte] = "DATA_BOXES_BYTES".getBytes() + private val CONTRACT_HASH_TOKEN_IDS: Array[Byte] = "CONTRACT_HASH_TOKEN_IDS".getBytes() + private val CONTRACT_HASH_ACCOUNTS: VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash("CONTRACT_HASH_ACCOUNTS") + def hashToTokens(userHash: ContractHash): VersionalLevelDbKey = + VersionalLevelDbKey @@ Algos.hash(userHash ++ CONTRACT_HASH_TOKEN_IDS) + private def assetBoxesByContractHashKey(userHash: ContractHash): VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash(userHash ++ ASSET_BOXES_BYTES) @@ -74,6 +237,4 @@ class WalletDBImpl private ( VersionalLevelDbKey @@ Algos.hash(userHash ++ tokenId) } -object WalletDBImpl { - -} \ No newline at end of file +object WalletDBImpl {} From 1f43cea15856a0c0a4b2655c94badcfbada8491c Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Mon, 17 Feb 2020 16:09:39 +0300 Subject: [PATCH 09/36] balanceSheet reworked --- .../scala/encry/utils/BalanceCalculator.scala | 80 +++++++++++-------- .../encry/view/wallet/WalletDBImpl.scala | 18 +---- 2 files changed, 50 insertions(+), 48 deletions(-) diff --git a/src/main/scala/encry/utils/BalanceCalculator.scala b/src/main/scala/encry/utils/BalanceCalculator.scala index 82a17d0e82..3941fcb7c9 100644 --- a/src/main/scala/encry/utils/BalanceCalculator.scala +++ b/src/main/scala/encry/utils/BalanceCalculator.scala @@ -1,11 +1,11 @@ package encry.utils -import cats.syntax.semigroup._ import cats.instances.long._ import cats.instances.map._ +import cats.syntax.semigroup._ import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryBaseBox, TokenIssuingBox} +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, TokenIssuingBox } import org.encryfoundation.common.utils.Algos import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash @@ -14,41 +14,53 @@ object BalanceCalculator { def balanceSheet(bxs: Traversable[EncryBaseBox], defaultTokenId: TokenId, excludeTokenIssuance: Boolean = false): Map[(ContractHash, TokenId), Amount] = - bxs.foldLeft(Map.empty[(ByteStr, ByteStr), Amount]) { - case (cache, bx: AssetBox) => - val tokenId: ByteStr = ByteStr(bx.tokenIdOpt.getOrElse(defaultTokenId)) - val contractHash: ByteStr = ByteStr(bx.proposition.contractHash) - cache.get(contractHash -> tokenId).map { amount => - cache.updated(contractHash -> tokenId, amount + bx.amount) - }.getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) - case (cache, bx: TokenIssuingBox) if !excludeTokenIssuance => - val contractHash: ByteStr = ByteStr(bx.proposition.contractHash) - val tokenId: ByteStr = ByteStr(bx.tokenId) - cache.get(contractHash -> tokenId).map { amount => - cache.updated(contractHash -> tokenId, amount + bx.amount) - }.getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) - case (cache, _) => cache - }.map { case ((hash, id), am) => (hash.arr -> id.arr) -> am } + bxs + .foldLeft(Map.empty[(ByteStr, ByteStr), Amount]) { + case (cache, bx: AssetBox) => + val tokenId: ByteStr = ByteStr(bx.tokenIdOpt.getOrElse(defaultTokenId)) + val contractHash: ByteStr = ByteStr(bx.proposition.contractHash) + cache + .get(contractHash -> tokenId) + .map { amount => + cache.updated(contractHash -> tokenId, amount + bx.amount) + } + .getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) + case (cache, bx: TokenIssuingBox) if !excludeTokenIssuance => + val contractHash: ByteStr = ByteStr(bx.proposition.contractHash) + val tokenId: ByteStr = ByteStr(bx.tokenId) + cache + .get(contractHash -> tokenId) + .map { amount => + cache.updated(contractHash -> tokenId, amount + bx.amount) + } + .getOrElse(cache.updated(contractHash -> tokenId, bx.amount)) + case (cache, _) => cache + } + .map { case ((hash, id), am) => (hash.arr -> id.arr) -> am } def balanceSheet1( boxes: List[EncryBaseBox], defaultTokenId: TokenId, excludeTokenIssuing: Boolean = false - ): Map[ContractHash, Map[TokenId, Amount]] = { - boxes.foldLeft(Map.empty[String, Map[String, Amount]]) { - case (hashToValue, box: AssetBox) => - val tokenId = Algos.encode(box.tokenIdOpt.getOrElse(defaultTokenId)) - val toAdd = Map(Algos.encode(box.proposition.contractHash) -> Map(tokenId -> box.amount)) - hashToValue |+| toAdd - case (hashToValue, box: TokenIssuingBox) => - val tokenId = Algos.encode(box.tokenId) - val toAdd = Map(Algos.encode(box.proposition.contractHash) -> Map(tokenId -> box.amount)) - hashToValue |+| toAdd - case (hashToValue, _) => hashToValue - }.map { - case (str, stringToAmount) => Algos.decode(str).get -> stringToAmount.map { - case (str, amount) => Algos.decode(str).get -> amount + ): Map[ContractHash, Map[TokenId, Amount]] = + boxes + .foldLeft(Map.empty[String, Map[String, Amount]]) { + case (hashToValue: Map[String, Map[String, Amount]], box: AssetBox) => + hashToValue |+| Map( + Algos.encode(box.proposition.contractHash) -> Map( + Algos.encode(box.tokenIdOpt.getOrElse(defaultTokenId)) -> box.amount + ) + ) + case (hashToValue: Map[String, Map[String, Amount]], box: TokenIssuingBox) => + hashToValue |+| Map( + Algos.encode(box.proposition.contractHash) -> Map(Algos.encode(box.tokenId) -> box.amount) + ) + case (hashToValue: Map[String, Map[String, Amount]], _) => hashToValue + } + .map { + case (str: String, stringToAmount: Map[String, Amount]) => + Algos.decode(str).get -> stringToAmount.map { + case (str: String, amount: Amount) => Algos.decode(str).get -> amount + } } - } - } -} \ No newline at end of file +} diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 914183f612..18cc4672cc 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -1,36 +1,26 @@ package encry.view.wallet -import cats.syntax.semigroup._ +import cats.instances.list._ import cats.instances.long._ import cats.instances.map._ import cats.instances.set._ -import cats.instances.list._ +import cats.syntax.semigroup._ import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue } +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ - AssetBox, - DataBox, - EncryBaseBox, - EncryBox, - MonetaryBox, - TokenIssuingBox -} +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import supertagged.@@ - -import scala.collection.immutable class WalletDBImpl private ( levelDb: VersionalLevelDB, From c975c6c2aa72232b731c7ecc07c042ea560071ec Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Mon, 17 Feb 2020 16:36:24 +0300 Subject: [PATCH 10/36] balancesToInsert improved + cleaned --- .../encry/view/wallet/WalletDBImpl.scala | 79 ++++++++++--------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 18cc4672cc..d7e38ff200 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -69,32 +69,47 @@ class WalletDBImpl private ( intrinsicTokenId: ADKey ): Unit = { val boxesToInsert = newBxs.filterNot(spentBxs.contains) - val newBalances: Map[ContractHash, Map[TokenId, Amount]] = { - val toAdd = BalanceCalculator.balanceSheet1(newBxs, intrinsicTokenId).map { - case (hash, idToAmount) => - Algos.encode(hash) -> idToAmount.map { - case (id, amount) => Algos.encode(id) -> amount - } - } - val toRemove = BalanceCalculator.balanceSheet1(spentBxs, intrinsicTokenId).map { - case (hash, idToAmount) => - Algos.encode(hash) -> idToAmount.map { - case (id, amount) => Algos.encode(id) -> amount - } - } - val current = getBalances.map { - case (hash, idToAmount) => - Algos.encode(hash) -> idToAmount.map { - case (id, amount) => Algos.encode(id) -> amount - } - } - (toAdd |+| toRemove |+| current).map { - case (str, stringToAmount) => - Algos.decode(str).get -> stringToAmount.map { - case (str, amount) => Algos.decode(str).get -> amount - } - } + + val balancesToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { + val toAddBalances: Map[String, Map[String, Amount]] = + BalanceCalculator.balanceSheet1(newBxs, intrinsicTokenId).map { + case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => + Algos.encode(hash) -> idToAmount.map { + case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount + } + } + val toRemoveBalances: Map[String, Map[String, Amount]] = + BalanceCalculator.balanceSheet1(spentBxs, intrinsicTokenId).map { + case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => + Algos.encode(hash) -> idToAmount.map { + case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount + } + } + val currentBalances: Map[String, Map[String, Amount]] = + getBalances.map { + case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => + Algos.encode(hash) -> idToAmount.map { + case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount + } + } + (toAddBalances |+| toRemoveBalances |+| currentBalances).flatMap { + case (hash: String, idToAmount: Map[String, Amount]) => + val decodedHash: Array[Byte] = Algos.decode(hash).get + val newTokenIds: Set[String] = idToAmount.keys.toSet + val currentTokenIds: Set[String] = getTokenIds(decodedHash).map(Algos.encode).toSet + val tokenIdsToUpdate: (VersionalLevelDbKey, VersionalLevelDbValue) = hashToTokens(decodedHash) -> + VersionalLevelDbValue @@ (newTokenIds ++ currentTokenIds).flatMap { id => + Algos.decode(id).get + }.toArray + val tokenIdUpdatedAmount: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = idToAmount.map { + case (id: String, amount: Amount) => + tokenKeyByContractHash(decodedHash, Algos.decode(id).get) -> + VersionalLevelDbValue @@ Longs.toByteArray(amount) + }.toList + tokenIdsToUpdate :: tokenIdUpdatedAmount + }.toList } + val accs: List[ContractHash] = getAllWallets val a: (List[AssetBox], List[DataBox], List[TokenIssuingBox]) = @@ -173,20 +188,6 @@ class WalletDBImpl private ( .toArray }.toList - val balancesToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newBalances.flatMap { - case (hash, idToAmount) => - val keys = idToAmount.keys.map(Algos.encode).toSet - val curK: Set[String] = getTokenIds(hash).map(Algos.encode).toSet - val newKeys: Set[String] = (keys ++ curK) - val upd: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = idToAmount.map { - case (id, amount) => - tokenKeyByContractHash(hash, id) -> VersionalLevelDbValue @@ Longs.toByteArray(amount) - }.toList - val newKeysupd = hashToTokens(hash) -> VersionalLevelDbValue @@ newKeys - .flatMap(j => Algos.decode(j).get) - .toArray - newKeysupd :: upd - }.toList val toIns = LevelDbDiff( LevelDBVersion @@ modifierId.untag(ModifierId), balancesToInsert ::: newTokenKeys ::: newDataKeys ::: newAssetKeys ::: boxesToInsert.map { box => From 3777faaace970d7c7c2c70785571230cf0d6ffe9 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Mon, 17 Feb 2020 17:31:52 +0300 Subject: [PATCH 11/36] updateWallet improved + cleaned --- .../encry/view/wallet/WalletDBImpl.scala | 184 +++++++++--------- 1 file changed, 95 insertions(+), 89 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index d7e38ff200..5d4e80388d 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -13,7 +13,7 @@ import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ VersionalLevelDbKey, VersionalLevelDbValue } -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId @@ -21,6 +21,7 @@ import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, Encry import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import supertagged.@@ class WalletDBImpl private ( levelDb: VersionalLevelDB, @@ -68,7 +69,7 @@ class WalletDBImpl private ( spentBxs: List[EncryBaseBox], intrinsicTokenId: ADKey ): Unit = { - val boxesToInsert = newBxs.filterNot(spentBxs.contains) + val boxesToInsert: List[EncryBaseBox] = newBxs.filterNot(spentBxs.contains) val balancesToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { val toAddBalances: Map[String, Map[String, Amount]] = @@ -94,108 +95,113 @@ class WalletDBImpl private ( } (toAddBalances |+| toRemoveBalances |+| currentBalances).flatMap { case (hash: String, idToAmount: Map[String, Amount]) => - val decodedHash: Array[Byte] = Algos.decode(hash).get - val newTokenIds: Set[String] = idToAmount.keys.toSet - val currentTokenIds: Set[String] = getTokenIds(decodedHash).map(Algos.encode).toSet - val tokenIdsToUpdate: (VersionalLevelDbKey, VersionalLevelDbValue) = hashToTokens(decodedHash) -> - VersionalLevelDbValue @@ (newTokenIds ++ currentTokenIds).flatMap { id => - Algos.decode(id).get - }.toArray + val decodedHash: Array[Byte] = Algos.decode(hash).get + val tokenIdsToUpdate: (VersionalLevelDbKey, VersionalLevelDbValue) = + hashToTokens(decodedHash) -> VersionalLevelDbValue @@ (idToAmount.keys.toSet ++ getTokenIds( + decodedHash + ).map(Algos.encode).toSet) + .flatMap((id: String) => Algos.decode(id).get) + .toArray val tokenIdUpdatedAmount: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = idToAmount.map { case (id: String, amount: Amount) => tokenKeyByContractHash(decodedHash, Algos.decode(id).get) -> VersionalLevelDbValue @@ Longs.toByteArray(amount) }.toList + tokenIdsToUpdate :: tokenIdUpdatedAmount }.toList } - val accs: List[ContractHash] = getAllWallets - - val a: (List[AssetBox], List[DataBox], List[TokenIssuingBox]) = - boxesToInsert.foldLeft(List.empty[AssetBox], List.empty[DataBox], List.empty[TokenIssuingBox]) { - case ((asset, data, token), nextBox: AssetBox) => - (nextBox :: asset, data, token) - case ((asset, data, token), nextBox: DataBox) => - (asset, nextBox :: data, token) - case ((asset, data, token), nextBox: TokenIssuingBox) => - (asset, data, nextBox :: token) - case ((asset, data, token), _) => - (asset, data, token) + val boxesIdsToContractHashToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { + val ( + assetsFromDb: Map[String, Set[String]], + dataFromDB: Map[String, Set[String]], + tokensFRomDB: Map[String, Set[String]] + ) = getAllWallets.foldLeft( + Map.empty[String, Set[String]], + Map.empty[String, Set[String]], + Map.empty[String, Set[String]] + ) { + case ((hashToAssetIds, hashToDataIds, hashToTokenIds), nextHash) => + val nextHashEncoded: String = Algos.encode(nextHash) + (hashToAssetIds.updated( + nextHashEncoded, + assetKeysByHash(nextHash) + .filterNot(l => spentBxs.exists(_.id sameElements l)) + .map(Algos.encode) + .toSet + ), + hashToDataIds.updated( + nextHashEncoded, + dataKeysByHash(nextHash) + .filterNot(l => spentBxs.exists(_.id sameElements l)) + .map(Algos.encode) + .toSet + ), + hashToTokenIds.updated( + nextHashEncoded, + tokenKeysByHash(nextHash) + .filterNot(l => spentBxs.exists(_.id sameElements l)) + .map(Algos.encode) + .toSet + )) + } + val ( + hashToAssetBoxes: Map[String, Set[String]], + hashToDataBoxes: Map[String, Set[String]], + hashToTokenBoxes: Map[String, Set[String]] + ) = boxesToInsert.foldLeft( + Map.empty[String, Set[String]], + Map.empty[String, Set[String]], + Map.empty[String, Set[String]] + ) { + case ((assets, tokens, data), nextBox: AssetBox) => + val hash = Algos.encode(nextBox.proposition.contractHash) + (assets |+| Map(hash -> Set(Algos.encode(nextBox.id))), tokens, data) + case ((assets, tokens, data), nextBox: DataBox) => + val hash = Algos.encode(nextBox.proposition.contractHash) + (assets, tokens, data |+| Map(hash -> Set(Algos.encode(nextBox.id)))) + case ((assets, tokens, data), nextBox: TokenIssuingBox) => + val hash = Algos.encode(nextBox.proposition.contractHash) + (assets, tokens |+| Map(hash -> Set(Algos.encode(nextBox.id))), data) } - val assetByAcc: Map[String, List[AssetBox]] = a._1.foldLeft(Map.empty[String, List[AssetBox]]) { - case (hashToBoxes, nextBox) => - val hash = Algos.encode(nextBox.proposition.contractHash) - hashToBoxes |+| Map(hash -> List(nextBox)) - } - val dataByAcc: Map[String, List[DataBox]] = a._2.foldLeft(Map.empty[String, List[DataBox]]) { - case (hashToBoxes, nextBox) => - val hash = Algos.encode(nextBox.proposition.contractHash) - hashToBoxes |+| Map(hash -> List(nextBox)) - } - val tokenByAcc: Map[String, List[TokenIssuingBox]] = a._3.foldLeft(Map.empty[String, List[TokenIssuingBox]]) { - case (hashToBoxes, nextBox) => - val hash = Algos.encode(nextBox.proposition.contractHash) - hashToBoxes |+| Map(hash -> List(nextBox)) + val newAssetsToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = + (assetsFromDb |+| hashToAssetBoxes).map { + case (hash: String, value: Set[String]) => + assetBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + .flatMap(k => Algos.decode(k).get) + .toArray + }.toList + val newDataToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = + (dataFromDB |+| hashToDataBoxes).map { + case (hash: String, value: Set[String]) => + dataBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + .flatMap(k => Algos.decode(k).get) + .toArray + }.toList + val newTokenToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = + (tokensFRomDB |+| hashToTokenBoxes).map { + case (hash: String, value: Set[String]) => + tokenIssuingBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + .flatMap(k => Algos.decode(k).get) + .toArray + }.toList + newAssetsToDB ::: newDataToDB ::: newTokenToDB } - val assetFromDbCleaned: Map[String, Set[String]] = accs.map { hash => - Algos.encode(hash) -> assetKeysByHash(hash) - .filterNot(l => spentBxs.exists(_.id sameElements (l))) - .map(Algos.encode) - .toSet - }.toMap - val tokenFromDbCleaned: Map[String, Set[String]] = accs.map { hash => - Algos.encode(hash) -> tokenKeysByHash(hash) - .filterNot(l => spentBxs.exists(_.id sameElements (l))) - .map(Algos.encode) - .toSet - }.toMap - val dataFromDbCleaned: Map[String, Set[String]] = accs.map { hash => - Algos.encode(hash) -> dataKeysByHash(hash) - .filterNot(l => spentBxs.exists(_.id sameElements (l))) - .map(Algos.encode) - .toSet - }.toMap - - val newAsset: Map[String, Set[String]] = assetFromDbCleaned |+| assetByAcc.map { - case (str, boxes) => str -> boxes.map(k => Algos.encode(k.id)).toSet - } - val newToken: Map[String, Set[String]] = tokenFromDbCleaned |+| tokenByAcc.map { - case (str, boxes) => str -> boxes.map(k => Algos.encode(k.id)).toSet - } - val newData: Map[String, Set[String]] = dataFromDbCleaned |+| dataByAcc.map { - case (str, boxes) => str -> boxes.map(k => Algos.encode(k.id)).toSet - } + val toInsertBoxes: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = + boxesToInsert.map(box => (VersionalLevelDbKey @@ box.id.untag(ADKey)) -> VersionalLevelDbValue @@ box.bytes) + + val toRemoveBoxes: List[VersionalLevelDbKey] = spentBxs.map(l => VersionalLevelDbKey @@ l.id.untag(ADKey)) - val newAssetKeys: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newAsset.map { - case (hash, value) => - assetBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value - .flatMap(k => Algos.decode(k).get) - .toArray - }.toList - val newDataKeys: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newData.map { - case (hash, value) => - dataBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value - .flatMap(k => Algos.decode(k).get) - .toArray - }.toList - val newTokenKeys: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = newToken.map { - case (hash, value) => - tokenIssuingBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value - .flatMap(k => Algos.decode(k).get) - .toArray - }.toList - - val toIns = LevelDbDiff( - LevelDBVersion @@ modifierId.untag(ModifierId), - balancesToInsert ::: newTokenKeys ::: newDataKeys ::: newAssetKeys ::: boxesToInsert.map { box => - (VersionalLevelDbKey @@ box.id.untag(ADKey)) -> VersionalLevelDbValue @@ box.bytes - }, - spentBxs.map(l => VersionalLevelDbKey @@ l.id.untag(ADKey)) + levelDb.insert( + LevelDbDiff( + LevelDBVersion @@ modifierId.untag(ModifierId), + balancesToInsert ::: boxesIdsToContractHashToInsert ::: toInsertBoxes, + toRemoveBoxes + ) ) - levelDb.insert(toIns) } override def rollback(modId: ModifierId): Unit = ??? From 5bad14d12cab1687b1b32dfdc1aa937878dfa752 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Mon, 17 Feb 2020 17:55:27 +0300 Subject: [PATCH 12/36] fixed positive balance in toRemove balance --- src/main/scala/encry/view/wallet/WalletDBImpl.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 5d4e80388d..e8767c1436 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -1,6 +1,5 @@ package encry.view.wallet -import cats.instances.list._ import cats.instances.long._ import cats.instances.map._ import cats.instances.set._ @@ -13,7 +12,7 @@ import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ VersionalLevelDbKey, VersionalLevelDbValue } -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId @@ -21,7 +20,6 @@ import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, Encry import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import supertagged.@@ class WalletDBImpl private ( levelDb: VersionalLevelDB, @@ -83,7 +81,7 @@ class WalletDBImpl private ( BalanceCalculator.balanceSheet1(spentBxs, intrinsicTokenId).map { case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => Algos.encode(hash) -> idToAmount.map { - case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount + case (id: TokenId, amount: Amount) => Algos.encode(id) -> (-1 * amount) } } val currentBalances: Map[String, Map[String, Amount]] = From f5d1e1033abbf489a4335733370a5880e27a330a Mon Sep 17 00:00:00 2001 From: Lior Date: Tue, 18 Feb 2020 14:59:53 +0300 Subject: [PATCH 13/36] functionality to get different types of boxes --- .../scala/encry/cli/commands/GetBalance.scala | 2 +- .../encry/view/wallet/WalletDBImpl.scala | 102 +++++++++++++----- 2 files changed, 79 insertions(+), 25 deletions(-) diff --git a/src/main/scala/encry/cli/commands/GetBalance.scala b/src/main/scala/encry/cli/commands/GetBalance.scala index ee745df54d..c210b1230a 100644 --- a/src/main/scala/encry/cli/commands/GetBalance.scala +++ b/src/main/scala/encry/cli/commands/GetBalance.scala @@ -26,7 +26,7 @@ object GetBalance extends Command { { val balance: String = view.vault.getBalances.foldLeft("")((str, tokenInfo) => - if (tokenInfo._1._2 != Algos.encode(settings.constants.IntrinsicTokenId)) + if (Algos.encode(tokenInfo._1._2) != Algos.encode(settings.constants.IntrinsicTokenId)) str.concat(s"TokenID(${tokenInfo._1._2}) for key ${tokenInfo._1._1} : ${tokenInfo._2}\n") else str.concat(s"TokenID(${tokenInfo._1._2}) for key ${tokenInfo._1._1} : ${BigDecimal(tokenInfo._2) / 100000000}\n") ) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index e8767c1436..19441df261 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -1,24 +1,20 @@ package encry.view.wallet -import cats.instances.long._ -import cats.instances.map._ -import cats.instances.set._ -import cats.syntax.semigroup._ +import cats.Functor +import cats.implicits._ import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ - LevelDBVersion, - VersionalLevelDbKey, - VersionalLevelDbValue -} -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } +import encry.storage.levelDb.versionalLevelDB.{LevelDbDiff, VersionalLevelDB} +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue} import encry.utils.BalanceCalculator +import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box.EncryBox.BxTypeId import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } +import org.encryfoundation.common.modifiers.state.box.{AssetBox, DataBox, EncryBaseBox, TokenIssuingBox} import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } +import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash class WalletDBImpl private ( @@ -28,38 +24,96 @@ class WalletDBImpl private ( with StrictLogging with AutoCloseable { - override def getAllWallets: List[ContractHash] = ??? - - def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? + override def getAllWallets: List[ContractHash] = + levelDb + .get(CONTRACT_HASH_ACCOUNTS) + .map(_.grouped(32).toList) + .getOrElse(List.empty[Array[Byte]]) - override def getBoxById(id: ADKey): Option[EncryBaseBox] = ??? + override def getBoxById(id: ADKey): Option[EncryBaseBox] = + levelDb + .get(VersionalLevelDbKey @@ id.untag(ADKey)) + .flatMap(wrappedBx => StateModifierSerializer.parseBytes(wrappedBx, id.head).toOption) + def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? def getTokenIds(hash: ContractHash): List[TokenId] = ??? - def assetKeysByHash(hash: ContractHash): List[ADKey] = ??? def tokenKeysByHash(hash: ContractHash): List[ADKey] = ??? def dataKeysByHash(hash: ContractHash): List[ADKey] = ??? override def getAssetBoxesByPredicate( contractHash: ContractHash, - f: List[AssetBox] => Boolean - ): List[AssetBox] = ??? + f: List[AssetBox] => Boolean): List[AssetBox] = { + val listId: List[Array[Byte]] = levelDb + .get(assetBoxesByContractHashKey(contractHash)) + .map(bytes => bytes.grouped(32).toList).getOrElse(List.empty[Array[BxTypeId]]) + @scala.annotation.tailrec + def loop(acc: List[AssetBox], ids: List[Array[Byte]]): List[AssetBox] = { + ids.headOption match { + case None => acc + case Some(value) => + val tmpAcc = getBoxById(ADKey @@ value) match { + case Some(value: AssetBox) => acc :+ value + case None => acc + } + if (f(tmpAcc)) tmpAcc + else loop(tmpAcc, ids.drop(1)) + } + } + loop(List.empty[AssetBox], listId) + } override def getTokenIssuingBoxes( contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean - ): List[TokenIssuingBox] = ??? + ): List[TokenIssuingBox] = { + val listId: List[Array[Byte]] = levelDb + .get(assetBoxesByContractHashKey(contractHash)) + .map(bytes => bytes.grouped(32).toList).getOrElse(List.empty[Array[BxTypeId]]) + @scala.annotation.tailrec + def loop(acc: List[TokenIssuingBox], ids: List[Array[Byte]]): List[TokenIssuingBox] = { + ids.headOption match { + case None => acc + case Some(value) => + val tmpAcc = getBoxById(ADKey @@ value) match { + case Some(value: TokenIssuingBox) => acc :+ value + case None => acc + } + if (f(tmpAcc)) tmpAcc + else loop(tmpAcc, ids.drop(1)) + } + } + loop(List.empty[TokenIssuingBox], listId) + } override def getDataBoxes( contractHash: ContractHash, f: List[DataBox] => Boolean - ): List[DataBox] = ??? + ): List[DataBox] = { + val listId: List[Array[Byte]] = levelDb + .get(assetBoxesByContractHashKey(contractHash)) + .map(bytes => bytes.grouped(32).toList).getOrElse(List.empty[Array[BxTypeId]]) + @scala.annotation.tailrec + def loop(acc: List[DataBox], ids: List[Array[Byte]]): List[DataBox] = { + ids.headOption match { + case None => acc + case Some(value) => + val tmpAcc = getBoxById(ADKey @@ value) match { + case Some(value: DataBox) => acc :+ value + case None => acc + } + if (f(tmpAcc)) tmpAcc + else loop(tmpAcc, ids.drop(1)) + } + } + loop(List.empty[DataBox], listId) + } override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = ??? override def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = ??? - override def contains(id: ADKey): Boolean = ??? + override def contains(id: ADKey): Boolean = getBoxById(id).isDefined override def updateWallet( modifierId: ModifierId, @@ -202,9 +256,9 @@ class WalletDBImpl private ( ) } - override def rollback(modId: ModifierId): Unit = ??? + override def rollback(modId: ModifierId): Unit = levelDb.rollbackTo(LevelDBVersion @@ modId.untag(ModifierId)) - override def close(): Unit = ??? + override def close(): Unit = levelDb.close() private val ASSET_BOXES_BYTES: Array[Byte] = "ASSET_BOXES_BY_USER_KEY".getBytes() From 313fc681e2c4bdbf313a9ca95042d2773afd0b2d Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Tue, 18 Feb 2020 15:38:15 +0300 Subject: [PATCH 14/36] minor code cleaned up --- .../encry/view/wallet/WalletDBImpl.scala | 77 ++++++++++--------- 1 file changed, 41 insertions(+), 36 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 19441df261..40209a513d 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -5,18 +5,24 @@ import cats.implicits._ import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings -import encry.storage.levelDb.versionalLevelDB.{LevelDbDiff, VersionalLevelDB} -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue} +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ + LevelDBVersion, + VersionalLevelDbKey, + VersionalLevelDbValue +} import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.EncryBox.BxTypeId import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{AssetBox, DataBox, EncryBaseBox, TokenIssuingBox} +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} +import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import scala.annotation.tailrec + class WalletDBImpl private ( levelDb: VersionalLevelDB, settings: EncryAppSettings @@ -28,61 +34,60 @@ class WalletDBImpl private ( levelDb .get(CONTRACT_HASH_ACCOUNTS) .map(_.grouped(32).toList) - .getOrElse(List.empty[Array[Byte]]) + .getOrElse(List.empty[ContractHash]) override def getBoxById(id: ADKey): Option[EncryBaseBox] = levelDb .get(VersionalLevelDbKey @@ id.untag(ADKey)) - .flatMap(wrappedBx => StateModifierSerializer.parseBytes(wrappedBx, id.head).toOption) + .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? - def getTokenIds(hash: ContractHash): List[TokenId] = ??? - def assetKeysByHash(hash: ContractHash): List[ADKey] = ??? + def getTokenIds(hash: ContractHash): List[TokenId] = ??? + + def assetKeysByHash(contractHash: ContractHash): List[ADKey] = + levelDb + .get(assetBoxesByContractHashKey(contractHash)) + .map(_.grouped(32).toList.map(ADKey @@ _)) + .getOrElse(List.empty[ADKey]) + def tokenKeysByHash(hash: ContractHash): List[ADKey] = ??? def dataKeysByHash(hash: ContractHash): List[ADKey] = ??? + @tailrec + private def loop[BXT](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = + ids.headOption match { + case Some(boxId: ADKey) => + val newAcc: List[BXT] = getBoxById(boxId).fold(acc)(_ :: acc) + if (f(newAcc)) newAcc else loop(newAcc, ids.drop(1), f) + case None => List.empty[BXT] + } + + //List.empty override def getAssetBoxesByPredicate( contractHash: ContractHash, - f: List[AssetBox] => Boolean): List[AssetBox] = { - val listId: List[Array[Byte]] = levelDb - .get(assetBoxesByContractHashKey(contractHash)) - .map(bytes => bytes.grouped(32).toList).getOrElse(List.empty[Array[BxTypeId]]) - @scala.annotation.tailrec - def loop(acc: List[AssetBox], ids: List[Array[Byte]]): List[AssetBox] = { - ids.headOption match { - case None => acc - case Some(value) => - val tmpAcc = getBoxById(ADKey @@ value) match { - case Some(value: AssetBox) => acc :+ value - case None => acc - } - if (f(tmpAcc)) tmpAcc - else loop(tmpAcc, ids.drop(1)) - } - } - loop(List.empty[AssetBox], listId) - } + f: List[AssetBox] => Boolean + ): List[AssetBox] = loop[AssetBox](List.empty[AssetBox], assetKeysByHash(contractHash), f) override def getTokenIssuingBoxes( contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean ): List[TokenIssuingBox] = { - val listId: List[Array[Byte]] = levelDb + val listOfIds: List[Array[Byte]] = levelDb .get(assetBoxesByContractHashKey(contractHash)) - .map(bytes => bytes.grouped(32).toList).getOrElse(List.empty[Array[BxTypeId]]) + .map(bytes => bytes.grouped(32).toList) + .getOrElse(List.empty[Array[BxTypeId]]) @scala.annotation.tailrec - def loop(acc: List[TokenIssuingBox], ids: List[Array[Byte]]): List[TokenIssuingBox] = { + def loop(acc: List[TokenIssuingBox], ids: List[Array[Byte]]): List[TokenIssuingBox] = ids.headOption match { case None => acc case Some(value) => val tmpAcc = getBoxById(ADKey @@ value) match { case Some(value: TokenIssuingBox) => acc :+ value - case None => acc + case None => acc } if (f(tmpAcc)) tmpAcc else loop(tmpAcc, ids.drop(1)) } - } loop(List.empty[TokenIssuingBox], listId) } @@ -92,20 +97,20 @@ class WalletDBImpl private ( ): List[DataBox] = { val listId: List[Array[Byte]] = levelDb .get(assetBoxesByContractHashKey(contractHash)) - .map(bytes => bytes.grouped(32).toList).getOrElse(List.empty[Array[BxTypeId]]) + .map(bytes => bytes.grouped(32).toList) + .getOrElse(List.empty[Array[BxTypeId]]) @scala.annotation.tailrec - def loop(acc: List[DataBox], ids: List[Array[Byte]]): List[DataBox] = { + def loop(acc: List[DataBox], ids: List[Array[Byte]]): List[DataBox] = ids.headOption match { case None => acc case Some(value) => val tmpAcc = getBoxById(ADKey @@ value) match { case Some(value: DataBox) => acc :+ value - case None => acc + case None => acc } if (f(tmpAcc)) tmpAcc else loop(tmpAcc, ids.drop(1)) } - } loop(List.empty[DataBox], listId) } From 10a5b799c8b9a8f89694ff45a0c9ed67d799940c Mon Sep 17 00:00:00 2001 From: Lior Date: Tue, 18 Feb 2020 15:50:29 +0300 Subject: [PATCH 15/36] api changed --- .../encry/view/wallet/WalletDBImpl.scala | 59 ++++--------------- 1 file changed, 13 insertions(+), 46 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 40209a513d..3a5c5cb9af 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -17,9 +17,10 @@ import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.EncryBox.BxTypeId import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } -import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.{ Algos, TaggedTypes } import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import supertagged.@@ import scala.annotation.tailrec @@ -44,15 +45,11 @@ class WalletDBImpl private ( def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? def getTokenIds(hash: ContractHash): List[TokenId] = ??? - def assetKeysByHash(contractHash: ContractHash): List[ADKey] = + def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] = levelDb - .get(assetBoxesByContractHashKey(contractHash)) + .get(key) .map(_.grouped(32).toList.map(ADKey @@ _)) .getOrElse(List.empty[ADKey]) - - def tokenKeysByHash(hash: ContractHash): List[ADKey] = ??? - def dataKeysByHash(hash: ContractHash): List[ADKey] = ??? - @tailrec private def loop[BXT](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = ids.headOption match { @@ -66,53 +63,23 @@ class WalletDBImpl private ( override def getAssetBoxesByPredicate( contractHash: ContractHash, f: List[AssetBox] => Boolean - ): List[AssetBox] = loop[AssetBox](List.empty[AssetBox], assetKeysByHash(contractHash), f) + ): List[AssetBox] = + loop[AssetBox](List.empty[AssetBox], getBoxesIdsByKey(assetBoxesByContractHashKey(contractHash)), f) override def getTokenIssuingBoxes( contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean - ): List[TokenIssuingBox] = { - val listOfIds: List[Array[Byte]] = levelDb - .get(assetBoxesByContractHashKey(contractHash)) - .map(bytes => bytes.grouped(32).toList) - .getOrElse(List.empty[Array[BxTypeId]]) - @scala.annotation.tailrec - def loop(acc: List[TokenIssuingBox], ids: List[Array[Byte]]): List[TokenIssuingBox] = - ids.headOption match { - case None => acc - case Some(value) => - val tmpAcc = getBoxById(ADKey @@ value) match { - case Some(value: TokenIssuingBox) => acc :+ value - case None => acc - } - if (f(tmpAcc)) tmpAcc - else loop(tmpAcc, ids.drop(1)) - } - loop(List.empty[TokenIssuingBox], listId) - } + ): List[TokenIssuingBox] = + loop[TokenIssuingBox]( + List.empty[TokenIssuingBox], + getBoxesIdsByKey(tokenIssuingBoxesByContractHashKey(contractHash)), + f + ) override def getDataBoxes( contractHash: ContractHash, f: List[DataBox] => Boolean - ): List[DataBox] = { - val listId: List[Array[Byte]] = levelDb - .get(assetBoxesByContractHashKey(contractHash)) - .map(bytes => bytes.grouped(32).toList) - .getOrElse(List.empty[Array[BxTypeId]]) - @scala.annotation.tailrec - def loop(acc: List[DataBox], ids: List[Array[Byte]]): List[DataBox] = - ids.headOption match { - case None => acc - case Some(value) => - val tmpAcc = getBoxById(ADKey @@ value) match { - case Some(value: DataBox) => acc :+ value - case None => acc - } - if (f(tmpAcc)) tmpAcc - else loop(tmpAcc, ids.drop(1)) - } - loop(List.empty[DataBox], listId) - } + ): List[DataBox] = loop[DataBox](List.empty[DataBox], getBoxesIdsByKey(dataBoxesByContractHashKey(contractHash)), f) override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = ??? From 602e2beb3c1274616d3f2642d040cdb9f1e753df Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Tue, 18 Feb 2020 15:57:24 +0300 Subject: [PATCH 16/36] minor code cleaned up --- .../scala/encry/view/wallet/WalletDB.scala | 2 ++ .../encry/view/wallet/WalletDBImpl.scala | 21 +++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index 9c1693047a..864ac85d8d 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -10,6 +10,8 @@ import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash trait WalletDB { + def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] + def getAllWallets: List[ContractHash] def getBoxById(id: ADKey): Option[EncryBaseBox] diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 3a5c5cb9af..a307414f51 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -45,11 +45,12 @@ class WalletDBImpl private ( def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? def getTokenIds(hash: ContractHash): List[TokenId] = ??? - def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] = + private def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] = levelDb .get(key) .map(_.grouped(32).toList.map(ADKey @@ _)) .getOrElse(List.empty[ADKey]) + @tailrec private def loop[BXT](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = ids.headOption match { @@ -64,7 +65,11 @@ class WalletDBImpl private ( contractHash: ContractHash, f: List[AssetBox] => Boolean ): List[AssetBox] = - loop[AssetBox](List.empty[AssetBox], getBoxesIdsByKey(assetBoxesByContractHashKey(contractHash)), f) + loop[AssetBox]( + List.empty[AssetBox], + getBoxesIdsByKey(assetBoxesByContractHashKey(contractHash)), + f + ) override def getTokenIssuingBoxes( contractHash: ContractHash, @@ -79,7 +84,11 @@ class WalletDBImpl private ( override def getDataBoxes( contractHash: ContractHash, f: List[DataBox] => Boolean - ): List[DataBox] = loop[DataBox](List.empty[DataBox], getBoxesIdsByKey(dataBoxesByContractHashKey(contractHash)), f) + ): List[DataBox] = loop[DataBox]( + List.empty[DataBox], + getBoxesIdsByKey(dataBoxesByContractHashKey(contractHash)), + f + ) override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = ??? @@ -150,21 +159,21 @@ class WalletDBImpl private ( val nextHashEncoded: String = Algos.encode(nextHash) (hashToAssetIds.updated( nextHashEncoded, - assetKeysByHash(nextHash) + getBoxesIdsByKey(assetBoxesByContractHashKey(nextHash)) .filterNot(l => spentBxs.exists(_.id sameElements l)) .map(Algos.encode) .toSet ), hashToDataIds.updated( nextHashEncoded, - dataKeysByHash(nextHash) + getBoxesIdsByKey(dataBoxesByContractHashKey(nextHash)) .filterNot(l => spentBxs.exists(_.id sameElements l)) .map(Algos.encode) .toSet ), hashToTokenIds.updated( nextHashEncoded, - tokenKeysByHash(nextHash) + getBoxesIdsByKey(tokenIssuingBoxesByContractHashKey(nextHash)) .filterNot(l => spentBxs.exists(_.id sameElements l)) .map(Algos.encode) .toSet From eb241ef26222063bb87a227d6c6178a957650a7d Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Tue, 18 Feb 2020 16:19:02 +0300 Subject: [PATCH 17/36] implemented getBalancesByContractHash, getTokenBalanceByContractHash --- .../scala/encry/view/wallet/WalletDB.scala | 2 -- .../encry/view/wallet/WalletDBImpl.scala | 21 +++++++++++-------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index 864ac85d8d..9c1693047a 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -10,8 +10,6 @@ import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash trait WalletDB { - def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] - def getAllWallets: List[ContractHash] def getBoxById(id: ADKey): Option[EncryBaseBox] diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index a307414f51..01e9cb92a9 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -1,26 +1,23 @@ package encry.view.wallet -import cats.Functor import cats.implicits._ import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue } +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount -import org.encryfoundation.common.modifiers.state.box.EncryBox.BxTypeId import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } -import org.encryfoundation.common.utils.{ Algos, TaggedTypes } +import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import supertagged.@@ import scala.annotation.tailrec @@ -43,7 +40,8 @@ class WalletDBImpl private ( .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? - def getTokenIds(hash: ContractHash): List[TokenId] = ??? + + def getTokenIds(hash: ContractHash): List[TokenId] = ??? private def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] = levelDb @@ -90,9 +88,14 @@ class WalletDBImpl private ( f ) - override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = ??? + override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = + levelDb + .get(hashToTokens(contractHash)) + .map(_.grouped(32).map(id => id -> getTokenBalanceByContractHash(contractHash, id)).toMap) + .getOrElse(Map.empty) - override def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = ??? + override def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = + levelDb.get(tokenKeyByContractHash(contractHash, tokenId)).map(Longs.fromByteArray).getOrElse(0L) override def contains(id: ADKey): Boolean = getBoxById(id).isDefined @@ -159,7 +162,7 @@ class WalletDBImpl private ( val nextHashEncoded: String = Algos.encode(nextHash) (hashToAssetIds.updated( nextHashEncoded, - getBoxesIdsByKey(assetBoxesByContractHashKey(nextHash)) + getBoxesIdsByKey(assetBoxesByContractHashKey(nextHash)) .filterNot(l => spentBxs.exists(_.id sameElements l)) .map(Algos.encode) .toSet From 3f8106e60c19357b9a997ea59b459b27804c97a4 Mon Sep 17 00:00:00 2001 From: Lior Date: Tue, 18 Feb 2020 16:21:56 +0300 Subject: [PATCH 18/36] getBalance function --- .../encry/view/wallet/WalletDBImpl.scala | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 01e9cb92a9..2e713c511d 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -39,9 +39,24 @@ class WalletDBImpl private ( .get(VersionalLevelDbKey @@ id.untag(ADKey)) .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) - def getBalances: Map[ContractHash, Map[TokenId, Amount]] = ??? + def getBalances: Map[ContractHash, Map[TokenId, Amount]] = { + val accounts: List[ContractHash] = getAllWallets + val tokensToAccounts: Map[ContractHash, Map[TokenId, Amount]] = + accounts.foldLeft(Map.empty[ContractHash, Map[TokenId, Amount]]) { + case (balance, contractHash) => + val balancesByHash: Map[ContractHash, Map[TokenId, Amount]] = + Map(contractHash -> getBalancesByContractHash(contractHash)) + if (balancesByHash.nonEmpty) balance ++ balancesByHash + else balance + } + tokensToAccounts + } - def getTokenIds(hash: ContractHash): List[TokenId] = ??? + def getTokenIds(hash: ContractHash): List[TokenId] = + levelDb + .get(hashToTokens(hash)) + .map( + _.grouped(32).toList).getOrElse(List.empty[TokenId]) private def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] = levelDb From bc57e6f471df123b57e97c294f3c27bcdc8b1948 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Tue, 18 Feb 2020 16:45:36 +0300 Subject: [PATCH 19/36] code cleaned up --- .../scala/encry/view/wallet/WalletDBImpl.scala | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 2e713c511d..29ebc1653f 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -39,24 +39,14 @@ class WalletDBImpl private ( .get(VersionalLevelDbKey @@ id.untag(ADKey)) .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) - def getBalances: Map[ContractHash, Map[TokenId, Amount]] = { - val accounts: List[ContractHash] = getAllWallets - val tokensToAccounts: Map[ContractHash, Map[TokenId, Amount]] = - accounts.foldLeft(Map.empty[ContractHash, Map[TokenId, Amount]]) { - case (balance, contractHash) => - val balancesByHash: Map[ContractHash, Map[TokenId, Amount]] = - Map(contractHash -> getBalancesByContractHash(contractHash)) - if (balancesByHash.nonEmpty) balance ++ balancesByHash - else balance - } - tokensToAccounts - } + def getBalances: Map[ContractHash, Map[TokenId, Amount]] = + getAllWallets.map(hash => hash -> getBalancesByContractHash(hash)).toMap def getTokenIds(hash: ContractHash): List[TokenId] = levelDb .get(hashToTokens(hash)) - .map( - _.grouped(32).toList).getOrElse(List.empty[TokenId]) + .map(_.grouped(32).toList) + .getOrElse(List.empty[TokenId]) private def getBoxesIdsByKey(key: VersionalLevelDbKey): List[ADKey] = levelDb From ff998e817731d7a0c0df9b26133be1de0d17f7d8 Mon Sep 17 00:00:00 2001 From: Lior Date: Wed, 19 Feb 2020 10:55:02 +0300 Subject: [PATCH 20/36] code cleanup --- .../encry/view/wallet/WalletDBImpl.scala | 98 ++++++++----------- 1 file changed, 40 insertions(+), 58 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 29ebc1653f..e8a33a7fdd 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -9,7 +9,7 @@ import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ VersionalLevelDbKey, VersionalLevelDbValue } -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount @@ -18,6 +18,7 @@ import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, Encry import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import supertagged.@@ import scala.annotation.tailrec @@ -112,21 +113,17 @@ class WalletDBImpl private ( ): Unit = { val boxesToInsert: List[EncryBaseBox] = newBxs.filterNot(spentBxs.contains) + def balanceSheetFunction(list: List[EncryBaseBox], x: Long = -1) = + BalanceCalculator.balanceSheet1(list, intrinsicTokenId).map { + case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => + Algos.encode(hash) -> idToAmount.map { + case (id: TokenId, amount: Amount) => Algos.encode(id) -> (x * amount) + } + } + val balancesToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { - val toAddBalances: Map[String, Map[String, Amount]] = - BalanceCalculator.balanceSheet1(newBxs, intrinsicTokenId).map { - case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => - Algos.encode(hash) -> idToAmount.map { - case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount - } - } - val toRemoveBalances: Map[String, Map[String, Amount]] = - BalanceCalculator.balanceSheet1(spentBxs, intrinsicTokenId).map { - case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => - Algos.encode(hash) -> idToAmount.map { - case (id: TokenId, amount: Amount) => Algos.encode(id) -> (-1 * amount) - } - } + val toAddBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(newBxs, 1L) + val toRemoveBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(spentBxs) val currentBalances: Map[String, Map[String, Amount]] = getBalances.map { case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => @@ -154,10 +151,16 @@ class WalletDBImpl private ( } val boxesIdsToContractHashToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { + def updatedFunction(hashToBxIds: Map[String, Set[String]], nextHash: String, key: VersionalLevelDbKey) = + hashToBxIds.updated(nextHash, + getBoxesIdsByKey(key) + .filterNot(l => spentBxs.exists(_.id sameElements l)) + .map(Algos.encode) + .toSet) val ( assetsFromDb: Map[String, Set[String]], dataFromDB: Map[String, Set[String]], - tokensFRomDB: Map[String, Set[String]] + tokensFromDB: Map[String, Set[String]] ) = getAllWallets.foldLeft( Map.empty[String, Set[String]], Map.empty[String, Set[String]], @@ -165,27 +168,9 @@ class WalletDBImpl private ( ) { case ((hashToAssetIds, hashToDataIds, hashToTokenIds), nextHash) => val nextHashEncoded: String = Algos.encode(nextHash) - (hashToAssetIds.updated( - nextHashEncoded, - getBoxesIdsByKey(assetBoxesByContractHashKey(nextHash)) - .filterNot(l => spentBxs.exists(_.id sameElements l)) - .map(Algos.encode) - .toSet - ), - hashToDataIds.updated( - nextHashEncoded, - getBoxesIdsByKey(dataBoxesByContractHashKey(nextHash)) - .filterNot(l => spentBxs.exists(_.id sameElements l)) - .map(Algos.encode) - .toSet - ), - hashToTokenIds.updated( - nextHashEncoded, - getBoxesIdsByKey(tokenIssuingBoxesByContractHashKey(nextHash)) - .filterNot(l => spentBxs.exists(_.id sameElements l)) - .map(Algos.encode) - .toSet - )) + (updatedFunction(hashToAssetIds, nextHashEncoded, assetBoxesByContractHashKey(nextHash)), + updatedFunction(hashToDataIds, nextHashEncoded, dataBoxesByContractHashKey(nextHash)), + updatedFunction(hashToTokenIds, nextHashEncoded, tokenIssuingBoxesByContractHashKey(nextHash))) } val ( hashToAssetBoxes: Map[String, Set[String]], @@ -196,38 +181,35 @@ class WalletDBImpl private ( Map.empty[String, Set[String]], Map.empty[String, Set[String]] ) { - case ((assets, tokens, data), nextBox: AssetBox) => + case ((assets, data, token), nextBox: AssetBox) => val hash = Algos.encode(nextBox.proposition.contractHash) - (assets |+| Map(hash -> Set(Algos.encode(nextBox.id))), tokens, data) - case ((assets, tokens, data), nextBox: DataBox) => + (assets |+| Map(hash -> Set(Algos.encode(nextBox.id))), token, data) + case ((assets, data, token), nextBox: DataBox) => val hash = Algos.encode(nextBox.proposition.contractHash) - (assets, tokens, data |+| Map(hash -> Set(Algos.encode(nextBox.id)))) - case ((assets, tokens, data), nextBox: TokenIssuingBox) => + (assets, data |+| Map(hash -> Set(Algos.encode(nextBox.id))), token) + case ((assets, data, token), nextBox: TokenIssuingBox) => val hash = Algos.encode(nextBox.proposition.contractHash) - (assets, tokens |+| Map(hash -> Set(Algos.encode(nextBox.id))), data) + (assets, data, token |+| Map(hash -> Set(Algos.encode(nextBox.id)))) } - val newAssetsToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = - (assetsFromDb |+| hashToAssetBoxes).map { + def hashToBxsIdsToDB( + typeToDb: Map[String, Set[String]], + hashType: Map[String, Set[String]], + key: ContractHash => VersionalLevelDbKey + ): List[(VersionalLevelDbKey, VersionalLevelDbValue)] = + (typeToDb |+| hashType).map { case (hash: String, value: Set[String]) => - assetBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + key(hash) -> VersionalLevelDbValue @@ value .flatMap(k => Algos.decode(k).get) .toArray }.toList + + val newAssetsToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = + hashToBxsIdsToDB(assetsFromDb, hashToAssetBoxes, assetBoxesByContractHashKey) val newDataToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = - (dataFromDB |+| hashToDataBoxes).map { - case (hash: String, value: Set[String]) => - dataBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value - .flatMap(k => Algos.decode(k).get) - .toArray - }.toList + hashToBxsIdsToDB(dataFromDB, hashToDataBoxes, tokenIssuingBoxesByContractHashKey) val newTokenToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = - (tokensFRomDB |+| hashToTokenBoxes).map { - case (hash: String, value: Set[String]) => - tokenIssuingBoxesByContractHashKey(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value - .flatMap(k => Algos.decode(k).get) - .toArray - }.toList + hashToBxsIdsToDB(tokensFromDB, hashToTokenBoxes, dataBoxesByContractHashKey) newAssetsToDB ::: newDataToDB ::: newTokenToDB } From 23e1a9d0247d0441335b656d64ac134c76ad9400 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Wed, 19 Feb 2020 11:53:01 +0300 Subject: [PATCH 21/36] fixed compile error --- .../encry/view/wallet/WalletDBImpl.scala | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index e8a33a7fdd..27173e2a22 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -4,23 +4,19 @@ import cats.implicits._ import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ - LevelDBVersion, - VersionalLevelDbKey, - VersionalLevelDbValue -} -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue} +import encry.storage.levelDb.versionalLevelDB.{LevelDbDiff, VersionalLevelDB} import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } +import org.encryfoundation.common.modifiers.state.box.{AssetBox, Box, DataBox, EncryBaseBox, TokenIssuingBox} import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } +import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import supertagged.@@ import scala.annotation.tailrec +import scala.reflect.ClassTag class WalletDBImpl private ( levelDb: VersionalLevelDB, @@ -40,6 +36,12 @@ class WalletDBImpl private ( .get(VersionalLevelDbKey @@ id.untag(ADKey)) .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) + def getTypedBoxById[BXT: ClassTag](id: ADKey): Option[BXT] = + levelDb + .get(VersionalLevelDbKey @@ id.untag(ADKey)) + .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) + .collect { case box: BXT => box } + def getBalances: Map[ContractHash, Map[TokenId, Amount]] = getAllWallets.map(hash => hash -> getBalancesByContractHash(hash)).toMap @@ -56,10 +58,10 @@ class WalletDBImpl private ( .getOrElse(List.empty[ADKey]) @tailrec - private def loop[BXT](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = + private def loop[BXT: ClassTag](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = ids.headOption match { case Some(boxId: ADKey) => - val newAcc: List[BXT] = getBoxById(boxId).fold(acc)(_ :: acc) + val newAcc: List[BXT] = getTypedBoxById[BXT](boxId).fold(acc)(_ :: acc) if (f(newAcc)) newAcc else loop(newAcc, ids.drop(1), f) case None => List.empty[BXT] } @@ -199,7 +201,7 @@ class WalletDBImpl private ( ): List[(VersionalLevelDbKey, VersionalLevelDbValue)] = (typeToDb |+| hashType).map { case (hash: String, value: Set[String]) => - key(hash) -> VersionalLevelDbValue @@ value + key(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value .flatMap(k => Algos.decode(k).get) .toArray }.toList From 169f5f7eb048cc7c32cc4b4dbec37e1dd7a6d8ac Mon Sep 17 00:00:00 2001 From: Lior Date: Thu, 20 Feb 2020 13:35:21 +0300 Subject: [PATCH 22/36] tests added --- .../scala/encry/view/wallet/EncryWallet.scala | 4 - .../encry/view/wallet/WalletDBImpl.scala | 49 ++++++---- .../encry/view/wallet/WalletDbSpec.scala | 94 +++++++++++++++++++ 3 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 src/test/scala/encry/view/wallet/WalletDbSpec.scala diff --git a/src/main/scala/encry/view/wallet/EncryWallet.scala b/src/main/scala/encry/view/wallet/EncryWallet.scala index 9f66f6fac4..e10e4642cc 100644 --- a/src/main/scala/encry/view/wallet/EncryWallet.scala +++ b/src/main/scala/encry/view/wallet/EncryWallet.scala @@ -1,7 +1,6 @@ package encry.view.wallet import java.io.File - import cats.data.NonEmptyChain._ import cats.data.{NonEmptyChain, Validated} import cats.instances.string._ @@ -27,12 +26,9 @@ import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{EncryBaseBox, EncryProposition, MonetaryBox} -import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.ModifierId -import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import org.iq80.leveldb.{DB, Options} import scorex.crypto.signatures.PublicKey - import scala.util.{Failure, Success, Try} case class EncryWallet(walletStorage: WalletVersionalLevelDB, accountManagers: Seq[AccountManager], private val accountStore: Store) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 27173e2a22..aeeaf4ab74 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -4,21 +4,25 @@ import cats.implicits._ import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{LevelDBVersion, VersionalLevelDbKey, VersionalLevelDbValue} -import encry.storage.levelDb.versionalLevelDB.{LevelDbDiff, VersionalLevelDB} +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ + LevelDBVersion, + VersionalLevelDbKey, + VersionalLevelDbValue +} +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{AssetBox, Box, DataBox, EncryBaseBox, TokenIssuingBox} +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, Box, DataBox, EncryBaseBox, TokenIssuingBox } import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} +import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash - +import scorex.crypto.hash.Digest32 import scala.annotation.tailrec import scala.reflect.ClassTag -class WalletDBImpl private ( +class WalletDBImpl( levelDb: VersionalLevelDB, settings: EncryAppSettings ) extends WalletDB @@ -46,7 +50,7 @@ class WalletDBImpl private ( getAllWallets.map(hash => hash -> getBalancesByContractHash(hash)).toMap def getTokenIds(hash: ContractHash): List[TokenId] = - levelDb + levelDb .get(hashToTokens(hash)) .map(_.grouped(32).toList) .getOrElse(List.empty[TokenId]) @@ -58,13 +62,14 @@ class WalletDBImpl private ( .getOrElse(List.empty[ADKey]) @tailrec - private def loop[BXT: ClassTag](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = + private def loop[BXT: ClassTag](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = { ids.headOption match { case Some(boxId: ADKey) => val newAcc: List[BXT] = getTypedBoxById[BXT](boxId).fold(acc)(_ :: acc) if (f(newAcc)) newAcc else loop(newAcc, ids.drop(1), f) case None => List.empty[BXT] } + } //List.empty override def getAssetBoxesByPredicate( @@ -99,7 +104,7 @@ class WalletDBImpl private ( override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = levelDb .get(hashToTokens(contractHash)) - .map(_.grouped(32).map(id => id -> getTokenBalanceByContractHash(contractHash, id)).toMap) + .map(_.grouped(32).toList.map(id => id -> getTokenBalanceByContractHash(contractHash, id)).toMap) .getOrElse(Map.empty) override def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = @@ -115,7 +120,7 @@ class WalletDBImpl private ( ): Unit = { val boxesToInsert: List[EncryBaseBox] = newBxs.filterNot(spentBxs.contains) - def balanceSheetFunction(list: List[EncryBaseBox], x: Long = -1) = + def balanceSheetFunction(list: List[EncryBaseBox], x: Long = -1): Map[String, Map[String, Amount]] = BalanceCalculator.balanceSheet1(list, intrinsicTokenId).map { case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => Algos.encode(hash) -> idToAmount.map { @@ -123,7 +128,8 @@ class WalletDBImpl private ( } } - val balancesToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { + + val infoAboutWalletToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { val toAddBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(newBxs, 1L) val toRemoveBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(spentBxs) val currentBalances: Map[String, Map[String, Amount]] = @@ -133,22 +139,24 @@ class WalletDBImpl private ( case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount } } - (toAddBalances |+| toRemoveBalances |+| currentBalances).flatMap { + val updatedWallets = toAddBalances |+| toRemoveBalances |+| currentBalances + val newContractHashes: Array[Byte] = updatedWallets.keys.toList.flatMap(id => Algos.decode(id).get).toArray + val contractHashToInsert: (VersionalLevelDbKey, VersionalLevelDbValue) = CONTRACT_HASH_ACCOUNTS -> VersionalLevelDbValue @@ newContractHashes + + updatedWallets.flatMap { case (hash: String, idToAmount: Map[String, Amount]) => val decodedHash: Array[Byte] = Algos.decode(hash).get val tokenIdsToUpdate: (VersionalLevelDbKey, VersionalLevelDbValue) = - hashToTokens(decodedHash) -> VersionalLevelDbValue @@ (idToAmount.keys.toSet ++ getTokenIds( - decodedHash - ).map(Algos.encode).toSet) - .flatMap((id: String) => Algos.decode(id).get) - .toArray + hashToTokens(decodedHash) -> VersionalLevelDbValue @@ (idToAmount.keys.toList + .flatMap(elem => Algos.decode(elem).get) + .toArray ++ getTokenIds(decodedHash).flatten) val tokenIdUpdatedAmount: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = idToAmount.map { case (id: String, amount: Amount) => tokenKeyByContractHash(decodedHash, Algos.decode(id).get) -> VersionalLevelDbValue @@ Longs.toByteArray(amount) }.toList - tokenIdsToUpdate :: tokenIdUpdatedAmount + contractHashToInsert :: tokenIdsToUpdate :: tokenIdUpdatedAmount }.toList } @@ -169,6 +177,7 @@ class WalletDBImpl private ( Map.empty[String, Set[String]] ) { case ((hashToAssetIds, hashToDataIds, hashToTokenIds), nextHash) => + val nextHashEncoded: String = Algos.encode(nextHash) (updatedFunction(hashToAssetIds, nextHashEncoded, assetBoxesByContractHashKey(nextHash)), updatedFunction(hashToDataIds, nextHashEncoded, dataBoxesByContractHashKey(nextHash)), @@ -223,7 +232,7 @@ class WalletDBImpl private ( levelDb.insert( LevelDbDiff( LevelDBVersion @@ modifierId.untag(ModifierId), - balancesToInsert ::: boxesIdsToContractHashToInsert ::: toInsertBoxes, + infoAboutWalletToInsert ::: boxesIdsToContractHashToInsert ::: toInsertBoxes, toRemoveBoxes ) ) @@ -241,7 +250,7 @@ class WalletDBImpl private ( private val CONTRACT_HASH_TOKEN_IDS: Array[Byte] = "CONTRACT_HASH_TOKEN_IDS".getBytes() - private val CONTRACT_HASH_ACCOUNTS: VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash("CONTRACT_HASH_ACCOUNTS") + private val CONTRACT_HASH_ACCOUNTS: VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash("CONTRACT_HASH_ACCOUNTS").untag(Digest32) def hashToTokens(userHash: ContractHash): VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash(userHash ++ CONTRACT_HASH_TOKEN_IDS) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala new file mode 100644 index 0000000000..e7ede274a8 --- /dev/null +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -0,0 +1,94 @@ +package encry.view.wallet + +import com.typesafe.scalalogging.StrictLogging +import encry.modifiers.InstanceFactory +import encry.settings.{ EncryAppSettings, LevelDBSettings, Settings } +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, LevelDbFactory, VersionalLevelDBCompanion } +import encry.utils.{ EncryGenerator, FileHelper } +import org.encryfoundation.common.modifiers.history.{ Block, Payload } +import org.encryfoundation.common.modifiers.mempool.transaction.Transaction +import org.encryfoundation.common.modifiers.state.box.Box.Amount +import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, MonetaryBox } +import org.encryfoundation.common.utils.Algos +import org.encryfoundation.common.utils.TaggedTypes.ModifierId +import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash +import org.iq80.leveldb.Options +import org.scalatest.{ Matchers, PropSpec, WordSpecLike } +import org.scalatest.mockito.MockitoSugar +import scorex.utils.Random + +class WalletDbSpec + extends WordSpecLike + with Matchers + with InstanceFactory + with EncryGenerator + with StrictLogging + with Settings + with MockitoSugar { + + val levelDbElemsQty = 10 + + val dummyLevelDBSettings: LevelDBSettings = LevelDBSettings(5) + + val levelDBInit = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) + + val vldbInit = VersionalLevelDBCompanion(levelDBInit, dummyLevelDBSettings) + + val settingsR: EncryAppSettings = EncryAppSettings.read() + + def init: WalletDBImpl = new WalletDBImpl(vldbInit, settingsR) + + val api: WalletDBImpl = init + val validTxs: Seq[Transaction] = genValidPaymentTxs(3) + val useBox: AssetBox = validTxs.head.newBoxes.head.asInstanceOf[AssetBox] + val spentTx: Transaction = genValidPaymentTxToAddrWithSpentBoxes(IndexedSeq(useBox), randomAddress) + val blockPayload: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), validTxs) + val blockPayloadWithSpentTx: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), Seq(spentTx)) + val newTxs: List[EncryBaseBox] = blockPayload.txs.flatMap(_.newBoxes).toList + val spentTxs: List[EncryBaseBox] = blockPayloadWithSpentTx.txs.flatMap(_.newBoxes).toList + val res: Unit = + api.updateWallet(ModifierId @@ Random.randomBytes(), newTxs, spentTxs, settingsR.constants.IntrinsicTokenId) + + "Needs to take what was inserted" should { + "ids are the same" in { + + (api.getBoxById(newTxs.head.id).get.id sameElements newTxs.head.id) shouldBe true + api.getAllWallets.nonEmpty shouldBe true + } + "amount in storage should be correct" in { + val amountInStorage = api.getAllWallets + .map(ch => api.getTokenBalanceByContractHash(ch, settingsR.constants.IntrinsicTokenId)) + .foldLeft(0L) { + case (acc, amount) => acc + amount + } + val amountToInsert: Long = { + val newTx: Long = newTxs.map { + case a: MonetaryBox => a.amount + }.foldLeft(0L) { + case (acc, amount) => acc + amount + } + val spent: Long = spentTxs.map { + case a: MonetaryBox => a.amount + }.foldLeft(0L) { + case (acc, amount) => acc + amount + } + newTx - spent + } + val getBalance: Amount = api.getAllWallets.map(x => api.getBalancesByContractHash(x)).foldLeft(0L) { + case (acc, amount) => acc + amount.values.sum + } + + amountInStorage shouldEqual amountToInsert + amountInStorage shouldEqual getBalance + amountToInsert shouldEqual getBalance + + } + "should contain intrinsic tokenId" in { + Algos.encode(api.getTokenIds(api.getAllWallets.head).head) shouldEqual Algos.encode( + settingsR.constants.IntrinsicTokenId + ) + } + } + +} From ab9cbcd7f967ba161073870462b4e5f596c558df Mon Sep 17 00:00:00 2001 From: Lior Date: Thu, 20 Feb 2020 13:49:58 +0300 Subject: [PATCH 23/36] tests added --- .../encry/view/wallet/WalletDbSpec.scala | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index e7ede274a8..9b14e28f06 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -37,18 +37,19 @@ class WalletDbSpec val settingsR: EncryAppSettings = EncryAppSettings.read() - def init: WalletDBImpl = new WalletDBImpl(vldbInit, settingsR) + def init: WalletDBImpl = new WalletDBImpl(vldbInit, settingsR) + + val api: WalletDBImpl = init + val validTxs: Seq[Transaction] = genValidPaymentTxs(3) + val useBox: AssetBox = validTxs.head.newBoxes.head.asInstanceOf[AssetBox] + val spentTx: Transaction = genValidPaymentTxToAddrWithSpentBoxes(IndexedSeq(useBox), randomAddress) + val blockPayload: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), validTxs) + val blockPayloadWithSpentTx: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), Seq(spentTx)) + val newTxs: List[EncryBaseBox] = blockPayload.txs.flatMap(_.newBoxes).toList + val spentTxs: List[EncryBaseBox] = blockPayloadWithSpentTx.txs.flatMap(_.newBoxes).toList + val res: Unit = + api.updateWallet(ModifierId @@ Random.randomBytes(), newTxs, spentTxs, settingsR.constants.IntrinsicTokenId) - val api: WalletDBImpl = init - val validTxs: Seq[Transaction] = genValidPaymentTxs(3) - val useBox: AssetBox = validTxs.head.newBoxes.head.asInstanceOf[AssetBox] - val spentTx: Transaction = genValidPaymentTxToAddrWithSpentBoxes(IndexedSeq(useBox), randomAddress) - val blockPayload: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), validTxs) - val blockPayloadWithSpentTx: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), Seq(spentTx)) - val newTxs: List[EncryBaseBox] = blockPayload.txs.flatMap(_.newBoxes).toList - val spentTxs: List[EncryBaseBox] = blockPayloadWithSpentTx.txs.flatMap(_.newBoxes).toList - val res: Unit = - api.updateWallet(ModifierId @@ Random.randomBytes(), newTxs, spentTxs, settingsR.constants.IntrinsicTokenId) "Needs to take what was inserted" should { "ids are the same" in { @@ -89,6 +90,9 @@ class WalletDbSpec settingsR.constants.IntrinsicTokenId ) } + "" in { + api.getAssetBoxesByPredicate(api.getAllWallets.head, x => x.map(_.amount).sum > 0).nonEmpty shouldBe true + } } } From 61bf9f720cfd3917cf57fa3ba5dd0ae75e893743 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Thu, 20 Feb 2020 14:22:40 +0300 Subject: [PATCH 24/36] fixed hashToBxsIdsToDB function --- src/main/scala/encry/view/wallet/WalletDBImpl.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index aeeaf4ab74..b68616b040 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -194,7 +194,7 @@ class WalletDBImpl( ) { case ((assets, data, token), nextBox: AssetBox) => val hash = Algos.encode(nextBox.proposition.contractHash) - (assets |+| Map(hash -> Set(Algos.encode(nextBox.id))), token, data) + (assets |+| Map(hash -> Set(Algos.encode(nextBox.id))), data, token) case ((assets, data, token), nextBox: DataBox) => val hash = Algos.encode(nextBox.proposition.contractHash) (assets, data |+| Map(hash -> Set(Algos.encode(nextBox.id))), token) @@ -210,9 +210,8 @@ class WalletDBImpl( ): List[(VersionalLevelDbKey, VersionalLevelDbValue)] = (typeToDb |+| hashType).map { case (hash: String, value: Set[String]) => - key(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value + key(Algos.decode(hash).get) -> VersionalLevelDbValue @@ value.toArray .flatMap(k => Algos.decode(k).get) - .toArray }.toList val newAssetsToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = From fc4d1d0ecc8e198881197b843f73a20a4445ac62 Mon Sep 17 00:00:00 2001 From: Lior Date: Thu, 20 Feb 2020 14:39:13 +0300 Subject: [PATCH 25/36] tests added --- .../encry/view/wallet/WalletDbSpec.scala | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 9b14e28f06..7fab799c7f 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -2,19 +2,19 @@ package encry.view.wallet import com.typesafe.scalalogging.StrictLogging import encry.modifiers.InstanceFactory -import encry.settings.{ EncryAppSettings, LevelDBSettings, Settings } -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, LevelDbFactory, VersionalLevelDBCompanion } -import encry.utils.{ EncryGenerator, FileHelper } -import org.encryfoundation.common.modifiers.history.{ Block, Payload } +import encry.settings.{EncryAppSettings, LevelDBSettings, Settings} +import encry.storage.levelDb.versionalLevelDB.{LevelDbDiff, LevelDbFactory, VersionalLevelDBCompanion} +import encry.utils.{EncryGenerator, FileHelper} +import org.encryfoundation.common.modifiers.history.{Block, Payload} import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, MonetaryBox } +import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryBaseBox, MonetaryBox} import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.ModifierId +import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import org.iq80.leveldb.Options -import org.scalatest.{ Matchers, PropSpec, WordSpecLike } +import org.scalatest.{Matchers, PropSpec, WordSpecLike} import org.scalatest.mockito.MockitoSugar import scorex.utils.Random @@ -37,8 +37,8 @@ class WalletDbSpec val settingsR: EncryAppSettings = EncryAppSettings.read() + val state = { def init: WalletDBImpl = new WalletDBImpl(vldbInit, settingsR) - val api: WalletDBImpl = init val validTxs: Seq[Transaction] = genValidPaymentTxs(3) val useBox: AssetBox = validTxs.head.newBoxes.head.asInstanceOf[AssetBox] @@ -49,15 +49,16 @@ class WalletDbSpec val spentTxs: List[EncryBaseBox] = blockPayloadWithSpentTx.txs.flatMap(_.newBoxes).toList val res: Unit = api.updateWallet(ModifierId @@ Random.randomBytes(), newTxs, spentTxs, settingsR.constants.IntrinsicTokenId) - + (api, newTxs, spentTxs) + } "Needs to take what was inserted" should { - "ids are the same" in { - + "getBoxById" in { + val (api, newTxs, _) = state (api.getBoxById(newTxs.head.id).get.id sameElements newTxs.head.id) shouldBe true - api.getAllWallets.nonEmpty shouldBe true } "amount in storage should be correct" in { + val (api, newTxs, spentTxs) = state val amountInStorage = api.getAllWallets .map(ch => api.getTokenBalanceByContractHash(ch, settingsR.constants.IntrinsicTokenId)) .foldLeft(0L) { @@ -86,13 +87,20 @@ class WalletDbSpec } "should contain intrinsic tokenId" in { + val (api, _, _) = state Algos.encode(api.getTokenIds(api.getAllWallets.head).head) shouldEqual Algos.encode( settingsR.constants.IntrinsicTokenId ) } - "" in { - api.getAssetBoxesByPredicate(api.getAllWallets.head, x => x.map(_.amount).sum > 0).nonEmpty shouldBe true + "getAllWallets" in { + val (api, _, _) = state + api.getAllWallets.nonEmpty shouldBe true + } + "getTypedBoxById" in { + val (api, newTxs, _) = state + api.getTypedBoxById[AssetBox](newTxs.head.id).isDefined shouldBe true } + } } From b68395af592c3a21d3fcebeb995bb76a4e806b06 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Thu, 20 Feb 2020 15:07:28 +0300 Subject: [PATCH 26/36] db tests improved --- .../scala/encry/view/wallet/WalletDB.scala | 12 ++- .../encry/view/wallet/WalletDBImpl.scala | 31 ++++---- .../encry/view/wallet/WalletDbSpec.scala | 73 ++++++++++++++----- 3 files changed, 78 insertions(+), 38 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index 9c1693047a..69ca22b38d 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -1,19 +1,19 @@ package encry.view.wallet -import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.VersionalLevelDbKey +import encry.settings.EncryAppSettings +import encry.storage.levelDb.versionalLevelDB.VersionalLevelDB import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } -import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash trait WalletDB { - def getAllWallets: List[ContractHash] - def getBoxById(id: ADKey): Option[EncryBaseBox] + def getAllWallets: List[ContractHash] + def getAssetBoxesByPredicate(contractHash: ContractHash, f: List[AssetBox] => Boolean): List[AssetBox] def getTokenIssuingBoxes(contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean): List[TokenIssuingBox] @@ -35,3 +35,7 @@ trait WalletDB { def rollback(modId: ModifierId): Unit } + +object WalletDB { + def apply(levelDb: VersionalLevelDB, settings: EncryAppSettings): WalletDB = WalletDBImpl.apply(levelDb, settings) +} diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index b68616b040..88df8777c6 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -35,10 +35,7 @@ class WalletDBImpl( .map(_.grouped(32).toList) .getOrElse(List.empty[ContractHash]) - override def getBoxById(id: ADKey): Option[EncryBaseBox] = - levelDb - .get(VersionalLevelDbKey @@ id.untag(ADKey)) - .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) + override def getBoxById(id: ADKey): Option[EncryBaseBox] = getTypedBoxById[EncryBaseBox](id) def getTypedBoxById[BXT: ClassTag](id: ADKey): Option[BXT] = levelDb @@ -50,7 +47,7 @@ class WalletDBImpl( getAllWallets.map(hash => hash -> getBalancesByContractHash(hash)).toMap def getTokenIds(hash: ContractHash): List[TokenId] = - levelDb + levelDb .get(hashToTokens(hash)) .map(_.grouped(32).toList) .getOrElse(List.empty[TokenId]) @@ -62,14 +59,13 @@ class WalletDBImpl( .getOrElse(List.empty[ADKey]) @tailrec - private def loop[BXT: ClassTag](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = { + private def loop[BXT: ClassTag](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = ids.headOption match { case Some(boxId: ADKey) => val newAcc: List[BXT] = getTypedBoxById[BXT](boxId).fold(acc)(_ :: acc) if (f(newAcc)) newAcc else loop(newAcc, ids.drop(1), f) case None => List.empty[BXT] } - } //List.empty override def getAssetBoxesByPredicate( @@ -128,7 +124,6 @@ class WalletDBImpl( } } - val infoAboutWalletToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { val toAddBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(newBxs, 1L) val toRemoveBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(spentBxs) @@ -141,7 +136,8 @@ class WalletDBImpl( } val updatedWallets = toAddBalances |+| toRemoveBalances |+| currentBalances val newContractHashes: Array[Byte] = updatedWallets.keys.toList.flatMap(id => Algos.decode(id).get).toArray - val contractHashToInsert: (VersionalLevelDbKey, VersionalLevelDbValue) = CONTRACT_HASH_ACCOUNTS -> VersionalLevelDbValue @@ newContractHashes + val contractHashToInsert + : (VersionalLevelDbKey, VersionalLevelDbValue) = CONTRACT_HASH_ACCOUNTS -> VersionalLevelDbValue @@ newContractHashes updatedWallets.flatMap { case (hash: String, idToAmount: Map[String, Amount]) => @@ -163,10 +159,10 @@ class WalletDBImpl( val boxesIdsToContractHashToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { def updatedFunction(hashToBxIds: Map[String, Set[String]], nextHash: String, key: VersionalLevelDbKey) = hashToBxIds.updated(nextHash, - getBoxesIdsByKey(key) - .filterNot(l => spentBxs.exists(_.id sameElements l)) - .map(Algos.encode) - .toSet) + getBoxesIdsByKey(key) + .filterNot(l => spentBxs.exists(_.id sameElements l)) + .map(Algos.encode) + .toSet) val ( assetsFromDb: Map[String, Set[String]], dataFromDB: Map[String, Set[String]], @@ -177,7 +173,6 @@ class WalletDBImpl( Map.empty[String, Set[String]] ) { case ((hashToAssetIds, hashToDataIds, hashToTokenIds), nextHash) => - val nextHashEncoded: String = Algos.encode(nextHash) (updatedFunction(hashToAssetIds, nextHashEncoded, assetBoxesByContractHashKey(nextHash)), updatedFunction(hashToDataIds, nextHashEncoded, dataBoxesByContractHashKey(nextHash)), @@ -249,7 +244,9 @@ class WalletDBImpl( private val CONTRACT_HASH_TOKEN_IDS: Array[Byte] = "CONTRACT_HASH_TOKEN_IDS".getBytes() - private val CONTRACT_HASH_ACCOUNTS: VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash("CONTRACT_HASH_ACCOUNTS").untag(Digest32) + private val CONTRACT_HASH_ACCOUNTS: VersionalLevelDbKey = VersionalLevelDbKey @@ Algos + .hash("CONTRACT_HASH_ACCOUNTS") + .untag(Digest32) def hashToTokens(userHash: ContractHash): VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash(userHash ++ CONTRACT_HASH_TOKEN_IDS) @@ -267,4 +264,6 @@ class WalletDBImpl( VersionalLevelDbKey @@ Algos.hash(userHash ++ tokenId) } -object WalletDBImpl {} +object WalletDBImpl { + def apply(levelDb: VersionalLevelDB, settings: EncryAppSettings): WalletDBImpl = new WalletDBImpl(levelDb, settings) +} diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 7fab799c7f..886bcc79c3 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -2,19 +2,24 @@ package encry.view.wallet import com.typesafe.scalalogging.StrictLogging import encry.modifiers.InstanceFactory -import encry.settings.{EncryAppSettings, LevelDBSettings, Settings} -import encry.storage.levelDb.versionalLevelDB.{LevelDbDiff, LevelDbFactory, VersionalLevelDBCompanion} -import encry.utils.{EncryGenerator, FileHelper} -import org.encryfoundation.common.modifiers.history.{Block, Payload} +import encry.settings.{ EncryAppSettings, LevelDBSettings, Settings } +import encry.storage.levelDb.versionalLevelDB.{ + LevelDbDiff, + LevelDbFactory, + VersionalLevelDB, + VersionalLevelDBCompanion +} +import encry.utils.{ EncryGenerator, FileHelper } +import org.encryfoundation.common.modifiers.history.{ Block, Payload } import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{AssetBox, EncryBaseBox, MonetaryBox} +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, MonetaryBox } import org.encryfoundation.common.utils.Algos -import org.encryfoundation.common.utils.TaggedTypes.{ADKey, ModifierId} +import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash -import org.iq80.leveldb.Options -import org.scalatest.{Matchers, PropSpec, WordSpecLike} +import org.iq80.leveldb.{ DB, Options } +import org.scalatest.{ Matchers, PropSpec, WordSpecLike } import org.scalatest.mockito.MockitoSugar import scorex.utils.Random @@ -37,24 +42,56 @@ class WalletDbSpec val settingsR: EncryAppSettings = EncryAppSettings.read() - val state = { - def init: WalletDBImpl = new WalletDBImpl(vldbInit, settingsR) - val api: WalletDBImpl = init - val validTxs: Seq[Transaction] = genValidPaymentTxs(3) - val useBox: AssetBox = validTxs.head.newBoxes.head.asInstanceOf[AssetBox] - val spentTx: Transaction = genValidPaymentTxToAddrWithSpentBoxes(IndexedSeq(useBox), randomAddress) - val blockPayload: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), validTxs) + def state = { + def init: WalletDBImpl = new WalletDBImpl(vldbInit, settingsR) + val api: WalletDBImpl = init + val validTxs: Seq[Transaction] = genValidPaymentTxs(3) + val useBox: AssetBox = validTxs.head.newBoxes.head.asInstanceOf[AssetBox] + val spentTx: Transaction = genValidPaymentTxToAddrWithSpentBoxes(IndexedSeq(useBox), randomAddress) + val blockPayload: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), validTxs) val blockPayloadWithSpentTx: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), Seq(spentTx)) - val newTxs: List[EncryBaseBox] = blockPayload.txs.flatMap(_.newBoxes).toList - val spentTxs: List[EncryBaseBox] = blockPayloadWithSpentTx.txs.flatMap(_.newBoxes).toList + val newTxs: List[EncryBaseBox] = blockPayload.txs.flatMap(_.newBoxes).toList + val spentTxs: List[EncryBaseBox] = blockPayloadWithSpentTx.txs.flatMap(_.newBoxes).toList val res: Unit = api.updateWallet(ModifierId @@ Random.randomBytes(), newTxs, spentTxs, settingsR.constants.IntrinsicTokenId) (api, newTxs, spentTxs) } + def initTestState: (WalletDB, Seq[EncryBaseBox]) = { + val levelDB: DB = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) + val vlDB: VersionalLevelDB = VersionalLevelDBCompanion(levelDB, dummyLevelDBSettings) + def dbInstance: WalletDB = WalletDB.apply(vlDB, settingsR) + val validTxs: Seq[Transaction] = genValidPaymentTxs(3) + val boxesToInsert: List[EncryBaseBox] = validTxs.flatMap(_.newBoxes).toList + dbInstance.updateWallet( + ModifierId @@ Random.randomBytes(), + boxesToInsert, + List.empty, + settings.constants.IntrinsicTokenId + ) + (dbInstance, boxesToInsert) + } + + "WalletDbStorage.getBoxById" should { + "return non empty value for existed box in db" in { + val (walletDb: WalletDBImpl, inserted: Seq[EncryBaseBox]) = initTestState + val comparisonResult: Boolean = inserted.forall { box => + val boxFromDB: Option[EncryBaseBox] = walletDb.getBoxById(box.id) + boxFromDB.isDefined && boxFromDB.forall { takenBox => + box.bytes.sameElements(takenBox.bytes) + } + } + comparisonResult shouldBe true + } + "return empty value for non existed box in db" in { + val (walletDb: WalletDBImpl, _: Seq[EncryBaseBox]) = initTestState + walletDb.getBoxById(ADKey @@ Random.randomBytes()).isEmpty shouldBe true + } + } + "Needs to take what was inserted" should { "getBoxById" in { - val (api, newTxs, _) = state + val (api, newTxs, _) = state (api.getBoxById(newTxs.head.id).get.id sameElements newTxs.head.id) shouldBe true } "amount in storage should be correct" in { From 22283047e6d942d00de6eac1db85a8ffc385fda1 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Thu, 20 Feb 2020 15:37:40 +0300 Subject: [PATCH 27/36] more tests added old tests improved --- .../encry/view/wallet/WalletDbSpec.scala | 48 +++++++++++++++++-- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 886bcc79c3..8702fa4225 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -61,8 +61,7 @@ class WalletDbSpec val levelDB: DB = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) val vlDB: VersionalLevelDB = VersionalLevelDBCompanion(levelDB, dummyLevelDBSettings) def dbInstance: WalletDB = WalletDB.apply(vlDB, settingsR) - val validTxs: Seq[Transaction] = genValidPaymentTxs(3) - val boxesToInsert: List[EncryBaseBox] = validTxs.flatMap(_.newBoxes).toList + val boxesToInsert: List[EncryBaseBox] = genValidPaymentTxs(3).flatMap(_.newBoxes).toList dbInstance.updateWallet( ModifierId @@ Random.randomBytes(), boxesToInsert, @@ -72,7 +71,7 @@ class WalletDbSpec (dbInstance, boxesToInsert) } - "WalletDbStorage.getBoxById" should { + "WalletDb.getBoxById" should { "return non empty value for existed box in db" in { val (walletDb: WalletDBImpl, inserted: Seq[EncryBaseBox]) = initTestState val comparisonResult: Boolean = inserted.forall { box => @@ -82,13 +81,54 @@ class WalletDbSpec } } comparisonResult shouldBe true + + val boxesToInsert: List[EncryBaseBox] = genValidPaymentTxs(5).flatMap(_.newBoxes).toList + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + boxesToInsert, + inserted.toList, + settings.constants.IntrinsicTokenId + ) + val comparisonResultNegative: Boolean = inserted.forall { box => + walletDb.getBoxById(box.id).isEmpty + } + val comparisonResultPositive: Boolean = boxesToInsert.forall { box => + val boxFromDB: Option[EncryBaseBox] = walletDb.getBoxById(box.id) + boxFromDB.isDefined && boxFromDB.forall { takenBox => + box.bytes.sameElements(takenBox.bytes) + } + } + (comparisonResultNegative && comparisonResultPositive) shouldBe true } "return empty value for non existed box in db" in { - val (walletDb: WalletDBImpl, _: Seq[EncryBaseBox]) = initTestState + val (walletDb: WalletDBImpl, _) = initTestState walletDb.getBoxById(ADKey @@ Random.randomBytes()).isEmpty shouldBe true } } + "WalletDb.getAllWallets" should { + "return all inserted wallets" in { + val (walletDb: WalletDBImpl, inserted: Seq[EncryBaseBox]) = initTestState + val wallets = walletDb.getAllWallets.map(Algos.encode) + val neededWallets = inserted.map(l => Algos.encode(l.proposition.contractHash)).toSet + (wallets.size == neededWallets.size && wallets.forall(neededWallets.contains)) shouldBe true + + val boxesToInsert: List[EncryBaseBox] = genValidPaymentTxs(5).flatMap(_.newBoxes).toList + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + boxesToInsert, + inserted.toList, + settings.constants.IntrinsicTokenId + ) + + val walletsNew = walletDb.getAllWallets.map(Algos.encode) + val neededWalletsNew = inserted.map(l => Algos.encode(l.proposition.contractHash)) ++ boxesToInsert.map( + l => Algos.encode(l.proposition.contractHash) + ) + (walletsNew.size == neededWalletsNew.size && walletsNew.forall(neededWalletsNew.contains)) shouldBe true + } + } + "Needs to take what was inserted" should { "getBoxById" in { val (api, newTxs, _) = state From 41126f9c01754a9f7f4f92b774d628f9e2c72583 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Thu, 20 Feb 2020 16:02:21 +0300 Subject: [PATCH 28/36] more test added --- .../scala/encry/view/wallet/WalletDB.scala | 1 + .../encry/view/wallet/WalletDbSpec.scala | 65 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index 69ca22b38d..bd60fb6134 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -20,6 +20,7 @@ trait WalletDB { def getDataBoxes(contractHash: ContractHash, f: List[DataBox] => Boolean): List[DataBox] + def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 8702fa4225..31c856c43c 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -2,6 +2,7 @@ package encry.view.wallet import com.typesafe.scalalogging.StrictLogging import encry.modifiers.InstanceFactory +import encry.modifiers.mempool.TransactionFactory import encry.settings.{ EncryAppSettings, LevelDBSettings, Settings } import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, @@ -9,12 +10,14 @@ import encry.storage.levelDb.versionalLevelDB.{ VersionalLevelDB, VersionalLevelDBCompanion } +import encry.utils.TestHelper.Props import encry.utils.{ EncryGenerator, FileHelper } +import org.encryfoundation.common.crypto.PrivateKey25519 import org.encryfoundation.common.modifiers.history.{ Block, Payload } import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, MonetaryBox } +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, EncryProposition, MonetaryBox } import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash @@ -110,7 +113,9 @@ class WalletDbSpec "return all inserted wallets" in { val (walletDb: WalletDBImpl, inserted: Seq[EncryBaseBox]) = initTestState val wallets = walletDb.getAllWallets.map(Algos.encode) - val neededWallets = inserted.map(l => Algos.encode(l.proposition.contractHash)).toSet + val neededWallets = inserted + .map(l => Algos.encode(l.proposition.contractHash)) + .toSet (wallets.size == neededWallets.size && wallets.forall(neededWallets.contains)) shouldBe true val boxesToInsert: List[EncryBaseBox] = genValidPaymentTxs(5).flatMap(_.newBoxes).toList @@ -129,6 +134,62 @@ class WalletDbSpec } } + "WalletDb.getAssetBoxesByPredicate" should { + "return a correct result if the result satisfies the predicate" in { + val (walletDb: WalletDBImpl, _) = initTestState + val moreBoxes: IndexedSeq[AssetBox] = { + val key: PrivateKey25519 = genPrivKeys(1).head + IndexedSeq( + AssetBox(EncryProposition.addressLocked(key.publicImage.address.address), 11, 999, None), + AssetBox(EncryProposition.addressLocked(key.publicImage.address.address), 111, 9999, None), + AssetBox(EncryProposition.addressLocked(key.publicImage.address.address), 112, 9991, None), + AssetBox(EncryProposition.addressLocked(key.publicImage.address.address), 113, 999654, None), + ) + } + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + moreBoxes.toList, + List.empty, + settings.constants.IntrinsicTokenId + ) + val boxes = walletDb.getAssetBoxesByPredicate( + moreBoxes.head.proposition.contractHash, + list => list.map(_.amount).sum == moreBoxes.map(_.amount).sum + ) + boxes.nonEmpty && boxes.forall { box => + moreBoxes.exists(_.bytes sameElements box.bytes) + } shouldBe true + + walletDb.getAssetBoxesByPredicate( + moreBoxes.head.proposition.contractHash, + list => list.map(_.amount).sum > moreBoxes.map(_.amount).sum + ).isEmpty shouldBe true + + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + List.empty, + moreBoxes.take(2).toList, + settings.constants.IntrinsicTokenId + ) + + val boxesNew = walletDb.getAssetBoxesByPredicate( + moreBoxes.head.proposition.contractHash, + list => list.map(_.amount).sum == moreBoxes.map(_.amount).sum + ) + boxesNew.nonEmpty && boxesNew.forall { box => + moreBoxes.exists(_.bytes sameElements box.bytes) + } shouldBe false + + val boxesNew1 = walletDb.getAssetBoxesByPredicate( + moreBoxes.head.proposition.contractHash, + list => list.map(_.amount).sum == moreBoxes.drop(2).map(_.amount).sum + ) + boxesNew1.nonEmpty && boxesNew1.forall { box => + moreBoxes.drop(2).exists(_.bytes sameElements box.bytes) + } shouldBe true + } + } + "Needs to take what was inserted" should { "getBoxById" in { val (api, newTxs, _) = state From 5b0f4f7fe528c45f074c09538721463a749f597b Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Thu, 20 Feb 2020 16:42:11 +0300 Subject: [PATCH 29/36] more test added --- .../scala/encry/view/wallet/WalletDB.scala | 8 +- .../encry/view/wallet/WalletDBImpl.scala | 48 +++++----- .../encry/view/wallet/WalletDbSpec.scala | 90 +++++++++++++++++-- 3 files changed, 116 insertions(+), 30 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index bd60fb6134..3dc6b8944d 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -16,10 +16,12 @@ trait WalletDB { def getAssetBoxesByPredicate(contractHash: ContractHash, f: List[AssetBox] => Boolean): List[AssetBox] - def getTokenIssuingBoxes(contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean): List[TokenIssuingBox] - - def getDataBoxes(contractHash: ContractHash, f: List[DataBox] => Boolean): List[DataBox] + def getTokenIssuingBoxesByPredicate( + contractHash: ContractHash, + f: List[TokenIssuingBox] => Boolean + ): List[TokenIssuingBox] + def getDataBoxesByPredicate(contractHash: ContractHash, f: List[DataBox] => Boolean): List[DataBox] def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 88df8777c6..c11902de44 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -71,24 +71,22 @@ class WalletDBImpl( override def getAssetBoxesByPredicate( contractHash: ContractHash, f: List[AssetBox] => Boolean - ): List[AssetBox] = - loop[AssetBox]( - List.empty[AssetBox], - getBoxesIdsByKey(assetBoxesByContractHashKey(contractHash)), - f - ) + ): List[AssetBox] = loop[AssetBox]( + List.empty[AssetBox], + getBoxesIdsByKey(assetBoxesByContractHashKey(contractHash)), + f + ) - override def getTokenIssuingBoxes( + override def getTokenIssuingBoxesByPredicate( contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean - ): List[TokenIssuingBox] = - loop[TokenIssuingBox]( - List.empty[TokenIssuingBox], - getBoxesIdsByKey(tokenIssuingBoxesByContractHashKey(contractHash)), - f - ) + ): List[TokenIssuingBox] = loop[TokenIssuingBox]( + List.empty[TokenIssuingBox], + getBoxesIdsByKey(tokenIssuingBoxesByContractHashKey(contractHash)), + f + ) - override def getDataBoxes( + override def getDataBoxesByPredicate( contractHash: ContractHash, f: List[DataBox] => Boolean ): List[DataBox] = loop[DataBox]( @@ -157,12 +155,18 @@ class WalletDBImpl( } val boxesIdsToContractHashToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { - def updatedFunction(hashToBxIds: Map[String, Set[String]], nextHash: String, key: VersionalLevelDbKey) = - hashToBxIds.updated(nextHash, - getBoxesIdsByKey(key) - .filterNot(l => spentBxs.exists(_.id sameElements l)) - .map(Algos.encode) - .toSet) + def updatedFunction( + hashToBxIds: Map[String, Set[String]], + nextHash: String, + key: VersionalLevelDbKey + ): Map[String, Set[String]] = + hashToBxIds.updated( + nextHash, + getBoxesIdsByKey(key) + .filterNot(l => spentBxs.exists(_.id sameElements l)) + .map(Algos.encode) + .toSet + ) val ( assetsFromDb: Map[String, Set[String]], dataFromDB: Map[String, Set[String]], @@ -212,9 +216,9 @@ class WalletDBImpl( val newAssetsToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = hashToBxsIdsToDB(assetsFromDb, hashToAssetBoxes, assetBoxesByContractHashKey) val newDataToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = - hashToBxsIdsToDB(dataFromDB, hashToDataBoxes, tokenIssuingBoxesByContractHashKey) + hashToBxsIdsToDB(dataFromDB, hashToDataBoxes, dataBoxesByContractHashKey) val newTokenToDB: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = - hashToBxsIdsToDB(tokensFromDB, hashToTokenBoxes, dataBoxesByContractHashKey) + hashToBxsIdsToDB(tokensFromDB, hashToTokenBoxes, tokenIssuingBoxesByContractHashKey) newAssetsToDB ::: newDataToDB ::: newTokenToDB } diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 31c856c43c..449dc66687 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -17,7 +17,14 @@ import org.encryfoundation.common.modifiers.history.{ Block, Payload } import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ AssetBox, EncryBaseBox, EncryProposition, MonetaryBox } +import org.encryfoundation.common.modifiers.state.box.{ + AssetBox, + DataBox, + EncryBaseBox, + EncryProposition, + MonetaryBox, + TokenIssuingBox +} import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash @@ -160,10 +167,12 @@ class WalletDbSpec moreBoxes.exists(_.bytes sameElements box.bytes) } shouldBe true - walletDb.getAssetBoxesByPredicate( - moreBoxes.head.proposition.contractHash, - list => list.map(_.amount).sum > moreBoxes.map(_.amount).sum - ).isEmpty shouldBe true + walletDb + .getAssetBoxesByPredicate( + moreBoxes.head.proposition.contractHash, + list => list.map(_.amount).sum > moreBoxes.map(_.amount).sum + ) + .isEmpty shouldBe true walletDb.updateWallet( ModifierId @@ Random.randomBytes(), @@ -190,6 +199,77 @@ class WalletDbSpec } } + "WalletDb.getTokenIssuingBoxes" should { + "return a correct result if the result satisfies the predicate" in { + val (walletDb: WalletDBImpl, _) = initTestState + val dataBoxesToInsert: IndexedSeq[DataBox] = { + val key: PrivateKey25519 = genPrivKeys(1).head + IndexedSeq( + DataBox(EncryProposition.addressLocked(key.publicImage.address.address), 99, Random.randomBytes()), + DataBox(EncryProposition.addressLocked(key.publicImage.address.address), 991, Random.randomBytes()), + DataBox(EncryProposition.addressLocked(key.publicImage.address.address), 992, Random.randomBytes()) + ) + } + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + dataBoxesToInsert.toList, + List.empty, + settings.constants.IntrinsicTokenId + ) + + walletDb + .getDataBoxesByPredicate( + dataBoxesToInsert.head.proposition.contractHash, + boxes => boxes.size == dataBoxesToInsert.size + ) + .nonEmpty shouldBe true + + walletDb + .getDataBoxesByPredicate( + dataBoxesToInsert.head.proposition.contractHash, + boxes => boxes.size > dataBoxesToInsert.size + ) + .nonEmpty shouldBe false + } + } + + "WalletDb.getDataBoxes" should { + "return a correct result if the result satisfies the predicate" in { + val (walletDb: WalletDBImpl, _) = initTestState + val tid1 = Random.randomBytes() + val tid2 = Random.randomBytes() + val tokenBoxesToInsert: IndexedSeq[TokenIssuingBox] = { + val key: PrivateKey25519 = genPrivKeys(1).head + IndexedSeq( + TokenIssuingBox(EncryProposition.addressLocked(key.publicImage.address.address), 1234L, 999, tid1), + TokenIssuingBox(EncryProposition.addressLocked(key.publicImage.address.address), 4321L, 9991, tid1), + TokenIssuingBox(EncryProposition.addressLocked(key.publicImage.address.address), 9887L, 9992, tid2), + TokenIssuingBox(EncryProposition.addressLocked(key.publicImage.address.address), 768594L, 9993, tid2) + ) + } + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + tokenBoxesToInsert.toList, + List.empty, + settings.constants.IntrinsicTokenId + ) + + walletDb + .getTokenIssuingBoxesByPredicate( + tokenBoxesToInsert.head.proposition.contractHash, + boxes => boxes.size == tokenBoxesToInsert.size + ) + .nonEmpty shouldBe true + + walletDb + .getDataBoxesByPredicate( + tokenBoxesToInsert.head.proposition.contractHash, + boxes => boxes.size > tokenBoxesToInsert.size + ) + .nonEmpty shouldBe false + } + } + "Needs to take what was inserted" should { "getBoxById" in { val (api, newTxs, _) = state From 3d76491ade82a37153255244fbf8601c61100a6f Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Thu, 20 Feb 2020 17:10:11 +0300 Subject: [PATCH 30/36] code cleaned up --- .../scala/encry/view/wallet/WalletDB.scala | 2 +- .../encry/view/wallet/WalletDBImpl.scala | 10 ++--- .../encry/view/wallet/WalletDbSpec.scala | 41 +++---------------- 3 files changed, 12 insertions(+), 41 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index 3dc6b8944d..b05374b0b3 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -25,7 +25,7 @@ trait WalletDB { def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] - def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount + def getBalances: Map[ContractHash, Map[TokenId, Amount]] def contains(id: ADKey): Boolean diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index c11902de44..bacd111886 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -37,16 +37,16 @@ class WalletDBImpl( override def getBoxById(id: ADKey): Option[EncryBaseBox] = getTypedBoxById[EncryBaseBox](id) - def getTypedBoxById[BXT: ClassTag](id: ADKey): Option[BXT] = + private def getTypedBoxById[BXT: ClassTag](id: ADKey): Option[BXT] = levelDb .get(VersionalLevelDbKey @@ id.untag(ADKey)) .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) .collect { case box: BXT => box } - def getBalances: Map[ContractHash, Map[TokenId, Amount]] = + override def getBalances: Map[ContractHash, Map[TokenId, Amount]] = getAllWallets.map(hash => hash -> getBalancesByContractHash(hash)).toMap - def getTokenIds(hash: ContractHash): List[TokenId] = + private def getTokenIds(hash: ContractHash): List[TokenId] = levelDb .get(hashToTokens(hash)) .map(_.grouped(32).toList) @@ -101,7 +101,7 @@ class WalletDBImpl( .map(_.grouped(32).toList.map(id => id -> getTokenBalanceByContractHash(contractHash, id)).toMap) .getOrElse(Map.empty) - override def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = + private def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = levelDb.get(tokenKeyByContractHash(contractHash, tokenId)).map(Longs.fromByteArray).getOrElse(0L) override def contains(id: ADKey): Boolean = getBoxById(id).isDefined @@ -252,7 +252,7 @@ class WalletDBImpl( .hash("CONTRACT_HASH_ACCOUNTS") .untag(Digest32) - def hashToTokens(userHash: ContractHash): VersionalLevelDbKey = + private def hashToTokens(userHash: ContractHash): VersionalLevelDbKey = VersionalLevelDbKey @@ Algos.hash(userHash ++ CONTRACT_HASH_TOKEN_IDS) private def assetBoxesByContractHashKey(userHash: ContractHash): VersionalLevelDbKey = diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 449dc66687..719f7c5519 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -2,35 +2,19 @@ package encry.view.wallet import com.typesafe.scalalogging.StrictLogging import encry.modifiers.InstanceFactory -import encry.modifiers.mempool.TransactionFactory import encry.settings.{ EncryAppSettings, LevelDBSettings, Settings } -import encry.storage.levelDb.versionalLevelDB.{ - LevelDbDiff, - LevelDbFactory, - VersionalLevelDB, - VersionalLevelDBCompanion -} -import encry.utils.TestHelper.Props +import encry.storage.levelDb.versionalLevelDB.{ LevelDbFactory, VersionalLevelDB, VersionalLevelDBCompanion } import encry.utils.{ EncryGenerator, FileHelper } import org.encryfoundation.common.crypto.PrivateKey25519 -import org.encryfoundation.common.modifiers.history.{ Block, Payload } +import org.encryfoundation.common.modifiers.history.Payload import org.encryfoundation.common.modifiers.mempool.transaction.Transaction import org.encryfoundation.common.modifiers.state.box.Box.Amount -import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ - AssetBox, - DataBox, - EncryBaseBox, - EncryProposition, - MonetaryBox, - TokenIssuingBox -} +import org.encryfoundation.common.modifiers.state.box._ import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } -import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import org.iq80.leveldb.{ DB, Options } -import org.scalatest.{ Matchers, PropSpec, WordSpecLike } import org.scalatest.mockito.MockitoSugar +import org.scalatest.{ Matchers, WordSpecLike } import scorex.utils.Random class WalletDbSpec @@ -199,7 +183,7 @@ class WalletDbSpec } } - "WalletDb.getTokenIssuingBoxes" should { + "WalletDb.getDataBoxes" should { "return a correct result if the result satisfies the predicate" in { val (walletDb: WalletDBImpl, _) = initTestState val dataBoxesToInsert: IndexedSeq[DataBox] = { @@ -233,7 +217,7 @@ class WalletDbSpec } } - "WalletDb.getDataBoxes" should { + "WalletDb.getTokenIssuingBoxes" should { "return a correct result if the result satisfies the predicate" in { val (walletDb: WalletDBImpl, _) = initTestState val tid1 = Random.randomBytes() @@ -271,10 +255,6 @@ class WalletDbSpec } "Needs to take what was inserted" should { - "getBoxById" in { - val (api, newTxs, _) = state - (api.getBoxById(newTxs.head.id).get.id sameElements newTxs.head.id) shouldBe true - } "amount in storage should be correct" in { val (api, newTxs, spentTxs) = state val amountInStorage = api.getAllWallets @@ -310,15 +290,6 @@ class WalletDbSpec settingsR.constants.IntrinsicTokenId ) } - "getAllWallets" in { - val (api, _, _) = state - api.getAllWallets.nonEmpty shouldBe true - } - "getTypedBoxById" in { - val (api, newTxs, _) = state - api.getTypedBoxById[AssetBox](newTxs.head.id).isDefined shouldBe true - } - } } From f4a26929bbba8aac2026d2cbbb569d760bed8546 Mon Sep 17 00:00:00 2001 From: Lior Date: Fri, 21 Feb 2020 16:29:25 +0300 Subject: [PATCH 31/36] tests added --- .../encry/view/wallet/WalletDBImpl.scala | 21 ++- .../encry/view/wallet/WalletDbSpec.scala | 150 +++++++++++++----- 2 files changed, 131 insertions(+), 40 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index bacd111886..f901af32df 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -124,15 +124,22 @@ class WalletDBImpl( val infoAboutWalletToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { val toAddBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(newBxs, 1L) + println(s"toAdd = $toAddBalances") val toRemoveBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(spentBxs) + println(s"toRemove = $toRemoveBalances") val currentBalances: Map[String, Map[String, Amount]] = getBalances.map { case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => + println(s"idsToAmount = ${idToAmount.map{ + case (a,b) => Algos.encode(a) -> b} + }") Algos.encode(hash) -> idToAmount.map { case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount } } + println(s"currentBalance = $currentBalances") val updatedWallets = toAddBalances |+| toRemoveBalances |+| currentBalances + println(s"updateWallets = $updatedWallets") val newContractHashes: Array[Byte] = updatedWallets.keys.toList.flatMap(id => Algos.decode(id).get).toArray val contractHashToInsert : (VersionalLevelDbKey, VersionalLevelDbValue) = CONTRACT_HASH_ACCOUNTS -> VersionalLevelDbValue @@ newContractHashes @@ -140,10 +147,16 @@ class WalletDBImpl( updatedWallets.flatMap { case (hash: String, idToAmount: Map[String, Amount]) => val decodedHash: Array[Byte] = Algos.decode(hash).get + val a: List[String] = getTokenIds(decodedHash).map(l => Algos.encode(l)) + val b: List[String] = idToAmount.keys.toList + val c: List[String] = a ::: b + val d: Set[String] = c.toSet + val e: List[String] = d.toList + val f: List[Array[Byte]] = e.map(j => Algos.decode(j).get) + val g: Array[Array[Byte]] = f.toArray + val h: Array[Byte] = g.flatten val tokenIdsToUpdate: (VersionalLevelDbKey, VersionalLevelDbValue) = - hashToTokens(decodedHash) -> VersionalLevelDbValue @@ (idToAmount.keys.toList - .flatMap(elem => Algos.decode(elem).get) - .toArray ++ getTokenIds(decodedHash).flatten) + hashToTokens(decodedHash) -> VersionalLevelDbValue @@ h val tokenIdUpdatedAmount: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = idToAmount.map { case (id: String, amount: Amount) => tokenKeyByContractHash(decodedHash, Algos.decode(id).get) -> @@ -154,6 +167,8 @@ class WalletDBImpl( }.toList } + println(s"infoAboutWalletToInsert -> ${infoAboutWalletToInsert.map(l => Algos.encode(l._1) -> Algos.encode(l._2)).mkString(",")}") + val boxesIdsToContractHashToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { def updatedFunction( hashToBxIds: Map[String, Set[String]], diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 719f7c5519..d0b5020b72 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -125,6 +125,12 @@ class WalletDbSpec } } + "WalletDb.getBalances" should { + "return a correct result" in { + + } + } + "WalletDb.getAssetBoxesByPredicate" should { "return a correct result if the result satisfies the predicate" in { val (walletDb: WalletDBImpl, _) = initTestState @@ -217,6 +223,82 @@ class WalletDbSpec } } + "WalletDb.getBalances" should { + "return correct balances" in { + val (walletDb: WalletDBImpl, _) = initTestState + val tkId1 = Random.randomBytes() + val tkId2 = Random.randomBytes() + val key = genPrivKeys(1) + val key2 = genPrivKeys(1) + val boxesToInsertForPerson1: IndexedSeq[EncryBox[EncryProposition]] = { + IndexedSeq( + TokenIssuingBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 1234L, 340, tkId1), + TokenIssuingBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 4321L, 570, tkId1), + DataBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 99, Random.randomBytes()), + AssetBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 11, 2000, None), + AssetBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 111, 3000, None) + ) + } + val boxesToInsertForPerson2: IndexedSeq[EncryBox[EncryProposition]] = { + IndexedSeq( + TokenIssuingBox(EncryProposition.addressLocked(key2.head.publicImage.address.address), 1234L, 999, tkId2), + TokenIssuingBox(EncryProposition.addressLocked(key2.head.publicImage.address.address), 4321L, 9991, tkId2), + DataBox(EncryProposition.addressLocked(key2.head.publicImage.address.address), 99, Random.randomBytes()), + AssetBox(EncryProposition.addressLocked(key2.head.publicImage.address.address), 11, 999, None), + AssetBox(EncryProposition.addressLocked(key2.head.publicImage.address.address), 111, 9999, None) + ) + } + val ch1 = boxesToInsertForPerson1.head.proposition.contractHash + val ch2 = boxesToInsertForPerson2.head.proposition.contractHash + println("qwe") + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + boxesToInsertForPerson1.toList , + List.empty, + settings.constants.IntrinsicTokenId + ) + println("<<< Algos.encode(x._1))) + println("qwe>>>>>") + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + boxesToInsertForPerson2.toList , + List.empty, + settings.constants.IntrinsicTokenId + ) + boxesToInsertForPerson1.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual + walletDb.getBalancesByContractHash(ch1).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + + boxesToInsertForPerson2.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual + walletDb.getBalancesByContractHash(ch2).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + + boxesToInsertForPerson1.drop(3).map(x => x.asInstanceOf[AssetBox]).map(x => x.amount).sum shouldEqual + walletDb.getBalancesByContractHash(ch1).filter{case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + + boxesToInsertForPerson2.drop(3).map(x => x.asInstanceOf[AssetBox]).map(x => x.amount).sum shouldEqual + walletDb.getBalancesByContractHash(ch2).filter{case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + + val boxesToRemoveForPerson1 = IndexedSeq( + TokenIssuingBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 1234L, 900, tkId1), + AssetBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 111, 4000, None) + ) + + val boxesToRemoveForPerson2 = IndexedSeq( + TokenIssuingBox(EncryProposition.addressLocked(key.last.publicImage.address.address), 1234L, 900, tkId2), + AssetBox(EncryProposition.addressLocked(key.last.publicImage.address.address), 111, 4000, None) + ) + + walletDb.updateWallet( + ModifierId @@ Random.randomBytes(), + List.empty, + boxesToRemoveForPerson1.toList ++ boxesToRemoveForPerson2.toList, + settings.constants.IntrinsicTokenId + ) + + println("123 " + walletDb.getBalancesByContractHash(ch1).map(x => Algos.encode(x._1))) + } + } + "WalletDb.getTokenIssuingBoxes" should { "return a correct result if the result satisfies the predicate" in { val (walletDb: WalletDBImpl, _) = initTestState @@ -254,42 +336,36 @@ class WalletDbSpec } } - "Needs to take what was inserted" should { - "amount in storage should be correct" in { - val (api, newTxs, spentTxs) = state - val amountInStorage = api.getAllWallets - .map(ch => api.getTokenBalanceByContractHash(ch, settingsR.constants.IntrinsicTokenId)) - .foldLeft(0L) { - case (acc, amount) => acc + amount - } - val amountToInsert: Long = { - val newTx: Long = newTxs.map { - case a: MonetaryBox => a.amount - }.foldLeft(0L) { - case (acc, amount) => acc + amount - } - val spent: Long = spentTxs.map { - case a: MonetaryBox => a.amount - }.foldLeft(0L) { - case (acc, amount) => acc + amount - } - newTx - spent - } - val getBalance: Amount = api.getAllWallets.map(x => api.getBalancesByContractHash(x)).foldLeft(0L) { - case (acc, amount) => acc + amount.values.sum - } - - amountInStorage shouldEqual amountToInsert - amountInStorage shouldEqual getBalance - amountToInsert shouldEqual getBalance - - } - "should contain intrinsic tokenId" in { - val (api, _, _) = state - Algos.encode(api.getTokenIds(api.getAllWallets.head).head) shouldEqual Algos.encode( - settingsR.constants.IntrinsicTokenId - ) - } - } +// "Needs to take what was inserted" should { +// "amount in storage should be correct" in { +// val (api, newTxs, spentTxs) = state +// val amountInStorage = api.getAllWallets +// .map(ch => api.getTokenBalanceByContractHash(ch, settingsR.constants.IntrinsicTokenId)) +// .foldLeft(0L) { +// case (acc, amount) => acc + amount +// } +// val amountToInsert: Long = { +// val newTx: Long = newTxs.map { +// case a: MonetaryBox => a.amount +// }.foldLeft(0L) { +// case (acc, amount) => acc + amount +// } +// val spent: Long = spentTxs.map { +// case a: MonetaryBox => a.amount +// }.foldLeft(0L) { +// case (acc, amount) => acc + amount +// } +// newTx - spent +// } +// val getBalance: Amount = api.getAllWallets.map(x => api.getBalancesByContractHash(x)).foldLeft(0L) { +// case (acc, amount) => acc + amount.values.sum +// } +// +// amountInStorage shouldEqual amountToInsert +// amountInStorage shouldEqual getBalance +// amountToInsert shouldEqual getBalance +// +// } +// } } From b815650a8d97fe9464fbf16ec0a6773ea5ca75b2 Mon Sep 17 00:00:00 2001 From: Lior Date: Fri, 21 Feb 2020 16:43:59 +0300 Subject: [PATCH 32/36] tests changed --- .../encry/view/wallet/WalletDbSpec.scala | 25 ++++++++----------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index d0b5020b72..69c507ee69 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -250,22 +250,13 @@ class WalletDbSpec } val ch1 = boxesToInsertForPerson1.head.proposition.contractHash val ch2 = boxesToInsertForPerson2.head.proposition.contractHash - println("qwe") walletDb.updateWallet( ModifierId @@ Random.randomBytes(), - boxesToInsertForPerson1.toList , - List.empty, - settings.constants.IntrinsicTokenId - ) - println("<<< Algos.encode(x._1))) - println("qwe>>>>>") - walletDb.updateWallet( - ModifierId @@ Random.randomBytes(), - boxesToInsertForPerson2.toList , + boxesToInsertForPerson1.toList ::: boxesToInsertForPerson2.toList, List.empty, settings.constants.IntrinsicTokenId ) + boxesToInsertForPerson1.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual walletDb.getBalancesByContractHash(ch1).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum @@ -284,8 +275,8 @@ class WalletDbSpec ) val boxesToRemoveForPerson2 = IndexedSeq( - TokenIssuingBox(EncryProposition.addressLocked(key.last.publicImage.address.address), 1234L, 900, tkId2), - AssetBox(EncryProposition.addressLocked(key.last.publicImage.address.address), 111, 4000, None) + TokenIssuingBox(EncryProposition.addressLocked(key2.head.publicImage.address.address), 1234L, 900, tkId2), + AssetBox(EncryProposition.addressLocked(key2.head.publicImage.address.address), 111, 4000, None) ) walletDb.updateWallet( @@ -295,7 +286,13 @@ class WalletDbSpec settings.constants.IntrinsicTokenId ) - println("123 " + walletDb.getBalancesByContractHash(ch1).map(x => Algos.encode(x._1))) + boxesToInsertForPerson1.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum - + boxesToRemoveForPerson1.take(1).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual + walletDb.getBalancesByContractHash(ch1).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + + boxesToInsertForPerson2.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum - + boxesToRemoveForPerson2.take(1).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual + walletDb.getBalancesByContractHash(ch2).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum } } From 8981e6b6a3c17d699b87f655efe8c7101fc9d455 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Fri, 21 Feb 2020 16:45:29 +0300 Subject: [PATCH 33/36] code cleaned up --- .../scala/encry/view/wallet/WalletDB.scala | 2 - .../encry/view/wallet/WalletDBImpl.scala | 78 ++++++++++--------- 2 files changed, 42 insertions(+), 38 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDB.scala b/src/main/scala/encry/view/wallet/WalletDB.scala index b05374b0b3..90b2deda93 100644 --- a/src/main/scala/encry/view/wallet/WalletDB.scala +++ b/src/main/scala/encry/view/wallet/WalletDB.scala @@ -12,8 +12,6 @@ trait WalletDB { def getBoxById(id: ADKey): Option[EncryBaseBox] - def getAllWallets: List[ContractHash] - def getAssetBoxesByPredicate(contractHash: ContractHash, f: List[AssetBox] => Boolean): List[AssetBox] def getTokenIssuingBoxesByPredicate( diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index f901af32df..8d5ae9593d 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -1,6 +1,9 @@ package encry.view.wallet -import cats.implicits._ +import cats.syntax.semigroup._ +import cats.instances.map._ +import cats.instances.long._ +import cats.instances.set._ import com.google.common.primitives.Longs import com.typesafe.scalalogging.StrictLogging import encry.settings.EncryAppSettings @@ -9,16 +12,17 @@ import encry.storage.levelDb.versionalLevelDB.VersionalLevelDBCompanion.{ VersionalLevelDbKey, VersionalLevelDbValue } -import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB, VersionalLevelDBCompanion } +import encry.storage.levelDb.versionalLevelDB.{ LevelDbDiff, VersionalLevelDB } import encry.utils.BalanceCalculator import org.encryfoundation.common.modifiers.state.StateModifierSerializer import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box.TokenIssuingBox.TokenId -import org.encryfoundation.common.modifiers.state.box.{ AssetBox, Box, DataBox, EncryBaseBox, TokenIssuingBox } +import org.encryfoundation.common.modifiers.state.box.{ AssetBox, DataBox, EncryBaseBox, TokenIssuingBox } import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } import org.encryfoundation.prismlang.compiler.CompiledContract.ContractHash import scorex.crypto.hash.Digest32 + import scala.annotation.tailrec import scala.reflect.ClassTag @@ -29,23 +33,29 @@ class WalletDBImpl( with StrictLogging with AutoCloseable { - override def getAllWallets: List[ContractHash] = - levelDb - .get(CONTRACT_HASH_ACCOUNTS) - .map(_.grouped(32).toList) - .getOrElse(List.empty[ContractHash]) - - override def getBoxById(id: ADKey): Option[EncryBaseBox] = getTypedBoxById[EncryBaseBox](id) - private def getTypedBoxById[BXT: ClassTag](id: ADKey): Option[BXT] = levelDb .get(VersionalLevelDbKey @@ id.untag(ADKey)) .flatMap(StateModifierSerializer.parseBytes(_, id.head).toOption) .collect { case box: BXT => box } + override def getBoxById(id: ADKey): Option[EncryBaseBox] = getTypedBoxById[EncryBaseBox](id) + override def getBalances: Map[ContractHash, Map[TokenId, Amount]] = getAllWallets.map(hash => hash -> getBalancesByContractHash(hash)).toMap + override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = + levelDb + .get(hashToTokens(contractHash)) + .map(_.grouped(32).toList.map(id => id -> getTokenBalanceByContractHash(contractHash, id)).toMap) + .getOrElse(Map.empty) + + private def getAllWallets: List[ContractHash] = + levelDb + .get(CONTRACT_HASH_ACCOUNTS) + .map(_.grouped(32).toList) + .getOrElse(List.empty[ContractHash]) + private def getTokenIds(hash: ContractHash): List[TokenId] = levelDb .get(hashToTokens(hash)) @@ -59,11 +69,11 @@ class WalletDBImpl( .getOrElse(List.empty[ADKey]) @tailrec - private def loop[BXT: ClassTag](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = + private def computePredicate[BXT: ClassTag](acc: List[BXT], ids: List[ADKey], f: List[BXT] => Boolean): List[BXT] = ids.headOption match { case Some(boxId: ADKey) => val newAcc: List[BXT] = getTypedBoxById[BXT](boxId).fold(acc)(_ :: acc) - if (f(newAcc)) newAcc else loop(newAcc, ids.drop(1), f) + if (f(newAcc)) newAcc else computePredicate(newAcc, ids.drop(1), f) case None => List.empty[BXT] } @@ -71,7 +81,7 @@ class WalletDBImpl( override def getAssetBoxesByPredicate( contractHash: ContractHash, f: List[AssetBox] => Boolean - ): List[AssetBox] = loop[AssetBox]( + ): List[AssetBox] = computePredicate[AssetBox]( List.empty[AssetBox], getBoxesIdsByKey(assetBoxesByContractHashKey(contractHash)), f @@ -80,7 +90,7 @@ class WalletDBImpl( override def getTokenIssuingBoxesByPredicate( contractHash: ContractHash, f: List[TokenIssuingBox] => Boolean - ): List[TokenIssuingBox] = loop[TokenIssuingBox]( + ): List[TokenIssuingBox] = computePredicate[TokenIssuingBox]( List.empty[TokenIssuingBox], getBoxesIdsByKey(tokenIssuingBoxesByContractHashKey(contractHash)), f @@ -89,18 +99,12 @@ class WalletDBImpl( override def getDataBoxesByPredicate( contractHash: ContractHash, f: List[DataBox] => Boolean - ): List[DataBox] = loop[DataBox]( + ): List[DataBox] = computePredicate[DataBox]( List.empty[DataBox], getBoxesIdsByKey(dataBoxesByContractHashKey(contractHash)), f ) - override def getBalancesByContractHash(contractHash: ContractHash): Map[TokenId, Amount] = - levelDb - .get(hashToTokens(contractHash)) - .map(_.grouped(32).toList.map(id => id -> getTokenBalanceByContractHash(contractHash, id)).toMap) - .getOrElse(Map.empty) - private def getTokenBalanceByContractHash(contractHash: ContractHash, tokenId: TokenId): Amount = levelDb.get(tokenKeyByContractHash(contractHash, tokenId)).map(Longs.fromByteArray).getOrElse(0L) @@ -123,22 +127,22 @@ class WalletDBImpl( } val infoAboutWalletToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { - val toAddBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(newBxs, 1L) + val toAddBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(newBxs, 1L) println(s"toAdd = $toAddBalances") val toRemoveBalances: Map[String, Map[String, Amount]] = balanceSheetFunction(spentBxs) println(s"toRemove = $toRemoveBalances") val currentBalances: Map[String, Map[String, Amount]] = getBalances.map { case (hash: ContractHash, idToAmount: Map[TokenId, Amount]) => - println(s"idsToAmount = ${idToAmount.map{ - case (a,b) => Algos.encode(a) -> b} - }") + println(s"idsToAmount = ${idToAmount.map { + case (a, b) => Algos.encode(a) -> b + }}") Algos.encode(hash) -> idToAmount.map { case (id: TokenId, amount: Amount) => Algos.encode(id) -> amount } } println(s"currentBalance = $currentBalances") - val updatedWallets = toAddBalances |+| toRemoveBalances |+| currentBalances + val updatedWallets = toAddBalances |+| toRemoveBalances |+| currentBalances println(s"updateWallets = $updatedWallets") val newContractHashes: Array[Byte] = updatedWallets.keys.toList.flatMap(id => Algos.decode(id).get).toArray val contractHashToInsert @@ -147,14 +151,14 @@ class WalletDBImpl( updatedWallets.flatMap { case (hash: String, idToAmount: Map[String, Amount]) => val decodedHash: Array[Byte] = Algos.decode(hash).get - val a: List[String] = getTokenIds(decodedHash).map(l => Algos.encode(l)) - val b: List[String] = idToAmount.keys.toList - val c: List[String] = a ::: b - val d: Set[String] = c.toSet - val e: List[String] = d.toList - val f: List[Array[Byte]] = e.map(j => Algos.decode(j).get) - val g: Array[Array[Byte]] = f.toArray - val h: Array[Byte] = g.flatten + val a: List[String] = getTokenIds(decodedHash).map(l => Algos.encode(l)) + val b: List[String] = idToAmount.keys.toList + val c: List[String] = a ::: b + val d: Set[String] = c.toSet + val e: List[String] = d.toList + val f: List[Array[Byte]] = e.map(j => Algos.decode(j).get) + val g: Array[Array[Byte]] = f.toArray + val h: Array[Byte] = g.flatten val tokenIdsToUpdate: (VersionalLevelDbKey, VersionalLevelDbValue) = hashToTokens(decodedHash) -> VersionalLevelDbValue @@ h val tokenIdUpdatedAmount: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = idToAmount.map { @@ -167,7 +171,9 @@ class WalletDBImpl( }.toList } - println(s"infoAboutWalletToInsert -> ${infoAboutWalletToInsert.map(l => Algos.encode(l._1) -> Algos.encode(l._2)).mkString(",")}") + println( + s"infoAboutWalletToInsert -> ${infoAboutWalletToInsert.map(l => Algos.encode(l._1) -> Algos.encode(l._2)).mkString(",")}" + ) val boxesIdsToContractHashToInsert: List[(VersionalLevelDbKey, VersionalLevelDbValue)] = { def updatedFunction( From 4c6e859fd0158d5338edf4e7803ada1532266ae8 Mon Sep 17 00:00:00 2001 From: Lior Date: Fri, 21 Feb 2020 16:48:27 +0300 Subject: [PATCH 34/36] code cleanup --- .../encry/view/wallet/WalletDbSpec.scala | 54 ++++++++++++++----- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 69c507ee69..48f8294206 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -126,9 +126,7 @@ class WalletDbSpec } "WalletDb.getBalances" should { - "return a correct result" in { - - } + "return a correct result" in {} } "WalletDb.getAssetBoxesByPredicate" should { @@ -226,10 +224,10 @@ class WalletDbSpec "WalletDb.getBalances" should { "return correct balances" in { val (walletDb: WalletDBImpl, _) = initTestState - val tkId1 = Random.randomBytes() - val tkId2 = Random.randomBytes() - val key = genPrivKeys(1) - val key2 = genPrivKeys(1) + val tkId1 = Random.randomBytes() + val tkId2 = Random.randomBytes() + val key = genPrivKeys(1) + val key2 = genPrivKeys(1) val boxesToInsertForPerson1: IndexedSeq[EncryBox[EncryProposition]] = { IndexedSeq( TokenIssuingBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 1234L, 340, tkId1), @@ -258,16 +256,36 @@ class WalletDbSpec ) boxesToInsertForPerson1.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual - walletDb.getBalancesByContractHash(ch1).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + walletDb + .getBalancesByContractHash(ch1) + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .values + .toList + .sum boxesToInsertForPerson2.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual - walletDb.getBalancesByContractHash(ch2).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + walletDb + .getBalancesByContractHash(ch2) + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .values + .toList + .sum boxesToInsertForPerson1.drop(3).map(x => x.asInstanceOf[AssetBox]).map(x => x.amount).sum shouldEqual - walletDb.getBalancesByContractHash(ch1).filter{case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + walletDb + .getBalancesByContractHash(ch1) + .filter { case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId) } + .values + .toList + .sum boxesToInsertForPerson2.drop(3).map(x => x.asInstanceOf[AssetBox]).map(x => x.amount).sum shouldEqual - walletDb.getBalancesByContractHash(ch2).filter{case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + walletDb + .getBalancesByContractHash(ch2) + .filter { case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId) } + .values + .toList + .sum val boxesToRemoveForPerson1 = IndexedSeq( TokenIssuingBox(EncryProposition.addressLocked(key.head.publicImage.address.address), 1234L, 900, tkId1), @@ -288,11 +306,21 @@ class WalletDbSpec boxesToInsertForPerson1.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum - boxesToRemoveForPerson1.take(1).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual - walletDb.getBalancesByContractHash(ch1).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + walletDb + .getBalancesByContractHash(ch1) + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .values + .toList + .sum boxesToInsertForPerson2.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum - boxesToRemoveForPerson2.take(1).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual - walletDb.getBalancesByContractHash(ch2).filter{case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId)}.values.toList.sum + walletDb + .getBalancesByContractHash(ch2) + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .values + .toList + .sum } } From a7f53d5a42831c1c29a6ba536caf9eb52121b162 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Fri, 21 Feb 2020 16:49:56 +0300 Subject: [PATCH 35/36] code cleaned up --- .../encry/view/wallet/WalletDBImpl.scala | 2 +- .../encry/view/wallet/WalletDbSpec.scala | 69 ++----------------- 2 files changed, 5 insertions(+), 66 deletions(-) diff --git a/src/main/scala/encry/view/wallet/WalletDBImpl.scala b/src/main/scala/encry/view/wallet/WalletDBImpl.scala index 8d5ae9593d..709e1e9e12 100644 --- a/src/main/scala/encry/view/wallet/WalletDBImpl.scala +++ b/src/main/scala/encry/view/wallet/WalletDBImpl.scala @@ -50,7 +50,7 @@ class WalletDBImpl( .map(_.grouped(32).toList.map(id => id -> getTokenBalanceByContractHash(contractHash, id)).toMap) .getOrElse(Map.empty) - private def getAllWallets: List[ContractHash] = + def getAllWallets: List[ContractHash] = levelDb .get(CONTRACT_HASH_ACCOUNTS) .map(_.grouped(32).toList) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 48f8294206..60dbd141df 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -2,13 +2,10 @@ package encry.view.wallet import com.typesafe.scalalogging.StrictLogging import encry.modifiers.InstanceFactory -import encry.settings.{ EncryAppSettings, LevelDBSettings, Settings } +import encry.settings.{ LevelDBSettings, Settings } import encry.storage.levelDb.versionalLevelDB.{ LevelDbFactory, VersionalLevelDB, VersionalLevelDBCompanion } import encry.utils.{ EncryGenerator, FileHelper } import org.encryfoundation.common.crypto.PrivateKey25519 -import org.encryfoundation.common.modifiers.history.Payload -import org.encryfoundation.common.modifiers.mempool.transaction.Transaction -import org.encryfoundation.common.modifiers.state.box.Box.Amount import org.encryfoundation.common.modifiers.state.box._ import org.encryfoundation.common.utils.Algos import org.encryfoundation.common.utils.TaggedTypes.{ ADKey, ModifierId } @@ -26,35 +23,10 @@ class WalletDbSpec with Settings with MockitoSugar { - val levelDbElemsQty = 10 - - val dummyLevelDBSettings: LevelDBSettings = LevelDBSettings(5) - - val levelDBInit = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) - - val vldbInit = VersionalLevelDBCompanion(levelDBInit, dummyLevelDBSettings) - - val settingsR: EncryAppSettings = EncryAppSettings.read() - - def state = { - def init: WalletDBImpl = new WalletDBImpl(vldbInit, settingsR) - val api: WalletDBImpl = init - val validTxs: Seq[Transaction] = genValidPaymentTxs(3) - val useBox: AssetBox = validTxs.head.newBoxes.head.asInstanceOf[AssetBox] - val spentTx: Transaction = genValidPaymentTxToAddrWithSpentBoxes(IndexedSeq(useBox), randomAddress) - val blockPayload: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), validTxs) - val blockPayloadWithSpentTx: Payload = Payload(ModifierId @@ Array.fill(32)(19: Byte), Seq(spentTx)) - val newTxs: List[EncryBaseBox] = blockPayload.txs.flatMap(_.newBoxes).toList - val spentTxs: List[EncryBaseBox] = blockPayloadWithSpentTx.txs.flatMap(_.newBoxes).toList - val res: Unit = - api.updateWallet(ModifierId @@ Random.randomBytes(), newTxs, spentTxs, settingsR.constants.IntrinsicTokenId) - (api, newTxs, spentTxs) - } - def initTestState: (WalletDB, Seq[EncryBaseBox]) = { val levelDB: DB = LevelDbFactory.factory.open(FileHelper.getRandomTempDir, new Options) - val vlDB: VersionalLevelDB = VersionalLevelDBCompanion(levelDB, dummyLevelDBSettings) - def dbInstance: WalletDB = WalletDB.apply(vlDB, settingsR) + val vlDB: VersionalLevelDB = VersionalLevelDBCompanion(levelDB, LevelDBSettings(5)) + def dbInstance: WalletDB = WalletDB.apply(vlDB, settings) val boxesToInsert: List[EncryBaseBox] = genValidPaymentTxs(3).flatMap(_.newBoxes).toList dbInstance.updateWallet( ModifierId @@ Random.randomBytes(), @@ -67,7 +39,7 @@ class WalletDbSpec "WalletDb.getBoxById" should { "return non empty value for existed box in db" in { - val (walletDb: WalletDBImpl, inserted: Seq[EncryBaseBox]) = initTestState + val (walletDb: WalletDB, inserted: Seq[EncryBaseBox]) = initTestState val comparisonResult: Boolean = inserted.forall { box => val boxFromDB: Option[EncryBaseBox] = walletDb.getBoxById(box.id) boxFromDB.isDefined && boxFromDB.forall { takenBox => @@ -360,37 +332,4 @@ class WalletDbSpec .nonEmpty shouldBe false } } - -// "Needs to take what was inserted" should { -// "amount in storage should be correct" in { -// val (api, newTxs, spentTxs) = state -// val amountInStorage = api.getAllWallets -// .map(ch => api.getTokenBalanceByContractHash(ch, settingsR.constants.IntrinsicTokenId)) -// .foldLeft(0L) { -// case (acc, amount) => acc + amount -// } -// val amountToInsert: Long = { -// val newTx: Long = newTxs.map { -// case a: MonetaryBox => a.amount -// }.foldLeft(0L) { -// case (acc, amount) => acc + amount -// } -// val spent: Long = spentTxs.map { -// case a: MonetaryBox => a.amount -// }.foldLeft(0L) { -// case (acc, amount) => acc + amount -// } -// newTx - spent -// } -// val getBalance: Amount = api.getAllWallets.map(x => api.getBalancesByContractHash(x)).foldLeft(0L) { -// case (acc, amount) => acc + amount.values.sum -// } -// -// amountInStorage shouldEqual amountToInsert -// amountInStorage shouldEqual getBalance -// amountToInsert shouldEqual getBalance -// -// } -// } - } From 2fc78242137ff0022599063b92f5afe59122e926 Mon Sep 17 00:00:00 2001 From: Timofey Gusev Date: Fri, 21 Feb 2020 16:50:20 +0300 Subject: [PATCH 36/36] code cleaned up --- src/test/scala/encry/view/wallet/WalletDbSpec.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/scala/encry/view/wallet/WalletDbSpec.scala b/src/test/scala/encry/view/wallet/WalletDbSpec.scala index 60dbd141df..bb2a527e70 100644 --- a/src/test/scala/encry/view/wallet/WalletDbSpec.scala +++ b/src/test/scala/encry/view/wallet/WalletDbSpec.scala @@ -230,7 +230,7 @@ class WalletDbSpec boxesToInsertForPerson1.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual walletDb .getBalancesByContractHash(ch1) - .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settings.constants.IntrinsicTokenId) } .values .toList .sum @@ -238,7 +238,7 @@ class WalletDbSpec boxesToInsertForPerson2.take(2).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual walletDb .getBalancesByContractHash(ch2) - .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settings.constants.IntrinsicTokenId) } .values .toList .sum @@ -246,7 +246,7 @@ class WalletDbSpec boxesToInsertForPerson1.drop(3).map(x => x.asInstanceOf[AssetBox]).map(x => x.amount).sum shouldEqual walletDb .getBalancesByContractHash(ch1) - .filter { case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId) } + .filter { case (id, _) => Algos.encode(id) == Algos.encode(settings.constants.IntrinsicTokenId) } .values .toList .sum @@ -254,7 +254,7 @@ class WalletDbSpec boxesToInsertForPerson2.drop(3).map(x => x.asInstanceOf[AssetBox]).map(x => x.amount).sum shouldEqual walletDb .getBalancesByContractHash(ch2) - .filter { case (id, _) => Algos.encode(id) == Algos.encode(settingsR.constants.IntrinsicTokenId) } + .filter { case (id, _) => Algos.encode(id) == Algos.encode(settings.constants.IntrinsicTokenId) } .values .toList .sum @@ -280,7 +280,7 @@ class WalletDbSpec boxesToRemoveForPerson1.take(1).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual walletDb .getBalancesByContractHash(ch1) - .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settings.constants.IntrinsicTokenId) } .values .toList .sum @@ -289,7 +289,7 @@ class WalletDbSpec boxesToRemoveForPerson2.take(1).map(x => x.asInstanceOf[TokenIssuingBox]).map(x => x.amount).sum shouldEqual walletDb .getBalancesByContractHash(ch2) - .filter { case (id, _) => Algos.encode(id) != Algos.encode(settingsR.constants.IntrinsicTokenId) } + .filter { case (id, _) => Algos.encode(id) != Algos.encode(settings.constants.IntrinsicTokenId) } .values .toList .sum