true
@@ -304,6 +304,7 @@
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
org.qortal.controller.Controller
+ true
. ..
diff --git a/src/main/java/org/qortal/api/resource/CrossChainBitcoinResource.java b/src/main/java/org/qortal/api/resource/CrossChainBitcoinResource.java
index dd967451..1e276e59 100644
--- a/src/main/java/org/qortal/api/resource/CrossChainBitcoinResource.java
+++ b/src/main/java/org/qortal/api/resource/CrossChainBitcoinResource.java
@@ -14,6 +14,7 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -35,6 +36,37 @@ public class CrossChainBitcoinResource {
@Context
HttpServletRequest request;
+ @GET
+ @Path("/height")
+ @Operation(
+ summary = "Returns current Bitcoin block height",
+ description = "Returns the height of the most recent block in the Bitcoin chain.",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(
+ type = "number"
+ )
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ public String getBitcoinHeight() {
+ Bitcoin bitcoin = Bitcoin.getInstance();
+
+ try {
+ Integer height = bitcoin.getBlockchainHeight();
+ if (height == null)
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+
+ return height.toString();
+
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/walletbalance")
@Operation(
@@ -118,6 +150,45 @@ public class CrossChainBitcoinResource {
}
}
+ @POST
+ @Path("/unusedaddress")
+ @Operation(
+ summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
+ description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
+ requestBody = @RequestBody(
+ required = true,
+ content = @Content(
+ mediaType = MediaType.TEXT_PLAIN,
+ schema = @Schema(
+ type = "string",
+ description = "BIP32 'm' private/public key in base58",
+ example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
+ )
+ )
+ ),
+ responses = {
+ @ApiResponse(
+ content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
+ )
+ }
+ )
+ @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ @SecurityRequirement(name = "apiKey")
+ public String getUnusedBitcoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
+ Security.checkApiCallAllowed(request);
+
+ Bitcoin bitcoin = Bitcoin.getInstance();
+
+ if (!bitcoin.isValidDeterministicKey(key58))
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
+
+ try {
+ return bitcoin.getUnusedReceiveAddress(key58);
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/send")
@Operation(
diff --git a/src/main/java/org/qortal/api/resource/CrossChainDigibyteResource.java b/src/main/java/org/qortal/api/resource/CrossChainDigibyteResource.java
index 31d51c73..781d78f6 100644
--- a/src/main/java/org/qortal/api/resource/CrossChainDigibyteResource.java
+++ b/src/main/java/org/qortal/api/resource/CrossChainDigibyteResource.java
@@ -14,6 +14,7 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -35,6 +36,37 @@ public class CrossChainDigibyteResource {
@Context
HttpServletRequest request;
+ @GET
+ @Path("/height")
+ @Operation(
+ summary = "Returns current Digibyte block height",
+ description = "Returns the height of the most recent block in the Digibyte chain.",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(
+ type = "number"
+ )
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ public String getDigibyteHeight() {
+ Digibyte digibyte = Digibyte.getInstance();
+
+ try {
+ Integer height = digibyte.getBlockchainHeight();
+ if (height == null)
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+
+ return height.toString();
+
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/walletbalance")
@Operation(
@@ -118,6 +150,45 @@ public class CrossChainDigibyteResource {
}
}
+ @POST
+ @Path("/unusedaddress")
+ @Operation(
+ summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
+ description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
+ requestBody = @RequestBody(
+ required = true,
+ content = @Content(
+ mediaType = MediaType.TEXT_PLAIN,
+ schema = @Schema(
+ type = "string",
+ description = "BIP32 'm' private/public key in base58",
+ example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
+ )
+ )
+ ),
+ responses = {
+ @ApiResponse(
+ content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
+ )
+ }
+ )
+ @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ @SecurityRequirement(name = "apiKey")
+ public String getUnusedDigibyteReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
+ Security.checkApiCallAllowed(request);
+
+ Digibyte digibyte = Digibyte.getInstance();
+
+ if (!digibyte.isValidDeterministicKey(key58))
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
+
+ try {
+ return digibyte.getUnusedReceiveAddress(key58);
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/send")
@Operation(
diff --git a/src/main/java/org/qortal/api/resource/CrossChainDogecoinResource.java b/src/main/java/org/qortal/api/resource/CrossChainDogecoinResource.java
index 28bebfb8..ff1d6d14 100644
--- a/src/main/java/org/qortal/api/resource/CrossChainDogecoinResource.java
+++ b/src/main/java/org/qortal/api/resource/CrossChainDogecoinResource.java
@@ -21,6 +21,7 @@ import org.qortal.crosschain.SimpleTransaction;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -33,6 +34,37 @@ public class CrossChainDogecoinResource {
@Context
HttpServletRequest request;
+ @GET
+ @Path("/height")
+ @Operation(
+ summary = "Returns current Dogecoin block height",
+ description = "Returns the height of the most recent block in the Dogecoin chain.",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(
+ type = "number"
+ )
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ public String getDogecoinHeight() {
+ Dogecoin dogecoin = Dogecoin.getInstance();
+
+ try {
+ Integer height = dogecoin.getBlockchainHeight();
+ if (height == null)
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+
+ return height.toString();
+
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/walletbalance")
@Operation(
@@ -116,6 +148,45 @@ public class CrossChainDogecoinResource {
}
}
+ @POST
+ @Path("/unusedaddress")
+ @Operation(
+ summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
+ description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
+ requestBody = @RequestBody(
+ required = true,
+ content = @Content(
+ mediaType = MediaType.TEXT_PLAIN,
+ schema = @Schema(
+ type = "string",
+ description = "BIP32 'm' private/public key in base58",
+ example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
+ )
+ )
+ ),
+ responses = {
+ @ApiResponse(
+ content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
+ )
+ }
+ )
+ @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ @SecurityRequirement(name = "apiKey")
+ public String getUnusedDogecoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
+ Security.checkApiCallAllowed(request);
+
+ Dogecoin dogecoin = Dogecoin.getInstance();
+
+ if (!dogecoin.isValidDeterministicKey(key58))
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
+
+ try {
+ return dogecoin.getUnusedReceiveAddress(key58);
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/send")
@Operation(
diff --git a/src/main/java/org/qortal/api/resource/CrossChainLitecoinResource.java b/src/main/java/org/qortal/api/resource/CrossChainLitecoinResource.java
index d12dd94c..3e2ff799 100644
--- a/src/main/java/org/qortal/api/resource/CrossChainLitecoinResource.java
+++ b/src/main/java/org/qortal/api/resource/CrossChainLitecoinResource.java
@@ -14,6 +14,7 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -35,6 +36,37 @@ public class CrossChainLitecoinResource {
@Context
HttpServletRequest request;
+ @GET
+ @Path("/height")
+ @Operation(
+ summary = "Returns current Litecoin block height",
+ description = "Returns the height of the most recent block in the Litecoin chain.",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(
+ type = "number"
+ )
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ public String getLitecoinHeight() {
+ Litecoin litecoin = Litecoin.getInstance();
+
+ try {
+ Integer height = litecoin.getBlockchainHeight();
+ if (height == null)
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+
+ return height.toString();
+
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/walletbalance")
@Operation(
@@ -118,6 +150,45 @@ public class CrossChainLitecoinResource {
}
}
+ @POST
+ @Path("/unusedaddress")
+ @Operation(
+ summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
+ description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
+ requestBody = @RequestBody(
+ required = true,
+ content = @Content(
+ mediaType = MediaType.TEXT_PLAIN,
+ schema = @Schema(
+ type = "string",
+ description = "BIP32 'm' private/public key in base58",
+ example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
+ )
+ )
+ ),
+ responses = {
+ @ApiResponse(
+ content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
+ )
+ }
+ )
+ @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ @SecurityRequirement(name = "apiKey")
+ public String getUnusedLitecoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
+ Security.checkApiCallAllowed(request);
+
+ Litecoin litecoin = Litecoin.getInstance();
+
+ if (!litecoin.isValidDeterministicKey(key58))
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
+
+ try {
+ return litecoin.getUnusedReceiveAddress(key58);
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/send")
@Operation(
diff --git a/src/main/java/org/qortal/api/resource/CrossChainPirateChainResource.java b/src/main/java/org/qortal/api/resource/CrossChainPirateChainResource.java
index bd7bf57d..6989e7c7 100644
--- a/src/main/java/org/qortal/api/resource/CrossChainPirateChainResource.java
+++ b/src/main/java/org/qortal/api/resource/CrossChainPirateChainResource.java
@@ -20,6 +20,7 @@ import org.qortal.crosschain.SimpleTransaction;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -32,6 +33,37 @@ public class CrossChainPirateChainResource {
@Context
HttpServletRequest request;
+ @GET
+ @Path("/height")
+ @Operation(
+ summary = "Returns current PirateChain block height",
+ description = "Returns the height of the most recent block in the PirateChain chain.",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(
+ type = "number"
+ )
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ public String getPirateChainHeight() {
+ PirateChain pirateChain = PirateChain.getInstance();
+
+ try {
+ Integer height = pirateChain.getBlockchainHeight();
+ if (height == null)
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+
+ return height.toString();
+
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/walletbalance")
@Operation(
diff --git a/src/main/java/org/qortal/api/resource/CrossChainRavencoinResource.java b/src/main/java/org/qortal/api/resource/CrossChainRavencoinResource.java
index 97550392..b1d6aed4 100644
--- a/src/main/java/org/qortal/api/resource/CrossChainRavencoinResource.java
+++ b/src/main/java/org/qortal/api/resource/CrossChainRavencoinResource.java
@@ -14,6 +14,7 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
@@ -35,6 +36,37 @@ public class CrossChainRavencoinResource {
@Context
HttpServletRequest request;
+ @GET
+ @Path("/height")
+ @Operation(
+ summary = "Returns current Ravencoin block height",
+ description = "Returns the height of the most recent block in the Ravencoin chain.",
+ responses = {
+ @ApiResponse(
+ content = @Content(
+ schema = @Schema(
+ type = "number"
+ )
+ )
+ )
+ }
+ )
+ @ApiErrors({ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ public String getRavencoinHeight() {
+ Ravencoin ravencoin = Ravencoin.getInstance();
+
+ try {
+ Integer height = ravencoin.getBlockchainHeight();
+ if (height == null)
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+
+ return height.toString();
+
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/walletbalance")
@Operation(
@@ -118,6 +150,45 @@ public class CrossChainRavencoinResource {
}
}
+ @POST
+ @Path("/unusedaddress")
+ @Operation(
+ summary = "Returns first unused address for hierarchical, deterministic BIP32 wallet",
+ description = "Supply BIP32 'm' private/public key in base58, starting with 'xprv'/'xpub' for mainnet, 'tprv'/'tpub' for testnet",
+ requestBody = @RequestBody(
+ required = true,
+ content = @Content(
+ mediaType = MediaType.TEXT_PLAIN,
+ schema = @Schema(
+ type = "string",
+ description = "BIP32 'm' private/public key in base58",
+ example = "tpubD6NzVbkrYhZ4XTPc4btCZ6SMgn8CxmWkj6VBVZ1tfcJfMq4UwAjZbG8U74gGSypL9XBYk2R2BLbDBe8pcEyBKM1edsGQEPKXNbEskZozeZc"
+ )
+ )
+ ),
+ responses = {
+ @ApiResponse(
+ content = @Content(array = @ArraySchema( schema = @Schema( implementation = SimpleTransaction.class ) ) )
+ )
+ }
+ )
+ @ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE})
+ @SecurityRequirement(name = "apiKey")
+ public String getUnusedRavencoinReceiveAddress(@HeaderParam(Security.API_KEY_HEADER) String apiKey, String key58) {
+ Security.checkApiCallAllowed(request);
+
+ Ravencoin ravencoin = Ravencoin.getInstance();
+
+ if (!ravencoin.isValidDeterministicKey(key58))
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_PRIVATE_KEY);
+
+ try {
+ return ravencoin.getUnusedReceiveAddress(key58);
+ } catch (ForeignBlockchainException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.FOREIGN_BLOCKCHAIN_NETWORK_ISSUE);
+ }
+ }
+
@POST
@Path("/send")
@Operation(
diff --git a/src/main/java/org/qortal/api/restricted/resource/AdminResource.java b/src/main/java/org/qortal/api/restricted/resource/AdminResource.java
index 06bafcc6..253939f7 100644
--- a/src/main/java/org/qortal/api/restricted/resource/AdminResource.java
+++ b/src/main/java/org/qortal/api/restricted/resource/AdminResource.java
@@ -222,6 +222,42 @@ public class AdminResource {
}
}
+ @GET
+ @Path("/summary/alltime")
+ @Operation(
+ summary = "Summary of activity since genesis",
+ responses = {
+ @ApiResponse(
+ content = @Content(schema = @Schema(implementation = ActivitySummary.class))
+ )
+ }
+ )
+ @ApiErrors({ApiError.REPOSITORY_ISSUE})
+ @SecurityRequirement(name = "apiKey")
+ public ActivitySummary allTimeSummary(@HeaderParam(Security.API_KEY_HEADER) String apiKey) {
+ Security.checkApiCallAllowed(request);
+
+ ActivitySummary summary = new ActivitySummary();
+
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ int startHeight = 1;
+ long start = repository.getBlockRepository().fromHeight(startHeight).getTimestamp();
+ int endHeight = repository.getBlockRepository().getBlockchainHeight();
+
+ summary.setBlockCount(endHeight - startHeight);
+
+ summary.setTransactionCountByType(repository.getTransactionRepository().getTransactionSummary(startHeight + 1, endHeight));
+
+ summary.setAssetsIssued(repository.getAssetRepository().getRecentAssetIds(start).size());
+
+ summary.setNamesRegistered (repository.getNameRepository().getRecentNames(start).size());
+
+ return summary;
+ } catch (DataException e) {
+ throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
+ }
+ }
+
@GET
@Path("/enginestats")
@Operation(
diff --git a/src/main/java/org/qortal/api/restricted/resource/BootstrapResource.java b/src/main/java/org/qortal/api/restricted/resource/BootstrapResource.java
index bbe03c61..47b7cf42 100644
--- a/src/main/java/org/qortal/api/restricted/resource/BootstrapResource.java
+++ b/src/main/java/org/qortal/api/restricted/resource/BootstrapResource.java
@@ -60,7 +60,7 @@ public class BootstrapResource {
bootstrap.validateBlockchain();
return bootstrap.create();
- } catch (DataException | InterruptedException | IOException e) {
+ } catch (Exception e) {
LOGGER.info("Unable to create bootstrap", e);
throw ApiExceptionFactory.INSTANCE.createCustomException(request, ApiError.REPOSITORY_ISSUE, e.getMessage());
}
diff --git a/src/main/java/org/qortal/arbitrary/misc/Service.java b/src/main/java/org/qortal/arbitrary/misc/Service.java
index 01419d2f..5ea1b7aa 100644
--- a/src/main/java/org/qortal/arbitrary/misc/Service.java
+++ b/src/main/java/org/qortal/arbitrary/misc/Service.java
@@ -84,6 +84,8 @@ public enum Service {
QCHAT_IMAGE(420, true, 500*1024L, null),
VIDEO(500, false, null, null),
AUDIO(600, false, null, null),
+ QCHAT_AUDIO(610, true, 10*1024*1024L, null),
+ QCHAT_VOICE(620, true, 10*1024*1024L, null),
BLOG(700, false, null, null),
BLOG_POST(777, false, null, null),
BLOG_COMMENT(778, false, null, null),
diff --git a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java
index 39425b7e..34acf0cb 100644
--- a/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java
+++ b/src/main/java/org/qortal/controller/arbitrary/ArbitraryDataCleanupManager.java
@@ -204,7 +204,7 @@ public class ArbitraryDataCleanupManager extends Thread {
if (completeFileExists && !allChunksExist) {
// We have the complete file but not the chunks, so let's convert it
- LOGGER.info(String.format("Transaction %s has complete file but no chunks",
+ LOGGER.debug(String.format("Transaction %s has complete file but no chunks",
Base58.encode(arbitraryTransactionData.getSignature())));
ArbitraryTransactionUtils.convertFileToChunks(arbitraryTransactionData, now, STALE_FILE_TIMEOUT);
diff --git a/src/main/java/org/qortal/controller/repository/AtStatesPruner.java b/src/main/java/org/qortal/controller/repository/AtStatesPruner.java
index 064fe0ea..f06efdb8 100644
--- a/src/main/java/org/qortal/controller/repository/AtStatesPruner.java
+++ b/src/main/java/org/qortal/controller/repository/AtStatesPruner.java
@@ -39,9 +39,10 @@ public class AtStatesPruner implements Runnable {
try (final Repository repository = RepositoryManager.getRepository()) {
int pruneStartHeight = repository.getATRepository().getAtPruneHeight();
+ int maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
repository.discardChanges();
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
repository.saveChanges();
while (!Controller.isStopping()) {
@@ -92,7 +93,8 @@ public class AtStatesPruner implements Runnable {
if (upperPrunableHeight > upperBatchHeight) {
pruneStartHeight = upperBatchHeight;
repository.getATRepository().setAtPruneHeight(pruneStartHeight);
- repository.getATRepository().rebuildLatestAtStates();
+ maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
+ repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
repository.saveChanges();
final int finalPruneStartHeight = pruneStartHeight;
diff --git a/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java b/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java
index 6c026385..125628f1 100644
--- a/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java
+++ b/src/main/java/org/qortal/controller/repository/AtStatesTrimmer.java
@@ -26,9 +26,10 @@ public class AtStatesTrimmer implements Runnable {
try (final Repository repository = RepositoryManager.getRepository()) {
int trimStartHeight = repository.getATRepository().getAtTrimHeight();
+ int maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
repository.discardChanges();
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
repository.saveChanges();
while (!Controller.isStopping()) {
@@ -70,7 +71,8 @@ public class AtStatesTrimmer implements Runnable {
if (upperTrimmableHeight > upperBatchHeight) {
trimStartHeight = upperBatchHeight;
repository.getATRepository().setAtTrimHeight(trimStartHeight);
- repository.getATRepository().rebuildLatestAtStates();
+ maxLatestAtStatesHeight = PruneManager.getMaxHeightForLatestAtStates(repository);
+ repository.getATRepository().rebuildLatestAtStates(maxLatestAtStatesHeight);
repository.saveChanges();
final int finalTrimStartHeight = trimStartHeight;
diff --git a/src/main/java/org/qortal/controller/repository/PruneManager.java b/src/main/java/org/qortal/controller/repository/PruneManager.java
index ec27456f..dfb6290b 100644
--- a/src/main/java/org/qortal/controller/repository/PruneManager.java
+++ b/src/main/java/org/qortal/controller/repository/PruneManager.java
@@ -157,4 +157,18 @@ public class PruneManager {
return (height < latestUnprunedHeight);
}
+ /**
+ * When rebuilding the latest AT states, we need to specify a maxHeight, so that we aren't tracking
+ * very recent AT states that could potentially be orphaned. This method ensures that AT states
+ * are given a sufficient number of blocks to confirm before being tracked as a latest AT state.
+ */
+ public static int getMaxHeightForLatestAtStates(Repository repository) throws DataException {
+ // Get current chain height, and subtract a certain number of "confirmation" blocks
+ // This is to ensure we are basing our latest AT states data on confirmed blocks -
+ // ones that won't be orphaned in any normal circumstances
+ final int confirmationBlocks = 250;
+ final int chainHeight = repository.getBlockRepository().getBlockchainHeight();
+ return chainHeight - confirmationBlocks;
+ }
+
}
diff --git a/src/main/java/org/qortal/crosschain/Bitcoin.java b/src/main/java/org/qortal/crosschain/Bitcoin.java
index 7fec5a17..b65bac8e 100644
--- a/src/main/java/org/qortal/crosschain/Bitcoin.java
+++ b/src/main/java/org/qortal/crosschain/Bitcoin.java
@@ -49,6 +49,7 @@ public class Bitcoin extends Bitcoiny {
//CLOSED new Server("bitcoin.grey.pw", Server.ConnectionType.SSL, 50002),
//CLOSED new Server("btc.litepay.ch", Server.ConnectionType.SSL, 50002),
//CLOSED new Server("electrum.pabu.io", Server.ConnectionType.SSL, 50002),
+ //CLOSED new Server("electrumx.dev", Server.ConnectionType.SSL, 50002),
//CLOSED new Server("electrumx.hodlwallet.com", Server.ConnectionType.SSL, 50002),
//CLOSED new Server("gd42.org", Server.ConnectionType.SSL, 50002),
//CLOSED new Server("korea.electrum-server.com", Server.ConnectionType.SSL, 50002),
@@ -56,28 +57,75 @@ public class Bitcoin extends Bitcoiny {
//1.15.0 new Server("alviss.coinjoined.com", Server.ConnectionType.SSL, 50002),
//1.15.0 new Server("electrum.acinq.co", Server.ConnectionType.SSL, 50002),
//1.14.0 new Server("electrum.coinext.com.br", Server.ConnectionType.SSL, 50002),
+ //F1.7.0 new Server("btc.lastingcoin.net", Server.ConnectionType.SSL, 50002),
new Server("104.248.139.211", Server.ConnectionType.SSL, 50002),
+ new Server("128.0.190.26", Server.ConnectionType.SSL, 50002),
new Server("142.93.6.38", Server.ConnectionType.SSL, 50002),
new Server("157.245.172.236", Server.ConnectionType.SSL, 50002),
new Server("167.172.226.175", Server.ConnectionType.SSL, 50002),
new Server("167.172.42.31", Server.ConnectionType.SSL, 50002),
new Server("178.62.80.20", Server.ConnectionType.SSL, 50002),
new Server("185.64.116.15", Server.ConnectionType.SSL, 50002),
+ new Server("188.165.206.215", Server.ConnectionType.SSL, 50002),
+ new Server("188.165.211.112", Server.ConnectionType.SSL, 50002),
+ new Server("2azzarita.hopto.org", Server.ConnectionType.SSL, 50002),
+ new Server("2electrumx.hopto.me", Server.ConnectionType.SSL, 56022),
+ new Server("2ex.digitaleveryware.com", Server.ConnectionType.SSL, 50002),
+ new Server("65.39.140.37", Server.ConnectionType.SSL, 50002),
new Server("68.183.188.105", Server.ConnectionType.SSL, 50002),
+ new Server("71.73.14.254", Server.ConnectionType.SSL, 50002),
+ new Server("94.23.247.135", Server.ConnectionType.SSL, 50002),
+ new Server("assuredly.not.fyi", Server.ConnectionType.SSL, 50002),
+ new Server("ax101.blockeng.ch", Server.ConnectionType.SSL, 50002),
+ new Server("ax102.blockeng.ch", Server.ConnectionType.SSL, 50002),
+ new Server("b.1209k.com", Server.ConnectionType.SSL, 50002),
+ new Server("b6.1209k.com", Server.ConnectionType.SSL, 50002),
+ new Server("bitcoin.dermichi.com", Server.ConnectionType.SSL, 50002),
+ new Server("bitcoin.lu.ke", Server.ConnectionType.SSL, 50002),
new Server("bitcoin.lukechilds.co", Server.ConnectionType.SSL, 50002),
new Server("blkhub.net", Server.ConnectionType.SSL, 50002),
- new Server("btc.lastingcoin.net", Server.ConnectionType.SSL, 50002),
+ new Server("btc.electroncash.dk", Server.ConnectionType.SSL, 60002),
+ new Server("btc.ocf.sh", Server.ConnectionType.SSL, 50002),
new Server("btce.iiiiiii.biz", Server.ConnectionType.SSL, 50002),
new Server("caleb.vegas", Server.ConnectionType.SSL, 50002),
new Server("eai.coincited.net", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.bhoovd.com", Server.ConnectionType.SSL, 50002),
new Server("electrum.bitaroo.net", Server.ConnectionType.SSL, 50002),
- new Server("electrumx.dev", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.bitcoinlizard.net", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.blockstream.info", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.emzy.de", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.exan.tech", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.kendigisland.xyz", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.mmitech.info", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.petrkr.net", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.stippy.com", Server.ConnectionType.SSL, 50002),
+ new Server("electrum.thomasfischbach.de", Server.ConnectionType.SSL, 50002),
+ new Server("electrum0.snel.it", Server.ConnectionType.SSL, 50002),
+ new Server("electrum1.cipig.net", Server.ConnectionType.SSL, 50002),
+ new Server("electrum2.cipig.net", Server.ConnectionType.SSL, 50002),
+ new Server("electrum3.cipig.net", Server.ConnectionType.SSL, 50002),
+ new Server("electrumx.alexridevski.net", Server.ConnectionType.SSL, 50002),
+ new Server("electrumx-core.1209k.com", Server.ConnectionType.SSL, 50002),
new Server("elx.bitske.com", Server.ConnectionType.SSL, 50002),
+ new Server("ex03.axalgo.com", Server.ConnectionType.SSL, 50002),
+ new Server("ex05.axalgo.com", Server.ConnectionType.SSL, 50002),
+ new Server("ex07.axalgo.com", Server.ConnectionType.SSL, 50002),
new Server("fortress.qtornado.com", Server.ConnectionType.SSL, 50002),
+ new Server("fulcrum.grey.pw", Server.ConnectionType.SSL, 50002),
+ new Server("fulcrum.sethforprivacy.com", Server.ConnectionType.SSL, 51002),
new Server("guichet.centure.cc", Server.ConnectionType.SSL, 50002),
- new Server("kareoke.qoppa.org", Server.ConnectionType.SSL, 50002),
new Server("hodlers.beer", Server.ConnectionType.SSL, 50002),
+ new Server("kareoke.qoppa.org", Server.ConnectionType.SSL, 50002),
+ new Server("kirsche.emzy.de", Server.ConnectionType.SSL, 50002),
new Server("node1.btccuracao.com", Server.ConnectionType.SSL, 50002),
+ new Server("osr1ex1.compumundohipermegared.one", Server.ConnectionType.SSL, 50002),
+ new Server("smmalis37.ddns.net", Server.ConnectionType.SSL, 50002),
+ new Server("ulrichard.ch", Server.ConnectionType.SSL, 50002),
+ new Server("vmd104012.contaboserver.net", Server.ConnectionType.SSL, 50002),
+ new Server("vmd104014.contaboserver.net", Server.ConnectionType.SSL, 50002),
+ new Server("vmd63185.contaboserver.net", Server.ConnectionType.SSL, 50002),
+ new Server("vmd71287.contaboserver.net", Server.ConnectionType.SSL, 50002),
+ new Server("vmd84592.contaboserver.net", Server.ConnectionType.SSL, 50002),
new Server("xtrum.com", Server.ConnectionType.SSL, 50002));
}
diff --git a/src/main/java/org/qortal/crosschain/Bitcoiny.java b/src/main/java/org/qortal/crosschain/Bitcoiny.java
index c08bd91e..d1523b50 100644
--- a/src/main/java/org/qortal/crosschain/Bitcoiny.java
+++ b/src/main/java/org/qortal/crosschain/Bitcoiny.java
@@ -167,6 +167,16 @@ public abstract class Bitcoiny implements ForeignBlockchain {
return blockTimestamps.get(5);
}
+ /**
+ * Returns height from latest block.
+ *
+ * @throws ForeignBlockchainException if error occurs
+ */
+ public int getBlockchainHeight() throws ForeignBlockchainException {
+ int height = this.blockchainProvider.getCurrentHeight();
+ return height;
+ }
+
/** Returns fee per transaction KB. To be overridden for testnet/regtest. */
public Coin getFeePerKb() {
return this.bitcoinjContext.getFeePerKb();
diff --git a/src/main/java/org/qortal/crosschain/Digibyte.java b/src/main/java/org/qortal/crosschain/Digibyte.java
index 4358b3b3..c5d96383 100644
--- a/src/main/java/org/qortal/crosschain/Digibyte.java
+++ b/src/main/java/org/qortal/crosschain/Digibyte.java
@@ -45,6 +45,9 @@ public class Digibyte extends Bitcoiny {
return Arrays.asList(
// Servers chosen on NO BASIS WHATSOEVER from various sources!
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=dgb
+ new Server("electrum.qortal.link", Server.ConnectionType.SSL, 55002),
+ new Server("electrum-dgb.qortal.online", ConnectionType.SSL, 50002),
+ new Server("electrum1-dgb.qortal.online", ConnectionType.SSL, 50002),
new Server("electrum1.cipig.net", ConnectionType.SSL, 20059),
new Server("electrum2.cipig.net", ConnectionType.SSL, 20059),
new Server("electrum3.cipig.net", ConnectionType.SSL, 20059));
diff --git a/src/main/java/org/qortal/crosschain/Dogecoin.java b/src/main/java/org/qortal/crosschain/Dogecoin.java
index 9af8d990..99f557a5 100644
--- a/src/main/java/org/qortal/crosschain/Dogecoin.java
+++ b/src/main/java/org/qortal/crosschain/Dogecoin.java
@@ -45,11 +45,13 @@ public class Dogecoin extends Bitcoiny {
public Collection getServers() {
return Arrays.asList(
// Servers chosen on NO BASIS WHATSOEVER from various sources!
+ // Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=doge
+ new Server("electrum.qortal.link", Server.ConnectionType.SSL, 54002),
+ new Server("electrum-doge.qortal.online", ConnectionType.SSL, 50002),
+ new Server("electrum1-doge.qortal.online", ConnectionType.SSL, 50002),
new Server("electrum1.cipig.net", ConnectionType.SSL, 20060),
new Server("electrum2.cipig.net", ConnectionType.SSL, 20060),
- new Server("electrum3.cipig.net", ConnectionType.SSL, 20060),
- new Server("161.97.137.235", ConnectionType.SSL, 50002));
- // TODO: add more mainnet servers. It's too centralized.
+ new Server("electrum3.cipig.net", ConnectionType.SSL, 20060));
}
@Override
diff --git a/src/main/java/org/qortal/crosschain/Litecoin.java b/src/main/java/org/qortal/crosschain/Litecoin.java
index 6fc6ba50..1dd9037a 100644
--- a/src/main/java/org/qortal/crosschain/Litecoin.java
+++ b/src/main/java/org/qortal/crosschain/Litecoin.java
@@ -45,17 +45,20 @@ public class Litecoin extends Bitcoiny {
return Arrays.asList(
// Servers chosen on NO BASIS WHATSOEVER from various sources!
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=ltc
- //CLOSED new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
//CLOSED new Server("electrum-ltc.someguy123.net", Server.ConnectionType.SSL, 50002),
+ //CLOSED new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022),
+ //BEHIND new Server("62.171.169.176", Server.ConnectionType.SSL, 50002),
//PHISHY new Server("electrum-ltc.bysh.me", Server.ConnectionType.SSL, 50002),
new Server("backup.electrum-ltc.org", Server.ConnectionType.SSL, 443),
+ new Server("electrum.qortal.link", Server.ConnectionType.SSL, 50002),
new Server("electrum.ltc.xurious.com", Server.ConnectionType.SSL, 50002),
+ new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
+ new Server("electrum-ltc.qortal.online", Server.ConnectionType.SSL, 50002),
+ new Server("electrum1-ltc.qortal.online", Server.ConnectionType.SSL, 50002),
new Server("electrum1.cipig.net", Server.ConnectionType.SSL, 20063),
new Server("electrum2.cipig.net", Server.ConnectionType.SSL, 20063),
new Server("electrum3.cipig.net", Server.ConnectionType.SSL, 20063),
- new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022),
- new Server("ltc.rentonrisk.com", Server.ConnectionType.SSL, 50002),
- new Server("62.171.169.176", Server.ConnectionType.SSL, 50002));
+ new Server("ltc.rentonrisk.com", Server.ConnectionType.SSL, 50002));
}
@Override
diff --git a/src/main/java/org/qortal/crosschain/PirateChain.java b/src/main/java/org/qortal/crosschain/PirateChain.java
index 09b37481..a1d31a4e 100644
--- a/src/main/java/org/qortal/crosschain/PirateChain.java
+++ b/src/main/java/org/qortal/crosschain/PirateChain.java
@@ -57,9 +57,9 @@ public class PirateChain extends Bitcoiny {
public Collection getServers() {
return Arrays.asList(
// Servers chosen on NO BASIS WHATSOEVER from various sources!
- new Server("arrrlightd.qortal.online", ConnectionType.SSL, 443),
- new Server("arrrlightd1.qortal.online", ConnectionType.SSL, 443),
- new Server("arrrlightd2.qortal.online", ConnectionType.SSL, 443),
+ new Server("wallet-arrr1.qortal.online", ConnectionType.SSL, 443),
+ new Server("wallet-arrr2.qortal.online", ConnectionType.SSL, 443),
+ new Server("wallet-arrr3.qortal.online", ConnectionType.SSL, 443),
new Server("lightd.pirate.black", ConnectionType.SSL, 443));
}
diff --git a/src/main/java/org/qortal/crosschain/Ravencoin.java b/src/main/java/org/qortal/crosschain/Ravencoin.java
index 7bf5b20f..6030fa50 100644
--- a/src/main/java/org/qortal/crosschain/Ravencoin.java
+++ b/src/main/java/org/qortal/crosschain/Ravencoin.java
@@ -45,13 +45,17 @@ public class Ravencoin extends Bitcoiny {
return Arrays.asList(
// Servers chosen on NO BASIS WHATSOEVER from various sources!
// Status verified at https://1209k.com/bitcoin-eye/ele.php?chain=rvn
- new Server("aethyn.com", ConnectionType.SSL, 50002),
- new Server("electrum2.rvn.rocks", ConnectionType.SSL, 50002),
- new Server("rvn-dashboard.com", ConnectionType.SSL, 50002),
- new Server("rvn4lyfe.com", ConnectionType.SSL, 50002),
+ //CLOSED new Server("aethyn.com", ConnectionType.SSL, 50002),
+ //CLOSED new Server("electrum2.rvn.rocks", ConnectionType.SSL, 50002),
+ //BEHIND new Server("electrum3.rvn.rocks", ConnectionType.SSL, 50002),
+ new Server("electrum.qortal.link", Server.ConnectionType.SSL, 56002),
+ new Server("electrum-rvn.qortal.online", ConnectionType.SSL, 50002),
+ new Server("electrum1-rvn.qortal.online", ConnectionType.SSL, 50002),
new Server("electrum1.cipig.net", ConnectionType.SSL, 20051),
new Server("electrum2.cipig.net", ConnectionType.SSL, 20051),
- new Server("electrum3.cipig.net", ConnectionType.SSL, 20051));
+ new Server("electrum3.cipig.net", ConnectionType.SSL, 20051),
+ new Server("rvn-dashboard.com", ConnectionType.SSL, 50002),
+ new Server("rvn4lyfe.com", ConnectionType.SSL, 50002));
}
@Override
diff --git a/src/main/java/org/qortal/data/chat/ActiveChats.java b/src/main/java/org/qortal/data/chat/ActiveChats.java
index c546d637..d5ebcf3f 100644
--- a/src/main/java/org/qortal/data/chat/ActiveChats.java
+++ b/src/main/java/org/qortal/data/chat/ActiveChats.java
@@ -17,17 +17,21 @@ public class ActiveChats {
private Long timestamp;
private String sender;
private String senderName;
+ private byte[] signature;
+ private byte[] data;
protected GroupChat() {
/* JAXB */
}
- public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName) {
+ public GroupChat(int groupId, String groupName, Long timestamp, String sender, String senderName, byte[] signature, byte[] data) {
this.groupId = groupId;
this.groupName = groupName;
this.timestamp = timestamp;
this.sender = sender;
this.senderName = senderName;
+ this.signature = signature;
+ this.data = data;
}
public int getGroupId() {
@@ -49,6 +53,14 @@ public class ActiveChats {
public String getSenderName() {
return this.senderName;
}
+
+ public byte[] getSignature() {
+ return this.signature;
+ }
+
+ public byte[] getData() {
+ return this.data;
+ }
}
@XmlAccessorType(XmlAccessType.FIELD)
@@ -118,4 +130,4 @@ public class ActiveChats {
return this.direct;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/qortal/naming/Name.java b/src/main/java/org/qortal/naming/Name.java
index ecf826a5..1751cca8 100644
--- a/src/main/java/org/qortal/naming/Name.java
+++ b/src/main/java/org/qortal/naming/Name.java
@@ -16,6 +16,8 @@ import org.qortal.repository.Repository;
import org.qortal.transaction.Transaction.TransactionType;
import org.qortal.utils.Unicode;
+import java.util.Objects;
+
public class Name {
// Properties
@@ -116,7 +118,7 @@ public class Name {
this.repository.getNameRepository().save(this.nameData);
- if (!updateNameTransactionData.getNewName().isEmpty())
+ if (!updateNameTransactionData.getNewName().isEmpty() && !Objects.equals(updateNameTransactionData.getName(), updateNameTransactionData.getNewName()))
// Name has changed, delete old entry
this.repository.getNameRepository().delete(updateNameTransactionData.getNewName());
diff --git a/src/main/java/org/qortal/repository/ATRepository.java b/src/main/java/org/qortal/repository/ATRepository.java
index 0f537ae9..93da924c 100644
--- a/src/main/java/org/qortal/repository/ATRepository.java
+++ b/src/main/java/org/qortal/repository/ATRepository.java
@@ -119,7 +119,7 @@ public interface ATRepository {
*
* NOTE: performs implicit repository.saveChanges().
*/
- public void rebuildLatestAtStates() throws DataException;
+ public void rebuildLatestAtStates(int maxHeight) throws DataException;
/** Returns height of first trimmable AT state. */
diff --git a/src/main/java/org/qortal/repository/Bootstrap.java b/src/main/java/org/qortal/repository/Bootstrap.java
index 626433e8..2d2605cc 100644
--- a/src/main/java/org/qortal/repository/Bootstrap.java
+++ b/src/main/java/org/qortal/repository/Bootstrap.java
@@ -279,7 +279,9 @@ public class Bootstrap {
LOGGER.info("Generating checksum file...");
String checksum = Crypto.digestHexString(compressedOutputPath.toFile(), 1024*1024);
+ LOGGER.info("checksum: {}", checksum);
Path checksumPath = Paths.get(String.format("%s.sha256", compressedOutputPath.toString()));
+ LOGGER.info("Writing checksum to path: {}", checksumPath);
Files.writeString(checksumPath, checksum, StandardOpenOption.CREATE);
// Return the path to the compressed bootstrap file
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java
index 04823925..dd0404a8 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBATRepository.java
@@ -603,7 +603,7 @@ public class HSQLDBATRepository implements ATRepository {
@Override
- public void rebuildLatestAtStates() throws DataException {
+ public void rebuildLatestAtStates(int maxHeight) throws DataException {
// latestATStatesLock is to prevent concurrent updates on LatestATStates
// that could result in one process using a partial or empty dataset
// because it was in the process of being rebuilt by another thread
@@ -624,11 +624,12 @@ public class HSQLDBATRepository implements ATRepository {
+ "CROSS JOIN LATERAL("
+ "SELECT height FROM ATStates "
+ "WHERE ATStates.AT_address = ATs.AT_address "
+ + "AND height <= ?"
+ "ORDER BY AT_address DESC, height DESC LIMIT 1"
+ ") "
+ ")";
try {
- this.repository.executeCheckedUpdate(insertSql);
+ this.repository.executeCheckedUpdate(insertSql, maxHeight);
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to populate temporary latest AT states cache in repository", e);
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBChatRepository.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBChatRepository.java
index 08226d53..a995a0b3 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBChatRepository.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBChatRepository.java
@@ -177,11 +177,11 @@ public class HSQLDBChatRepository implements ChatRepository {
private List getActiveGroupChats(String address) throws DataException {
// Find groups where address is a member and potential latest message details
- String groupsSql = "SELECT group_id, group_name, latest_timestamp, sender, sender_name "
+ String groupsSql = "SELECT group_id, group_name, latest_timestamp, sender, sender_name, signature, data "
+ "FROM GroupMembers "
+ "JOIN Groups USING (group_id) "
+ "LEFT OUTER JOIN LATERAL("
- + "SELECT created_when AS latest_timestamp, sender, name AS sender_name "
+ + "SELECT created_when AS latest_timestamp, sender, name AS sender_name, signature, data "
+ "FROM ChatTransactions "
+ "JOIN Transactions USING (signature) "
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
@@ -205,8 +205,10 @@ public class HSQLDBChatRepository implements ChatRepository {
String sender = resultSet.getString(4);
String senderName = resultSet.getString(5);
+ byte[] signature = resultSet.getBytes(6);
+ byte[] data = resultSet.getBytes(7);
- GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName);
+ GroupChat groupChat = new GroupChat(groupId, groupName, timestamp, sender, senderName, signature, data);
groupChats.add(groupChat);
} while (resultSet.next());
}
@@ -215,7 +217,7 @@ public class HSQLDBChatRepository implements ChatRepository {
}
// We need different SQL to handle group-less chat
- String grouplessSql = "SELECT created_when, sender, SenderNames.name "
+ String grouplessSql = "SELECT created_when, sender, SenderNames.name, signature, data "
+ "FROM ChatTransactions "
+ "JOIN Transactions USING (signature) "
+ "LEFT OUTER JOIN Names AS SenderNames ON SenderNames.owner = sender "
@@ -228,15 +230,19 @@ public class HSQLDBChatRepository implements ChatRepository {
Long timestamp = null;
String sender = null;
String senderName = null;
+ byte[] signature = null;
+ byte[] data = null;
if (resultSet != null) {
// We found a recipient-less, group-less CHAT message, so report its details
timestamp = resultSet.getLong(1);
sender = resultSet.getString(2);
senderName = resultSet.getString(3);
+ signature = resultSet.getBytes(4);
+ data = resultSet.getBytes(5);
}
- GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName);
+ GroupChat groupChat = new GroupChat(0, null, timestamp, sender, senderName, signature, data);
groupChats.add(groupChat);
} catch (SQLException e) {
throw new DataException("Unable to fetch active group chats from repository", e);
@@ -291,4 +297,4 @@ public class HSQLDBChatRepository implements ChatRepository {
return directChats;
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabasePruning.java b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabasePruning.java
index 978ba25e..e2bfc9ef 100644
--- a/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabasePruning.java
+++ b/src/main/java/org/qortal/repository/hsqldb/HSQLDBDatabasePruning.java
@@ -99,7 +99,7 @@ public class HSQLDBDatabasePruning {
// It's essential that we rebuild the latest AT states here, as we are using this data in the next query.
// Failing to do this will result in important AT states being deleted, rendering the database unusable.
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(endHeight);
// Loop through all the LatestATStates and copy them to the new table
diff --git a/src/main/java/org/qortal/settings/Settings.java b/src/main/java/org/qortal/settings/Settings.java
index c39cb9f5..b5571c4c 100644
--- a/src/main/java/org/qortal/settings/Settings.java
+++ b/src/main/java/org/qortal/settings/Settings.java
@@ -216,7 +216,7 @@ public class Settings {
public long recoveryModeTimeout = 10 * 60 * 1000L;
/** Minimum peer version number required in order to sync with them */
- private String minPeerVersion = "3.8.2";
+ private String minPeerVersion = "3.8.7";
/** Whether to allow connections with peers below minPeerVersion
* If true, we won't sync with them but they can still sync with us, and will show in the peers list
* If false, sync will be blocked both ways, and they will not appear in the peers list */
diff --git a/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java b/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java
index 788492a9..876f0aed 100644
--- a/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java
+++ b/src/main/java/org/qortal/transaction/CancelSellNameTransaction.java
@@ -5,6 +5,7 @@ import java.util.List;
import org.qortal.account.Account;
import org.qortal.asset.Asset;
+import org.qortal.controller.repository.NamesDatabaseIntegrityCheck;
import org.qortal.data.naming.NameData;
import org.qortal.data.transaction.CancelSellNameTransactionData;
import org.qortal.data.transaction.TransactionData;
@@ -81,7 +82,13 @@ public class CancelSellNameTransaction extends Transaction {
@Override
public void preProcess() throws DataException {
- // Nothing to do
+ CancelSellNameTransactionData cancelSellNameTransactionData = (CancelSellNameTransactionData) transactionData;
+
+ // Rebuild this name in the Names table from the transaction history
+ // This is necessary because in some rare cases names can be missing from the Names table after registration
+ // but we have been unable to reproduce the issue and track down the root cause
+ NamesDatabaseIntegrityCheck namesDatabaseIntegrityCheck = new NamesDatabaseIntegrityCheck();
+ namesDatabaseIntegrityCheck.rebuildName(cancelSellNameTransactionData.getName(), this.repository);
}
@Override
diff --git a/src/main/resources/i18n/ApiError_pl.properties b/src/main/resources/i18n/ApiError_pl.properties
new file mode 100644
index 00000000..fcb6191c
--- /dev/null
+++ b/src/main/resources/i18n/ApiError_pl.properties
@@ -0,0 +1,83 @@
+#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
+# Keys are from api.ApiError enum
+
+# "localeLang": "pl",
+
+### Common ###
+JSON = nie udało się przetworzyć wiadomości JSON
+
+INSUFFICIENT_BALANCE = niedostateczne środki
+
+UNAUTHORIZED = nieautoryzowane połączenie API
+
+REPOSITORY_ISSUE = błąd repozytorium
+
+NON_PRODUCTION = to wywołanie API nie jest dozwolone dla systemów produkcyjnych
+
+BLOCKCHAIN_NEEDS_SYNC = blockchain musi się najpierw zsynchronizować
+
+NO_TIME_SYNC = zegar się jeszcze nie zsynchronizował
+
+### Validation ###
+INVALID_SIGNATURE = nieprawidłowa sygnatura
+
+INVALID_ADDRESS = nieprawidłowy adres
+
+INVALID_PUBLIC_KEY = nieprawidłowy klucz publiczny
+
+INVALID_DATA = nieprawidłowe dane
+
+INVALID_NETWORK_ADDRESS = nieprawidłowy adres sieci
+
+ADDRESS_UNKNOWN = nieznany adres konta
+
+INVALID_CRITERIA = nieprawidłowe kryteria wyszukiwania
+
+INVALID_REFERENCE = nieprawidłowe skierowanie
+
+TRANSFORMATION_ERROR = nie udało się przekształcić JSON w transakcję
+
+INVALID_PRIVATE_KEY = klucz prywatny jest niepoprawny
+
+INVALID_HEIGHT = nieprawidłowa wysokość bloku
+
+CANNOT_MINT = konto nie możne bić monet
+
+### Blocks ###
+BLOCK_UNKNOWN = blok nieznany
+
+### Transactions ###
+TRANSACTION_UNKNOWN = nieznana transakcja
+
+PUBLIC_KEY_NOT_FOUND = nie znaleziono klucza publicznego
+
+# this one is special in that caller expected to pass two additional strings, hence the two %s
+TRANSACTION_INVALID = transakcja nieważna: %s (%s)
+
+### Naming ###
+NAME_UNKNOWN = nazwa nieznana
+
+### Asset ###
+INVALID_ASSET_ID = nieprawidłowy identyfikator aktywy
+
+INVALID_ORDER_ID = nieprawidłowy identyfikator zlecenia aktywy
+
+ORDER_UNKNOWN = nieznany identyfikator zlecenia aktywy
+
+### Groups ###
+GROUP_UNKNOWN = nieznana grupa
+
+### Foreign Blockchain ###
+FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = obcy blockchain lub problem z siecią ElectrumX
+
+FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = niewystarczające środki na obcym blockchainie
+
+FOREIGN_BLOCKCHAIN_TOO_SOON = zbyt wczesne nadawanie transakcji na obcym blockchainie (okres karencji/średni czas bloku)
+
+### Trade Portal ###
+ORDER_SIZE_TOO_SMALL = zbyt niska kwota zlecenia
+
+### Data ###
+FILE_NOT_FOUND = plik nie został znaleziony
+
+NO_REPLY = peer nie odpowiedział w wyznaczonym czasie
diff --git a/src/main/resources/i18n/ApiError_ru.properties b/src/main/resources/i18n/ApiError_ru.properties
index 52580ac8..1367f29b 100644
--- a/src/main/resources/i18n/ApiError_ru.properties
+++ b/src/main/resources/i18n/ApiError_ru.properties
@@ -16,7 +16,7 @@ NON_PRODUCTION = этот вызов API не разрешен для произ
BLOCKCHAIN_NEEDS_SYNC = блокчейн должен сначала синхронизироваться
-NO_TIME_SYNC = пока нет синхронизации часов
+NO_TIME_SYNC = время не синхронизировано
### Validation ###
INVALID_SIGNATURE = недействительная подпись
@@ -72,7 +72,7 @@ FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = проблема с внешним блокч
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = недостаточный баланс на внешнем блокчейне
-FOREIGN_BLOCKCHAIN_TOO_SOON = слишком рано для трансляции транзакции во внений блокчей (время блокировки/среднее время блока)
+FOREIGN_BLOCKCHAIN_TOO_SOON = слишком рано для трансляции транзакции во внешний блокчей (время блокировки/среднее время блока)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = слишком маленькая сумма ордера
@@ -80,4 +80,4 @@ ORDER_SIZE_TOO_SMALL = слишком маленькая сумма ордера
### Data ###
FILE_NOT_FOUND = файл не найден
-NO_REPLY = узел не ответил данными
+NO_REPLY = нет ответа
diff --git a/src/main/resources/i18n/SysTray_de.properties b/src/main/resources/i18n/SysTray_de.properties
index 4dc7edd2..7fa041b3 100644
--- a/src/main/resources/i18n/SysTray_de.properties
+++ b/src/main/resources/i18n/SysTray_de.properties
@@ -5,11 +5,11 @@ APPLYING_UPDATE_AND_RESTARTING = Automatisches Update anwenden und neu starten
AUTO_UPDATE = Automatisches Update
-BLOCK_HEIGHT = height
+BLOCK_HEIGHT = Blockhöhe
BLOCKS_REMAINING = blocks remaining
-BUILD_VERSION = Build-Version
+BUILD_VERSION = Entwicklungs-Version
CHECK_TIME_ACCURACY = Prüfe Zeitgenauigkeit
@@ -23,7 +23,7 @@ CREATING_BACKUP_OF_DB_FILES = Erstellen Backup von Datenbank Dateien …
DB_BACKUP = Datenbank Backup
-DB_CHECKPOINT = Datenbank Kontrollpunkt
+DB_CHECKPOINT = Datenbank Check
DB_MAINTENANCE = Datenbank Instandhaltung
@@ -31,18 +31,18 @@ EXIT = Verlassen
LITE_NODE = Lite node
-MINTING_DISABLED = NOT minting
+MINTING_DISABLED = Kein minting
-MINTING_ENABLED = \u2714 Minting
+MINTING_ENABLED = \u2714 Minting aktiviert
OPEN_UI = Öffne UI
-PERFORMING_DB_CHECKPOINT = Speichern nicht übergebener Datenbank Änderungen …
+PERFORMING_DB_CHECKPOINT = Speichern von unbestätigten Datenbankänderungen...
PERFORMING_DB_MAINTENANCE = Planmäßige Wartung durchführen...
SYNCHRONIZE_CLOCK = Synchronisiere Uhr
-SYNCHRONIZING_BLOCKCHAIN = Synchronisierung
+SYNCHRONIZING_BLOCKCHAIN = Synchronisierung der Blockchain
-SYNCHRONIZING_CLOCK = Synchronisierung Uhr
+SYNCHRONIZING_CLOCK = Synchronisierung der Uhr
diff --git a/src/main/resources/i18n/SysTray_pl.properties b/src/main/resources/i18n/SysTray_pl.properties
new file mode 100644
index 00000000..84740da0
--- /dev/null
+++ b/src/main/resources/i18n/SysTray_pl.properties
@@ -0,0 +1,46 @@
+#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
+# SysTray pop-up menu
+
+APPLYING_UPDATE_AND_RESTARTING = Zastosowanie automatycznej aktualizacji i ponowne uruchomienie...
+
+AUTO_UPDATE = Automatyczna aktualizacja
+
+BLOCK_HEIGHT = wysokość
+
+BUILD_VERSION = Wersja kompilacji
+
+CHECK_TIME_ACCURACY = Sprawdz dokładność czasu
+
+CONNECTING = Łączenie
+
+CONNECTION = połączenie
+
+CONNECTIONS = połączenia
+
+CREATING_BACKUP_OF_DB_FILES = Tworzenie kopii zapasowej plików bazy danych...
+
+DB_BACKUP = Kopia zapasowa bazy danych
+
+DB_CHECKPOINT = Punkt kontrolny bazy danych...
+
+DB_MAINTENANCE = Konserwacja bazy danych
+
+EXIT = Zakończ
+
+LITE_NODE = Lite node
+
+MINTING_DISABLED = Mennica zamknięta
+
+MINTING_ENABLED = \u2714 Mennica aktywna
+
+OPEN_UI = Otwórz interfejs użytkownika
+
+PERFORMING_DB_CHECKPOINT = Zapisywanie niezaksięgowanych zmian w bazie danych...
+
+PERFORMING_DB_MAINTENANCE = Performing scheduled maintenance...
+
+SYNCHRONIZE_CLOCK = Synchronizuj zegar
+
+SYNCHRONIZING_BLOCKCHAIN = Synchronizacja
+
+SYNCHRONIZING_CLOCK = Synchronizacja zegara
diff --git a/src/main/resources/i18n/SysTray_ru.properties b/src/main/resources/i18n/SysTray_ru.properties
index ff346304..c8615f73 100644
--- a/src/main/resources/i18n/SysTray_ru.properties
+++ b/src/main/resources/i18n/SysTray_ru.properties
@@ -1,7 +1,7 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
-APPLYING_UPDATE_AND_RESTARTING = Применение автоматического обновления и перезапуска...
+APPLYING_UPDATE_AND_RESTARTING = Применение автоматического обновления и перезапуск...
AUTO_UPDATE = Автоматическое обновление
diff --git a/src/main/resources/i18n/TransactionValidity_de.properties b/src/main/resources/i18n/TransactionValidity_de.properties
new file mode 100644
index 00000000..1827482b
--- /dev/null
+++ b/src/main/resources/i18n/TransactionValidity_de.properties
@@ -0,0 +1,195 @@
+#
+
+ACCOUNT_ALREADY_EXISTS = Account existiert bereits
+
+ACCOUNT_CANNOT_REWARD_SHARE = Account kann keine Belohnung teilen
+
+ADDRESS_ABOVE_RATE_LIMIT = address hat das angegebene Geschwindigkeitlimit erreicht
+
+ADDRESS_BLOCKED = Addresse ist geblockt
+
+ALREADY_GROUP_ADMIN = bereits Gruppen Admin
+
+ALREADY_GROUP_MEMBER = bereits Gruppen Mitglied
+
+ALREADY_VOTED_FOR_THAT_OPTION = bereits für diese Option gestimmt
+
+ASSET_ALREADY_EXISTS = asset existiert bereits
+
+ASSET_DOES_NOT_EXIST = asset nicht gefunden
+
+ASSET_DOES_NOT_MATCH_AT = asset passt nicht mit AT's asset
+
+ASSET_NOT_SPENDABLE = asset ist nicht ausgabefähig
+
+AT_ALREADY_EXISTS = AT existiert bereits
+
+AT_IS_FINISHED = AT ist fertig
+
+AT_UNKNOWN = AT unbekannt
+
+BAN_EXISTS = ban besteht bereits
+
+BAN_UNKNOWN = ban unbekannt
+
+BANNED_FROM_GROUP = von der gruppe gebannt
+
+BUYER_ALREADY_OWNER = Käufer ist bereits Besitzer
+
+CLOCK_NOT_SYNCED = Uhr nicht synchronisiert
+
+DUPLICATE_MESSAGE = Adresse sendete doppelte Nachricht
+
+DUPLICATE_OPTION = Duplizierungsmöglichkeit
+
+GROUP_ALREADY_EXISTS = Gruppe besteht bereits
+
+GROUP_APPROVAL_DECIDED = Gruppenfreigabe bereits beschlossen
+
+GROUP_APPROVAL_NOT_REQUIRED = Gruppenfreigabe nicht erforderlich
+
+GROUP_DOES_NOT_EXIST = Gruppe nicht vorhanden
+
+GROUP_ID_MISMATCH = Gruppen-ID stimmt nicht überein
+
+GROUP_OWNER_CANNOT_LEAVE = Gruppenbesitzer kann Gruppe nicht verlassen
+
+HAVE_EQUALS_WANT = das bessesene-asset ist das selbe wie das gesuchte-asset
+
+INCORRECT_NONCE = falsche PoW-Nonce
+
+INSUFFICIENT_FEE = unzureichende Gebühr
+
+INVALID_ADDRESS = ungültige Adresse
+
+INVALID_AMOUNT = ungültiger Betrag
+
+INVALID_ASSET_OWNER = Ungültiger Eigentümer
+
+INVALID_AT_TRANSACTION = ungültige AT-Transaktion
+
+INVALID_AT_TYPE_LENGTH = ungültige AT 'Typ' Länge
+
+INVALID_BUT_OK = ungültig aber OK
+
+INVALID_CREATION_BYTES = ungültige Erstellungs der bytes
+
+INVALID_DATA_LENGTH = ungültige Datenlänge
+
+INVALID_DESCRIPTION_LENGTH = ungültige Länge der Beschreibung
+
+INVALID_GROUP_APPROVAL_THRESHOLD = ungültiger Schwellenwert für die Gruppenzulassung
+
+INVALID_GROUP_BLOCK_DELAY = Ungültige Blockverzögerung der Gruppenfreigabe
+
+INVALID_GROUP_ID = ungültige Gruppen-ID
+
+INVALID_GROUP_OWNER = ungültiger Gruppenbesitzer
+
+INVALID_LIFETIME = unzulässige Lebensdauer
+
+INVALID_NAME_LENGTH = ungültige Namenslänge
+
+INVALID_NAME_OWNER = ungültiger Besitzername
+
+INVALID_OPTION_LENGTH = ungültige Länge der Optionen
+
+INVALID_OPTIONS_COUNT = Anzahl ungültiger Optionen
+
+INVALID_ORDER_CREATOR = ungültiger Auftragsersteller
+
+INVALID_PAYMENTS_COUNT = Anzahl ungültiger Zahlungen
+
+INVALID_PUBLIC_KEY = ungültiger öffentlicher Schlüssel
+
+INVALID_QUANTITY = unzulässige Menge
+
+INVALID_REFERENCE = ungültige Referenz
+
+INVALID_RETURN = ungültige Rückgabe
+
+INVALID_REWARD_SHARE_PERCENT = ungültig Prozent der Belohnunganteile
+
+INVALID_SELLER = unzulässiger Verkäufer
+
+INVALID_TAGS_LENGTH = ungültige 'tags'-Länge
+
+INVALID_TIMESTAMP_SIGNATURE = Ungültige Zeitstempel-Signatur
+
+INVALID_TX_GROUP_ID = Ungültige Transaktionsgruppen-ID
+
+INVALID_VALUE_LENGTH = ungültige 'Wert'-Länge
+
+INVITE_UNKNOWN = Gruppeneinladung unbekannt
+
+JOIN_REQUEST_EXISTS = Gruppeneinladung existiert bereits
+
+MAXIMUM_REWARD_SHARES = die maximale Anzahl von Reward-Shares für dieses Konto erreicht
+
+MISSING_CREATOR = fehlender Ersteller
+
+MULTIPLE_NAMES_FORBIDDEN = mehrere registrierte Namen pro Konto sind untersagt
+
+NAME_ALREADY_FOR_SALE = Name bereits zum Verkauf
+
+NAME_ALREADY_REGISTERED = Name bereits registriert
+
+NAME_BLOCKED = Name geblockt
+
+NAME_DOES_NOT_EXIST = Name nicht vorhanden
+
+NAME_NOT_FOR_SALE = Name ist unverkäuflich
+
+NAME_NOT_NORMALIZED = Name nicht in Unicode-'normalisierter' Form
+
+NEGATIVE_AMOUNT = ungültiger/negativer Betrag
+
+NEGATIVE_FEE = ungültige/negative Gebühr
+
+NEGATIVE_PRICE = ungültiger/negativer Preis
+
+NO_BALANCE = unzureichendes Guthaben
+
+NO_BLOCKCHAIN_LOCK = die Blockchain des Knotens ist beschäftigt
+
+NO_FLAG_PERMISSION = Konto hat diese Berechtigung nicht
+
+NOT_GROUP_ADMIN = Account ist kein Gruppenadmin
+
+NOT_GROUP_MEMBER = Account kein Gruppenmitglied
+
+NOT_MINTING_ACCOUNT = Account kann nicht minten
+
+NOT_YET_RELEASED = Funktion noch nicht freigegeben
+
+OK = OK
+
+ORDER_ALREADY_CLOSED = Asset Trade Order ist bereits geschlossen
+
+ORDER_DOES_NOT_EXIST = asset trade order existiert nicht
+
+POLL_ALREADY_EXISTS = Umfrage bereits vorhanden
+
+POLL_DOES_NOT_EXIST = Umfrage nicht vorhanden
+
+POLL_OPTION_DOES_NOT_EXIST = Umfrageoption existiert nicht
+
+PUBLIC_KEY_UNKNOWN = öffentlicher Schlüssel unbekannt
+
+REWARD_SHARE_UNKNOWN = Geteilte Belohnungen unbekant
+
+SELF_SHARE_EXISTS = Selbstbeteiligung (Geteilte Belohnungen) sind breits vorhanden
+
+TIMESTAMP_TOO_NEW = Zeitstempel zu neu
+
+TIMESTAMP_TOO_OLD = Zeitstempel zu alt
+
+TOO_MANY_UNCONFIRMED = Account hat zu viele unbestätigte Transaktionen am laufen
+
+TRANSACTION_ALREADY_CONFIRMED = Transaktionen sind bereits bestätigt
+
+TRANSACTION_ALREADY_EXISTS = Transaktionen existiert bereits
+
+TRANSACTION_UNKNOWN = Unbekante Transaktion
+
+TX_GROUP_ID_MISMATCH = Transaktion Gruppen ID stimmt nicht überein
diff --git a/src/main/resources/i18n/TransactionValidity_pl.properties b/src/main/resources/i18n/TransactionValidity_pl.properties
new file mode 100644
index 00000000..bcdceb6e
--- /dev/null
+++ b/src/main/resources/i18n/TransactionValidity_pl.properties
@@ -0,0 +1,196 @@
+#
+
+ACCOUNT_ALREADY_EXISTS = konto już istnieje
+
+ACCOUNT_CANNOT_REWARD_SHARE = konto nie może udostępniać nagród
+
+ADDRESS_ABOVE_RATE_LIMIT = adres osiągnął określony limit stawki
+
+ADDRESS_BLOCKED = ten adres jest zablokowany
+
+ALREADY_GROUP_ADMIN = już adminem grupy
+
+ALREADY_GROUP_MEMBER = już członkiem grupy
+
+ALREADY_VOTED_FOR_THAT_OPTION = już zagłosowano na ta opcje
+
+ASSET_ALREADY_EXISTS = aktywa już istnieje
+
+ASSET_DOES_NOT_EXIST = aktywa nie istnieje
+
+ASSET_DOES_NOT_MATCH_AT = aktywa nie pasuje do aktywy AT
+
+ASSET_NOT_SPENDABLE = aktywa nie jest rozporządzalna
+
+AT_ALREADY_EXISTS = AT już istnieje
+
+AT_IS_FINISHED = AT zakończył
+
+AT_UNKNOWN = AT nieznany
+
+BAN_EXISTS = ban już istnieje
+
+BAN_UNKNOWN = ban nieznany
+
+BANNED_FROM_GROUP = zbanowany z grupy
+
+BUYER_ALREADY_OWNER = kupca jest już właścicielem
+
+CLOCK_NOT_SYNCED = zegar nie zsynchronizowany
+
+DUPLICATE_MESSAGE = adres wysłał duplikat wiadomości
+
+DUPLICATE_OPTION = duplikat opcji
+
+GROUP_ALREADY_EXISTS = grupa już istnieje
+
+GROUP_APPROVAL_DECIDED = zatwierdzenie grupy już zdecydowano
+
+GROUP_APPROVAL_NOT_REQUIRED = zatwierdzenie grupy nie jest wymagane
+
+GROUP_DOES_NOT_EXIST = grupa nie istnieje
+
+GROUP_ID_MISMATCH = niedopasowanie identyfikatora grupy
+
+GROUP_OWNER_CANNOT_LEAVE = właściciel grupy nie może opuścić grupy
+
+HAVE_EQUALS_WANT = posiadana aktywa równa się chcianej aktywie
+
+INCORRECT_NONCE = nieprawidłowy nonce PoW
+
+INSUFFICIENT_FEE = niewystarczająca opłata
+
+INVALID_ADDRESS = nieprawidłowy adres
+
+INVALID_AMOUNT = nieprawidłowa kwota
+
+INVALID_ASSET_OWNER = nieprawidłowy właściciel aktywów
+
+INVALID_AT_TRANSACTION = nieważna transakcja AT
+
+INVALID_AT_TYPE_LENGTH = nieprawidłowa długość typu AT
+
+INVALID_BUT_OK = nieważne, ale OK
+
+INVALID_CREATION_BYTES = nieprawidłowe bajty tworzenia
+
+INVALID_DATA_LENGTH = nieprawidłowa długość danych
+
+INVALID_DESCRIPTION_LENGTH = nieprawidłowa długość opisu
+
+INVALID_GROUP_APPROVAL_THRESHOLD = nieprawidłowy próg zatwierdzenia grupy
+
+INVALID_GROUP_BLOCK_DELAY = nieprawidłowe opóźnienie bloku zatwierdzenia grupy
+
+INVALID_GROUP_ID = nieprawidłowy identyfikator grupy
+
+INVALID_GROUP_OWNER = nieprawidłowy właściciel grupy
+
+INVALID_LIFETIME = nieprawidłowy czas istnienia
+
+INVALID_NAME_LENGTH = nieprawidłowa długość nazwy
+
+INVALID_NAME_OWNER = nieprawidłowy właściciel nazwy
+
+INVALID_OPTION_LENGTH = nieprawidłowa długość opcji
+
+INVALID_OPTIONS_COUNT = nieprawidłowa liczba opcji
+
+INVALID_ORDER_CREATOR = nieprawidłowy twórca zlecenia
+
+INVALID_PAYMENTS_COUNT = nieprawidłowa liczba płatności
+
+INVALID_PUBLIC_KEY = nieprawidłowy klucz publiczny
+
+INVALID_QUANTITY = nieprawidłowa ilość
+
+INVALID_REFERENCE = nieprawidłowe skierowanie
+
+INVALID_RETURN = nieprawidłowy zwrot
+
+INVALID_REWARD_SHARE_PERCENT = nieprawidłowy procent udziału w nagrodzie
+
+INVALID_SELLER = nieprawidłowy sprzedawca
+
+INVALID_TAGS_LENGTH = nieprawidłowa długość tagów
+
+INVALID_TIMESTAMP_SIGNATURE = nieprawidłowa sygnatura znacznika czasu
+
+INVALID_TX_GROUP_ID = nieprawidłowy identyfikator grupy transakcji
+
+INVALID_VALUE_LENGTH = nieprawidłowa długość wartości
+
+INVITE_UNKNOWN = zaproszenie do grupy nieznane
+
+JOIN_REQUEST_EXISTS = wniosek o dołączenie do grupy już istnieje
+
+MAXIMUM_REWARD_SHARES = osiągnięto już maksymalną liczbę udziałów w nagrodzie dla tego konta
+
+MISSING_CREATOR = brak twórcy
+
+MULTIPLE_NAMES_FORBIDDEN = zabronione jest używanie wielu nazw na jednym koncie
+
+NAME_ALREADY_FOR_SALE = nazwa już wystawiona na sprzedaż
+
+NAME_ALREADY_REGISTERED = nazwa już zarejestrowana
+
+NAME_BLOCKED = ta nazwa jest zablokowana
+
+NAME_DOES_NOT_EXIST = nazwa nie istnieje
+
+NAME_NOT_FOR_SALE = nazwa nie jest przeznaczona do sprzedaży
+
+NAME_NOT_NORMALIZED = nazwa nie jest w formie 'znormalizowanej' Unicode
+
+NEGATIVE_AMOUNT = nieprawidłowa/ujemna kwota
+
+NEGATIVE_FEE = nieprawidłowa/ujemna opłata
+
+NEGATIVE_PRICE = nieprawidłowa/ujemna cena
+
+NO_BALANCE = niewystarczające środki
+
+NO_BLOCKCHAIN_LOCK = węzeł blockchain jest obecnie zajęty
+
+NO_FLAG_PERMISSION = konto nie ma tego uprawnienia
+
+NOT_GROUP_ADMIN = konto nie jest adminem grupy
+
+NOT_GROUP_MEMBER = konto nie jest członkiem grupy
+
+NOT_MINTING_ACCOUNT = konto nie może bić monet
+
+NOT_YET_RELEASED = funkcja nie została jeszcze udostępniona
+
+OK = OK
+
+ORDER_ALREADY_CLOSED = zlecenie handlu aktywami jest już zakończone
+
+ORDER_DOES_NOT_EXIST = zlecenie sprzedaży aktywów nie istnieje
+
+POLL_ALREADY_EXISTS = ankieta już istnieje
+
+POLL_DOES_NOT_EXIST = ankieta nie istnieje
+
+POLL_OPTION_DOES_NOT_EXIST = opcja ankiety nie istnieje
+
+PUBLIC_KEY_UNKNOWN = klucz publiczny nieznany
+
+REWARD_SHARE_UNKNOWN = nieznany udział w nagrodzie
+
+SELF_SHARE_EXISTS = samoudział (udział w nagrodzie) już istnieje
+
+TIMESTAMP_TOO_NEW = zbyt nowy znacznik czasu
+
+TIMESTAMP_TOO_OLD = zbyt stary znacznik czasu
+
+TOO_MANY_UNCONFIRMED = rachunek ma zbyt wiele niepotwierdzonych transakcji w toku
+
+TRANSACTION_ALREADY_CONFIRMED = transakcja została już potwierdzona
+
+TRANSACTION_ALREADY_EXISTS = transakcja już istnieje
+
+TRANSACTION_UNKNOWN = transakcja nieznana
+
+TX_GROUP_ID_MISMATCH = niezgodność ID grupy transakcji
+
diff --git a/src/test/java/org/qortal/test/BlockArchiveTests.java b/src/test/java/org/qortal/test/BlockArchiveTests.java
index 3bfa4e84..8b3de67b 100644
--- a/src/test/java/org/qortal/test/BlockArchiveTests.java
+++ b/src/test/java/org/qortal/test/BlockArchiveTests.java
@@ -23,7 +23,6 @@ import org.qortal.transform.TransformationException;
import org.qortal.transform.block.BlockTransformation;
import org.qortal.utils.BlockArchiveUtils;
import org.qortal.utils.NTP;
-import org.qortal.utils.Triple;
import java.io.File;
import java.io.IOException;
@@ -314,9 +313,10 @@ public class BlockArchiveTests extends Common {
repository.getBlockRepository().setBlockPruneHeight(901);
// Prune the AT states for the archived blocks
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(900);
+ repository.saveChanges();
int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 900);
- assertEquals(900-1, numATStatesPruned);
+ assertEquals(900-2, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state
repository.getATRepository().setAtPruneHeight(901);
// Now ensure the SQL repository is missing blocks 2 and 900...
@@ -563,16 +563,23 @@ public class BlockArchiveTests extends Common {
// Trim the first 500 blocks
repository.getBlockRepository().trimOldOnlineAccountsSignatures(0, 500);
repository.getBlockRepository().setOnlineAccountsSignaturesTrimHeight(501);
+ repository.getATRepository().rebuildLatestAtStates(500);
repository.getATRepository().trimAtStates(0, 500, 1000);
repository.getATRepository().setAtTrimHeight(501);
- // Now block 500 should only have the AT state data hash
- block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500);
- atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500);
+ // Now block 499 should only have the AT state data hash
+ List block499AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(499);
+ atStatesData = repository.getATRepository().getATStateAtHeight(block499AtStatesData.get(0).getATAddress(), 499);
assertNotNull(atStatesData.getStateHash());
assertNull(atStatesData.getStateData());
- // ... but block 501 should have the full data
+ // ... but block 500 should have the full data (due to being retained as the "latest" AT state in the trimmed range
+ block500AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(500);
+ atStatesData = repository.getATRepository().getATStateAtHeight(block500AtStatesData.get(0).getATAddress(), 500);
+ assertNotNull(atStatesData.getStateHash());
+ assertNotNull(atStatesData.getStateData());
+
+ // ... and block 501 should also have the full data
List block501AtStatesData = repository.getATRepository().getBlockATStatesAtHeight(501);
atStatesData = repository.getATRepository().getATStateAtHeight(block501AtStatesData.get(0).getATAddress(), 501);
assertNotNull(atStatesData.getStateHash());
@@ -612,9 +619,10 @@ public class BlockArchiveTests extends Common {
repository.getBlockRepository().setBlockPruneHeight(501);
// Prune the AT states for the archived blocks
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(500);
+ repository.saveChanges();
int numATStatesPruned = repository.getATRepository().pruneAtStates(2, 500);
- assertEquals(499, numATStatesPruned);
+ assertEquals(498, numATStatesPruned); // Minus 1 for genesis block, and another for the latest AT state
repository.getATRepository().setAtPruneHeight(501);
// Now ensure the SQL repository is missing blocks 2 and 500...
diff --git a/src/test/java/org/qortal/test/BootstrapTests.java b/src/test/java/org/qortal/test/BootstrapTests.java
index aa641e71..b60b412c 100644
--- a/src/test/java/org/qortal/test/BootstrapTests.java
+++ b/src/test/java/org/qortal/test/BootstrapTests.java
@@ -176,7 +176,8 @@ public class BootstrapTests extends Common {
repository.getBlockRepository().setBlockPruneHeight(901);
// Prune the AT states for the archived blocks
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(900);
+ repository.saveChanges();
repository.getATRepository().pruneAtStates(0, 900);
repository.getATRepository().setAtPruneHeight(901);
diff --git a/src/test/java/org/qortal/test/PruneTests.java b/src/test/java/org/qortal/test/PruneTests.java
index 0914d794..5a31146e 100644
--- a/src/test/java/org/qortal/test/PruneTests.java
+++ b/src/test/java/org/qortal/test/PruneTests.java
@@ -1,16 +1,33 @@
package org.qortal.test;
+import com.google.common.hash.HashCode;
import org.junit.Before;
import org.junit.Test;
+import org.qortal.account.Account;
import org.qortal.account.PrivateKeyAccount;
+import org.qortal.asset.Asset;
+import org.qortal.block.Block;
import org.qortal.controller.BlockMinter;
+import org.qortal.crosschain.AcctMode;
+import org.qortal.crosschain.LitecoinACCTv3;
+import org.qortal.data.at.ATData;
import org.qortal.data.at.ATStateData;
import org.qortal.data.block.BlockData;
+import org.qortal.data.crosschain.CrossChainTradeData;
+import org.qortal.data.transaction.BaseTransactionData;
+import org.qortal.data.transaction.DeployAtTransactionData;
+import org.qortal.data.transaction.MessageTransactionData;
+import org.qortal.data.transaction.TransactionData;
+import org.qortal.group.Group;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.test.common.AtUtils;
+import org.qortal.test.common.BlockUtils;
import org.qortal.test.common.Common;
+import org.qortal.test.common.TransactionUtils;
+import org.qortal.transaction.DeployAtTransaction;
+import org.qortal.transaction.MessageTransaction;
import java.util.ArrayList;
import java.util.List;
@@ -19,6 +36,13 @@ import static org.junit.Assert.*;
public class PruneTests extends Common {
+ // Constants for test AT (an LTC ACCT)
+ public static final byte[] litecoinPublicKeyHash = HashCode.fromString("bb00bb11bb22bb33bb44bb55bb66bb77bb88bb99").asBytes();
+ public static final int tradeTimeout = 20; // blocks
+ public static final long redeemAmount = 80_40200000L;
+ public static final long fundingAmount = 123_45600000L;
+ public static final long litecoinAmount = 864200L; // 0.00864200 LTC
+
@Before
public void beforeTest() throws DataException {
Common.useDefaultSettings();
@@ -62,23 +86,32 @@ public class PruneTests extends Common {
repository.getBlockRepository().setBlockPruneHeight(6);
// Prune AT states for blocks 2-5
+ repository.getATRepository().rebuildLatestAtStates(5);
+ repository.saveChanges();
int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 5);
- assertEquals(4, numATStatesPruned);
+ assertEquals(3, numATStatesPruned);
repository.getATRepository().setAtPruneHeight(6);
- // Make sure that blocks 2-5 are now missing block data and AT states data
- for (Integer i=2; i <= 5; i++) {
+ // Make sure that blocks 2-4 are now missing block data and AT states data
+ for (Integer i=2; i <= 4; i++) {
BlockData blockData = repository.getBlockRepository().fromHeight(i);
assertNull(blockData);
List atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(i);
assertTrue(atStatesDataList.isEmpty());
}
- // ... but blocks 6-10 have block data and full AT states data
+ // Block 5 should have full AT states data even though it was pruned.
+ // This is because we identified that as the "latest" AT state in that block range
+ BlockData blockData = repository.getBlockRepository().fromHeight(5);
+ assertNull(blockData);
+ List atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(5);
+ assertEquals(1, atStatesDataList.size());
+
+ // Blocks 6-10 have block data and full AT states data
for (Integer i=6; i <= 10; i++) {
- BlockData blockData = repository.getBlockRepository().fromHeight(i);
+ blockData = repository.getBlockRepository().fromHeight(i);
assertNotNull(blockData.getSignature());
- List atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(i);
+ atStatesDataList = repository.getATRepository().getBlockATStatesAtHeight(i);
assertNotNull(atStatesDataList);
assertFalse(atStatesDataList.isEmpty());
ATStateData atStatesData = repository.getATRepository().getATStateAtHeight(atStatesDataList.get(0).getATAddress(), i);
@@ -88,4 +121,102 @@ public class PruneTests extends Common {
}
}
+ @Test
+ public void testPruneSleepingAt() throws DataException {
+ try (final Repository repository = RepositoryManager.getRepository()) {
+ PrivateKeyAccount deployer = Common.getTestAccount(repository, "chloe");
+ PrivateKeyAccount tradeAccount = Common.getTestAccount(repository, "alice");
+
+ DeployAtTransaction deployAtTransaction = doDeploy(repository, deployer, tradeAccount.getAddress());
+ Account at = deployAtTransaction.getATAccount();
+ String atAddress = at.getAddress();
+
+ // Mint enough blocks to take the original DEPLOY_AT past the prune threshold (in this case 20)
+ Block block = BlockUtils.mintBlocks(repository, 25);
+
+ // Send creator's address to AT, instead of typical partner's address
+ byte[] messageData = LitecoinACCTv3.getInstance().buildCancelMessage(deployer.getAddress());
+ long txTimestamp = block.getBlockData().getTimestamp();
+ MessageTransaction messageTransaction = sendMessage(repository, deployer, messageData, atAddress, txTimestamp);
+
+ // AT should process 'cancel' message in next block
+ BlockUtils.mintBlock(repository);
+
+ // Prune AT states up to block 20
+ repository.getATRepository().rebuildLatestAtStates(20);
+ repository.saveChanges();
+ int numATStatesPruned = repository.getATRepository().pruneAtStates(0, 20);
+ assertEquals(1, numATStatesPruned); // deleted state at heights 2, but state at height 3 remains
+
+ // Check AT is finished
+ ATData atData = repository.getATRepository().fromATAddress(atAddress);
+ assertTrue(atData.getIsFinished());
+
+ // AT should be in CANCELLED mode
+ CrossChainTradeData tradeData = LitecoinACCTv3.getInstance().populateTradeData(repository, atData);
+ assertEquals(AcctMode.CANCELLED, tradeData.mode);
+
+ // Test orphaning - should be possible because the previous AT state at height 3 is still available
+ BlockUtils.orphanLastBlock(repository);
+ }
+ }
+
+
+ // Helper methods for AT testing
+ private DeployAtTransaction doDeploy(Repository repository, PrivateKeyAccount deployer, String tradeAddress) throws DataException {
+ byte[] creationBytes = LitecoinACCTv3.buildQortalAT(tradeAddress, litecoinPublicKeyHash, redeemAmount, litecoinAmount, tradeTimeout);
+
+ long txTimestamp = System.currentTimeMillis();
+ byte[] lastReference = deployer.getLastReference();
+
+ if (lastReference == null) {
+ System.err.println(String.format("Qortal account %s has no last reference", deployer.getAddress()));
+ System.exit(2);
+ }
+
+ Long fee = null;
+ String name = "QORT-LTC cross-chain trade";
+ String description = String.format("Qortal-Litecoin cross-chain trade");
+ String atType = "ACCT";
+ String tags = "QORT-LTC ACCT";
+
+ BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, deployer.getPublicKey(), fee, null);
+ TransactionData deployAtTransactionData = new DeployAtTransactionData(baseTransactionData, name, description, atType, tags, creationBytes, fundingAmount, Asset.QORT);
+
+ DeployAtTransaction deployAtTransaction = new DeployAtTransaction(repository, deployAtTransactionData);
+
+ fee = deployAtTransaction.calcRecommendedFee();
+ deployAtTransactionData.setFee(fee);
+
+ TransactionUtils.signAndMint(repository, deployAtTransactionData, deployer);
+
+ return deployAtTransaction;
+ }
+
+ private MessageTransaction sendMessage(Repository repository, PrivateKeyAccount sender, byte[] data, String recipient, long txTimestamp) throws DataException {
+ byte[] lastReference = sender.getLastReference();
+
+ if (lastReference == null) {
+ System.err.println(String.format("Qortal account %s has no last reference", sender.getAddress()));
+ System.exit(2);
+ }
+
+ Long fee = null;
+ int version = 4;
+ int nonce = 0;
+ long amount = 0;
+ Long assetId = null; // because amount is zero
+
+ BaseTransactionData baseTransactionData = new BaseTransactionData(txTimestamp, Group.NO_GROUP, lastReference, sender.getPublicKey(), fee, null);
+ TransactionData messageTransactionData = new MessageTransactionData(baseTransactionData, version, nonce, recipient, amount, assetId, data, false, false);
+
+ MessageTransaction messageTransaction = new MessageTransaction(repository, messageTransactionData);
+
+ fee = messageTransaction.calcRecommendedFee();
+ messageTransactionData.setFee(fee);
+
+ TransactionUtils.signAndMint(repository, messageTransactionData, sender);
+
+ return messageTransaction;
+ }
}
diff --git a/src/test/java/org/qortal/test/at/AtRepositoryTests.java b/src/test/java/org/qortal/test/at/AtRepositoryTests.java
index 8ef4c774..8441731f 100644
--- a/src/test/java/org/qortal/test/at/AtRepositoryTests.java
+++ b/src/test/java/org/qortal/test/at/AtRepositoryTests.java
@@ -2,29 +2,20 @@ package org.qortal.test.at;
import static org.junit.Assert.*;
-import java.nio.ByteBuffer;
import java.util.List;
-import org.ciyam.at.CompilationException;
import org.ciyam.at.MachineState;
-import org.ciyam.at.OpCode;
import org.junit.Before;
import org.junit.Test;
import org.qortal.account.PrivateKeyAccount;
-import org.qortal.asset.Asset;
import org.qortal.data.at.ATData;
import org.qortal.data.at.ATStateData;
-import org.qortal.data.transaction.BaseTransactionData;
-import org.qortal.data.transaction.DeployAtTransactionData;
-import org.qortal.data.transaction.TransactionData;
-import org.qortal.group.Group;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.test.common.AtUtils;
import org.qortal.test.common.BlockUtils;
import org.qortal.test.common.Common;
-import org.qortal.test.common.TransactionUtils;
import org.qortal.transaction.DeployAtTransaction;
public class AtRepositoryTests extends Common {
@@ -76,7 +67,7 @@ public class AtRepositoryTests extends Common {
Integer testHeight = maxHeight - 2;
// Trim AT state data
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(maxHeight);
repository.getATRepository().trimAtStates(2, maxHeight, 1000);
ATStateData atStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight);
@@ -130,7 +121,7 @@ public class AtRepositoryTests extends Common {
Integer testHeight = blockchainHeight;
// Trim AT state data
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(maxHeight);
// COMMIT to check latest AT states persist / TEMPORARY table interaction
repository.saveChanges();
@@ -163,8 +154,8 @@ public class AtRepositoryTests extends Common {
int maxTrimHeight = blockchainHeight - 4;
Integer testHeight = maxTrimHeight + 1;
- // Trim AT state data
- repository.getATRepository().rebuildLatestAtStates();
+ // Trim AT state data (using a max height of maxTrimHeight + 1, so it is beyond the trimmed range)
+ repository.getATRepository().rebuildLatestAtStates(maxTrimHeight + 1);
repository.saveChanges();
repository.getATRepository().trimAtStates(2, maxTrimHeight, 1000);
@@ -333,7 +324,7 @@ public class AtRepositoryTests extends Common {
Integer testHeight = maxHeight - 2;
// Trim AT state data
- repository.getATRepository().rebuildLatestAtStates();
+ repository.getATRepository().rebuildLatestAtStates(maxHeight);
repository.getATRepository().trimAtStates(2, maxHeight, 1000);
List atStates = repository.getATRepository().getBlockATStatesAtHeight(testHeight);
diff --git a/src/test/java/org/qortal/test/common/BlockUtils.java b/src/test/java/org/qortal/test/common/BlockUtils.java
index 3077b65b..ab57dadf 100644
--- a/src/test/java/org/qortal/test/common/BlockUtils.java
+++ b/src/test/java/org/qortal/test/common/BlockUtils.java
@@ -20,6 +20,15 @@ public class BlockUtils {
return BlockMinter.mintTestingBlock(repository, mintingAccount);
}
+ /** Mints multiple blocks using "alice-reward-share" test account, and returns the final block. */
+ public static Block mintBlocks(Repository repository, int count) throws DataException {
+ Block block = null;
+ for (int i=0; i