Skip to content

Commit

Permalink
Merge branch 'v5.0.15' of github.com:ergoplatform/ergo into v5.0.15
Browse files Browse the repository at this point in the history
  • Loading branch information
kushti committed Oct 2, 2023
2 parents 75636c2 + 8e42b79 commit d9505c0
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 26 deletions.
14 changes: 14 additions & 0 deletions src/main/resources/api/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6134,6 +6134,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 address
Expand Down Expand Up @@ -6289,6 +6296,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 with wanted ergotree
Expand Down
45 changes: 24 additions & 21 deletions src/main/scala/org/ergoplatform/http/api/BlockchainApiRoute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting
}
private val sortDir: Directive[Tuple1[Direction]] = parameters("sortDirection".as(sortMarshaller) ? DESC)

private val unconfirmed: Directive[Tuple1[Boolean]] = parameters("includeUnconfirmed".as[Boolean] ? false)

/**
* Total number of boxes/transactions that can be requested at once to avoid too heavy requests ([[BlocksApiRoute.MaxHeaders]])
*/
Expand Down Expand Up @@ -94,11 +96,11 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting
private def getHistoryWithMempool: Future[(ErgoHistoryReader,ErgoMemPoolReader)] =
(readersHolder ? GetReaders).mapTo[Readers].map(r => (r.h, r.m))

private def getAddress(tree: ErgoTree)(history: ErgoHistoryReader): Option[IndexedErgoAddress] = {
private def getAddress(tree: ErgoTree)(history: ErgoHistoryReader): Option[IndexedErgoAddress] =
history.typedExtraIndexById[IndexedErgoAddress](hashErgoTree(tree))
}

private def getAddress(addr: ErgoAddress)(history: ErgoHistoryReader): Option[IndexedErgoAddress] = getAddress(addr.script)(history)
private def getAddress(addr: ErgoAddress)(history: ErgoHistoryReader): Option[IndexedErgoAddress] =
getAddress(addr.script)(history)

private def getTxById(id: ModifierId)(history: ErgoHistoryReader): Option[IndexedErgoTransaction] =
history.typedExtraIndexById[IndexedErgoTransaction](id) match {
Expand Down Expand Up @@ -237,37 +239,38 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting
validateAndGetBoxesByAddress(address, offset, limit)
}

private def getBoxesByAddressUnspent(addr: ErgoAddress, offset: Int, limit: Int, sortDir: Direction): Future[Seq[IndexedErgoBox]] =
getHistory.map { history =>
private def getBoxesByAddressUnspent(addr: ErgoAddress, offset: Int, limit: Int, sortDir: Direction, unconfirmed: Boolean): Future[Seq[IndexedErgoBox]] =
getHistoryWithMempool.map { case (history, mempool) =>
getAddress(addr)(history) match {
case Some(addr) => addr.retrieveUtxos(history, offset, limit, sortDir)
case Some(addr) => addr.retrieveUtxos(history, mempool, offset, limit, sortDir, unconfirmed)
case None => Seq.empty[IndexedErgoBox]
}
}

private def validateAndGetBoxesByAddressUnspent(address: ErgoAddress,
offset: Int,
limit: Int,
dir: Direction): Route = {
dir: Direction,
unconfirmed: Boolean): Route = {
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(getBoxesByAddressUnspent(address, offset, limit, dir))
ApiResponse(getBoxesByAddressUnspent(address, offset, limit, dir, unconfirmed))
}
}

