Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
efd5c71
getBalance type change
liorl00 Feb 10, 2020
b2a00b0
wallet types were clarified
GusevTimofey Feb 10, 2020
8404eea
taking as many boxes as we need
liorl00 Feb 10, 2020
b6c0aea
code cleanup
liorl00 Feb 11, 2020
1cef015
Merge branch 'master' into ec-835
liorl00 Feb 11, 2020
e41d901
code cleanup
liorl00 Feb 11, 2020
ea09a39
Merge remote-tracking branch 'origin/ec-835' into ec-835
liorl00 Feb 11, 2020
ab4a11f
Merge branch 'master' into ec-835
liorl00 Feb 11, 2020
9b28641
test changed
liorl00 Feb 11, 2020
9bd38ef
added frame for new wallet db impl
GusevTimofey Feb 12, 2020
b9406c5
first impl of updateWallet function
GusevTimofey Feb 17, 2020
1f43cea
balanceSheet reworked
GusevTimofey Feb 17, 2020
c975c6c
balancesToInsert improved + cleaned
GusevTimofey Feb 17, 2020
5761ab0
Merge branch 'master' into ec-835
GusevTimofey Feb 17, 2020
3777faa
updateWallet improved + cleaned
GusevTimofey Feb 17, 2020
5bad14d
fixed positive balance in toRemove balance
GusevTimofey Feb 17, 2020
f5d1e10
functionality to get different types of boxes
liorl00 Feb 18, 2020
313fc68
minor code cleaned up
GusevTimofey Feb 18, 2020
10a5b79
api changed
liorl00 Feb 18, 2020
602e2be
minor code cleaned up
GusevTimofey Feb 18, 2020
eb241ef
implemented getBalancesByContractHash, getTokenBalanceByContractHash
GusevTimofey Feb 18, 2020
3f8106e
getBalance function
liorl00 Feb 18, 2020
bc57e6f
code cleaned up
GusevTimofey Feb 18, 2020
ff998e8
code cleanup
liorl00 Feb 19, 2020
23e1a9d
fixed compile error
GusevTimofey Feb 19, 2020
169f5f7
tests added
liorl00 Feb 20, 2020
ab9cbcd
tests added
liorl00 Feb 20, 2020
61bf9f7
fixed hashToBxsIdsToDB function
GusevTimofey Feb 20, 2020
fc4d1d0
tests added
liorl00 Feb 20, 2020
b68395a
db tests improved
GusevTimofey Feb 20, 2020
2228304
more tests added
GusevTimofey Feb 20, 2020
41126f9
more test added
GusevTimofey Feb 20, 2020
5b0f4f7
more test added
GusevTimofey Feb 20, 2020
3d76491
code cleaned up
GusevTimofey Feb 20, 2020
f4a2692
tests added
liorl00 Feb 21, 2020
b815650
tests changed
liorl00 Feb 21, 2020
8981e6b
code cleaned up
GusevTimofey Feb 21, 2020
4c6e859
code cleanup
liorl00 Feb 21, 2020
a7f53d5
code cleaned up
GusevTimofey Feb 21, 2020
2fc7824
code cleaned up
GusevTimofey Feb 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions src/main/scala/encry/api/http/DataHolderForApi.scala
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -26,11 +27,14 @@ 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 scorex.crypto.signatures.PublicKey

import scala.concurrent.Future

class DataHolderForApi(settings: EncryAppSettings, ntp: NetworkTimeProvider)
Expand Down Expand Up @@ -197,12 +201,8 @@ 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
}).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 =>
Expand Down
300 changes: 167 additions & 133 deletions src/main/scala/encry/api/http/routes/WalletInfoApiRoute.scala

Large diffs are not rendered by default.

