Skip to content

Commit

Permalink
Added includeUnconfirmed option to Token API
Browse files Browse the repository at this point in the history
  • Loading branch information
jellymlg committed Oct 3, 2023
1 parent 85339a0 commit 07a68f7
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 19 deletions.
7 changes: 7 additions & 0 deletions src/main/resources/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6130,6 +6130,13 @@ paths:
schema:
type: string
default: desc
- in: query
name: includeUnconfirmed
required: false
description: if true include unconfirmed transactions from mempool
schema:
type: boolean
default: false
responses:
'200':
description: unspent boxes associated with wanted token
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -350,21 +350,21 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting
ApiResponse(getBoxesByTokenId(id, offset, limit))
}

private def getBoxesByTokenIdUnspent(id: ModifierId, offset: Int, limit: Int, sortDir: Direction): Future[Seq[IndexedErgoBox]] =
getHistory.map { history =>
private def getBoxesByTokenIdUnspent(id: ModifierId, offset: Int, limit: Int, sortDir: Direction, unconfirmed: Boolean): Future[Seq[IndexedErgoBox]] =
getHistoryWithMempool.map { case (history, mempool) =>
history.typedExtraIndexById[IndexedToken](uniqueId(id)) match {
case Some(token) => token.retrieveUtxos(history, offset, limit, sortDir)
case Some(token) => token.retrieveUtxos(history, mempool, offset, limit, sortDir, unconfirmed)
case None => Seq.empty[IndexedErgoBox]
}
}

private def getBoxesByTokenIdUnspentR: Route = (get & pathPrefix("box" / "unspent" / "byTokenId") & modifierId & paging & sortDir) { (id, offset, limit, dir) =>
private def getBoxesByTokenIdUnspentR: Route = (get & pathPrefix("box" / "unspent" / "byTokenId") & modifierId & paging & sortDir & unconfirmed) { (id, offset, limit, dir, unconfirmed) =>
if (limit > MaxItems) {
BadRequest(s"No more than $MaxItems boxes can be requested")
} else if (dir == SortDirection.INVALID) {
BadRequest("Invalid parameter for sort direction, valid values are 'ASC' and 'DESC'")
} else {
ApiResponse(getBoxesByTokenIdUnspent(id, offset, limit, dir))
ApiResponse(getBoxesByTokenIdUnspent(id, offset, limit, dir, unconfirmed))
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package org.ergoplatform.nodeView.history.extra

import org.ergoplatform.ErgoAddressEncoder
import org.ergoplatform.{ErgoAddressEncoder, ErgoBox}
import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader}
import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes}
import org.ergoplatform.nodeView.history.extra.IndexedErgoAddress.{getBoxes, getFromSegments, getTxs}
import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId}
import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader
import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.hashErgoTree
import org.ergoplatform.settings.Algos
import scorex.core.serialization.ErgoSerializer
import scorex.util.{ModifierId, bytesToId}
Expand Down Expand Up @@ -93,6 +91,16 @@ case class IndexedErgoAddress(treeHash: ModifierId,

toRemove.toArray
}

/**
* Filter mempool boxes if API call requires it
*
* @param boxes - all boxes in mempool
* @return associated boxes
*/
override private[extra] def filterMempool(boxes: Seq[ErgoBox]): Seq[ErgoBox] =
boxes.filter(box => hashErgoTree(box.ergoTree) == treeHash)

}

object IndexedErgoAddressSerializer extends ErgoSerializer[IndexedErgoAddress] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.ergoplatform.nodeView.history.extra

import org.ergoplatform.ErgoAddressEncoder
import org.ergoplatform.{ErgoAddressEncoder, ErgoBox}
import org.ergoplatform.ErgoBox.{R4, R5, R6}
import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader}
import org.ergoplatform.nodeView.history.extra.ExtraIndexer.{ExtraIndexTypeId, fastIdToBytes}
Expand Down Expand Up @@ -90,6 +90,16 @@ case class IndexedToken(tokenId: ModifierId,
findAndModBox(iEb.globalIndex, historyOpt.get)
this
}

/**
* Filter mempool boxes if API call requires it
*
* @param boxes - all boxes in mempool
* @return associated boxes
*/
override private[extra] def filterMempool(boxes: Seq[ErgoBox]): Seq[ErgoBox] =
boxes.filter(_.additionalTokens.exists(_._1.toModifierId == tokenId))

}

