diff --git a/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainDataSource.java b/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainDataSource.java index 62b615ee..a6a9cd5d 100644 --- a/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainDataSource.java +++ b/lib-api/src/main/java/org/ergoplatform/appkit/BlockchainDataSource.java @@ -71,6 +71,31 @@ public interface BlockchainDataSource { */ List getUnconfirmedUnspentBoxesFor(Address address, int offset, int limit); + /** + * Get all unspent boxes in the current node wallet, without unconfirmed boxes + * @return List of input boxes representing all known + */ + List getUnspentWalletBoxes(); + /** + * Get all unspent boxes in the current node wallet, without unconfirmed boxes + * @param minConfirmations Minimum number of confirmations for a box to be included in the returned list + * @param minInclusionHeight Minimum creation height for a box to be included in the returned list + * @return List of input boxes representing all known + */ + List getUnspentWalletBoxes(int minConfirmations, int minInclusionHeight); + + /** + * Get all unspent boxes in the current node wallet, including unconfirmed boxes from the MemPool + * @return List of all unspent boxes in this node's current wallet + */ + List getUnconfirmedUnspentWalletBoxes(); + /** + * Get all unspent boxes in the current node wallet, including unconfirmed boxes from the MemPool + * @param minInclusionHeight Minimum creation height for a box to be included in the returned list + * @return List of all unspent boxes in this node's current wallet + */ + List getUnconfirmedUnspentWalletBoxes(int minInclusionHeight); + /** * @return unconfirmed transactions from mempool */ diff --git a/lib-api/src/main/java/org/ergoplatform/appkit/NodeWalletUnspentBoxesLoader.java b/lib-api/src/main/java/org/ergoplatform/appkit/NodeWalletUnspentBoxesLoader.java new file mode 100644 index 00000000..70d46642 --- /dev/null +++ b/lib-api/src/main/java/org/ergoplatform/appkit/NodeWalletUnspentBoxesLoader.java @@ -0,0 +1,57 @@ +package org.ergoplatform.appkit; + +import javax.annotation.Nonnull; +import java.util.List; + +/** + * {@link BoxOperations.IUnspentBoxesLoader} implementation to be used with + * {@link BoxOperations#withInputBoxesLoader(BoxOperations.IUnspentBoxesLoader)} + *

+ * Pre-loads all boxes from node wallet, then returns pages of boxes from the master list. + * May optionally allow boxes to be grabbed from the MemPool as well. + */ +public class NodeWalletUnspentBoxesLoader implements BoxOperations.IUnspentBoxesLoader { + private List allBoxes; + private boolean withMemPoolBoxes = false; + + public NodeWalletUnspentBoxesLoader withMemPoolBoxes(boolean withMemPoolBoxes) { + this.withMemPoolBoxes = withMemPoolBoxes; + return this; + } + + public List getAllBoxes() { + return allBoxes; + } + + @Override + public void prepare(@Nonnull BlockchainContext ctx, List

addresses, long grossAmount, @Nonnull List tokensToSpend) { + BlockchainDataSource dataSource = ctx.getDataSource(); + if(withMemPoolBoxes) + allBoxes = dataSource.getUnconfirmedUnspentWalletBoxes(); + else + allBoxes = dataSource.getUnspentWalletBoxes(); + } + + @Override + public void prepareForAddress(Address address) { + // Do nothing, actual address used will be from node wallet anyway + // Maybe interface should be changed to be more abstracted? Or a separate interface is needed? + } + + @Nonnull + @Override + public List loadBoxesPage(@Nonnull BlockchainContext ctx, @Nonnull Address sender, @Nonnull Integer page) { + int currentOffset = ctx.DEFAULT_LIMIT_FOR_API * page; + int pageEndIndex = currentOffset + ctx.DEFAULT_LIMIT_FOR_API; + + if(currentOffset > allBoxes.size()) + return allBoxes.subList(0,0); // Return empty list in case current offset is greater than ending index + + if(pageEndIndex > allBoxes.size()) + pageEndIndex = allBoxes.size(); // Shorten returned list to last index in case pageEndIndex is greater than size of list + + return allBoxes.subList(currentOffset, pageEndIndex); + } +} + + diff --git a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/NodeAndExplorerDataSourceImpl.java b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/NodeAndExplorerDataSourceImpl.java index 50849386..c11b5c34 100644 --- a/lib-impl/src/main/java/org/ergoplatform/appkit/impl/NodeAndExplorerDataSourceImpl.java +++ b/lib-impl/src/main/java/org/ergoplatform/appkit/impl/NodeAndExplorerDataSourceImpl.java @@ -18,18 +18,7 @@ import org.ergoplatform.explorer.client.ExplorerApiClient; import org.ergoplatform.explorer.client.model.OutputInfo; import org.ergoplatform.explorer.client.model.TransactionInfo; -import org.ergoplatform.restapi.client.ApiClient; -import org.ergoplatform.restapi.client.BlocksApi; -import org.ergoplatform.restapi.client.ErgoTransaction; -import org.ergoplatform.restapi.client.ErgoTransactionDataInput; -import org.ergoplatform.restapi.client.ErgoTransactionInput; -import org.ergoplatform.restapi.client.ErgoTransactionOutput; -import org.ergoplatform.restapi.client.InfoApi; -import org.ergoplatform.restapi.client.NodeInfo; -import org.ergoplatform.restapi.client.Transactions; -import org.ergoplatform.restapi.client.TransactionsApi; -import org.ergoplatform.restapi.client.UtxoApi; -import org.ergoplatform.restapi.client.WalletApi; +import org.ergoplatform.restapi.client.*; import java.math.BigDecimal; import java.util.ArrayList; @@ -135,6 +124,29 @@ public List getUnspentBoxesFor(Address address, int offset, int limit) List boxes = executeCall(explorerApi.getApiV1BoxesUnspentByaddressP1(address.toString(), offset, limit, "asc")).getItems(); return getInputBoxes(boxes); } + @Override + public List getUnspentWalletBoxes(int minConfirmations, int minInclusionHeight) { + Preconditions.checkArgument(minConfirmations >= 0, "Minimum confirmations was less than 0"); + Preconditions.checkArgument(minInclusionHeight >= 0, "Minimum inclusion height was less than 0"); + List walletBoxes = executeCall(getNodeWalletApi().walletBoxes(minConfirmations, minInclusionHeight)); + return getFromWalletBoxes(walletBoxes, false); + } + @Override + public List getUnspentWalletBoxes() { + List walletBoxes = executeCall(getNodeWalletApi().walletBoxes(0, 0)); + return getFromWalletBoxes(walletBoxes, false); + } + @Override + public List getUnconfirmedUnspentWalletBoxes(int minInclusionHeight) { + Preconditions.checkArgument(minInclusionHeight >= 0, "Minimum inclusion height was less than 0"); + List walletBoxes = executeCall(getNodeWalletApi().walletBoxes(-1, minInclusionHeight)); + return getFromWalletBoxes(walletBoxes, true); + } + @Override + public List getUnconfirmedUnspentWalletBoxes() { + List walletBoxes = executeCall(getNodeWalletApi().walletBoxes(-1, 0)); + return getFromWalletBoxes(walletBoxes, true); + } @Override public List getUnconfirmedUnspentBoxesFor(Address address, int offset, int limit) { @@ -191,6 +203,24 @@ private List getInputBoxes(List boxes) { return returnList; } + private List getFromWalletBoxes(List boxes, boolean withMemPool) { + ArrayList returnList = new ArrayList<>(boxes.size()); + + for (WalletBox box : boxes) { + String boxId = box.getBox().getBoxId(); + InputBox boxInfo; + if(withMemPool) { + boxInfo = getBoxByIdWithMemPool(boxId); + }else{ + boxInfo = getBoxById(boxId); + } + if (boxInfo != null) { + returnList.add(boxInfo); + } + } + + return returnList; + } protected T executeCall(Call apiCall) throws ErgoClientException { try {