From e9b75b051b44b503efdb8f19886a4dff39c8be94 Mon Sep 17 00:00:00 2001 From: PhilReact Date: Tue, 24 Dec 2024 14:39:31 +0200 Subject: [PATCH] added seller/buyer to filter completed trades --- .../api/resource/CrossChainResource.java | 10 +++++- .../api/websocket/TradeOffersWebSocket.java | 6 ++-- .../org/qortal/repository/ATRepository.java | 2 +- .../repository/hsqldb/HSQLDBATRepository.java | 33 +++++++++++++++---- .../java/org/qortal/test/RepositoryTests.java | 2 +- .../qortal/test/api/CrossChainApiTests.java | 6 ++-- .../org/qortal/test/at/AtRepositoryTests.java | 4 +++ 7 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/qortal/api/resource/CrossChainResource.java b/src/main/java/org/qortal/api/resource/CrossChainResource.java index 9e411127..748dcbe4 100644 --- a/src/main/java/org/qortal/api/resource/CrossChainResource.java +++ b/src/main/java/org/qortal/api/resource/CrossChainResource.java @@ -52,6 +52,8 @@ import java.util.*; import java.util.function.Supplier; import java.util.stream.Collectors; + + @Path("/crosschain") @Tag(name = "Cross-Chain") public class CrossChainResource { @@ -255,6 +257,12 @@ public class CrossChainResource { description = "Only return trades that completed on/after this timestamp (milliseconds since epoch)", example = "1597310000000" ) @QueryParam("minimumTimestamp") Long minimumTimestamp, + @Parameter( + description = "Optionally filter by buyer Qortal address" + ) @QueryParam("buyerAddress") String buyerAddress, + @Parameter( + description = "Optionally filter by seller Qortal address" + ) @QueryParam("sellerAddress") String sellerAddress, @Parameter( ref = "limit") @QueryParam("limit") Integer limit, @Parameter( ref = "offset" ) @QueryParam("offset") Integer offset, @Parameter( ref = "reverse" ) @QueryParam("reverse") Boolean reverse) { @@ -296,7 +304,7 @@ public class CrossChainResource { byte[] codeHash = acctInfo.getKey().value; ACCT acct = acctInfo.getValue().get(); - List atStates = repository.getATRepository().getMatchingFinalATStates(codeHash, + List atStates = repository.getATRepository().getMatchingFinalATStates(codeHash, buyerAddress, sellerAddress, isFinished, acct.getModeByteOffset(), (long) AcctMode.REDEEMED.value, minimumFinalHeight, limit, offset, reverse); diff --git a/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java b/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java index 96257f4a..911cf188 100644 --- a/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java +++ b/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java @@ -98,7 +98,7 @@ public class TradeOffersWebSocket extends ApiWebSocket implements Listener { byte[] codeHash = acctInfo.getKey().value; ACCT acct = acctInfo.getValue().get(); - List atStates = repository.getATRepository().getMatchingFinalATStates(codeHash, + List atStates = repository.getATRepository().getMatchingFinalATStates(codeHash, null, null, isFinished, dataByteOffset, expectedValue, minimumFinalHeight, null, null, null); @@ -259,7 +259,7 @@ public class TradeOffersWebSocket extends ApiWebSocket implements Listener { ACCT acct = acctInfo.getValue().get(); Integer dataByteOffset = acct.getModeByteOffset(); - List initialAtStates = repository.getATRepository().getMatchingFinalATStates(codeHash, + List initialAtStates = repository.getATRepository().getMatchingFinalATStates(codeHash, null, null, isFinished, dataByteOffset, expectedValue, minimumFinalHeight, null, null, null); @@ -298,7 +298,7 @@ public class TradeOffersWebSocket extends ApiWebSocket implements Listener { byte[] codeHash = acctInfo.getKey().value; ACCT acct = acctInfo.getValue().get(); - List historicAtStates = repository.getATRepository().getMatchingFinalATStates(codeHash, + List historicAtStates = repository.getATRepository().getMatchingFinalATStates(codeHash, null, null, isFinished, dataByteOffset, expectedValue, minimumFinalHeight, null, null, null); diff --git a/src/main/java/org/qortal/repository/ATRepository.java b/src/main/java/org/qortal/repository/ATRepository.java index 455ba393..fe001137 100644 --- a/src/main/java/org/qortal/repository/ATRepository.java +++ b/src/main/java/org/qortal/repository/ATRepository.java @@ -76,7 +76,7 @@ public interface ATRepository { * Although expectedValue, if provided, is natively an unsigned long, * the data segment comparison is done via unsigned hex string. */ - public List getMatchingFinalATStates(byte[] codeHash, Boolean isFinished, + public List getMatchingFinalATStates(byte[] codeHash, String buyerAddress, String sellerAddress, Boolean isFinished, Integer dataByteOffset, Long expectedValue, Integer minimumFinalHeight, Integer limit, Integer offset, Boolean reverse) throws DataException; diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java index 80fc62dc..71a95428 100644 --- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java +++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java @@ -1,6 +1,7 @@ package org.qortal.repository.hsqldb; import com.google.common.primitives.Longs; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.qortal.controller.Controller; @@ -16,6 +17,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; +import org.qortal.data.account.AccountData; + public class HSQLDBATRepository implements ATRepository { private static final Logger LOGGER = LogManager.getLogger(HSQLDBATRepository.class); @@ -400,7 +403,7 @@ public class HSQLDBATRepository implements ATRepository { } @Override - public List getMatchingFinalATStates(byte[] codeHash, Boolean isFinished, + public List getMatchingFinalATStates(byte[] codeHash, String buyerAddress, String sellerAddress, Boolean isFinished, Integer dataByteOffset, Long expectedValue, Integer minimumFinalHeight, Integer limit, Integer offset, Boolean reverse) throws DataException { StringBuilder sql = new StringBuilder(1024); @@ -421,10 +424,14 @@ public class HSQLDBATRepository implements ATRepository { // Order by AT_address and height to use compound primary key as index // Both must be the same direction (DESC) also - sql.append("ORDER BY ATStates.AT_address DESC, ATStates.height DESC " - + "LIMIT 1 " - + ") AS FinalATStates " - + "WHERE code_hash = ? "); + sql.append("ORDER BY ATStates.height DESC LIMIT 1) AS FinalATStates "); + + // Optional LEFT JOIN with ATTRANSACTIONS for buyerAddress + if (buyerAddress != null && !buyerAddress.isEmpty()) { + sql.append("LEFT JOIN ATTRANSACTIONS tx ON tx.at_address = ATs.AT_address "); + } + + sql.append("WHERE ATs.code_hash = ? "); bindParams.add(codeHash); if (isFinished != null) { @@ -443,6 +450,20 @@ public class HSQLDBATRepository implements ATRepository { bindParams.add(rawExpectedValue); } + if (buyerAddress != null && !buyerAddress.isEmpty()) { + sql.append("AND tx.recipient = ? "); + bindParams.add(buyerAddress); + } + + + if (sellerAddress != null && !sellerAddress.isEmpty()) { + // Convert sellerAddress to publicKey (method depends on your implementation) + AccountData accountData = this.repository.getAccountRepository().getAccount(sellerAddress); + byte[] publicKey = accountData.getPublicKey(); + sql.append("AND ATs.creator = ? "); + bindParams.add(publicKey); + } + sql.append(" ORDER BY FinalATStates.height "); if (reverse != null && reverse) sql.append("DESC"); @@ -483,7 +504,7 @@ public class HSQLDBATRepository implements ATRepository { Integer dataByteOffset, Long expectedValue, int minimumCount, int maximumCount, long minimumPeriod) throws DataException { // We need most recent entry first so we can use its timestamp to slice further results - List mostRecentStates = this.getMatchingFinalATStates(codeHash, isFinished, + List mostRecentStates = this.getMatchingFinalATStates(codeHash, null, null, isFinished, dataByteOffset, expectedValue, null, 1, 0, true); diff --git a/src/test/java/org/qortal/test/RepositoryTests.java b/src/test/java/org/qortal/test/RepositoryTests.java index 1b0a0e52..51d2535e 100644 --- a/src/test/java/org/qortal/test/RepositoryTests.java +++ b/src/test/java/org/qortal/test/RepositoryTests.java @@ -405,7 +405,7 @@ public class RepositoryTests extends Common { Integer offset = null; Boolean reverse = null; - hsqldb.getATRepository().getMatchingFinalATStates(codeHash, isFinished, dataByteOffset, expectedValue, minimumFinalHeight, limit, offset, reverse); + hsqldb.getATRepository().getMatchingFinalATStates(codeHash,null, null, isFinished, dataByteOffset, expectedValue, minimumFinalHeight, limit, offset, reverse); } catch (DataException e) { fail("HSQLDB bug #1580"); } diff --git a/src/test/java/org/qortal/test/api/CrossChainApiTests.java b/src/test/java/org/qortal/test/api/CrossChainApiTests.java index d4f25bce..e70193b2 100644 --- a/src/test/java/org/qortal/test/api/CrossChainApiTests.java +++ b/src/test/java/org/qortal/test/api/CrossChainApiTests.java @@ -26,7 +26,7 @@ public class CrossChainApiTests extends ApiCommon { @Test public void testGetCompletedTrades() { long minimumTimestamp = System.currentTimeMillis(); - assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, minimumTimestamp, limit, offset, reverse)); + assertNoApiError((limit, offset, reverse) -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, minimumTimestamp, null, null, limit, offset, reverse)); } @Test @@ -35,8 +35,8 @@ public class CrossChainApiTests extends ApiCommon { Integer offset = null; Boolean reverse = null; - 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)); + assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, -1L /*minimumTimestamp*/, null, null, limit, offset, reverse)); + assertApiError(ApiError.INVALID_CRITERIA, () -> this.crossChainResource.getCompletedTrades(SPECIFIC_BLOCKCHAIN, 0L /*minimumTimestamp*/, null, null, limit, offset, reverse)); } } diff --git a/src/test/java/org/qortal/test/at/AtRepositoryTests.java b/src/test/java/org/qortal/test/at/AtRepositoryTests.java index 5472fdb8..5ee74497 100644 --- a/src/test/java/org/qortal/test/at/AtRepositoryTests.java +++ b/src/test/java/org/qortal/test/at/AtRepositoryTests.java @@ -218,6 +218,8 @@ public class AtRepositoryTests extends Common { List atStates = repository.getATRepository().getMatchingFinalATStates( codeHash, + null, + null, isFinished, dataByteOffset, expectedValue, @@ -264,6 +266,8 @@ public class AtRepositoryTests extends Common { List atStates = repository.getATRepository().getMatchingFinalATStates( codeHash, + null, + null, isFinished, dataByteOffset, expectedValue,