object IndexedTokenSerializer extends ErgoSerializer[IndexedToken] {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package org.ergoplatform.nodeView.history.extra

import org.ergoplatform.ErgoAddressEncoder
import org.ergoplatform.{ErgoAddressEncoder, ErgoBox}
import org.ergoplatform.http.api.SortDirection.{ASC, DESC, Direction}
import org.ergoplatform.nodeView.history.extra.ExtraIndexer.fastIdToBytes
import org.ergoplatform.nodeView.history.{ErgoHistory, ErgoHistoryReader}
import org.ergoplatform.nodeView.history.extra.SegmentSerializer._
import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader
import org.ergoplatform.sdk.JavaHelpers.Algos
import scorex.util.serialization.{Reader, Writer}
import scorex.util.{ModifierId, ScorexLogging, bytesToId}
Expand Down Expand Up @@ -235,15 +236,22 @@ abstract class Segment[T <: Segment[_] : ClassTag](val parentId: ModifierId,
/**
* Get a range of the boxes associated with the parent that are NOT spent
*
* @param history - history to use
* @param offset - items to skip from the start
* @param limit - items to retrieve
* @param sortDir - whether to start retreival from newest box ([[DESC]]) or oldest box ([[ASC]])
* @param history - history to use
* @param mempool - mempool to use, if unconfirmed is true
* @param offset - items to skip from the start
* @param limit - items to retrieve
* @param sortDir - whether to start retreival from newest box ([[DESC]]) or oldest box ([[ASC]])
* @param unconfirmed - whether to include unconfirmed boxes
* @return array of unspent boxes
*/
def retrieveUtxos(history: ErgoHistoryReader, offset: Int, limit: Int, sortDir: Direction): Array[IndexedErgoBox] = {
def retrieveUtxos(history: ErgoHistoryReader,
mempool: ErgoMemPoolReader,
offset: Int,
limit: Int,
sortDir: Direction,
unconfirmed: Boolean): Seq[IndexedErgoBox] = {
val data: ArrayBuffer[IndexedErgoBox] = ArrayBuffer.empty[IndexedErgoBox]
sortDir match {
val confirmedBoxes: Seq[IndexedErgoBox] = sortDir match {
case DESC =>
data ++= boxes.filter(_ > 0).map(n => NumericBoxIndex.getBoxByNumber(history, n).get)
var segment: Int = boxSegmentCount
Expand All @@ -252,7 +260,7 @@ abstract class Segment[T <: Segment[_] : ClassTag](val parentId: ModifierId,
history.typedExtraIndexById[T](idMod(boxSegmentId(parentId, segment))).get.boxes
.filter(_ > 0).map(n => NumericBoxIndex.getBoxByNumber(history, n).get) ++=: data
}
data.reverse.slice(offset, offset + limit).toArray
data.reverse.slice(offset, offset + limit)
case ASC =>
var segment: Int = 0
while(data.length < (limit + offset) && segment < boxSegmentCount) {
Expand All @@ -262,8 +270,17 @@ abstract class Segment[T <: Segment[_] : ClassTag](val parentId: ModifierId,
}
if (data.length < (limit + offset))
data ++= boxes.filter(_ > 0).map(n => NumericBoxIndex.getBoxByNumber(history, n).get)
data.slice(offset, offset + limit).toArray
data.slice(offset, offset + limit)
}
if(unconfirmed) {
val mempoolBoxes = filterMempool(mempool.getAll.flatMap(_.transaction.outputs))
val unconfirmedBoxes = mempoolBoxes.map(new IndexedErgoBox(0, None, None, _, 0))
sortDir match {
case DESC => unconfirmedBoxes ++ confirmedBoxes
case ASC => confirmedBoxes ++ unconfirmedBoxes
}
} else
confirmedBoxes
}

/**
Expand Down Expand Up @@ -349,6 +366,14 @@ abstract class Segment[T <: Segment[_] : ClassTag](val parentId: ModifierId,
*/
private[extra] def spendBox(iEb: IndexedErgoBox, historyOpt: Option[ErgoHistoryReader] = None)(implicit ae: ErgoAddressEncoder): T

/**
* Filter mempool boxes if API call requires it
*
* @param boxes - all boxes in mempool
* @return associated boxes
*/
private[extra] def filterMempool(boxes: Seq[ErgoBox]): Seq[ErgoBox]

}

object SegmentSerializer {
Expand Down

0 comments on commit 07a68f7

Please sign in to comment.