38 changes: 18 additions & 20 deletions src/main/scala/encry/api/http/routes/WalletRoute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import com.typesafe.scalalogging.StrictLogging
import encry.api.http.DataHolderForApi.{GetViewGetBalance, GetViewPrintPubKeys}
import encry.settings.{EncryAppSettings, RESTApiSettings}
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
Expand All @@ -24,19 +25,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[(PublicKey, TokenId), Amount]] =
(dataHolder ? GetViewGetBalance)
.mapTo[Map[String, List[(String, Amount)]]]
.mapTo[Map[(PublicKey, 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[(PublicKey, 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[(PublicKey, TokenId), Amount]): Text.TypedTag[String] = {
html(
scalatags.Text.all.head(
meta(charset := "utf-8"),
Expand Down Expand Up @@ -426,10 +426,9 @@ case class WalletRoute(settings: RESTApiSettings,
div(cls := "form-group",
select(cls := "form-control", id :="coin", name:="coin",
for {
coinI <- balances.toList
coinIds <- coinI._2
walletMap <- balances.toList
} yield {
option(value := coinIds._1, if (coinIds._1 == EttTokenId) s"ETT (${coinIds._2/100000000})" else coinIds._1)
option(value := walletMap._1._2.toString, if (Algos.encode(walletMap._1._2) == EttTokenId) s"ETT (${walletMap._2/100000000})" else Algos.encode(walletMap._1._2))
}
)
),
Expand Down Expand Up @@ -686,9 +685,9 @@ 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)
)
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 := "", "")
}
Expand Down Expand Up @@ -721,17 +720,16 @@ case class WalletRoute(settings: RESTApiSettings,
tbody(
if (balances.nonEmpty) {
(for {
mapKeyValue <- balances
tokenAmount <- mapKeyValue._2
mapKeyValue <- balances.toList
} yield {
val tknStr = tokenAmount._1 match {
case tokenId if tokenId == EttTokenId => "ETT"
case tokenId => tokenId
val tknStr = mapKeyValue._1._2 match {
case tokenId if Algos.encode(tokenId) == EttTokenId => "ETT"
case tokenId => Algos.encode(tokenId)
}
tr(
th(mapKeyValue._1),
th(Algos.encode(mapKeyValue._1._1)),
th(tknStr),
if (tokenAmount._1 == EttTokenId ) th(tokenAmount._2/100000000) else th(tokenAmount._2)
if (Algos.encode(mapKeyValue._1._2) == EttTokenId ) th(mapKeyValue._2/100000000) else th(mapKeyValue._2)
)
}).toList
} else {
Expand Down Expand Up @@ -764,7 +762,7 @@ case class WalletRoute(settings: RESTApiSettings,
),
tbody(
if(balances.keys.nonEmpty) {
for (p <- balances.keys.toList) yield {
for (p <- balances.toList.map(x => Algos.encode(x._1._1))) yield {
tr(th(attr("scope") := "row", p))
}
} else {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -13,8 +14,10 @@ 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 scala.util.Success

case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends StrictLogging with AutoCloseable {
Expand All @@ -32,9 +35,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

Expand All @@ -43,17 +48,27 @@ 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[(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
}
val res = toRemoveFromBalance |+| toAddToBalance |+| prevBalance
res.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 ++ Algos.decode(tokenId).get ++ 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),
Expand All @@ -62,10 +77,10 @@ case class WalletVersionalLevelDB(db: DB, settings: LevelDBSettings) extends Str
)
}

def getBalances: Map[(String, String), Amount] =
def getBalances: Map[(ContractHash, 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 => (ch.take(32), ch.slice(32, 64)) -> Longs.fromByteArray(ch.takeRight(8)))
.toMap).getOrElse(Map.empty)

override def close(): Unit = levelDb.close()
Expand Down
14 changes: 7 additions & 7 deletions src/main/scala/encry/utils/BalanceCalculator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, ByteStr), Amount]) {
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 = Algos.encode(bx.proposition.contractHash)
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 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.arr) -> am }
}
}.map { case ((hash, id), am) => (hash.arr -> id.arr) -> am }
}
39 changes: 22 additions & 17 deletions src/main/scala/encry/view/wallet/EncryWallet.scala
Original file line number Diff line number Diff line change
@@ -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._
Expand All @@ -14,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}
Expand All @@ -23,10 +24,15 @@ 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.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)
Expand Down Expand Up @@ -87,21 +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[((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: 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 {
case ((hash, tokenId), amount) => (contractHashToKey(ByteStr(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.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)).toList

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
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(" ")
Expand Down Expand Up @@ -154,7 +158,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
Expand All @@ -172,9 +176,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)
}
}
}
Loading