From 3484047ad48fa4eaea03be7504c9c220b012a919 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Tue, 22 Mar 2022 08:51:01 +0000 Subject: [PATCH] Added GET /transactions/address/{address} API endpoint This is a more standardized alternative to using GET /transactions/search?address=xyz. This avoids the need to build full transaction search ability into the lite node protocols right away. --- .../api/resource/TransactionsResource.java | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/src/main/java/org/qortal/api/resource/TransactionsResource.java b/src/main/java/org/qortal/api/resource/TransactionsResource.java index 55ad7cde..79195d7c 100644 --- a/src/main/java/org/qortal/api/resource/TransactionsResource.java +++ b/src/main/java/org/qortal/api/resource/TransactionsResource.java @@ -12,6 +12,7 @@ import io.swagger.v3.oas.annotations.tags.Tag; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; +import java.util.Comparator; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -32,6 +33,8 @@ import org.qortal.api.ApiException; import org.qortal.api.ApiExceptionFactory; import org.qortal.api.model.SimpleTransactionSignRequest; import org.qortal.controller.Controller; +import org.qortal.controller.LiteNode; +import org.qortal.crypto.Crypto; import org.qortal.data.transaction.TransactionData; import org.qortal.globalization.Translator; import org.qortal.repository.DataException; @@ -366,6 +369,73 @@ public class TransactionsResource { } } + @GET + @Path("/address/{address}") + @Operation( + summary = "Returns transactions for given address", + responses = { + @ApiResponse( + description = "transactions", + content = @Content( + array = @ArraySchema( + schema = @Schema( + implementation = TransactionData.class + ) + ) + ) + ) + } + ) + @ApiErrors({ApiError.INVALID_ADDRESS, ApiError.REPOSITORY_ISSUE}) + public List getAddressTransactions(@PathParam("address") String address, + @Parameter(ref = "limit") @QueryParam("limit") Integer limit, + @Parameter(ref = "offset") @QueryParam("offset") Integer offset, + @Parameter(ref = "reverse") @QueryParam("reverse") Boolean reverse) { + if (!Crypto.isValidAddress(address)) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS); + } + + if (limit == null) { + limit = 0; + } + if (offset == null) { + offset = 0; + } + + List transactions; + + if (Settings.getInstance().isLite()) { + // Fetch from network + transactions = LiteNode.getInstance().fetchAccountTransactions(address, limit, offset); + + // Sort the data, since we can't guarantee the order that a peer sent it in + if (reverse) { + transactions.sort(Comparator.comparingLong(TransactionData::getTimestamp).reversed()); + } else { + transactions.sort(Comparator.comparingLong(TransactionData::getTimestamp)); + } + } + else { + // Fetch from local db + try (final Repository repository = RepositoryManager.getRepository()) { + List signatures = repository.getTransactionRepository().getSignaturesMatchingCriteria(null, null, null, + null, null, null, address, TransactionsResource.ConfirmationStatus.CONFIRMED, limit, offset, reverse); + + // Expand signatures to transactions + transactions = new ArrayList<>(signatures.size()); + for (byte[] signature : signatures) { + transactions.add(repository.getTransactionRepository().fromSignature(signature)); + } + } catch (ApiException e) { + throw e; + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + } + + return transactions; + } + @GET @Path("/unitfee") @Operation(