diff --git a/src/main/java/org/qortal/api/model/crosschain/TradeBotCreateRequest.java b/src/main/java/org/qortal/api/model/crosschain/TradeBotCreateRequest.java index e748b458..1f96488e 100644 --- a/src/main/java/org/qortal/api/model/crosschain/TradeBotCreateRequest.java +++ b/src/main/java/org/qortal/api/model/crosschain/TradeBotCreateRequest.java @@ -27,7 +27,7 @@ public class TradeBotCreateRequest { @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) public Long bitcoinAmount; - @Schema(description = "Foreign blockchain. Note: default (BITCOIN) to be removed in the future", example = "BITCOIN", defaultValue = "BITCOIN") + @Schema(description = "Foreign blockchain. Note: default (BITCOIN) to be removed in the future", example = "BITCOIN", implementation = SupportedBlockchain.class) public SupportedBlockchain foreignBlockchain; @Schema(description = "Foreign blockchain amount wanted in return", example = "0.00864200", type = "number") diff --git a/src/main/java/org/qortal/api/resource/CrossChainResource.java b/src/main/java/org/qortal/api/resource/CrossChainResource.java index 223d9a39..5f679b40 100644 --- a/src/main/java/org/qortal/api/resource/CrossChainResource.java +++ b/src/main/java/org/qortal/api/resource/CrossChainResource.java @@ -80,7 +80,11 @@ public class CrossChainResource { ) @ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE}) public List getTradeOffers( - // TODO: we need a param to limit to specific foreign blockchain(s) + @Parameter( + description = "Limit to specific blockchain", + example = "LITECOIN", + schema = @Schema(implementation = SupportedBlockchain.class) + ) @QueryParam("foreignBlockchain") SupportedBlockchain foreignBlockchain, @Parameter( ref = "limit") @QueryParam("limit") Integer limit, @Parameter( ref = "offset" ) @QueryParam("offset") Integer offset, @Parameter( ref = "reverse" ) @QueryParam("reverse") Boolean reverse) { @@ -92,7 +96,7 @@ public class CrossChainResource { List crossChainTradesData = new ArrayList<>(); try (final Repository repository = RepositoryManager.getRepository()) { - Map> acctsByCodeHash = SupportedBlockchain.getAcctMap(); + Map> acctsByCodeHash = SupportedBlockchain.getFilteredAcctMap(foreignBlockchain); for (Map.Entry> acctInfo : acctsByCodeHash.entrySet()) { byte[] codeHash = acctInfo.getKey().value; @@ -131,6 +135,11 @@ public class CrossChainResource { ) @ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE}) public List getCompletedTrades( + @Parameter( + description = "Limit to specific blockchain", + example = "LITECOIN", + schema = @Schema(implementation = SupportedBlockchain.class) + ) @QueryParam("foreignBlockchain") SupportedBlockchain foreignBlockchain, @Parameter( description = "Only return trades that completed on/after this timestamp (milliseconds since epoch)", example = "1597310000000" @@ -165,7 +174,7 @@ public class CrossChainResource { List crossChainTrades = new ArrayList<>(); - Map> acctsByCodeHash = SupportedBlockchain.getAcctMap(); + Map> acctsByCodeHash = SupportedBlockchain.getFilteredAcctMap(foreignBlockchain); for (Map.Entry> acctInfo : acctsByCodeHash.entrySet()) { byte[] codeHash = acctInfo.getKey().value; diff --git a/src/main/java/org/qortal/api/resource/CrossChainTradeBotResource.java b/src/main/java/org/qortal/api/resource/CrossChainTradeBotResource.java index cf94181d..cd8766ca 100644 --- a/src/main/java/org/qortal/api/resource/CrossChainTradeBotResource.java +++ b/src/main/java/org/qortal/api/resource/CrossChainTradeBotResource.java @@ -1,6 +1,7 @@ package org.qortal.api.resource; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; @@ -9,12 +10,14 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import java.util.List; +import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.DELETE; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.Path; +import javax.ws.rs.QueryParam; import javax.ws.rs.core.Context; import javax.ws.rs.core.MediaType; @@ -30,6 +33,7 @@ import org.qortal.asset.Asset; import org.qortal.controller.tradebot.AcctTradeBot; import org.qortal.controller.tradebot.TradeBot; import org.qortal.crosschain.ForeignBlockchain; +import org.qortal.crosschain.SupportedBlockchain; import org.qortal.crosschain.ACCT; import org.qortal.crosschain.AcctMode; import org.qortal.crypto.Crypto; @@ -65,13 +69,20 @@ public class CrossChainTradeBotResource { ) @ApiErrors({ApiError.REPOSITORY_ISSUE}) public List getTradeBotStates( - // TODO: optional filter for foreign blockchain(s)? - ) { + @Parameter( + description = "Limit to specific blockchain", + example = "LITECOIN", + schema = @Schema(implementation = SupportedBlockchain.class) + ) @QueryParam("foreignBlockchain") SupportedBlockchain foreignBlockchain) { Security.checkApiCallAllowed(request); try (final Repository repository = RepositoryManager.getRepository()) { - // TODO: maybe sub-class returned trade-bot data according to which trade-bot/ACCT applies? - return repository.getCrossChainRepository().getAllTradeBotData(); + List allTradeBotData = repository.getCrossChainRepository().getAllTradeBotData(); + + if (foreignBlockchain == null) + return allTradeBotData; + + return allTradeBotData.stream().filter(tradeBotData -> tradeBotData.getForeignBlockchain().equals(foreignBlockchain.name())).collect(Collectors.toList()); } catch (DataException e) { throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); } diff --git a/src/main/java/org/qortal/crosschain/SupportedBlockchain.java b/src/main/java/org/qortal/crosschain/SupportedBlockchain.java index f835aaad..e7230ff5 100644 --- a/src/main/java/org/qortal/crosschain/SupportedBlockchain.java +++ b/src/main/java/org/qortal/crosschain/SupportedBlockchain.java @@ -1,6 +1,7 @@ package org.qortal.crosschain; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.function.Supplier; @@ -50,6 +51,9 @@ public enum SupportedBlockchain { .flatMap(List::stream) .collect(Collectors.toUnmodifiableMap(Triple::getA, Triple::getC)); + private static final Map blockchainsByName = Arrays.stream(SupportedBlockchain.values()) + .collect(Collectors.toUnmodifiableMap(Enum::name, blockchain -> blockchain)); + private final List>> supportedAccts; SupportedBlockchain(List>> supportedAccts) { @@ -63,6 +67,25 @@ public enum SupportedBlockchain { return supportedAcctsByCodeHash; } + public static Map> getFilteredAcctMap(SupportedBlockchain blockchain) { + if (blockchain == null) + return getAcctMap(); + + return blockchain.supportedAccts.stream() + .collect(Collectors.toUnmodifiableMap(triple -> new ByteArray(triple.getB()), Triple::getC)); + } + + public static Map> getFilteredAcctMap(String specificBlockchain) { + if (specificBlockchain == null) + return getAcctMap(); + + SupportedBlockchain blockchain = blockchainsByName.get(specificBlockchain); + if (blockchain == null) + return Collections.emptyMap(); + + return getFilteredAcctMap(blockchain); + } + public static ACCT getAcctByCodeHash(byte[] codeHash) { ByteArray wrappedCodeHash = new ByteArray(codeHash); diff --git a/src/test/java/org/qortal/test/api/CrossChainApiTests.java b/src/test/java/org/qortal/test/api/CrossChainApiTests.java index 16e12a43..d4f25bce 100644 --- a/src/test/java/org/qortal/test/api/CrossChainApiTests.java +++ b/src/test/java/org/qortal/test/api/CrossChainApiTests.java @@ -4,10 +4,13 @@ import org.junit.Before; import org.junit.Test; import org.qortal.api.ApiError; import org.qortal.api.resource.CrossChainResource; +import org.qortal.crosschain.SupportedBlockchain; import org.qortal.test.common.ApiCommon; public class CrossChainApiTests extends ApiCommon { + private static final SupportedBlockchain SPECIFIC_BLOCKCHAIN = null; + private CrossChainResource crossChainResource; @Before @@ -17,12 +20,13 @@ public class CrossChainApiTests extends ApiCommon { @Test public void testGetTradeOffers() { - assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getTradeOffers(limit, offset, reverse)); + assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getTradeOffers(SPECIFIC_BLOCKCHAIN, limit, offset, reverse)); } @Test public void testGetCompletedTrades() { - assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getCompletedTrades(System.currentTimeMillis() /*minimumTimestamp*/, limit, offset, reverse)); + long minimumTimestamp = System.currentTimeMillis(); + assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, minimumTimestamp, limit, offset, reverse)); } @Test @@ -31,8 +35,8 @@ public class CrossChainApiTests extends ApiCommon { Integer offset = null; Boolean reverse = null; - assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(-1L /*minimumTimestamp*/, limit, offset, reverse)); - assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(0L /*minimumTimestamp*/, limit, offset, reverse)); + assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, -1L /*minimumTimestamp*/, limit, offset, reverse)); + assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, 0L /*minimumTimestamp*/, limit, offset, reverse)); } }