private def getBoxesByAddressUnspentR: Route =
(post & pathPrefix("box" / "unspent" / "byAddress") & ergoAddress & paging & sortDir) {
(address, offset, limit, dir) =>
validateAndGetBoxesByAddressUnspent(address, offset, limit, dir)
(post & pathPrefix("box" / "unspent" / "byAddress") & ergoAddress & paging & sortDir & unconfirmed) {
(address, offset, limit, dir, unconfirmed) =>
validateAndGetBoxesByAddressUnspent(address, offset, limit, dir, unconfirmed)
}

private def getBoxesByAddressUnspentGetRoute: Route =
(pathPrefix("box" / "unspent" / "byAddress") & get & addressPass & paging & sortDir) {
(address, offset, limit, dir) =>
validateAndGetBoxesByAddressUnspent(address, offset, limit, dir)
(pathPrefix("box" / "unspent" / "byAddress") & get & addressPass & paging & sortDir & unconfirmed) {
(address, offset, limit, dir, unconfirmed) =>
validateAndGetBoxesByAddressUnspent(address, offset, limit, dir, unconfirmed)
}

private def getBoxRange(offset: Int, limit: Int): Future[Seq[ModifierId]] =
Expand Down Expand Up @@ -304,21 +307,21 @@ case class BlockchainApiRoute(readersHolder: ActorRef, ergoSettings: ErgoSetting
}
}

private def getBoxesByErgoTreeUnspent(tree: ErgoTree, offset: Int, limit: Int, sortDir: Direction): Future[Seq[IndexedErgoBox]] =
getHistory.map { history =>
private def getBoxesByErgoTreeUnspent(tree: ErgoTree, offset: Int, limit: Int, sortDir: Direction, unconfirmed: Boolean): Future[Seq[IndexedErgoBox]] =
getHistoryWithMempool.map { case (history, mempool) =>
getAddress(tree)(history) match {
case Some(iEa) => iEa.retrieveUtxos(history, offset, limit, sortDir)
case None => Seq.empty[IndexedErgoBox]
case Some(addr) => addr.retrieveUtxos(history, mempool, offset, limit, sortDir, unconfirmed)
case None => Seq.empty[IndexedErgoBox]
}
}

private def getBoxesByErgoTreeUnspentR: Route = (post & pathPrefix("box" / "unspent" / "byErgoTree") & ergoTree & paging & sortDir) { (tree, offset, limit, dir) =>
private def getBoxesByErgoTreeUnspentR: Route = (post & pathPrefix("box" / "unspent" / "byErgoTree") & ergoTree & paging & sortDir & unconfirmed) { (tree, 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\"")
BadRequest("Invalid parameter for sort direction, valid values are 'ASC' and 'DESC'")
}else {
ApiResponse(getBoxesByErgoTreeUnspent(tree, offset, limit, dir))
ApiResponse(getBoxesByErgoTreeUnspent(tree, offset, limit, dir, unconfirmed))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import org.ergoplatform.http.api.SortDirection.{ASC, DESC, Direction}
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, txSegmentId}
import org.ergoplatform.nodeView.history.extra.IndexedErgoAddressSerializer.{boxSegmentId, hashErgoTree, txSegmentId}
import org.ergoplatform.nodeView.mempool.ErgoMemPoolReader
import org.ergoplatform.settings.Algos
import scorex.core.serialization.ErgoSerializer
import scorex.util.{ModifierId, ScorexLogging, bytesToId}
Expand Down Expand Up @@ -141,14 +142,21 @@ case class IndexedErgoAddress(treeHash: ModifierId,
/**
* Get a range of the boxes associated with this address that are NOT spent
* @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 @@ -157,7 +165,7 @@ case class IndexedErgoAddress(treeHash: ModifierId,
history.typedExtraIndexById[IndexedErgoAddress](boxSegmentId(treeHash, 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 @@ -167,8 +175,18 @@ case class IndexedErgoAddress(treeHash: 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 = mempool.getAll.flatMap(_.transaction.outputs)
.filter(box => hashErgoTree(box.ergoTree) == treeHash)
val unconfirmedBoxes = mempoolBoxes.map(new IndexedErgoBox(0, None, None, _, 0))
sortDir match {
case DESC => unconfirmedBoxes ++ confirmedBoxes
case ASC => confirmedBoxes ++ unconfirmedBoxes
}
} else
confirmedBoxes
}

/**
Expand Down

0 comments on commit d9505c0

Please sign in to comment.