Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Candidate for 5.0.11 release #1989

Merged
merged 19 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
19 changes: 6 additions & 13 deletions avldb/src/main/scala/scorex/db/KVStoreReader.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,27 +95,20 @@ trait KVStoreReader extends AutoCloseable {
* @param end - end of the range (inclusive)
* @return
*/
def getRange(start: K, end: K, limit: Int = Int.MaxValue): Seq[(K, V)] = {
def getRange(start: K, end: K, limit: Int = Int.MaxValue): Array[(K, V)] = {
val ro = new ReadOptions()
ro.snapshot(db.getSnapshot)
val iter = db.iterator(ro)
try {
def check(key:Array[Byte]) = {
if (ByteArrayUtils.compare(key, end) <= 0) {
true
} else {
false
}
}
iter.seek(start)
val bf = mutable.ArrayBuffer.empty[(K, V)]
var elemCounter = 0
while (iter.hasNext && check(iter.peekNext.getKey) && elemCounter < limit) {
while (iter.hasNext && elemCounter < limit) {
val next = iter.next()
val key = next.getKey
val value = next.getValue
elemCounter += 1
bf += (key -> value)
if(ByteArrayUtils.compare(next.getKey, end) <= 0) {
elemCounter += 1
bf += (next.getKey -> next.getValue)
} else elemCounter = limit // break
}
bf.toArray[(K,V)]
} finally {
Expand Down
22 changes: 13 additions & 9 deletions src/main/resources/api/openapi.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: "3.0.2"

info:
version: "5.0.10"
version: "5.0.11"
title: Ergo Node API
description: API docs for Ergo Node. Models are shared between all Ergo products
contact:
Expand Down Expand Up @@ -3016,7 +3016,7 @@ paths:
application/json:
schema:
type: string
example: '02c9e71790399816b3e40b2207e9ade19a9b7fe0600186cfb8e2b115bfdb34b57f38cd3c9f2890d11720eb3bb993993f00ededf812a590d2993df094a7ca4f0213e4820e1ab831eed5dc5c72665396d3a01d2a12900f1c3ab77700b284ae24fa8e8f7754f86f2282c795db6b0b17df1c29cc0552e59d01f7d777c638a813333277271c2f8b4d99d01ff0e6ee8695697bdd5b568089395620d7198c6093ce8bc59b928611b1b12452c05addaa42f4beff6a0a6fe90000000380d0dbc3f40210090402040005c801040205c8010500040004000e2003faf2cb329f2e90d6d23b58d91bbb6c046aa143261cc21f52fbe2824bfcbf04d807d601e4c6a70408d602b2a5730000d603e4c6a70601d604e4c6a7080ed605e4c6a70505d606e4c6a70705d60795720399c1a7c1720299c17202c1a7eb027201d1ededededededededed93c27202c2a793e4c672020408720193e4c6720205059572039d9c72057eb272047301000573029d9c72057eb2720473030005730494e4c672020601720393e4c672020705720693e4c67202080e720493e4c67202090ec5a79572039072079c720672059272079c72067205917207730595ef720393b1db630872027306d801d608b2db63087202730700ed938c7208017308938c7208027206c8df35000508cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba0405c8a8c105010105dc8b020e0266608cdea8baf0380008cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba04c8df350000c0843d1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304c8df350000'
example: '"02c9e71790399816b3e40b2207e9ade19a9b7fe0600186cfb8e2b115bfdb34b57f38cd3c9f2890d11720eb3bb993993f00ededf812a590d2993df094a7ca4f0213e4820e1ab831eed5dc5c72665396d3a01d2a12900f1c3ab77700b284ae24fa8e8f7754f86f2282c795db6b0b17df1c29cc0552e59d01f7d777c638a813333277271c2f8b4d99d01ff0e6ee8695697bdd5b568089395620d7198c6093ce8bc59b928611b1b12452c05addaa42f4beff6a0a6fe90000000380d0dbc3f40210090402040005c801040205c8010500040004000e2003faf2cb329f2e90d6d23b58d91bbb6c046aa143261cc21f52fbe2824bfcbf04d807d601e4c6a70408d602b2a5730000d603e4c6a70601d604e4c6a7080ed605e4c6a70505d606e4c6a70705d60795720399c1a7c1720299c17202c1a7eb027201d1ededededededededed93c27202c2a793e4c672020408720193e4c6720205059572039d9c72057eb272047301000573029d9c72057eb2720473030005730494e4c672020601720393e4c672020705720693e4c67202080e720493e4c67202090ec5a79572039072079c720672059272079c72067205917207730595ef720393b1db630872027306d801d608b2db63087202730700ed938c7208017308938c7208027206c8df35000508cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba0405c8a8c105010105dc8b020e0266608cdea8baf0380008cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba04c8df350000c0843d1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304c8df350000"'
responses:
'200':
description: JSON with ID of the new transaction
Expand Down Expand Up @@ -3072,7 +3072,7 @@ paths:
application/json:
schema:
type: string
example: '02c9e71790399816b3e40b2207e9ade19a9b7fe0600186cfb8e2b115bfdb34b57f38cd3c9f2890d11720eb3bb993993f00ededf812a590d2993df094a7ca4f0213e4820e1ab831eed5dc5c72665396d3a01d2a12900f1c3ab77700b284ae24fa8e8f7754f86f2282c795db6b0b17df1c29cc0552e59d01f7d777c638a813333277271c2f8b4d99d01ff0e6ee8695697bdd5b568089395620d7198c6093ce8bc59b928611b1b12452c05addaa42f4beff6a0a6fe90000000380d0dbc3f40210090402040005c801040205c8010500040004000e2003faf2cb329f2e90d6d23b58d91bbb6c046aa143261cc21f52fbe2824bfcbf04d807d601e4c6a70408d602b2a5730000d603e4c6a70601d604e4c6a7080ed605e4c6a70505d606e4c6a70705d60795720399c1a7c1720299c17202c1a7eb027201d1ededededededededed93c27202c2a793e4c672020408720193e4c6720205059572039d9c72057eb272047301000573029d9c72057eb2720473030005730494e4c672020601720393e4c672020705720693e4c67202080e720493e4c67202090ec5a79572039072079c720672059272079c72067205917207730595ef720393b1db630872027306d801d608b2db63087202730700ed938c7208017308938c7208027206c8df35000508cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba0405c8a8c105010105dc8b020e0266608cdea8baf0380008cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba04c8df350000c0843d1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304c8df350000'
example: '"02c9e71790399816b3e40b2207e9ade19a9b7fe0600186cfb8e2b115bfdb34b57f38cd3c9f2890d11720eb3bb993993f00ededf812a590d2993df094a7ca4f0213e4820e1ab831eed5dc5c72665396d3a01d2a12900f1c3ab77700b284ae24fa8e8f7754f86f2282c795db6b0b17df1c29cc0552e59d01f7d777c638a813333277271c2f8b4d99d01ff0e6ee8695697bdd5b568089395620d7198c6093ce8bc59b928611b1b12452c05addaa42f4beff6a0a6fe90000000380d0dbc3f40210090402040005c801040205c8010500040004000e2003faf2cb329f2e90d6d23b58d91bbb6c046aa143261cc21f52fbe2824bfcbf04d807d601e4c6a70408d602b2a5730000d603e4c6a70601d604e4c6a7080ed605e4c6a70505d606e4c6a70705d60795720399c1a7c1720299c17202c1a7eb027201d1ededededededededed93c27202c2a793e4c672020408720193e4c6720205059572039d9c72057eb272047301000573029d9c72057eb2720473030005730494e4c672020601720393e4c672020705720693e4c67202080e720493e4c67202090ec5a79572039072079c720672059272079c72067205917207730595ef720393b1db630872027306d801d608b2db63087202730700ed938c7208017308938c7208027206c8df35000508cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba0405c8a8c105010105dc8b020e0266608cdea8baf0380008cd030c8f9c4dc08f3c006fa85a47c9156dedbede000a8b764c6e374fd097e873ba04c8df350000c0843d1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304c8df350000"'
responses:
'200':
description: JSON with ID of the new transaction
Expand Down Expand Up @@ -5781,7 +5781,8 @@ paths:
application/json:
description: adderess associated with transactions
schema:
$ref: '#/components/schemas/ErgoAddress'
type: string
example: '"3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt"'
parameters:
- in: query
name: offset
Expand Down Expand Up @@ -5946,7 +5947,8 @@ paths:
application/json:
description: adderess associated with boxes
schema:
$ref: '#/components/schemas/ErgoAddress'
type: string
example: '"3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt"'
parameters:
- in: query
name: offset
Expand Down Expand Up @@ -6005,7 +6007,8 @@ paths:
application/json:
description: adderess associated with unspent boxes
schema:
$ref: '#/components/schemas/ErgoAddress'
type: string
example: '"3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt"'
parameters:
- in: query
name: offset
Expand Down Expand Up @@ -6106,7 +6109,7 @@ paths:
description: hex encoded ergotree
schema:
type: string
example: '100204a00b08cd021cf943317b0fdb50f60892a46b9132b9ced337c7de79248b104b293d9f1f078eea02d192a39a8cc7a70173007301'
example: '"100204a00b08cd021cf943317b0fdb50f60892a46b9132b9ced337c7de79248b104b293d9f1f078eea02d192a39a8cc7a70173007301"'
parameters:
- in: query
name: offset
Expand Down Expand Up @@ -6160,7 +6163,7 @@ paths:
description: hex encoded ergotree
schema:
type: string
example: '100204a00b08cd021cf943317b0fdb50f60892a46b9132b9ced337c7de79248b104b293d9f1f078eea02d192a39a8cc7a70173007301'
example: '"100204a00b08cd021cf943317b0fdb50f60892a46b9132b9ced337c7de79248b104b293d9f1f078eea02d192a39a8cc7a70173007301"'
parameters:
- in: query
name: offset
Expand Down Expand Up @@ -6259,7 +6262,8 @@ paths:
application/json:
description: adderess with balance
schema:
$ref: '#/components/schemas/ErgoAddress'
type: string
example: '"3WwbzW6u8hKWBcL1W7kNVMr25s2UHfSBnYtwSHvrRQt7DdPuoXrt"'
responses:
'200':
description: balance information
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application.conf
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ scorex {
nodeName = "ergo-node"

# Network protocol version to be sent in handshakes
appVersion = 5.0.10
appVersion = 5.0.11

# Network agent name. May contain information about client code
# stack, starting from core code-base up to the end graphical interface.
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/mainnet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ scorex {
network {
magicBytes = [1, 0, 2, 4]
bindAddress = "0.0.0.0:9030"
nodeName = "ergo-mainnet-5.0.7"
nodeName = "ergo-mainnet-"${scorex.network.appVersion}
nodeName = ${?NODENAME}
knownPeers = [
"213.239.193.208:9030",
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/testnet.conf
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ scorex {
network {
magicBytes = [2, 0, 2, 3]
bindAddress = "0.0.0.0:9022"
nodeName = "ergo-testnet-5.0.4"
nodeName = "ergo-testnet-"${scorex.network.appVersion}
nodeName = ${?NODENAME}
knownPeers = [
"213.239.193.208:9022",
Expand Down
23 changes: 16 additions & 7 deletions src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.hash
import org.ergoplatform.nodeView.history.extra._
import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader
import org.ergoplatform.settings.ErgoSettings
import scorex.core.api.http.ApiError.BadRequest
import scorex.core.api.http.ApiError.{BadRequest, InternalError}
import scorex.core.api.http.ApiResponse
import scorex.core.settings.RESTApiSettings
import scorex.util.{ModifierId, bytesToId}
Expand Down Expand Up @@ -55,15 +55,16 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting

private val ergoAddress: Directive1[ErgoAddress] = entity(as[String]).flatMap(handleErgoAddress)

private def handleErgoAddress(value: String): Directive1[ErgoAddress] = {
ergoAddressEncoder.fromString(value) match {
private def handleErgoAddress(value: String): Directive1[ErgoAddress] =
ergoAddressEncoder.fromString(fromJsonOrPlain(value)) match {
case Success(addr) => provide(addr)
case _ => reject(ValidationRejection("Wrong address format"))
}
}

override val route: Route = pathPrefix("blockchain") {
getIndexedHeightR ~
override val route: Route =
if(ergoSettings.nodeSettings.extraIndex)
pathPrefix("blockchain") {
getIndexedHeightR ~
getTxByIdR ~
getTxByIndexR ~
getTxsByAddressR ~
Expand All @@ -77,7 +78,11 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting
getBoxesByErgoTreeUnspentR ~
getTokenInfoByIdR ~
getAddressBalanceTotalR
}
}
else
pathPrefix("blockchain") {
indexerNotEnabledR
}

private def getHistory: Future[ErgoHistoryReader] =
(readersHolder ? GetDataFromHistory[ErgoHistoryReader](r => r)).mapTo[ErgoHistoryReader]
Expand Down Expand Up @@ -114,6 +119,10 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting
ApiResponse(getIndexedHeightF)
}

private def indexerNotEnabledR: Route = get {
InternalError("Extra indexing is not enabled")
}

private def getTxByIdR: Route = (get & pathPrefix("transaction" / "byId") & modifierId) { id =>
ApiResponse(getTxByIdF(id))
}
Expand Down
10 changes: 9 additions & 1 deletion src/main/scala/org/ergoplatform/http/api/ErgoBaseApiRoute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import org.ergoplatform.settings.{Algos, ErgoSettings}
import scorex.core.api.http.{ApiError, ApiRoute}
import scorex.util.{ModifierId, bytesToId}
import akka.pattern.ask
import io.circe.syntax.EncoderOps
import org.ergoplatform.nodeView.ErgoNodeViewHolder.ReceivableMessages.LocallyGeneratedTransaction
import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome
import org.ergoplatform.nodeView.mempool.ErgoMemPool.ProcessingOutcome._
Expand All @@ -37,10 +38,17 @@ trait ErgoBaseApiRoute extends ApiRoute with ApiCodecs {
}
}

def fromJsonOrPlain(str: String): String =
str.asJson.as[String] match {
case Right(value) if value.startsWith("\"") && value.endsWith("\"") =>
value.substring(1, value.length - 1)
case _ => str
}

val ergoTree: Directive1[ErgoTree] = entity(as[String]).flatMap(handleErgoTree)

private def handleErgoTree(value: String): Directive1[ErgoTree] = {
Base16.decode(value) match {
Base16.decode(fromJsonOrPlain(value)) match {
case Success(bytes) => provide(ErgoTreeSerializer.DefaultSerializer.deserializeErgoTree(bytes))
case _ => reject(ValidationRejection("Invalid hex data"))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ case class TransactionsApiRoute(readersHolder: ActorRef,
* Validate and broadcast transaction given as hex-encoded bytes
*/
def sendTransactionAsBytesR: Route = (path("bytes") & pathEnd & post & entity(as[String])) { txBytesStr =>
Base16.decode(txBytesStr).flatMap(ErgoTransactionSerializer.parseBytesTry) match {
Base16.decode(fromJsonOrPlain(txBytesStr)).flatMap(ErgoTransactionSerializer.parseBytesTry) match {
case Success(tx) =>
validateTransactionAndProcess(tx)(validTx => sendLocalTransactionRoute(nodeViewActorRef, validTx))
case Failure(e) =>
Expand All @@ -124,7 +124,7 @@ case class TransactionsApiRoute(readersHolder: ActorRef,
* Check transaction given as hex-encoded bytes
*/
def checkTransactionAsBytesR: Route = (path("checkBytes") & post & entity(as[String])) { txBytesStr =>
Base16.decode(txBytesStr).flatMap(ErgoTransactionSerializer.parseBytesTry) match {
Base16.decode(fromJsonOrPlain(txBytesStr)).flatMap(ErgoTransactionSerializer.parseBytesTry) match {
case Success(tx) =>
validateTransactionAndProcess(tx)(validTx => ApiResponse(validTx.transaction.id))
case Failure(e) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import org.ergoplatform.nodeView.wallet.requests.{ExternalSecret, TransactionGen
import org.ergoplatform.nodeView.wallet.scanning.{Scan, ScanRequest}
import org.ergoplatform.settings.{ErgoSettings, Parameters}
import org.ergoplatform.wallet.Constants.ScanId
import org.ergoplatform.wallet.boxes.{BoxSelector, ErgoBoxSerializer}
import org.ergoplatform.wallet.boxes.{BoxSelector, ErgoBoxSerializer, TrackedBox}
import org.ergoplatform.wallet.interface4j.SecretString
import org.ergoplatform.wallet.interpreter.{ErgoProvingInterpreter, TransactionHintsBag}
import org.ergoplatform.wallet.mnemonic.Mnemonic
Expand All @@ -26,6 +26,7 @@ import sigmastate.Values.SigmaBoolean
import sigmastate.basics.DLogProtocol.DLogProverInput

import java.io.FileNotFoundException
import scala.collection.compat.immutable.ArraySeq
import scala.util.{Failure, Success, Try}

/**
Expand Down Expand Up @@ -417,14 +418,15 @@ class ErgoWalletServiceImpl(override val ergoSettings: ErgoSettings) extends Erg
}

override def getScanUnspentBoxes(state: ErgoWalletState, scanId: ScanId, considerUnconfirmed: Boolean, minHeight: Int, maxHeight: Int): Seq[WalletBox] = {
val unconfirmed = if (considerUnconfirmed) {
state.offChainRegistry.offChainBoxes.filter(_.scans.contains(scanId))
} else {
Seq.empty
}
val unconfirmed: Seq[TrackedBox] =
if (considerUnconfirmed) {
state.offChainRegistry.offChainBoxes.filter(_.scans.contains(scanId))
} else {
ArraySeq.empty[TrackedBox]
}

val currentHeight = state.fullHeight
val unspentBoxes = state.registry.unspentBoxesByInclusionHeight(scanId, minHeight, maxHeight)
val unspentBoxes: Seq[TrackedBox] = state.registry.unspentBoxesByInclusionHeight(scanId, minHeight, maxHeight)
(unspentBoxes ++ unconfirmed).map(tb => WalletBox(tb, currentHeight)).sortBy(_.trackedBox.inclusionHeightOpt)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ case class ErgoWalletState(
// State context used to sign transactions and check that coins found in the blockchain are indeed belonging
// to the wallet (by executing testing transactions against them).
// The state context is being updated by listening to state updates.
def stateContext: ErgoStateContext = storage.readStateContext(parameters)
def stateContext: ErgoStateContext = storage.getStateContext(parameters)

/**
* @return height of the last block scanned by the wallet
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import org.ergoplatform.wallet.boxes.TrackedBox
import scorex.util.{ModifierId, ScorexLogging}
import scorex.util.bytesToId

import scala.collection.compat.immutable.ArraySeq
import scala.collection.immutable.TreeSet
import scala.collection.mutable
import scala.util.Try

Expand Down Expand Up @@ -94,7 +96,7 @@ object WalletScanLogic extends ScorexLogging {
tb.copy(scans = Set(PaymentsScanId))
}

val initialScanResults = ScanResults(resolvedBoxes, Seq.empty, Seq.empty)
val initialScanResults = ScanResults(resolvedBoxes, ArraySeq.empty, ArraySeq.empty)

// Wallet unspent outputs, we fetch them only when Bloom filter shows that some outputs may be spent
val unspentBoxes = mutable.Map[ModifierId, TrackedBox]()
Expand Down Expand Up @@ -168,7 +170,7 @@ object WalletScanLogic extends ScorexLogging {
//data needed to update the offchain-registry
val walletUnspent = registry.walletUnspentBoxes()
val newOnChainIds = scanRes.outputs.map(x => encodedBoxId(x.box.id))
val updatedOffchainRegistry = offChainRegistry.updateOnBlock(height, walletUnspent, newOnChainIds)
val updatedOffchainRegistry = offChainRegistry.updateOnBlock(height, walletUnspent, newOnChainIds.to[TreeSet])

(registry, updatedOffchainRegistry, outputsFilter)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.ergoplatform.nodeView.wallet.persistence

import org.ergoplatform.nodeView.wallet.IdUtils._
import org.ergoplatform.nodeView.wallet.IdUtils.{EncodedBoxId, encodedBoxId}
import org.ergoplatform.wallet.boxes.TrackedBox
import scorex.util.{ModifierId, bytesToId}

case class Balance(id: EncodedBoxId,
value: Long,
assets: Map[EncodedTokenId, Long])
assets: Map[ModifierId, Long])

object Balance {
def apply(tb: TrackedBox): Balance = Balance(encodedBoxId(tb.box.id), tb.box.value,
tb.box.additionalTokens.toArray.map(x => encodedTokenId(x._1) -> x._2).toMap)
tb.box.additionalTokens.toArray.map(x => (bytesToId(x._1), x._2)).toMap)
}
Loading