diff --git a/Q-Apps.md b/Q-Apps.md
index 74b09791..de97200d 100644
--- a/Q-Apps.md
+++ b/Q-Apps.md
@@ -252,6 +252,7 @@ Here is a list of currently supported actions:
- GET_USER_ACCOUNT
- GET_ACCOUNT_DATA
- GET_ACCOUNT_NAMES
+- SEARCH_NAMES
- GET_NAME_DATA
- LIST_QDN_RESOURCES
- SEARCH_QDN_RESOURCES
@@ -324,6 +325,18 @@ let res = await qortalRequest({
});
```
+### Search names
+```
+let res = await qortalRequest({
+ action: "SEARCH_NAMES",
+ query: "search query goes here",
+ prefix: false, // Optional - if true, only the beginning of the name is matched
+ limit: 100,
+ offset: 0,
+ reverse: false
+});
+```
+
### Get name data
```
let res = await qortalRequest({
diff --git a/pom.xml b/pom.xml
index 78df68a7..0dfa0cf4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
org.qortal
qortal
- 4.0.2
+ 4.0.3
jar
true
diff --git a/src/main/java/org/qortal/api/model/PollVotes.java b/src/main/java/org/qortal/api/model/PollVotes.java
new file mode 100644
index 00000000..c57ebc37
--- /dev/null
+++ b/src/main/java/org/qortal/api/model/PollVotes.java
@@ -0,0 +1,56 @@
+package org.qortal.api.model;
+
+import java.util.List;
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlElement;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+
+import org.qortal.data.voting.VoteOnPollData;
+
+@Schema(description = "Poll vote info, including voters")
+// All properties to be converted to JSON via JAX-RS
+@XmlAccessorType(XmlAccessType.FIELD)
+public class PollVotes {
+
+ @Schema(description = "List of individual votes")
+ @XmlElement(name = "votes")
+ public List votes;
+
+ @Schema(description = "Total number of votes")
+ public Integer totalVotes;
+
+ @Schema(description = "List of vote counts for each option")
+ public List voteCounts;
+
+ // For JAX-RS
+ protected PollVotes() {
+ }
+
+ public PollVotes(List votes, Integer totalVotes, List voteCounts) {
+ this.votes = votes;
+ this.totalVotes = totalVotes;
+ this.voteCounts = voteCounts;
+ }
+
+ @Schema(description = "Vote info")
+ // All properties to be converted to JSON via JAX-RS
+ @XmlAccessorType(XmlAccessType.FIELD)
+ public static class OptionCount {
+ @Schema(description = "Option name")
+ public String optionName;
+
+ @Schema(description = "Vote count")
+ public Integer voteCount;
+
+ // For JAX-RS
+ protected OptionCount() {
+ }
+
+ public OptionCount(String optionName, Integer voteCount) {
+ this.optionName = optionName;
+ this.voteCount = voteCount;
+ }
+ }
+}
diff --git a/src/main/java/org/qortal/api/resource/CrossChainResource.java b/src/main/java/org/qortal/api/resource/CrossChainResource.java
index bb7c70a5..44ef62ad 100644
--- a/src/main/java/org/qortal/api/resource/CrossChainResource.java
+++ b/src/main/java/org/qortal/api/resource/CrossChainResource.java
@@ -115,6 +115,9 @@ public class CrossChainResource {
crossChainTrades.sort((a, b) -> Longs.compare(a.creationTimestamp, b.creationTimestamp));
}
+ // Remove any trades that have had too many failures
+ crossChainTrades = TradeBot.getInstance().removeFailedTrades(repository, crossChainTrades);
+
if (limit != null && limit > 0) {
// Make sure to not return more than the limit
int upperLimit = Math.min(limit, crossChainTrades.size());
@@ -129,6 +132,64 @@ public class CrossChainResource {
}
}
+ @GET
+ @Path("/tradeoffers/hidden")
+ @Operation(
+ summary = "Find cross-chain trade offers that have been hidden due to too many failures",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ array = @ArraySchema(
+ schema = @Schema(
+ implementation = CrossChainTradeData.class
+ )
+ )
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.INVALID_CRITERIA, ApiError.REPOSITORY_ISSUE})
+ public List getHiddenTradeOffers(
+ @Parameter(
+ description = "Limit to specific blockchain",
+ example = "LITECOIN",
+ schema = @Schema(implementation = SupportedBlockchain.class)
+ ) @QueryParam("foreignBlockchain") SupportedBlockchain foreignBlockchain) {
+
+ final boolean isExecutable = true;
+ List crossChainTrades = new ArrayList<>();
+
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ Map> acctsByCodeHash = SupportedBlockchain.getFilteredAcctMap(foreignBlockchain);
+
+ for (Map.Entry> acctInfo : acctsByCodeHash.entrySet()) {
+ byte[] codeHash = acctInfo.getKey().value;
+ ACCT acct = acctInfo.getValue().get();
+
+ List atsData = repository.getATRepository().getATsByFunctionality(codeHash, isExecutable, null, null, null);
+
+ for (ATData atData : atsData) {
+ CrossChainTradeData crossChainTradeData = acct.populateTradeData(repository, atData);
+ if (crossChainTradeData.mode == AcctMode.OFFERING) {
+ crossChainTrades.add(crossChainTradeData);
+ }
+ }
+ }
+
+ // Sort the trades by timestamp
+ crossChainTrades.sort((a, b) -> Longs.compare(a.creationTimestamp, b.creationTimestamp));
+
+ // Remove trades that haven't failed
+ crossChainTrades.removeIf(t -> !TradeBot.getInstance().isFailedTrade(repository, t));
+
+ crossChainTrades.stream().forEach(CrossChainResource::decorateTradeDataWithPresence);
+
+ return crossChainTrades;
+ } catch (DataException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
+ }
+ }
+
@GET
@Path("/trade/{ataddress}")
@Operation(
diff --git a/src/main/java/org/qortal/api/resource/NamesResource.java b/src/main/java/org/qortal/api/resource/NamesResource.java
index 03dffc08..6cde26b3 100644
--- a/src/main/java/org/qortal/api/resource/NamesResource.java
+++ b/src/main/java/org/qortal/api/resource/NamesResource.java
@@ -47,6 +47,7 @@ import org.qortal.transform.transaction.RegisterNameTransactionTransformer;
import org.qortal.transform.transaction.SellNameTransactionTransformer;
import org.qortal.transform.transaction.UpdateNameTransactionTransformer;
import org.qortal.utils.Base58;
+import org.qortal.utils.Unicode;
@Path("/names")
@Tag(name = "Names")
@@ -63,19 +64,19 @@ public class NamesResource {
description = "registered name info",
content = @Content(
mediaType = MediaType.APPLICATION_JSON,
- array = @ArraySchema(schema = @Schema(implementation = NameSummary.class))
+ array = @ArraySchema(schema = @Schema(implementation = NameData.class))
)
)
}
)
@ApiErrors({ApiError.REPOSITORY_ISSUE})
- public List getAllNames(@Parameter(ref = "limit") @QueryParam("limit") Integer limit, @Parameter(ref = "offset") @QueryParam("offset") Integer offset,
- @Parameter(ref="reverse") @QueryParam("reverse") Boolean reverse) {
+ public List getAllNames(@Parameter(description = "Return only names registered or updated after timestamp") @QueryParam("after") Long after,
+ @Parameter(ref = "limit") @QueryParam("limit") Integer limit,
+ @Parameter(ref = "offset") @QueryParam("offset") Integer offset,
+ @Parameter(ref="reverse") @QueryParam("reverse") Boolean reverse) {
try (final Repository repository = RepositoryManager.getRepository()) {
- List names = repository.getNameRepository().getAllNames(limit, offset, reverse);
- // Convert to summary
- return names.stream().map(NameSummary::new).collect(Collectors.toList());
+ return repository.getNameRepository().getAllNames(after, limit, offset, reverse);
} catch (DataException e) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
}
@@ -135,12 +136,13 @@ public class NamesResource {
public NameData getName(@PathParam("name") String name) {
try (final Repository repository = RepositoryManager.getRepository()) {
NameData nameData;
+ String reducedName = Unicode.sanitize(name);
if (Settings.getInstance().isLite()) {
nameData = LiteNode.getInstance().fetchNameData(name);
}
else {
- nameData = repository.getNameRepository().fromName(name);
+ nameData = repository.getNameRepository().fromReducedName(reducedName);
}
if (nameData == null) {
@@ -171,6 +173,7 @@ public class NamesResource {
)
@ApiErrors({ApiError.NAME_UNKNOWN, ApiError.REPOSITORY_ISSUE})
public List searchNames(@QueryParam("query") String query,
+ @Parameter(description = "Prefix only (if true, only the beginning of the name is matched)") @QueryParam("prefix") Boolean prefixOnly,
@Parameter(ref = "limit") @QueryParam("limit") Integer limit,
@Parameter(ref = "offset") @QueryParam("offset") Integer offset,
@Parameter(ref="reverse") @QueryParam("reverse") Boolean reverse) {
@@ -179,7 +182,9 @@ public class NamesResource {
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.INVALID_CRITERIA, "Missing query");
}
- return repository.getNameRepository().searchNames(query, limit, offset, reverse);
+ boolean usePrefixOnly = Boolean.TRUE.equals(prefixOnly);
+
+ return repository.getNameRepository().searchNames(query, usePrefixOnly, limit, offset, reverse);
} catch (ApiException e) {
throw e;
} catch (DataException e) {
@@ -442,4 +447,4 @@ public class NamesResource {
}
}
-}
\ No newline at end of file
+}
diff --git a/src/main/java/org/qortal/api/resource/PollsResource.java b/src/main/java/org/qortal/api/resource/PollsResource.java
index 952cbdc5..c64a8caf 100644
--- a/src/main/java/org/qortal/api/resource/PollsResource.java
+++ b/src/main/java/org/qortal/api/resource/PollsResource.java
@@ -31,12 +31,18 @@ import javax.ws.rs.core.MediaType;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.ArraySchema;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import javax.ws.rs.GET;
import javax.ws.rs.PathParam;
import javax.ws.rs.QueryParam;
import org.qortal.api.ApiException;
+import org.qortal.api.model.PollVotes;
import org.qortal.data.voting.PollData;
+import org.qortal.data.voting.PollOptionData;
+import org.qortal.data.voting.VoteOnPollData;
@Path("/polls")
@Tag(name = "Polls")
@@ -102,6 +108,61 @@ public class PollsResource {
}
}
+ @GET
+ @Path("/votes/{pollName}")
+ @Operation(
+ summary = "Votes on poll",
+ responses = {
+ @ApiResponse(
+ description = "poll votes",
+ content = @Content(
+ mediaType = MediaType.APPLICATION_JSON,
+ schema = @Schema(implementation = PollVotes.class)
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.REPOSITORY_ISSUE})
+ public PollVotes getPollVotes(@PathParam("pollName") String pollName, @QueryParam("onlyCounts") Boolean onlyCounts) {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PollData pollData = repository.getVotingRepository().fromPollName(pollName);
+ if (pollData == null)
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.POLL_NO_EXISTS);
+
+ List votes = repository.getVotingRepository().getVotes(pollName);
+
+ // Initialize map for counting votes
+ Map voteCountMap = new HashMap<>();
+ for (PollOptionData optionData : pollData.getPollOptions()) {
+ voteCountMap.put(optionData.getOptionName(), 0);
+ }
+
+ int totalVotes = 0;
+ for (VoteOnPollData vote : votes) {
+ String selectedOption = pollData.getPollOptions().get(vote.getOptionIndex()).getOptionName();
+ if (voteCountMap.containsKey(selectedOption)) {
+ voteCountMap.put(selectedOption, voteCountMap.get(selectedOption) + 1);
+ totalVotes++;
+ }
+ }
+
+ // Convert map to list of VoteInfo
+ List voteCounts = voteCountMap.entrySet().stream()
+ .map(entry -> new PollVotes.OptionCount(entry.getKey(), entry.getValue()))
+ .collect(Collectors.toList());
+
+ if (onlyCounts != null && onlyCounts) {
+ return new PollVotes(null, totalVotes, voteCounts);
+ } else {
+ return new PollVotes(votes, totalVotes, voteCounts);
+ }
+ } catch (ApiException e) {
+ throw e;
+ } catch (DataException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
+ }
+ }
+
@POST
@Path("/create")
@Operation(
diff --git a/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java b/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java
index 78c53dc3..9c48b018 100644
--- a/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java
+++ b/src/main/java/org/qortal/api/websocket/TradeOffersWebSocket.java
@@ -24,6 +24,7 @@ import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.qortal.api.model.CrossChainOfferSummary;
import org.qortal.controller.Controller;
import org.qortal.controller.Synchronizer;
+import org.qortal.controller.tradebot.TradeBot;
import org.qortal.crosschain.SupportedBlockchain;
import org.qortal.crosschain.ACCT;
import org.qortal.crosschain.AcctMode;
@@ -315,7 +316,7 @@ public class TradeOffersWebSocket extends ApiWebSocket implements Listener {
throw new DataException("Couldn't fetch historic trades from repository");
for (ATStateData historicAtState : historicAtStates) {
- CrossChainOfferSummary historicOfferSummary = produceSummary(repository, acct, historicAtState, null);
+ CrossChainOfferSummary historicOfferSummary = produceSummary(repository, acct, historicAtState, null, null);
if (!isHistoric.test(historicOfferSummary))
continue;
@@ -330,8 +331,10 @@ public class TradeOffersWebSocket extends ApiWebSocket implements Listener {
}
}
- private static CrossChainOfferSummary produceSummary(Repository repository, ACCT acct, ATStateData atState, Long timestamp) throws DataException {
- CrossChainTradeData crossChainTradeData = acct.populateTradeData(repository, atState);
+ private static CrossChainOfferSummary produceSummary(Repository repository, ACCT acct, ATStateData atState, CrossChainTradeData crossChainTradeData, Long timestamp) throws DataException {
+ if (crossChainTradeData == null) {
+ crossChainTradeData = acct.populateTradeData(repository, atState);
+ }
long atStateTimestamp;
@@ -346,9 +349,16 @@ public class TradeOffersWebSocket extends ApiWebSocket implements Listener {
private static List produceSummaries(Repository repository, ACCT acct, List atStates, Long timestamp) throws DataException {
List offerSummaries = new ArrayList<>();
+ for (ATStateData atState : atStates) {
+ CrossChainTradeData crossChainTradeData = acct.populateTradeData(repository, atState);
- for (ATStateData atState : atStates)
- offerSummaries.add(produceSummary(repository, acct, atState, timestamp));
+ // Ignore trade if it has failed
+ if (TradeBot.getInstance().isFailedTrade(repository, crossChainTradeData)) {
+ continue;
+ }
+
+ offerSummaries.add(produceSummary(repository, acct, atState, crossChainTradeData, timestamp));
+ }
return offerSummaries;
}
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java
index 8b7d1a69..d3aadc43 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataStorageManager.java
@@ -488,6 +488,11 @@ public class ArbitraryDataStorageManager extends Thread {
return false;
}
+ if (Settings.getInstance().getStoragePolicy() == StoragePolicy.ALL) {
+ // Using storage policy ALL, so don't limit anything per name
+ return true;
+ }
+
if (name == null) {
// This transaction doesn't have a name, so fall back to total space limitations
return true;
diff --git a/src/main/java/org/qortal/controller/tradebot/TradeBot.java b/src/main/java/org/qortal/controller/tradebot/TradeBot.java
index 5880f561..96eeaf36 100644
--- a/src/main/java/org/qortal/controller/tradebot/TradeBot.java
+++ b/src/main/java/org/qortal/controller/tradebot/TradeBot.java
@@ -10,6 +10,7 @@ import org.apache.logging.log4j.Logger;
import org.bitcoinj.core.ECKey;
import org.qortal.account.PrivateKeyAccount;
import org.qortal.api.model.crosschain.TradeBotCreateRequest;
+import org.qortal.api.resource.TransactionsResource;
import org.qortal.controller.Controller;
import org.qortal.controller.Synchronizer;
import org.qortal.controller.tradebot.AcctTradeBot.ResponseResult;
@@ -19,6 +20,7 @@ import org.qortal.data.at.ATData;
import org.qortal.data.crosschain.CrossChainTradeData;
import org.qortal.data.crosschain.TradeBotData;
import org.qortal.data.network.TradePresenceData;
+import org.qortal.data.transaction.TransactionData;
import org.qortal.event.Event;
import org.qortal.event.EventBus;
import org.qortal.event.Listener;
@@ -33,6 +35,7 @@ import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.repository.hsqldb.HSQLDBImportExport;
import org.qortal.settings.Settings;
+import org.qortal.transaction.Transaction;
import org.qortal.utils.ByteArray;
import org.qortal.utils.NTP;
@@ -113,6 +116,9 @@ public class TradeBot implements Listener {
private Map safeAllTradePresencesByPubkey = Collections.emptyMap();
private long nextTradePresenceBroadcastTimestamp = 0L;
+ private Map failedTrades = new HashMap<>();
+ private Map validTrades = new HashMap<>();
+
private TradeBot() {
EventBus.INSTANCE.addListener(event -> TradeBot.getInstance().listen(event));
}
@@ -674,6 +680,78 @@ public class TradeBot implements Listener {
});
}
+ /** Removes any trades that have had multiple failures */
+ public List removeFailedTrades(Repository repository, List crossChainTrades) {
+ Long now = NTP.getTime();
+ if (now == null) {
+ return crossChainTrades;
+ }
+
+ List updatedCrossChainTrades = new ArrayList<>(crossChainTrades);
+ int getMaxTradeOfferAttempts = Settings.getInstance().getMaxTradeOfferAttempts();
+
+ for (CrossChainTradeData crossChainTradeData : crossChainTrades) {
+ // We only care about trades in the OFFERING state
+ if (crossChainTradeData.mode != AcctMode.OFFERING) {
+ failedTrades.remove(crossChainTradeData.qortalAtAddress);
+ validTrades.remove(crossChainTradeData.qortalAtAddress);
+ continue;
+ }
+
+ // Return recently cached values if they exist
+ Long failedTimestamp = failedTrades.get(crossChainTradeData.qortalAtAddress);
+ if (failedTimestamp != null && now - failedTimestamp < 60 * 60 * 1000L) {
+ updatedCrossChainTrades.remove(crossChainTradeData);
+ //LOGGER.info("Removing cached failed trade AT {}", crossChainTradeData.qortalAtAddress);
+ continue;
+ }
+ Long validTimestamp = validTrades.get(crossChainTradeData.qortalAtAddress);
+ if (validTimestamp != null && now - validTimestamp < 60 * 60 * 1000L) {
+ //LOGGER.info("NOT removing cached valid trade AT {}", crossChainTradeData.qortalAtAddress);
+ continue;
+ }
+
+ try {
+ List signatures = repository.getTransactionRepository().getSignaturesMatchingCriteria(null, null, null, Arrays.asList(Transaction.TransactionType.MESSAGE), null, null, crossChainTradeData.qortalCreatorTradeAddress, TransactionsResource.ConfirmationStatus.CONFIRMED, null, null, null);
+ if (signatures.size() < getMaxTradeOfferAttempts) {
+ // Less than 3 (or user-specified number of) MESSAGE transactions relate to this trade, so assume it is ok
+ validTrades.put(crossChainTradeData.qortalAtAddress, now);
+ continue;
+ }
+
+ List transactions = new ArrayList<>(signatures.size());
+ for (byte[] signature : signatures) {
+ transactions.add(repository.getTransactionRepository().fromSignature(signature));
+ }
+ transactions.sort(Transaction.getDataComparator());
+
+ // Get timestamp of the first MESSAGE transaction
+ long firstMessageTimestamp = transactions.get(0).getTimestamp();
+
+ // Treat as failed if first buy attempt was more than 60 mins ago (as it's still in the OFFERING state)
+ boolean isFailed = (now - firstMessageTimestamp > 60*60*1000L);
+ if (isFailed) {
+ failedTrades.put(crossChainTradeData.qortalAtAddress, now);
+ updatedCrossChainTrades.remove(crossChainTradeData);
+ }
+ else {
+ validTrades.put(crossChainTradeData.qortalAtAddress, now);
+ }
+
+ } catch (DataException e) {
+ LOGGER.info("Unable to determine failed state of AT {}", crossChainTradeData.qortalAtAddress);
+ continue;
+ }
+ }
+
+ return updatedCrossChainTrades;
+ }
+
+ public boolean isFailedTrade(Repository repository, CrossChainTradeData crossChainTradeData) {
+ List results = removeFailedTrades(repository, Arrays.asList(crossChainTradeData));
+ return results.isEmpty();
+ }
+
private long generateExpiry(long timestamp) {
return ((timestamp - 1) / EXPIRY_ROUNDING) * EXPIRY_ROUNDING + PRESENCE_LIFETIME;
}
diff --git a/src/main/java/org/qortal/data/transaction/TransactionData.java b/src/main/java/org/qortal/data/transaction/TransactionData.java
index 838cffd3..4bf3152c 100644
--- a/src/main/java/org/qortal/data/transaction/TransactionData.java
+++ b/src/main/java/org/qortal/data/transaction/TransactionData.java
@@ -13,6 +13,7 @@ import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.eclipse.persistence.oxm.annotations.XmlDiscriminatorNode;
import org.qortal.crypto.Crypto;
import org.qortal.data.voting.PollData;
+import org.qortal.data.voting.VoteOnPollData;
import org.qortal.transaction.Transaction.ApprovalStatus;
import org.qortal.transaction.Transaction.TransactionType;
@@ -30,7 +31,7 @@ import io.swagger.v3.oas.annotations.media.Schema.AccessMode;
@XmlSeeAlso({GenesisTransactionData.class, PaymentTransactionData.class, RegisterNameTransactionData.class, UpdateNameTransactionData.class,
SellNameTransactionData.class, CancelSellNameTransactionData.class, BuyNameTransactionData.class,
CreatePollTransactionData.class, VoteOnPollTransactionData.class, ArbitraryTransactionData.class,
- PollData.class,
+ PollData.class, VoteOnPollData.class,
IssueAssetTransactionData.class, TransferAssetTransactionData.class,
CreateAssetOrderTransactionData.class, CancelAssetOrderTransactionData.class,
MultiPaymentTransactionData.class, DeployAtTransactionData.class, MessageTransactionData.class, ATTransactionData.class,
diff --git a/src/main/java/org/qortal/data/voting/VoteOnPollData.java b/src/main/java/org/qortal/data/voting/VoteOnPollData.java
index 47c06a54..531ed286 100644
--- a/src/main/java/org/qortal/data/voting/VoteOnPollData.java
+++ b/src/main/java/org/qortal/data/voting/VoteOnPollData.java
@@ -9,6 +9,11 @@ public class VoteOnPollData {
// Constructors
+ // For JAXB
+ protected VoteOnPollData() {
+ super();
+ }
+
public VoteOnPollData(String pollName, byte[] voterPublicKey, int optionIndex) {
this.pollName = pollName;
this.voterPublicKey = voterPublicKey;
@@ -21,12 +26,24 @@ public class VoteOnPollData {
return this.pollName;
}
+ public void setPollName(String pollName) {
+ this.pollName = pollName;
+ }
+
public byte[] getVoterPublicKey() {
return this.voterPublicKey;
}
+ public void setVoterPublicKey(byte[] voterPublicKey) {
+ this.voterPublicKey = voterPublicKey;
+ }
+
public int getOptionIndex() {
return this.optionIndex;
}
+ public void setOptionIndex(int optionIndex) {
+ this.optionIndex = optionIndex;
+ }
+
}
diff --git a/src/main/java/org/qortal/repository/NameRepository.java b/src/main/java/org/qortal/repository/NameRepository.java
index a8b2a3db..52a43a18 100644
--- a/src/main/java/org/qortal/repository/NameRepository.java
+++ b/src/main/java/org/qortal/repository/NameRepository.java
@@ -14,12 +14,12 @@ public interface NameRepository {
public boolean reducedNameExists(String reducedName) throws DataException;
- public List searchNames(String query, Integer limit, Integer offset, Boolean reverse) throws DataException;
+ public List searchNames(String query, boolean prefixOnly, Integer limit, Integer offset, Boolean reverse) throws DataException;
- public List getAllNames(Integer limit, Integer offset, Boolean reverse) throws DataException;
+ public List getAllNames(Long after, Integer limit, Integer offset, Boolean reverse) throws DataException;
public default List getAllNames() throws DataException {
- return getAllNames(null, null, null);
+ return getAllNames(null, null, null, null);
}
public List getNamesForSale(Integer limit, Integer offset, Boolean reverse) throws DataException;
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java
index 3e4a8e11..2fefcf8b 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBNameRepository.java
@@ -103,7 +103,7 @@ public class HSQLDBNameRepository implements NameRepository {
}
}
- public List searchNames(String query, Integer limit, Integer offset, Boolean reverse) throws DataException {
+ public List searchNames(String query, boolean prefixOnly, Integer limit, Integer offset, Boolean reverse) throws DataException {
StringBuilder sql = new StringBuilder(512);
List