Skip to content
Open
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
feat: enhance wallet box retrieval with height filtering options
  • Loading branch information
dimension-drifter committed Dec 14, 2025
commit c54057dd4dfde2a62c51c8bc298c97b444800e2e
12 changes: 10 additions & 2 deletions src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,16 @@ case class WalletApiRoute(readersHolder: ActorRef,
def unspentBoxesR: Route = (path("boxes" / "unspent") & get & boxParams) {
(minConfNum, maxConfNum, minHeight, maxHeight, limit, offset) =>
val considerUnconfirmed = minConfNum == -1
// Determine actual height range to query from DB
val (actualMinHeight, actualMaxHeight) = if (minHeight > 0 || maxHeight < Int.MaxValue) {
// Height-based filtering: use height range for DB query
(minHeight, if (maxHeight == -1) Int.MaxValue else maxHeight)
} else {
// No height filtering or only confirmation-based filtering
(0, Int.MaxValue)
}
withWallet { wallet =>
wallet.walletBoxes(unspentOnly = true, considerUnconfirmed)
wallet.walletBoxes(unspentOnly = true, considerUnconfirmed, actualMinHeight, actualMaxHeight)
.map { boxes =>
boxes
.filter(boxConfirmationHeightFilter(_, minConfNum, maxConfNum, minHeight, maxHeight))
Expand All @@ -321,7 +329,7 @@ case class WalletApiRoute(readersHolder: ActorRef,
(minConfNum, maxConfNum, minHeight, maxHeight, limit, offset) =>
val considerUnconfirmed = minConfNum == -1
withWallet {
_.walletBoxes(unspentOnly = false, considerUnconfirmed = considerUnconfirmed)
_.walletBoxes(unspentOnly = false, considerUnconfirmed = considerUnconfirmed, 0, Int.MaxValue)
.map {
_.filter(boxConfirmationHeightFilter(_, minConfNum, maxConfNum, minHeight, maxHeight))
.slice(offset, offset + limit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ class ErgoWalletActor(settings: ErgoSettings,
* Read wallet boxes, unspent only (if corresponding flag is set), or all (both spent and unspent).
* If considerUnconfirmed flag is set, mempool contents is considered as well.
*/
case GetWalletBoxes(unspent, considerUnconfirmed) =>
val boxes = ergoWalletService.getWalletBoxes(state, unspent, considerUnconfirmed)
case GetWalletBoxes(unspent, considerUnconfirmed, minHeight, maxHeight) =>
val boxes = ergoWalletService.getWalletBoxes(state, unspent, considerUnconfirmed, minHeight, maxHeight)
sender() ! boxes

case GetScanUnspentBoxes(scanId, considerUnconfirmed, minHeight, maxHeight) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,10 @@ object ErgoWalletActorMessages {
* @param unspentOnly - return only unspent boxes
* @param considerUnconfirmed - consider mempool (filter our unspent boxes spent in the pool if unspent = true, add
* boxes created in the pool for both values of unspentOnly).
* @param minHeight - min inclusion height of unspent boxes
* @param maxHeight - max inclusion height of unspent boxes
*/
final case class GetWalletBoxes(unspentOnly: Boolean, considerUnconfirmed: Boolean)
final case class GetWalletBoxes(unspentOnly: Boolean, considerUnconfirmed: Boolean, minHeight: Int, maxHeight: Int)

/**
* Get boxes by requested params
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ trait ErgoWalletReader extends NodeViewComponent {
def getPrivateKeyFromPath(path: DerivationPath): Future[Try[DLogProverInput]] =
(walletActor ? GetPrivateKeyFromPath(path)).mapTo[Try[DLogProverInput]]

def walletBoxes(unspentOnly: Boolean, considerUnconfirmed: Boolean): Future[Seq[WalletBox]] =
(walletActor ? GetWalletBoxes(unspentOnly, considerUnconfirmed)).mapTo[Seq[WalletBox]]
def walletBoxes(unspentOnly: Boolean, considerUnconfirmed: Boolean, minHeight: Int = 0, maxHeight: Int = Int.MaxValue): Future[Seq[WalletBox]] =
(walletActor ? GetWalletBoxes(unspentOnly, considerUnconfirmed, minHeight, maxHeight)).mapTo[Seq[WalletBox]]

def scanUnspentBoxes(scanId: ScanId, considerUnconfirmed: Boolean, minHeight: Int, maxHeight: Int): Future[Seq[WalletBox]] =
(walletActor ? GetScanUnspentBoxes(scanId, considerUnconfirmed, minHeight, maxHeight)).mapTo[Seq[WalletBox]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ trait ErgoWalletService {
*/
def recreateStorage(state: ErgoWalletState, settings: ErgoSettings): Try[ErgoWalletState]

def getWalletBoxes(state: ErgoWalletState, unspentOnly: Boolean, considerUnconfirmed: Boolean): Seq[WalletBox]
def getWalletBoxes(state: ErgoWalletState, unspentOnly: Boolean, considerUnconfirmed: Boolean, minHeight: Int, maxHeight: Int): Seq[WalletBox]

/**
* @param state current wallet state
Expand Down Expand Up @@ -396,10 +396,16 @@ class ErgoWalletServiceImpl(override val ergoSettings: ErgoSettings) extends Erg
state.copy(storage = WalletStorage.readOrCreate(settings))
}

override def getWalletBoxes(state: ErgoWalletState, unspentOnly: Boolean, considerUnconfirmed: Boolean): Seq[WalletBox] = {
override def getWalletBoxes(state: ErgoWalletState, unspentOnly: Boolean, considerUnconfirmed: Boolean, minHeight: Int, maxHeight: Int): Seq[WalletBox] = {
val currentHeight = state.fullHeight
val boxes = if (unspentOnly) {
val confirmed = state.registry.walletUnspentBoxes(state.maxInputsToUse * BoxSelector.ScanDepthFactor)
val confirmed = if (minHeight > 0 || maxHeight < Int.MaxValue) {
// Use height-based query to extract only boxes in given range from DB, like done for /wallet/transactions
state.registry.walletUnspentBoxesByInclusionHeight(minHeight, maxHeight)
} else {
// Use the original query when no height filtering is needed
state.registry.walletUnspentBoxes(state.maxInputsToUse * BoxSelector.ScanDepthFactor)
}
if (considerUnconfirmed) {
// We filter out spent boxes in the same way as wallet does when assembling a transaction
(confirmed ++ state.offChainRegistry.offChainBoxes).filter(state.walletFilter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,15 @@ class WalletRegistry(private val store: LDBVersionedStore)(ws: WalletSettings) e
* Unspent boxes belong to the wallet (payments scan)
*/
def walletUnspentBoxes(limit: Int = Int.MaxValue): Seq[TrackedBox] = unspentBoxes(Constants.PaymentsScanId, limit)

/**
* Read unspent wallet boxes within height range (payments scan)
*
* @param heightFrom - min inclusion height of unspent boxes
* @param heightTo - max inclusion height of unspent boxes
* @return sequences of wallet-related unspent boxes found in the database
*/
def walletUnspentBoxesByInclusionHeight(heightFrom: Height, heightTo: Height): Seq[TrackedBox] =
unspentBoxesByInclusionHeight(Constants.PaymentsScanId, heightFrom, heightTo)
/**
* Spent boxes belong to the wallet (payments scan)
*/
Expand Down