Compare commits

...

36 Commits

Author SHA1 Message Date
CalDescent
2479f2d65d Moved trimming and pruning classes into a single package (org.qortal.controller.repository) 2021-08-27 09:45:56 +01:00
CalDescent
9056cb7026 Increased atStatesPruneBatchSize from 10 to 25. 2021-08-27 09:45:56 +01:00
CalDescent
cd9d9b31ef Prune ATStatesData as well as the ATStates when switching to pruning mode. 2021-08-27 09:45:56 +01:00
CalDescent
ff841c28e3 Updated tests to use the renamed method. 2021-08-27 09:45:56 +01:00
CalDescent
ca1379d9f8 Unified the code to build the LatestATStates table, as it's now used by more than one class.
Note - the rebuildLatestAtStates() must never be used by two different classes at the same time, or AT states could be incorrectly deleted. It is okay at the moment as we don't run the AT states trimmer and pruner in the same app session. However we should probably synchronize this method so that we don't accidentally call it from two places in the future.
2021-08-27 09:45:56 +01:00
CalDescent
5127f94423 Added bulk pruning phase on node startup the first time that pruning mode is enabled.
When switching from a full node to a pruning node, we need to delete most of the database contents. If we do this entirely as a background process, it is very slow and can interfere with syncing. However, if we take the approach of transferring only the necessary rows to a new table and then deleting the original table, this makes the process much faster. It was taking several days to delete the AT states in the background, but only a couple of minutes to copy them to a new table.

The trade off is that we have to go through a form of "reshape" when starting the app for the first time after enabling pruning mode. But given that this is an opt-in mode, I don't think it will be a problem.

Once the pruning is complete, it automatically performs a CHECKPOINT DEFRAG in order to shrink the database file size down to a fraction of what it was before.

From this point, the original background process will run, but can be dialled right down so not to interfere with syncing.
2021-08-27 09:45:56 +01:00
CalDescent
f5910ab950 Break out of the AT pruning inner loops if we're stopping the app. 2021-08-27 09:45:56 +01:00
CalDescent
22efaccd4a Fixed NPE introduced in earlier commit. 2021-08-27 09:45:56 +01:00
CalDescent
c8466a2e7a Updated AT states pruner as it previously relied on blocks being present in the db to make decisions. As a side effect, this now prunes ATs up the the pruneBlockLimit too, rather than keeping the last 35 days or so. Will review this later but I don't think we will need the missing ones. 2021-08-27 09:45:56 +01:00
CalDescent
209a9fa8c3 Rework of Blockchain.validate() to account for pruning mode. 2021-08-27 09:45:56 +01:00
CalDescent
bc1af12655 Prune all blocks up until the blockPruneLimit
By default, this leaves only the last 1450 blocks in the database. Only applies when pruning mode is enabled.
2021-08-27 09:45:55 +01:00
CalDescent
e7e4cb7579 Started work on pruning mode (top-only-sync)
Initially just deleting old and unused AT states, to get this table under control. I have had to delete them individually as the table can't handle complex queries due to its size.

Nodes in pruning mode will be unable to serve older blocks to peers.
2021-08-27 09:45:55 +01:00
CalDescent
1b39db664c Added missing ATStatesHeightIndex to the reshape code.
This was accidentally missed out of the original code. Some pre-updated nodes on the network will be missing this index, but we can use the upcoming "auto-bootstrap" feature to get those back.
2021-08-27 08:54:46 +01:00
CalDescent
b4f980b349 Restrict lists API endpoints to local/apiKey requests only. 2021-08-12 19:52:49 +01:00
CalDescent
673f23b6a0 Improvement to commit f71516f
Now only skipping the HTLC redemption if the AT is finished and the balance has been redeemed by the buyer. This allows HTLCs to be refunded for ATs that have been refunded or cancelled.
2021-08-12 08:29:52 +01:00
QuickMythril
8c325f3a8a original design 2021-08-11 18:18:10 -04:00
CalDescent
f71516f36f Skip finished ATs in the refund API endpoints. 2021-08-11 21:26:29 +01:00
CalDescent
1752386a6c Fixed logging errors in previous commit. 2021-08-11 20:33:54 +01:00
CalDescent
112675c782 Better handling of RPC errors.
Previously, if an error was returned from an Electrum server (such as "server busy") it would throw a NetworkException that would be caught outside of the server loop and cause the entire request to fail.

Instead of throwing an exception, I am now logging the error and returning null, in the same way we do for IOException and NoSuchElementException further up in the same method.

This allows the caller - most likely connectedRpc() - to move on to the next server in the list and try again.

This should fix an issue seen where a "server busy" response from a single server was essentially breaking our implementation, as we would give up altogether instead of trying another server.
2021-08-11 19:22:53 +01:00
CalDescent
3b6ba7641d Updated icon for Qortal.exe 2021-08-10 19:35:03 +01:00
CalDescent
477a35a685 Fixed response schema for GET /lists/blacklist/addresses endpoint 2021-08-10 08:43:47 +01:00
CalDescent
2a0a39a95a Avoid creation of lists directory until the first item is added to a list. 2021-08-09 23:35:32 +01:00
CalDescent
dfc77db51d Merge pull request #57 from ScythianQortal/translations
Added Hungarian translations and reorganised existing translations
2021-08-09 10:05:13 +01:00
CalDescent
c9596fd8c4 Catch exceptions thrown during GUI initialization.
This is a workaround for an UnsupportedOperationException thrown when using X2Go, due to PERPIXEL_TRANSLUCENT translucency being unsupported in splashDialog.setBackground(). We could choose to use a different version of the splash screen with an opaque background in these cases, but it is low priority.
2021-08-09 10:02:24 +01:00
CalDescent
78373f3746 HTLC redeem/refund APIs switched from GET to POST. 2021-08-08 10:29:15 +01:00
CalDescent
ebc3db8aed Default file path for repository data imports set to "qortal-backup/TradeBotStates.json". This allows the trade bot backup to be imported in a single click, and can now be potentially added as a button in the UI. 2021-08-08 10:20:44 +01:00
CalDescent
756601c1ce Initialize to an empty list.
This fixes various bugs caused by the list being null when no blacklist JSON file was available.
2021-08-08 08:41:13 +01:00
CalDescent
8bb5077e76 Catch occasional NPE when setting tray icon. 2021-08-08 08:29:29 +01:00
CalDescent
5b85f01427 Added defensiveness to list management methods in ResourceList.java 2021-08-08 08:28:34 +01:00
CalDescent
a7d594e566 Log the AT states reshape progress, as it seems to be taking a very long time. 2021-08-07 19:18:20 +01:00
CalDescent
481e6671c2 Added GET /lists/blacklist/addresses API endpoint
This returns a JSON array containing the blacklisted addresses.
2021-08-07 16:27:16 +01:00
Scythian
b890e02a6a Added new TransactionValidity keys
Added ADDRESS_ABOVE_RATE_LIMIT and DUPLICATE_MESSAGE ValidationResults to localeLang translation keys
2021-08-07 15:09:48 +01:00
Scythian
4772840b4c Reorganised translations
Updated the "localeLang" files with new keys and removed old unused keys for English, German, Dutch, Italian, Finnish, Hungarian, Russian and Chinese translations
2021-08-07 14:05:10 +01:00
Scythian
76ec3473d6 Updated TransactionValidity keys
Added ADDRESS_IN_BLACKLIST ValidationResult to TransactionValidity translation keys
2021-08-07 10:47:48 +01:00
Scythian
0b53de1bb6 Added Hungarian translations 2021-08-06 19:28:56 +01:00
Scythian
746c68c9f6 Reorganised translations
Added new keys and removed old unused keys
Localised the "Build version" string in the SysTray
2021-08-06 19:27:28 +01:00
48 changed files with 2019 additions and 598 deletions

BIN
WindowsInstaller/qortal.ico Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -553,13 +553,13 @@ public class AdminResource {
@Path("/repository/data")
@Operation(
summary = "Import data into repository.",
description = "Imports data from file on local machine. Filename is forced to 'import.json' if apiKey is not set.",
description = "Imports data from file on local machine. Filename is forced to 'qortal-backup/TradeBotStates.json' if apiKey is not set.",
requestBody = @RequestBody(
required = true,
content = @Content(
mediaType = MediaType.TEXT_PLAIN,
schema = @Schema(
type = "string", example = "MintingAccounts.script"
type = "string", example = "qortal-backup/TradeBotStates.json"
)
)
),
@@ -577,7 +577,7 @@ public class AdminResource {
// Hard-coded because it's too dangerous to allow user-supplied filenames in weaker security contexts
if (Settings.getInstance().getApiKey() == null)
filename = "import.json";
filename = "qortal-backup/TradeBotStates.json";
try (final Repository repository = RepositoryManager.getRepository()) {
ReentrantLock blockchainLock = Controller.getInstance().getBlockchainLock();

View File

@@ -11,6 +11,7 @@ import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Context;
@@ -173,7 +174,7 @@ public class CrossChainHtlcResource {
}
}
@GET
@POST
@Path("/redeem/{ataddress}")
@Operation(
summary = "Redeems HTLC associated with supplied AT",
@@ -231,7 +232,7 @@ public class CrossChainHtlcResource {
}
}
@GET
@POST
@Path("/redeemAll")
@Operation(
summary = "Redeems HTLC for all applicable ATs in tradebot data",
@@ -415,7 +416,7 @@ public class CrossChainHtlcResource {
return false;
}
@GET
@POST
@Path("/refund/{ataddress}")
@Operation(
summary = "Refunds HTLC associated with supplied AT",
@@ -463,7 +464,7 @@ public class CrossChainHtlcResource {
}
@GET
@POST
@Path("/refundAll")
@Operation(
summary = "Refunds HTLC for all applicable ATs in tradebot data",
@@ -478,8 +479,6 @@ public class CrossChainHtlcResource {
)
@ApiErrors({ApiError.INVALID_CRITERIA, ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN})
public boolean refundAllHtlc() {
Security.checkApiCallAllowed(request);
Security.checkApiCallAllowed(request);
boolean success = false;
@@ -568,6 +567,13 @@ public class CrossChainHtlcResource {
if (crossChainTradeData == null)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
// If the AT is "finished" then it will have a zero balance
// In these cases we should avoid HTLC refunds if tbe QORT haven't been returned to the seller
if (atData.getIsFinished() && crossChainTradeData.mode != AcctMode.REFUNDED && crossChainTradeData.mode != AcctMode.CANCELLED) {
LOGGER.info(String.format("Skipping AT %s because the QORT has already been redemed", atAddress));
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
}
List<TradeBotData> allTradeBotData = repository.getCrossChainRepository().getAllTradeBotData();
TradeBotData tradeBotData = allTradeBotData.stream().filter(tradeBotDataItem -> tradeBotDataItem.getAtAddress().equals(atAddress)).findFirst().orElse(null);
if (tradeBotData == null)

View File

@@ -1,6 +1,7 @@
package org.qortal.api.resource;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
@@ -42,6 +43,8 @@ public class ListsResource {
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE})
public String addAddressToBlacklist(@PathParam("address") String address) {
Security.checkApiCallAllowed(request);
if (!Crypto.isValidAddress(address))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
@@ -84,6 +87,8 @@ public class ListsResource {
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE})
public String addAddressesToBlacklist(AddressListRequest addressListRequest) {
Security.checkApiCallAllowed(request);
if (addressListRequest == null || addressListRequest.addresses == null) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
}
@@ -146,6 +151,8 @@ public class ListsResource {
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE})
public String removeAddressFromBlacklist(@PathParam("address") String address) {
Security.checkApiCallAllowed(request);
if (!Crypto.isValidAddress(address))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
@@ -188,6 +195,8 @@ public class ListsResource {
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE})
public String removeAddressesFromBlacklist(AddressListRequest addressListRequest) {
Security.checkApiCallAllowed(request);
if (addressListRequest == null || addressListRequest.addresses == null) {
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_CRITERIA);
}
@@ -237,6 +246,22 @@ public class ListsResource {
}
}
@GET
@Path("/blacklist/addresses")
@Operation(
summary = "Fetch the list of blacklisted addresses",
responses = {
@ApiResponse(
description = "A JSON array of addresses",
content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = String.class)))
)
}
)
public String getAddressBlacklist() {
Security.checkApiCallAllowed(request);
return ResourceListManager.getInstance().getBlacklistJSONString();
}
@GET
@Path("/blacklist/address/{address}")
@Operation(
@@ -250,6 +275,8 @@ public class ListsResource {
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.ADDRESS_UNKNOWN, ApiError.REPOSITORY_ISSUE})
public String checkAddressInBlacklist(@PathParam("address") String address) {
Security.checkApiCallAllowed(request);
if (!Crypto.isValidAddress(address))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);

View File

@@ -506,28 +506,51 @@ public class BlockChain {
* @throws SQLException
*/
public static void validate() throws DataException {
// Check first block is Genesis Block
if (!isGenesisBlockValid())
rebuildBlockchain();
try (final Repository repository = RepositoryManager.getRepository()) {
boolean pruningEnabled = Settings.getInstance().isPruningEnabled();
BlockData chainTip = repository.getBlockRepository().getLastBlock();
boolean hasBlocks = (chainTip != null && chainTip.getHeight() > 1);
if (pruningEnabled && hasBlocks) {
// Pruning is enabled and we have blocks, so it's possible that the genesis block has been pruned
// It's best not to validate it, and there's no real need to
}
else {
// Check first block is Genesis Block
if (!isGenesisBlockValid()) {
rebuildBlockchain();
}
}
repository.checkConsistency();
int startHeight = Math.max(repository.getBlockRepository().getBlockchainHeight() - 1440, 1);
// Set the number of blocks to validate based on the pruned state of the chain
// If pruned, subtract an extra 10 to allow room for error
int blocksToValidate = pruningEnabled ? Settings.getInstance().getPruneBlockLimit() - 10 : 1440;
int startHeight = Math.max(repository.getBlockRepository().getBlockchainHeight() - blocksToValidate, 1);
BlockData detachedBlockData = repository.getBlockRepository().getDetachedBlockSignature(startHeight);
if (detachedBlockData != null) {
LOGGER.error(String.format("Block %d's reference does not match any block's signature", detachedBlockData.getHeight()));
// Wait for blockchain lock (whereas orphan() only tries to get lock)
ReentrantLock blockchainLock = Controller.getInstance().getBlockchainLock();
blockchainLock.lock();
try {
LOGGER.info(String.format("Orphaning back to block %d", detachedBlockData.getHeight() - 1));
orphan(detachedBlockData.getHeight() - 1);
} finally {
blockchainLock.unlock();
// Orphan if we aren't a pruning node
if (!Settings.getInstance().isPruningEnabled()) {
// Wait for blockchain lock (whereas orphan() only tries to get lock)
ReentrantLock blockchainLock = Controller.getInstance().getBlockchainLock();
blockchainLock.lock();
try {
LOGGER.info(String.format("Orphaning back to block %d", detachedBlockData.getHeight() - 1));
orphan(detachedBlockData.getHeight() - 1);
} finally {
blockchainLock.unlock();
}
}
else {
LOGGER.error(String.format("Not orphaning because we are in pruning mode. You may be on an " +
"invalid chain and should consider bootstrapping or re-syncing from genesis."));
}
}
}

View File

@@ -24,7 +24,6 @@ import java.util.Properties;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
@@ -46,6 +45,7 @@ import org.qortal.block.Block;
import org.qortal.block.BlockChain;
import org.qortal.block.BlockChain.BlockTimingByHeight;
import org.qortal.controller.Synchronizer.SynchronizationResult;
import org.qortal.controller.repository.PruneManager;
import org.qortal.controller.tradebot.TradeBot;
import org.qortal.crypto.Crypto;
import org.qortal.data.account.MintingAccountData;
@@ -95,7 +95,6 @@ import org.qortal.transaction.Transaction.TransactionType;
import org.qortal.transaction.Transaction.ValidationResult;
import org.qortal.utils.Base58;
import org.qortal.utils.ByteArray;
import org.qortal.utils.DaemonThreadFactory;
import org.qortal.utils.NTP;
import org.qortal.utils.Triple;
@@ -357,7 +356,7 @@ public class Controller extends Thread {
return this.savedArgs;
}
/* package */ static boolean isStopping() {
public static boolean isStopping() {
return isStopping;
}
@@ -415,6 +414,7 @@ public class Controller extends Thread {
try {
RepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(getRepositoryUrl());
RepositoryManager.setRepositoryFactory(repositoryFactory);
RepositoryManager.prune();
} catch (DataException e) {
// If exception has no cause then repository is in use by some other process.
if (e.getCause() == null) {
@@ -510,9 +510,8 @@ public class Controller extends Thread {
final long repositoryBackupInterval = Settings.getInstance().getRepositoryBackupInterval();
final long repositoryCheckpointInterval = Settings.getInstance().getRepositoryCheckpointInterval();
ExecutorService trimExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory());
trimExecutor.execute(new AtStatesTrimmer());
trimExecutor.execute(new OnlineAccountsSignaturesTrimmer());
// Start executor service for trimming or pruning
PruneManager.getInstance().start();
try {
while (!isStopping) {
@@ -597,13 +596,7 @@ public class Controller extends Thread {
Thread.interrupted();
// Fall-through to exit
} finally {
trimExecutor.shutdownNow();
try {
trimExecutor.awaitTermination(2L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// We tried...
}
PruneManager.getInstance().stop();
}
}
@@ -886,7 +879,7 @@ public class Controller extends Thread {
}
}
String tooltip = String.format("%s - %d %s - %s %d", actionText, numberOfPeers, connectionsText, heightText, height) + "\n" + String.format("Build version: %s", this.buildVersion);
String tooltip = String.format("%s - %d %s - %s %d", actionText, numberOfPeers, connectionsText, heightText, height) + "\n" + String.format("%s: %s", Translator.INSTANCE.translate("SysTray", "BUILD_VERSION"), this.buildVersion);
SysTray.getInstance().setToolTipText(tooltip);
this.callbackExecutor.execute(() -> {
@@ -1286,6 +1279,13 @@ public class Controller extends Thread {
try (final Repository repository = RepositoryManager.getRepository()) {
BlockData blockData = repository.getBlockRepository().fromSignature(signature);
if (blockData != null) {
if (PruneManager.getInstance().isBlockPruned(blockData.getHeight(), repository)) {
// If this is a pruned block, we likely only have partial data, so best not to sent it
blockData = null;
}
}
if (blockData == null) {
// We don't have this block
this.stats.getBlockMessageStats.unknownBlocks.getAndIncrement();
@@ -1407,6 +1407,14 @@ public class Controller extends Thread {
BlockData blockData = repository.getBlockRepository().fromReference(parentSignature);
if (blockData != null) {
if (PruneManager.getInstance().isBlockPruned(blockData.getHeight(), repository)) {
// If this request contains a pruned block, we likely only have partial data, so best not to sent anything
// We always prune from the oldest first, so it's fine to just check the first block requested
blockData = null;
}
}
while (blockData != null && blockSummaries.size() < numberRequested) {
BlockSummaryData blockSummary = new BlockSummaryData(blockData);
blockSummaries.add(blockSummary);

View File

@@ -0,0 +1,92 @@
package org.qortal.controller.repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.controller.Controller;
import org.qortal.data.block.BlockData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
public class AtStatesPruner implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(AtStatesPruner.class);
@Override
public void run() {
Thread.currentThread().setName("AT States pruner");
if (!Settings.getInstance().isPruningEnabled()) {
return;
}
try (final Repository repository = RepositoryManager.getRepository()) {
int pruneStartHeight = repository.getATRepository().getAtPruneHeight();
repository.getATRepository().rebuildLatestAtStates();
repository.saveChanges();
while (!Controller.isStopping()) {
repository.discardChanges();
Thread.sleep(Settings.getInstance().getAtStatesPruneInterval());
BlockData chainTip = Controller.getInstance().getChainTip();
if (chainTip == null || NTP.getTime() == null)
continue;
// Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
if (Controller.getInstance().isSynchronizing())
continue;
// Prune AT states for all blocks up until our latest minus pruneBlockLimit
final int ourLatestHeight = chainTip.getHeight();
final int upperPrunableHeight = ourLatestHeight - Settings.getInstance().getPruneBlockLimit();
int upperBatchHeight = pruneStartHeight + Settings.getInstance().getAtStatesPruneBatchSize();
int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
if (pruneStartHeight >= upperPruneHeight)
continue;
LOGGER.debug(String.format("Pruning AT states between blocks %d and %d...", pruneStartHeight, upperPruneHeight));
int numAtStatesPruned = repository.getATRepository().pruneAtStates(pruneStartHeight, upperPruneHeight);
repository.saveChanges();
int numAtStateDataRowsTrimmed = repository.getATRepository().trimAtStates(
pruneStartHeight, upperPruneHeight, Settings.getInstance().getAtStatesTrimLimit());
repository.saveChanges();
if (numAtStatesPruned > 0 || numAtStateDataRowsTrimmed > 0) {
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Pruned %d AT state%s between blocks %d and %d",
numAtStatesPruned, (numAtStatesPruned != 1 ? "s" : ""),
finalPruneStartHeight, upperPruneHeight));
} else {
// Can we move onto next batch?
if (upperPrunableHeight > upperBatchHeight) {
pruneStartHeight = upperBatchHeight;
repository.getATRepository().setAtPruneHeight(pruneStartHeight);
repository.getATRepository().rebuildLatestAtStates();
repository.saveChanges();
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Bumping AT state base prune height to %d", finalPruneStartHeight));
}
else {
// We've pruned up to the upper prunable height
// Back off for a while to save CPU for syncing
Thread.sleep(5*60*1000L);
}
}
}
} catch (DataException e) {
LOGGER.warn(String.format("Repository issue trying to prune AT states: %s", e.getMessage()));
} catch (InterruptedException e) {
// Time to exit
}
}
}

View File

@@ -1,7 +1,8 @@
package org.qortal.controller;
package org.qortal.controller.repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.controller.Controller;
import org.qortal.data.block.BlockData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
@@ -20,7 +21,7 @@ public class AtStatesTrimmer implements Runnable {
try (final Repository repository = RepositoryManager.getRepository()) {
int trimStartHeight = repository.getATRepository().getAtTrimHeight();
repository.getATRepository().prepareForAtStateTrimming();
repository.getATRepository().rebuildLatestAtStates();
repository.saveChanges();
while (!Controller.isStopping()) {
@@ -62,7 +63,7 @@ public class AtStatesTrimmer implements Runnable {
if (upperTrimmableHeight > upperBatchHeight) {
trimStartHeight = upperBatchHeight;
repository.getATRepository().setAtTrimHeight(trimStartHeight);
repository.getATRepository().prepareForAtStateTrimming();
repository.getATRepository().rebuildLatestAtStates();
repository.saveChanges();
final int finalTrimStartHeight = trimStartHeight;

View File

@@ -0,0 +1,86 @@
package org.qortal.controller.repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.controller.Controller;
import org.qortal.data.block.BlockData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import org.qortal.utils.NTP;
public class BlockPruner implements Runnable {
private static final Logger LOGGER = LogManager.getLogger(BlockPruner.class);
@Override
public void run() {
Thread.currentThread().setName("Block pruner");
if (!Settings.getInstance().isPruningEnabled()) {
return;
}
try (final Repository repository = RepositoryManager.getRepository()) {
int pruneStartHeight = repository.getBlockRepository().getBlockPruneHeight();
while (!Controller.isStopping()) {
repository.discardChanges();
Thread.sleep(Settings.getInstance().getBlockPruneInterval());
BlockData chainTip = Controller.getInstance().getChainTip();
if (chainTip == null || NTP.getTime() == null)
continue;
// Don't even attempt if we're mid-sync as our repository requests will be delayed for ages
if (Controller.getInstance().isSynchronizing())
continue;
// Prune all blocks up until our latest minus pruneBlockLimit
final int ourLatestHeight = chainTip.getHeight();
final int upperPrunableHeight = ourLatestHeight - Settings.getInstance().getPruneBlockLimit();
int upperBatchHeight = pruneStartHeight + Settings.getInstance().getBlockPruneBatchSize();
int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
if (pruneStartHeight >= upperPruneHeight) {
continue;
}
LOGGER.debug(String.format("Pruning blocks between %d and %d...", pruneStartHeight, upperPruneHeight));
int numBlocksPruned = repository.getBlockRepository().pruneBlocks(pruneStartHeight, upperPruneHeight);
repository.saveChanges();
if (numBlocksPruned > 0) {
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Pruned %d block%s between %d and %d",
numBlocksPruned, (numBlocksPruned != 1 ? "s" : ""),
finalPruneStartHeight, upperPruneHeight));
} else {
// Can we move onto next batch?
if (upperPrunableHeight > upperBatchHeight) {
pruneStartHeight = upperBatchHeight;
repository.getBlockRepository().setBlockPruneHeight(pruneStartHeight);
repository.saveChanges();
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Bumping block base prune height to %d", finalPruneStartHeight));
}
else {
// We've pruned up to the upper prunable height
// Back off for a while to save CPU for syncing
Thread.sleep(10*60*1000L);
}
}
}
} catch (DataException e) {
LOGGER.warn(String.format("Repository issue trying to prune blocks: %s", e.getMessage()));
} catch (InterruptedException e) {
// Time to exit
}
}
}

View File

@@ -1,8 +1,9 @@
package org.qortal.controller;
package org.qortal.controller.repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.block.BlockChain;
import org.qortal.controller.Controller;
import org.qortal.data.block.BlockData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;

View File

@@ -0,0 +1,87 @@
package org.qortal.controller.repository;
import org.qortal.controller.Controller;
import org.qortal.data.block.BlockData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.settings.Settings;
import org.qortal.utils.DaemonThreadFactory;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class PruneManager {
private static PruneManager instance;
private boolean pruningEnabled = Settings.getInstance().isPruningEnabled();
private int pruneBlockLimit = Settings.getInstance().getPruneBlockLimit();
private ExecutorService executorService;
private PruneManager() {
}
public static synchronized PruneManager getInstance() {
if (instance == null)
instance = new PruneManager();
return instance;
}
public void start() {
this.executorService = Executors.newCachedThreadPool(new DaemonThreadFactory());
// Don't allow both the pruner and the trimmer to run at the same time.
// In pruning mode, we are already deleting far more than we would when trimming.
// In non-pruning mode, we still need to trim to keep the non-essential data
// out of the database. There isn't a case where both are needed at once.
// If we ever do need to enable both at once, be very careful with the AT state
// trimming, since both currently rely on having exclusive access to the
// prepareForAtStateTrimming() method. For both trimming and pruning to take place
// at once, we would need to synchronize this method in a way that both can't
// call it at the same time, as otherwise active ATs would be pruned/trimmed when
// they should have been kept.
if (Settings.getInstance().isPruningEnabled()) {
// Pruning enabled - start the pruning processes
this.executorService.execute(new AtStatesPruner());
this.executorService.execute(new BlockPruner());
}
else {
// Pruning disabled - use trimming instead
this.executorService.execute(new AtStatesTrimmer());
this.executorService.execute(new OnlineAccountsSignaturesTrimmer());
}
}
public void stop() {
this.executorService.shutdownNow();
try {
this.executorService.awaitTermination(2L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// We tried...
}
}
public boolean isBlockPruned(int height, Repository repository) throws DataException {
if (!this.pruningEnabled) {
return false;
}
BlockData chainTip = Controller.getInstance().getChainTip();
if (chainTip == null) {
throw new DataException("Unable to determine chain tip when checking if a block is pruned");
}
final int ourLatestHeight = chainTip.getHeight();
final int latestUnprunedHeight = ourLatestHeight - this.pruneBlockLimit;
return (height < latestUnprunedHeight);
}
}

View File

@@ -653,18 +653,27 @@ public class ElectrumX extends BitcoinyBlockchainProvider {
Object errorObj = responseJson.get("error");
if (errorObj != null) {
if (errorObj instanceof String)
throw new ForeignBlockchainException.NetworkException(String.format("Unexpected error message from ElectrumX RPC %s: %s", method, (String) errorObj), this.currentServer);
if (errorObj instanceof String) {
LOGGER.debug(String.format("Unexpected error message from ElectrumX server %s for RPC method %s: %s", this.currentServer, method, (String) errorObj));
// Try another server
return null;
}
if (!(errorObj instanceof JSONObject))
throw new ForeignBlockchainException.NetworkException(String.format("Unexpected error response from ElectrumX RPC %s", method), this.currentServer);
if (!(errorObj instanceof JSONObject)) {
LOGGER.debug(String.format("Unexpected error response from ElectrumX server %s for RPC method %s", this.currentServer, method));
// Try another server
return null;
}
JSONObject errorJson = (JSONObject) errorObj;
Object messageObj = errorJson.get("message");
if (!(messageObj instanceof String))
throw new ForeignBlockchainException.NetworkException(String.format("Missing/invalid message in error response from ElectrumX RPC %s", method), this.currentServer);
if (!(messageObj instanceof String)) {
LOGGER.debug(String.format("Missing/invalid message in error response from ElectrumX server %s for RPC method %s", this.currentServer, method));
// Try another server
return null;
}
String message = (String) messageObj;

View File

@@ -23,17 +23,21 @@ public class Gui {
private SysTray sysTray = null;
private Gui() {
this.isHeadless = GraphicsEnvironment.isHeadless();
try {
this.isHeadless = GraphicsEnvironment.isHeadless();
if (!this.isHeadless) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// Use whatever look-and-feel comes by default then
if (!this.isHeadless) {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
// Use whatever look-and-feel comes by default then
}
showSplash();
}
showSplash();
} catch (Exception e) {
LOGGER.info("Unable to initialize GUI: {}", e.getMessage());
}
}

View File

@@ -291,19 +291,23 @@ public class SysTray {
public void setTrayIcon(int iconid) {
if (trayIcon != null) {
switch (iconid) {
case 1:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_syncing_time-alt.png"));
break;
case 2:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_minting.png"));
break;
case 3:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_syncing.png"));
break;
case 4:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_synced.png"));
break;
try {
switch (iconid) {
case 1:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_syncing_time-alt.png"));
break;
case 2:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_minting.png"));
break;
case 3:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_syncing.png"));
break;
case 4:
this.trayIcon.setImage(Gui.loadImage("icons/qortal_ui_tray_synced.png"));
break;
}
} catch (NullPointerException e) {
LOGGER.info("Unable to set tray icon");
}
}
}

View File

@@ -36,6 +36,7 @@ public class ResourceList {
public ResourceList(String category, String resourceName) throws IOException {
this.category = category;
this.resourceName = resourceName;
this.list = new ArrayList<>();
this.load();
}
@@ -45,13 +46,7 @@ public class ResourceList {
private Path getFilePath() {
String pathString = String.format("%s%s%s_%s.json", Settings.getInstance().getListsPath(),
File.separator, this.resourceName, this.category);
Path outputFilePath = Paths.get(pathString);
try {
Files.createDirectories(outputFilePath.getParent());
} catch (IOException e) {
throw new IllegalStateException("Unable to create lists directory");
}
return outputFilePath;
return Paths.get(pathString);
}
public void save() throws IOException {
@@ -62,8 +57,15 @@ public class ResourceList {
throw new IllegalStateException("Can't save list with missing category");
}
String jsonString = ResourceList.listToJSONString(this.list);
Path filePath = this.getFilePath();
// Create parent directory if needed
try {
Files.createDirectories(filePath.getParent());
} catch (IOException e) {
throw new IllegalStateException("Unable to create lists directory");
}
BufferedWriter writer = new BufferedWriter(new FileWriter(filePath.toString()));
writer.write(jsonString);
writer.close();
@@ -99,24 +101,35 @@ public class ResourceList {
/* List management */
public void add(String resource) {
if (resource == null || this.list == null) {
return;
}
if (!this.contains(resource)) {
this.list.add(resource);
}
}
public void remove(String resource) {
if (resource == null || this.list == null) {
return;
}
this.list.remove(resource);
}
public boolean contains(String resource) {
if (resource == null || this.list == null) {
return false;
}
return this.list.contains(resource);
}
/* Utils */
public static String listToJSONString(List<String> list) {
if (list == null) {
return null;
}
JSONArray items = new JSONArray();
for (String item : list) {
items.put(item);
@@ -125,6 +138,9 @@ public class ResourceList {
}
private static List<String> listFromJSONString(String jsonString) {
if (jsonString == null) {
return null;
}
JSONArray jsonList = new JSONArray(jsonString);
List<String> resourceList = new ArrayList<>();
for (int i=0; i<jsonList.length(); i++) {
@@ -134,4 +150,8 @@ public class ResourceList {
return resourceList;
}
public String getJSONString() {
return ResourceList.listToJSONString(this.list);
}
}

View File

@@ -4,6 +4,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.util.List;
public class ResourceListManager {
@@ -84,4 +85,11 @@ public class ResourceListManager {
this.addressBlacklist.revert();
}
public String getBlacklistJSONString() {
if (this.addressBlacklist == null) {
return null;
}
return this.addressBlacklist.getJSONString();
}
}

View File

@@ -112,6 +112,11 @@ public interface ATRepository {
*/
public List<ATStateData> getBlockATStatesAtHeight(int height) throws DataException;
/** Rebuild the latest AT states cache, necessary for AT state trimming/pruning. */
public void rebuildLatestAtStates() throws DataException;
/** Returns height of first trimmable AT state. */
public int getAtTrimHeight() throws DataException;
@@ -121,12 +126,23 @@ public interface ATRepository {
*/
public void setAtTrimHeight(int trimHeight) throws DataException;
/** Hook to allow repository to prepare/cache info for AT state trimming. */
public void prepareForAtStateTrimming() throws DataException;
/** Trims full AT state data between passed heights. Returns number of trimmed rows. */
public int trimAtStates(int minHeight, int maxHeight, int limit) throws DataException;
/** Returns height of first prunable AT state. */
public int getAtPruneHeight() throws DataException;
/** Sets new base height for AT state pruning.
* <p>
* NOTE: performs implicit <tt>repository.saveChanges()</tt>.
*/
public void setAtPruneHeight(int pruneHeight) throws DataException;
/** Prunes full AT state data between passed heights. Returns number of pruned rows. */
public int pruneAtStates(int minHeight, int maxHeight) throws DataException;
/**
* Save ATStateData into repository.
* <p>

View File

@@ -166,6 +166,20 @@ public interface BlockRepository {
*/
public BlockData getDetachedBlockSignature(int startHeight) throws DataException;
/** Returns height of first prunable block. */
public int getBlockPruneHeight() throws DataException;
/** Sets new base height for block pruning.
* <p>
* NOTE: performs implicit <tt>repository.saveChanges()</tt>.
*/
public void setBlockPruneHeight(int pruneHeight) throws DataException;
/** Prunes full block data between passed heights. Returns number of pruned rows. */
public int pruneBlocks(int minHeight, int maxHeight) throws DataException;
/**
* Saves block into repository.
*

View File

@@ -1,8 +1,14 @@
package org.qortal.repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.repository.hsqldb.HSQLDBDatabasePruning;
import org.qortal.settings.Settings;
import java.sql.SQLException;
public abstract class RepositoryManager {
private static final Logger LOGGER = LogManager.getLogger(RepositoryManager.class);
private static RepositoryFactory repositoryFactory = null;
@@ -51,6 +57,24 @@ public abstract class RepositoryManager {
}
}
public static void prune() {
// Bulk prune the database the first time we use pruning mode
if (Settings.getInstance().isPruningEnabled()) {
try {
boolean prunedATStates = HSQLDBDatabasePruning.pruneATStates();
boolean prunedBlocks = HSQLDBDatabasePruning.pruneBlocks();
// Perform repository maintenance to shrink the db size down
if (prunedATStates && prunedBlocks) {
HSQLDBDatabasePruning.performMaintenance();
}
} catch (SQLException | DataException e) {
LOGGER.info("Unable to bulk prune AT states. The database may have been left in an inconsistent state.");
}
}
}
public static void setRequestedCheckpoint(Boolean quick) {
quickCheckpointRequested = quick;
}

View File

@@ -8,6 +8,7 @@ import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.controller.Controller;
import org.qortal.data.at.ATData;
import org.qortal.data.at.ATStateData;
import org.qortal.repository.ATRepository;
@@ -600,6 +601,35 @@ public class HSQLDBATRepository implements ATRepository {
return atStates;
}
@Override
public void rebuildLatestAtStates() throws DataException {
// Rebuild cache of latest AT states that we can't trim
String deleteSql = "DELETE FROM LatestATStates";
try {
this.repository.executeCheckedUpdate(deleteSql);
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to delete temporary latest AT states cache from repository", e);
}
String insertSql = "INSERT INTO LatestATStates ("
+ "SELECT AT_address, height FROM ATs "
+ "CROSS JOIN LATERAL("
+ "SELECT height FROM ATStates "
+ "WHERE ATStates.AT_address = ATs.AT_address "
+ "ORDER BY AT_address DESC, height DESC LIMIT 1"
+ ") "
+ ")";
try {
this.repository.executeCheckedUpdate(insertSql);
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to populate temporary latest AT states cache in repository", e);
}
}
@Override
public int getAtTrimHeight() throws DataException {
String sql = "SELECT AT_trim_height FROM DatabaseInfo";
@@ -631,33 +661,6 @@ public class HSQLDBATRepository implements ATRepository {
}
}
@Override
public void prepareForAtStateTrimming() throws DataException {
// Rebuild cache of latest AT states that we can't trim
String deleteSql = "DELETE FROM LatestATStates";
try {
this.repository.executeCheckedUpdate(deleteSql);
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to delete temporary latest AT states cache from repository", e);
}
String insertSql = "INSERT INTO LatestATStates ("
+ "SELECT AT_address, height FROM ATs "
+ "CROSS JOIN LATERAL("
+ "SELECT height FROM ATStates "
+ "WHERE ATStates.AT_address = ATs.AT_address "
+ "ORDER BY AT_address DESC, height DESC LIMIT 1"
+ ") "
+ ")";
try {
this.repository.executeCheckedUpdate(insertSql);
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to populate temporary latest AT states cache in repository", e);
}
}
@Override
public int trimAtStates(int minHeight, int maxHeight, int limit) throws DataException {
if (minHeight >= maxHeight)
@@ -682,6 +685,94 @@ public class HSQLDBATRepository implements ATRepository {
}
}
@Override
public int getAtPruneHeight() throws DataException {
String sql = "SELECT AT_prune_height FROM DatabaseInfo";
try (ResultSet resultSet = this.repository.checkedExecute(sql)) {
if (resultSet == null)
return 0;
return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Unable to fetch AT state prune height from repository", e);
}
}
@Override
public void setAtPruneHeight(int pruneHeight) throws DataException {
// trimHeightsLock is to prevent concurrent update on DatabaseInfo
// that could result in "transaction rollback: serialization failure"
synchronized (this.repository.trimHeightsLock) {
String updateSql = "UPDATE DatabaseInfo SET AT_prune_height = ?";
try {
this.repository.executeCheckedUpdate(updateSql, pruneHeight);
this.repository.saveChanges();
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to set AT state prune height in repository", e);
}
}
}
@Override
public int pruneAtStates(int minHeight, int maxHeight) throws DataException {
int deletedCount = 0;
for (int height=minHeight; height<maxHeight; height++) {
// Give up if we're stopping
if (Controller.isStopping()) {
return deletedCount;
}
// Get latest AT states for this height
List<String> atAddresses = new ArrayList<>();
String updateSql = "SELECT AT_address FROM LatestATStates WHERE height = ?";
try (ResultSet resultSet = this.repository.checkedExecute(updateSql, height)) {
if (resultSet != null) {
do {
String atAddress = resultSet.getString(1);
atAddresses.add(atAddress);
} while (resultSet.next());
}
} catch (SQLException e) {
throw new DataException("Unable to fetch flagged accounts from repository", e);
}
List<ATStateData> atStates = this.getBlockATStatesAtHeight(height);
for (ATStateData atState : atStates) {
//LOGGER.info("Found atState {} at height {}", atState.getATAddress(), atState.getHeight());
// Give up if we're stopping
if (Controller.isStopping()) {
return deletedCount;
}
if (atAddresses.contains(atState.getATAddress())) {
// We don't want to delete this AT state because it is still active
LOGGER.info("Skipping atState {} at height {}", atState.getATAddress(), atState.getHeight());
continue;
}
// Safe to delete everything else for this height
try {
this.repository.delete("ATStates", "AT_address = ? AND height = ?",
atState.getATAddress(), atState.getHeight());
deletedCount++;
} catch (SQLException e) {
throw new DataException("Unable to delete AT state data from repository", e);
}
}
}
return deletedCount;
}
@Override
public void save(ATStateData atStateData) throws DataException {
// We shouldn't ever save partial ATStateData

View File

@@ -509,6 +509,53 @@ public class HSQLDBBlockRepository implements BlockRepository {
}
}
@Override
public int getBlockPruneHeight() throws DataException {
String sql = "SELECT block_prune_height FROM DatabaseInfo";
try (ResultSet resultSet = this.repository.checkedExecute(sql)) {
if (resultSet == null)
return 0;
return resultSet.getInt(1);
} catch (SQLException e) {
throw new DataException("Unable to fetch block prune height from repository", e);
}
}
@Override
public void setBlockPruneHeight(int pruneHeight) throws DataException {
// trimHeightsLock is to prevent concurrent update on DatabaseInfo
// that could result in "transaction rollback: serialization failure"
synchronized (this.repository.trimHeightsLock) {
String updateSql = "UPDATE DatabaseInfo SET block_prune_height = ?";
try {
this.repository.executeCheckedUpdate(updateSql, pruneHeight);
this.repository.saveChanges();
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to set block prune height in repository", e);
}
}
}
@Override
public int pruneBlocks(int minHeight, int maxHeight) throws DataException {
// Don't prune the genesis block
if (minHeight <= 1) {
minHeight = 2;
}
try {
return this.repository.delete("Blocks", "height BETWEEN ? AND ?", minHeight, maxHeight);
} catch (SQLException e) {
throw new DataException("Unable to prune blocks from repository", e);
}
}
@Override
public BlockData getDetachedBlockSignature(int startHeight) throws DataException {
String sql = "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks "

View File

@@ -0,0 +1,282 @@
package org.qortal.repository.hsqldb;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.controller.Controller;
import org.qortal.data.block.BlockData;
import org.qortal.repository.DataException;
import org.qortal.repository.RepositoryManager;
import org.qortal.settings.Settings;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
*
* When switching from a full node to a pruning node, we need to delete most of the database contents.
* If we do this entirely as a background process, it is very slow and can interfere with syncing.
* However, if we take the approach of transferring only the necessary rows to a new table and then
* deleting the original table, this makes the process much faster. It was taking several days to
* delete the AT states in the background, but only a couple of minutes to copy them to a new table.
*
* The trade off is that we have to go through a form of "reshape" when starting the app for the first
* time after enabling pruning mode. But given that this is an opt-in mode, I don't think it will be
* a problem.
*
* Once the pruning is complete, it automatically performs a CHECKPOINT DEFRAG in order to
* shrink the database file size down to a fraction of what it was before.
*
* From this point, the original background process will run, but can be dialled right down so not
* to interfere with syncing.
*
*/
public class HSQLDBDatabasePruning {
private static final Logger LOGGER = LogManager.getLogger(HSQLDBDatabasePruning.class);
public static boolean pruneATStates() throws SQLException, DataException {
try (final HSQLDBRepository repository = (HSQLDBRepository)RepositoryManager.getRepository()) {
// Only bulk prune AT states if we have never done so before
int pruneHeight = repository.getATRepository().getAtPruneHeight();
if (pruneHeight > 0) {
// Already pruned AT states
return false;
}
LOGGER.info("Starting bulk prune of AT states - this process could take a while... (approx. 2 mins on high spec)");
// Create new AT-states table to hold smaller dataset
repository.executeCheckedUpdate("DROP TABLE IF EXISTS ATStatesNew");
repository.executeCheckedUpdate("CREATE TABLE ATStatesNew ("
+ "AT_address QortalAddress, height INTEGER NOT NULL, state_hash ATStateHash NOT NULL, "
+ "fees QortalAmount NOT NULL, is_initial BOOLEAN NOT NULL, sleep_until_message_timestamp BIGINT, "
+ "PRIMARY KEY (AT_address, height), "
+ "FOREIGN KEY (AT_address) REFERENCES ATs (AT_address) ON DELETE CASCADE)");
repository.executeCheckedUpdate("SET TABLE ATStatesNew NEW SPACE");
repository.executeCheckedUpdate("CHECKPOINT");
// Find our latest block
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
if (latestBlock == null) {
LOGGER.info("Unable to determine blockchain height, necessary for bulk block pruning");
return false;
}
// Calculate some constants for later use
final int blockchainHeight = latestBlock.getHeight();
final int maximumBlockToTrim = blockchainHeight - Settings.getInstance().getPruneBlockLimit();
final int startHeight = maximumBlockToTrim;
final int endHeight = blockchainHeight;
final int blockStep = 10000;
// Loop through all the LatestATStates and copy them to the new table
LOGGER.info("Copying AT states...");
for (int height = 0; height < endHeight; height += blockStep) {
//LOGGER.info(String.format("Copying AT states between %d and %d...", height, height + blockStep - 1));
String sql = "SELECT height, AT_address FROM LatestATStates WHERE height BETWEEN ? AND ?";
try (ResultSet latestAtStatesResultSet = repository.checkedExecute(sql, height, height + blockStep - 1)) {
if (latestAtStatesResultSet != null) {
do {
int latestAtHeight = latestAtStatesResultSet.getInt(1);
String latestAtAddress = latestAtStatesResultSet.getString(2);
// Copy this latest ATState to the new table
//LOGGER.info(String.format("Copying AT %s at height %d...", latestAtAddress, latestAtHeight));
try {
String updateSql = "INSERT INTO ATStatesNew ("
+ "SELECT AT_address, height, state_hash, fees, is_initial, sleep_until_message_timestamp "
+ "FROM ATStates "
+ "WHERE height = ? AND AT_address = ?)";
repository.executeCheckedUpdate(updateSql, latestAtHeight, latestAtAddress);
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to copy ATStates", e);
}
if (height >= startHeight) {
// Now copy this AT states for each recent block it is present in
for (int i = startHeight; i < endHeight; i++) {
if (latestAtHeight < i) {
// This AT finished before this block so there is nothing to copy
continue;
}
//LOGGER.info(String.format("Copying recent AT %s at height %d...", latestAtAddress, i));
try {
// Copy each LatestATState to the new table
String updateSql = "INSERT IGNORE INTO ATStatesNew ("
+ "SELECT AT_address, height, state_hash, fees, is_initial, sleep_until_message_timestamp "
+ "FROM ATStates "
+ "WHERE height = ? AND AT_address = ?)";
repository.executeCheckedUpdate(updateSql, i, latestAtAddress);
} catch (SQLException e) {
repository.examineException(e);
throw new DataException("Unable to copy ATStates", e);
}
}
}
} while (latestAtStatesResultSet.next());
}
} catch (SQLException e) {
throw new DataException("Unable to copy AT states", e);
}
}
repository.saveChanges();
// Add a height index
LOGGER.info("Rebuilding AT states height index in repository");
repository.executeCheckedUpdate("CREATE INDEX IF NOT EXISTS ATStatesHeightIndex ON ATStatesNew (height)");
repository.executeCheckedUpdate("CHECKPOINT");
// Finally, drop the original table and rename
LOGGER.info("Deleting old AT states...");
repository.executeCheckedUpdate("DROP TABLE ATStates");
repository.executeCheckedUpdate("ALTER TABLE ATStatesNew RENAME TO ATStates");
repository.executeCheckedUpdate("CHECKPOINT");
// Update the prune height
repository.getATRepository().setAtPruneHeight(maximumBlockToTrim);
repository.saveChanges();
repository.executeCheckedUpdate("CHECKPOINT");
// Now prune/trim the ATStatesData, as this currently goes back over a month
return HSQLDBDatabasePruning.pruneATStateData();
}
}
/*
* Bulk prune ATStatesData to catch up with the now pruned ATStates table
* This uses the existing AT States trimming code but with a much higher end block
*/
private static boolean pruneATStateData() throws SQLException, DataException {
try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) {
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
if (latestBlock == null) {
LOGGER.info("Unable to determine blockchain height, necessary for bulk ATStatesData pruning");
return false;
}
final int blockchainHeight = latestBlock.getHeight();
final int upperPrunableHeight = blockchainHeight - Settings.getInstance().getPruneBlockLimit();
// ATStateData is already trimmed - so carry on from where we left off in the past
int pruneStartHeight = repository.getATRepository().getAtTrimHeight();
LOGGER.info("Starting bulk prune of AT states data - this process could take a while... (approx. 3 mins on high spec)");
while (pruneStartHeight < upperPrunableHeight) {
// Prune all AT state data up until our latest minus pruneBlockLimit
if (Controller.isStopping()) {
return false;
}
// Override batch size in the settings because this is a one-off process
final int batchSize = 1000;
final int rowLimitPerBatch = 50000;
int upperBatchHeight = pruneStartHeight + batchSize;
int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
LOGGER.trace(String.format("Pruning AT states data between %d and %d...", pruneStartHeight, upperPruneHeight));
int numATStatesPruned = repository.getATRepository().trimAtStates(pruneStartHeight, upperPruneHeight, rowLimitPerBatch);
repository.saveChanges();
if (numATStatesPruned > 0) {
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.trace(() -> String.format("Pruned %d AT states data rows between blocks %d and %d",
numATStatesPruned, finalPruneStartHeight, upperPruneHeight));
} else {
// Can we move onto next batch?
if (upperPrunableHeight > upperBatchHeight) {
pruneStartHeight = upperBatchHeight;
repository.getATRepository().setAtTrimHeight(pruneStartHeight);
// No need to rebuild the latest AT states as we aren't currently synchronizing
repository.saveChanges();
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Bumping AT states trim height to %d", finalPruneStartHeight));
}
else {
// We've finished pruning
break;
}
}
}
return true;
}
}
public static boolean pruneBlocks() throws SQLException, DataException {
try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) {
// Only bulk prune AT states if we have never done so before
int pruneHeight = repository.getBlockRepository().getBlockPruneHeight();
if (pruneHeight > 0) {
// Already pruned blocks
return false;
}
BlockData latestBlock = repository.getBlockRepository().getLastBlock();
if (latestBlock == null) {
LOGGER.info("Unable to determine blockchain height, necessary for bulk block pruning");
return false;
}
final int blockchainHeight = latestBlock.getHeight();
final int upperPrunableHeight = blockchainHeight - Settings.getInstance().getPruneBlockLimit();
int pruneStartHeight = 0;
LOGGER.info("Starting bulk prune of blocks - this process could take a while... (approx. 5 mins on high spec)");
while (pruneStartHeight < upperPrunableHeight) {
// Prune all blocks up until our latest minus pruneBlockLimit
int upperBatchHeight = pruneStartHeight + Settings.getInstance().getBlockPruneBatchSize();
int upperPruneHeight = Math.min(upperBatchHeight, upperPrunableHeight);
LOGGER.info(String.format("Pruning blocks between %d and %d...", pruneStartHeight, upperPruneHeight));
int numBlocksPruned = repository.getBlockRepository().pruneBlocks(pruneStartHeight, upperPruneHeight);
repository.saveChanges();
if (numBlocksPruned > 0) {
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.info(() -> String.format("Pruned %d block%s between %d and %d",
numBlocksPruned, (numBlocksPruned != 1 ? "s" : ""),
finalPruneStartHeight, upperPruneHeight));
} else {
// Can we move onto next batch?
if (upperPrunableHeight > upperBatchHeight) {
pruneStartHeight = upperBatchHeight;
repository.getBlockRepository().setBlockPruneHeight(pruneStartHeight);
repository.saveChanges();
final int finalPruneStartHeight = pruneStartHeight;
LOGGER.debug(() -> String.format("Bumping block base prune height to %d", finalPruneStartHeight));
}
else {
// We've finished pruning
break;
}
}
}
return true;
}
}
public static void performMaintenance() throws SQLException, DataException {
try (final HSQLDBRepository repository = (HSQLDBRepository) RepositoryManager.getRepository()) {
repository.performPeriodicMaintenance();
}
}
}

View File

@@ -837,6 +837,11 @@ public class HSQLDBDatabaseUpdates {
stmt.execute("SET TABLE ATStatesNew NEW SPACE");
stmt.execute("CHECKPOINT");
// Add the height index
LOGGER.info("Adding index to AT states table...");
stmt.execute("CREATE INDEX ATStatesNewHeightIndex ON ATStatesNew (height)");
stmt.execute("CHECKPOINT");
ResultSet resultSet = stmt.executeQuery("SELECT height FROM Blocks ORDER BY height DESC LIMIT 1");
final int blockchainHeight = resultSet.next() ? resultSet.getInt(1) : 0;
final int heightStep = 100;
@@ -849,14 +854,24 @@ public class HSQLDBDatabaseUpdates {
+ "WHERE height BETWEEN " + minHeight + " AND " + (minHeight + heightStep - 1)
+ ")");
stmt.execute("COMMIT");
int processed = Math.min(minHeight + heightStep - 1, blockchainHeight);
double percentage = (double)processed / (double)blockchainHeight * 100.0f;
LOGGER.info(String.format("Processed %d of %d blocks (%.1f%%)", processed, blockchainHeight, percentage));
}
stmt.execute("CHECKPOINT");
stmt.execute("DROP TABLE ATStates");
stmt.execute("ALTER TABLE ATStatesNew RENAME TO ATStates");
stmt.execute("ALTER INDEX ATStatesNewHeightIndex RENAME TO ATStatesHeightIndex");
stmt.execute("CHECKPOINT");
break;
}
case 35:
// Support for pruning
stmt.execute("ALTER TABLE DatabaseInfo ADD AT_prune_height INT NOT NULL DEFAULT 0");
stmt.execute("ALTER TABLE DatabaseInfo ADD block_prune_height INT NOT NULL DEFAULT 0");
break;
default:
// nothing to do

View File

@@ -109,6 +109,26 @@ public class Settings {
* This has a significant effect on execution time. */
private int onlineSignaturesTrimBatchSize = 100; // blocks
/** Whether we should prune old data to reduce database size
* This prevents the node from being able to serve older blocks */
private boolean pruningEnabled = false;
/** The amount of recent blocks we should keep when pruning */
private int pruneBlockLimit = 1450;
/** How often to attempt AT state pruning (ms). */
private long atStatesPruneInterval = 3219L; // milliseconds
/** Block height range to scan for prunable AT states.<br>
* This has a significant effect on execution time. */
private int atStatesPruneBatchSize = 25; // blocks
/** How often to attempt block pruning (ms). */
private long blockPruneInterval = 3219L; // milliseconds
/** Block height range to scan for prunable blocks.<br>
* This has a significant effect on execution time. */
private int blockPruneBatchSize = 10000; // blocks
// Peer-to-peer related
private boolean isTestNet = false;
/** Port number for inbound peer-to-peer connections. */
@@ -529,4 +549,29 @@ public class Settings {
return this.onlineSignaturesTrimBatchSize;
}
public boolean isPruningEnabled() {
return this.pruningEnabled;
}
public int getPruneBlockLimit() {
return this.pruneBlockLimit;
}
public long getAtStatesPruneInterval() {
return this.atStatesPruneInterval;
}
public int getAtStatesPruneBatchSize() {
return this.atStatesPruneBatchSize;
}
public long getBlockPruneInterval() {
return this.blockPruneInterval;
}
public int getBlockPruneBatchSize() {
return this.blockPruneBatchSize;
}
}

View File

@@ -1,14 +1,83 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# Keys are from api.ApiError enum
# "localeLang": "de",
### Common ###
JSON = JSON nachricht konnte nicht geparsed werden
INSUFFICIENT_BALANCE = insufficient balance
UNAUTHORIZED = API call unauthorized
REPOSITORY_ISSUE = repository error
NON_PRODUCTION = this API call is not permitted for production systems
BLOCKCHAIN_NEEDS_SYNC = blockchain needs to synchronize first
NO_TIME_SYNC = no clock synchronization yet
### Validation ###
INVALID_SIGNATURE = ungültige signatur
INVALID_ADDRESS = ungültige adresse
INVALID_ASSET_ID = ungültige asset ID
INVALID_PUBLIC_KEY = ungültiger public key
INVALID_DATA = ungültige daten
INVALID_PUBLIC_KEY = ungültiger public key
INVALID_NETWORK_ADDRESS = invalid network address
INVALID_SIGNATURE = ungültige signatur
ADDRESS_UNKNOWN = account address unknown
JSON = JSON nachricht konnte nicht geparsed werden
INVALID_CRITERIA = invalid search criteria
INVALID_REFERENCE = invalid reference
TRANSFORMATION_ERROR = could not transform JSON into transaction
INVALID_PRIVATE_KEY = invalid private key
INVALID_HEIGHT = invalid block height
CANNOT_MINT = account cannot mint
### Blocks ###
BLOCK_UNKNOWN = block unknown
### Transactions ###
TRANSACTION_UNKNOWN = transaction unknown
PUBLIC_KEY_NOT_FOUND = public key wurde nicht gefunden
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = transaction invalid: %s (%s)
### Naming ###
NAME_UNKNOWN = name unknown
### Asset ###
INVALID_ASSET_ID = ungültige asset ID
INVALID_ORDER_ID = invalid asset order ID
ORDER_UNKNOWN = unknown asset order ID
### Groups ###
GROUP_UNKNOWN = group unknown
### Foreign Blockchain ###
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blokchain or ElectrumX network issue
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain
FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = order amount too low
### Data ###
FILE_NOT_FOUND = file not found
NO_REPLY = peer did not reply with data

View File

@@ -1,68 +1,83 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# Keys are from api.ApiError enum
ADDRESS_UNKNOWN = account address unknown
BLOCKCHAIN_NEEDS_SYNC = blockchain needs to synchronize first
# Blocks
BLOCK_UNKNOWN = block unknown
BTC_BALANCE_ISSUE = insufficient Bitcoin balance
BTC_NETWORK_ISSUE = Bitcoin/ElectrumX network issue
BTC_TOO_SOON = too soon to broadcast Bitcoin transaction (lockTime/median block time)
CANNOT_MINT = account cannot mint
GROUP_UNKNOWN = group unknown
INVALID_ADDRESS = invalid address
# Assets
INVALID_ASSET_ID = invalid asset ID
INVALID_CRITERIA = invalid search criteria
INVALID_DATA = invalid data
INVALID_HEIGHT = invalid block height
INVALID_NETWORK_ADDRESS = invalid network address
INVALID_ORDER_ID = invalid asset order ID
INVALID_PRIVATE_KEY = invalid private key
INVALID_PUBLIC_KEY = invalid public key
INVALID_REFERENCE = invalid reference
# Validation
INVALID_SIGNATURE = invalid signature
# "localeLang": "en",
### Common ###
JSON = failed to parse JSON message
NAME_UNKNOWN = name unknown
NON_PRODUCTION = this API call is not permitted for production systems
NO_TIME_SYNC = no clock synchronization yet
ORDER_UNKNOWN = unknown asset order ID
PUBLIC_KEY_NOT_FOUND = public key not found
REPOSITORY_ISSUE = repository error
# This one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = transaction invalid: %s (%s)
TRANSACTION_UNKNOWN = transaction unknown
TRANSFORMATION_ERROR = could not transform JSON into transaction
INSUFFICIENT_BALANCE = insufficient balance
UNAUTHORIZED = API call unauthorized
ORDER_SIZE_TOO_SMALL = order size too small
REPOSITORY_ISSUE = repository error
NON_PRODUCTION = this API call is not permitted for production systems
BLOCKCHAIN_NEEDS_SYNC = blockchain needs to synchronize first
NO_TIME_SYNC = no clock synchronization yet
### Validation ###
INVALID_SIGNATURE = invalid signature
INVALID_ADDRESS = invalid address
INVALID_PUBLIC_KEY = invalid public key
INVALID_DATA = invalid data
INVALID_NETWORK_ADDRESS = invalid network address
ADDRESS_UNKNOWN = account address unknown
INVALID_CRITERIA = invalid search criteria
INVALID_REFERENCE = invalid reference
TRANSFORMATION_ERROR = could not transform JSON into transaction
INVALID_PRIVATE_KEY = invalid private key
INVALID_HEIGHT = invalid block height
CANNOT_MINT = account cannot mint
### Blocks ###
BLOCK_UNKNOWN = block unknown
### Transactions ###
TRANSACTION_UNKNOWN = transaction unknown
PUBLIC_KEY_NOT_FOUND = public key not found
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = transaction invalid: %s (%s)
### Naming ###
NAME_UNKNOWN = name unknown
### Asset ###
INVALID_ASSET_ID = invalid asset ID
INVALID_ORDER_ID = invalid asset order ID
ORDER_UNKNOWN = unknown asset order ID
### Groups ###
GROUP_UNKNOWN = group unknown
### Foreign Blockchain ###
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blokchain or ElectrumX network issue
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain
FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = order amount too low
### Data ###
FILE_NOT_FOUND = file not found
NO_REPLY = peer did not reply with data

View File

@@ -1,71 +1,86 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# Keys are from api.ApiError enum
#
# Kielen muuttaminen suomeksi tapahtuu settings.json-tiedostossa
#
# "localeLang": "fi",
# muista pilkku lopussa jos komento ei ole viimeisellä rivillä
ADDRESS_UNKNOWN = tilin osoite on tuntematon
BLOCKCHAIN_NEEDS_SYNC = lohkoketjun tarvitsee ensin synkronisoitua
# Blocks
BLOCK_UNKNOWN = tuntematon lohko
BTC_BALANCE_ISSUE = riittämätön Bitcoin-saldo
BTC_NETWORK_ISSUE = Bitcoin/ElectrumX -verkon ongelma
BTC_TOO_SOON = liian aikaista julkistaa Bitcoin-tapahtumaa (lukitusaika/mediiaanilohkoaika)
CANNOT_MINT = tili ei voi lyödä rahaa
GROUP_UNKNOWN = tuntematon ryhmä
INVALID_ADDRESS = osoite on kelvoton
# Assets
INVALID_ASSET_ID = kelvoton ID resurssille
INVALID_CRITERIA = kelvoton hakuehto
INVALID_DATA = kelvoton data
INVALID_HEIGHT = kelvoton lohkon korkeus
INVALID_NETWORK_ADDRESS = kelvoton verkko-osoite
INVALID_ORDER_ID = kelvoton resurssin tilaus-ID
INVALID_PRIVATE_KEY = kelvoton yksityinen avain
INVALID_PUBLIC_KEY = kelvoton julkinen avain
INVALID_REFERENCE = kelvoton viite
# Validation
INVALID_SIGNATURE = kelvoton allekirjoitus
### Common ###
JSON = JSON-viestin jaottelu epäonnistui
NAME_UNKNOWN = tuntematon nimi
INSUFFICIENT_BALANCE = insufficient balance
NON_PRODUCTION = tämä API-kutsu on kielletty tuotantoversiossa
NO_TIME_SYNC = kello vielä synkronisoimatta
ORDER_UNKNOWN = tuntematon resurssin tilaus-ID
PUBLIC_KEY_NOT_FOUND = julkista avainta ei löytynyt
UNAUTHORIZED = luvaton API-kutsu
REPOSITORY_ISSUE = tietovarantovirhe (repo)
# This one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = kelvoton transaktio: %s (%s)
NON_PRODUCTION = tämä API-kutsu on kielletty tuotantoversiossa
TRANSACTION_UNKNOWN = tuntematon transaktio
BLOCKCHAIN_NEEDS_SYNC = lohkoketjun tarvitsee ensin synkronisoitua
NO_TIME_SYNC = kello vielä synkronisoimatta
### Validation ###
INVALID_SIGNATURE = kelvoton allekirjoitus
INVALID_ADDRESS = osoite on kelvoton
INVALID_PUBLIC_KEY = kelvoton julkinen avain
INVALID_DATA = kelvoton data
INVALID_NETWORK_ADDRESS = kelvoton verkko-osoite
ADDRESS_UNKNOWN = tilin osoite on tuntematon
INVALID_CRITERIA = kelvoton hakuehto
INVALID_REFERENCE = kelvoton viite
TRANSFORMATION_ERROR = JSON:in muuntaminen transaktioksi epäonnistui
UNAUTHORIZED = luvaton API-kutsu
INVALID_PRIVATE_KEY = kelvoton yksityinen avain
INVALID_HEIGHT = kelvoton lohkon korkeus
CANNOT_MINT = tili ei voi lyödä rahaa
### Blocks ###
BLOCK_UNKNOWN = tuntematon lohko
### Transactions ###
TRANSACTION_UNKNOWN = tuntematon transaktio
PUBLIC_KEY_NOT_FOUND = julkista avainta ei löytynyt
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = kelvoton transaktio: %s (%s)
### Naming ###
NAME_UNKNOWN = tuntematon nimi
### Asset ###
INVALID_ASSET_ID = kelvoton ID resurssille
INVALID_ORDER_ID = kelvoton resurssin tilaus-ID
ORDER_UNKNOWN = tuntematon resurssin tilaus-ID
### Groups ###
GROUP_UNKNOWN = tuntematon ryhmä
### Foreign Blockchain ###
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blokchain or ElectrumX network issue
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain
FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = order amount too low
### Data ###
FILE_NOT_FOUND = file not found
NO_REPLY = peer did not reply with data

View File

@@ -0,0 +1,86 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# Keys are from api.ApiError enum
# Magyar myelvre forditotta: Szkíta (Scythian). 2021 Augusztus 7.
# Az alkalmazás nyelvének magyarra való változtatása a settings.json oldalon történik.
# "localeLang": "hu",
### Common ###
JSON = nem sikerült elemezni a JSON üzenetet
INSUFFICIENT_BALANCE = elégtelen egyenleg
UNAUTHORIZED = nem engedélyezett API-hívás
REPOSITORY_ISSUE = adattári hiba
NON_PRODUCTION = ez az API-hívás nem engedélyezett korlátozott rendszereken
BLOCKCHAIN_NEEDS_SYNC = a blokkláncnak még szinkronizálnia kell
NO_TIME_SYNC = az óraszinkronizálás még nem történt meg
### Validation ###
INVALID_SIGNATURE = érvénytelen aláírás
INVALID_ADDRESS = érvénytelen fiók cím
INVALID_PUBLIC_KEY = érvénytelen nyilvános kulcs
INVALID_DATA = érvénytelen adat
INVALID_NETWORK_ADDRESS = érvénytelen hálózat cím
ADDRESS_UNKNOWN = ismeretlen fiók cím
INVALID_CRITERIA = érvénytelen keresési feltétel
INVALID_REFERENCE = érvénytelen hivatkozás
TRANSFORMATION_ERROR = nem sikerült tranzakcióvá alakítani a JSON-t
INVALID_PRIVATE_KEY = érvénytelen privát kulcs
INVALID_HEIGHT = érvénytelen blokkmagasság
CANNOT_MINT = ez a fiók még nem tud QORT-ot verni
### Blocks ###
BLOCK_UNKNOWN = ismeretlen blokk
### Transactions ###
TRANSACTION_UNKNOWN = ismeretlen tranzakció
PUBLIC_KEY_NOT_FOUND = nyilvános kulcs nem található
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = érvénytelen tranzakció: %s (%s)
### Naming ###
NAME_UNKNOWN = ismeretlen név
### Asset ###
INVALID_ASSET_ID = érvénytelen eszközazonosító
INVALID_ORDER_ID = érvénytelen eszközrendelési azonosító
ORDER_UNKNOWN = ismeretlen eszközrendelési azonosító
### Groups ###
GROUP_UNKNOWN = ismeretlen csoport
### Foreign Blockchain ###
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = idegen blokklánc vagy ElectrumX hálózati probléma
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = elégtelen egyenleg az idegen blokkláncon
FOREIGN_BLOCKCHAIN_TOO_SOON = túl korai meghírdetni az idegen blokkláncon való tranzakciót (LockTime/medián blokkidő)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = rendelési összeg túl alacsony
### Data ###
FILE_NOT_FOUND = fájl nem található
NO_REPLY = a másik csomópont nem válaszolt

View File

@@ -7,66 +7,81 @@
# "localeLang": "it",
# Si prega ricordare la virgola alla fine, se questo comando non è sull'ultima riga
ADDRESS_UNKNOWN = indirizzo account sconosciuto
BLOCKCHAIN_NEEDS_SYNC = blockchain deve prima sincronizzarsi
# Blocks
BLOCK_UNKNOWN = blocco sconosciuto
BTC_BALANCE_ISSUE = saldo Bitcoin insufficiente
BTC_NETWORK_ISSUE = Bitcoin/ElectrumX problema di rete
BTC_TOO_SOON = troppo presto per trasmettere transazione Bitcoin (tempo di blocco / tempo di blocco mediano)
CANNOT_MINT = l'account non può coniare
GROUP_UNKNOWN = gruppo sconosciuto
INVALID_ADDRESS = indirizzo non valido
# Assets
INVALID_ASSET_ID = identificazione risorsa non valida
INVALID_CRITERIA = criteri di ricerca non validi
INVALID_DATA = dati non validi
INVALID_HEIGHT = altezza blocco non valida
INVALID_NETWORK_ADDRESS = indirizzo di rete non valido
INVALID_ORDER_ID = identificazione di ordine di risorsa non valida
INVALID_PRIVATE_KEY = chiave privata non valida
INVALID_PUBLIC_KEY = chiave pubblica non valida
INVALID_REFERENCE = riferimento non valido
# Validation
INVALID_SIGNATURE = firma non valida
### Common ###
JSON = Impossibile analizzare il messaggio JSON
NAME_UNKNOWN = nome sconosciuto
INSUFFICIENT_BALANCE = insufficient balance
NON_PRODUCTION = questa chiamata API non è consentita per i sistemi di produzione
NO_TIME_SYNC = nessuna sincronizzazione dell'orologio ancora
ORDER_UNKNOWN = identificazione di ordine di risorsa sconosciuta
PUBLIC_KEY_NOT_FOUND = chiave pubblica non trovata
UNAUTHORIZED = Chiamata API non autorizzata
REPOSITORY_ISSUE = errore del repositorio
# This one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = transazione non valida: %s (%s)
NON_PRODUCTION = questa chiamata API non è consentita per i sistemi di produzione
TRANSACTION_UNKNOWN = transazione sconosciuta
BLOCKCHAIN_NEEDS_SYNC = blockchain deve prima sincronizzarsi
NO_TIME_SYNC = nessuna sincronizzazione dell'orologio ancora
### Validation ###
INVALID_SIGNATURE = firma non valida
INVALID_ADDRESS = indirizzo non valido
INVALID_PUBLIC_KEY = chiave pubblica non valida
INVALID_DATA = dati non validi
INVALID_NETWORK_ADDRESS = indirizzo di rete non valido
ADDRESS_UNKNOWN = indirizzo account sconosciuto
INVALID_CRITERIA = criteri di ricerca non validi
INVALID_REFERENCE = riferimento non valido
TRANSFORMATION_ERROR = non è stato possibile trasformare JSON in transazione
UNAUTHORIZED = Chiamata API non autorizzata
INVALID_PRIVATE_KEY = chiave privata non valida
INVALID_HEIGHT = altezza blocco non valida
CANNOT_MINT = l'account non può coniare
### Blocks ###
BLOCK_UNKNOWN = blocco sconosciuto
### Transactions ###
TRANSACTION_UNKNOWN = transazione sconosciuta
PUBLIC_KEY_NOT_FOUND = chiave pubblica non trovata
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = transazione non valida: %s (%s)
### Naming ###
NAME_UNKNOWN = nome sconosciuto
### Asset ###
INVALID_ASSET_ID = identificazione risorsa non valida
INVALID_ORDER_ID = identificazione di ordine di risorsa non valida
ORDER_UNKNOWN = identificazione di ordine di risorsa sconosciuta
### Groups ###
GROUP_UNKNOWN = gruppo sconosciuto
### Foreign Blockchain ###
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blokchain or ElectrumX network issue
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain
FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = order amount too low
### Data ###
FILE_NOT_FOUND = file not found
NO_REPLY = peer did not reply with data

View File

@@ -1,66 +1,83 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# Keys are from api.ApiError enum
ADDRESS_UNKNOWN = account adres onbekend
BLOCKCHAIN_NEEDS_SYNC = blockchain dient eerst gesynchronizeerd te worden
# Blocks
BLOCK_UNKNOWN = blok onbekend
BTC_BALANCE_ISSUE = onvoldoende Bitcoin balans
BTC_NETWORK_ISSUE = Bitcoin/ElectrumX netwerk probleem
BTC_TOO_SOON = te vroeg om Bitcoin transactie te versturen (vergrendelingstijd/gemiddelde bloktijd)
CANNOT_MINT = account kan niet munten
GROUP_UNKNOWN = onbekende groep
INVALID_ADDRESS = ongeldig adres
# Assets
INVALID_ASSET_ID = ongeldige asset ID
INVALID_CRITERIA = ongeldige zoekcriteria
INVALID_DATA = ongeldige gegevens
INVALID_HEIGHT = ongeldige blokhoogte
INVALID_NETWORK_ADDRESS = ongeldig netwerkadres
INVALID_ORDER_ID = ongeldige asset order ID
INVALID_PRIVATE_KEY = ongeldige private key
INVALID_PUBLIC_KEY = ongeldige public key
INVALID_REFERENCE = ongeldige verwijzing
# Validation
INVALID_SIGNATURE = ongeldige handtekening
# "localeLang": "nl",
### Common ###
JSON = lezen van JSON bericht gefaald
NAME_UNKNOWN = onbekende naam
INSUFFICIENT_BALANCE = insufficient balance
NON_PRODUCTION = deze API call is niet toegestaan voor productiesystemen
NO_TIME_SYNC = klok nog niet gesynchronizeerd
ORDER_UNKNOWN = onbekende asset order ID
PUBLIC_KEY_NOT_FOUND = public key niet gevonden
UNAUTHORIZED = ongeautoriseerde API call
REPOSITORY_ISSUE = repository fout
# This one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = ongeldige transactie: %s (%s)
NON_PRODUCTION = deze API call is niet toegestaan voor productiesystemen
TRANSACTION_UNKNOWN = onbekende transactie
BLOCKCHAIN_NEEDS_SYNC = blockchain dient eerst gesynchronizeerd te worden
NO_TIME_SYNC = klok nog niet gesynchronizeerd
### Validation ###
INVALID_SIGNATURE = ongeldige handtekening
INVALID_ADDRESS = ongeldig adres
INVALID_PUBLIC_KEY = ongeldige public key
INVALID_DATA = ongeldige gegevens
INVALID_NETWORK_ADDRESS = ongeldig netwerkadres
ADDRESS_UNKNOWN = account adres onbekend
INVALID_CRITERIA = ongeldige zoekcriteria
INVALID_REFERENCE = ongeldige verwijzing
TRANSFORMATION_ERROR = JSON kon niet omgezet worden in transactie
UNAUTHORIZED = ongeautoriseerde API call
INVALID_PRIVATE_KEY = ongeldige private key
INVALID_HEIGHT = ongeldige blokhoogte
CANNOT_MINT = account kan niet munten
### Blocks ###
BLOCK_UNKNOWN = blok onbekend
### Transactions ###
TRANSACTION_UNKNOWN = onbekende transactie
PUBLIC_KEY_NOT_FOUND = public key niet gevonden
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = ongeldige transactie: %s (%s)
### Naming ###
NAME_UNKNOWN = onbekende naam
### Asset ###
INVALID_ASSET_ID = ongeldige asset ID
INVALID_ORDER_ID = ongeldige asset order ID
ORDER_UNKNOWN = onbekende asset order ID
### Groups ###
GROUP_UNKNOWN = onbekende groep
### Foreign Blockchain ###
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blokchain or ElectrumX network issue
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain
FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = order amount too low
### Data ###
FILE_NOT_FOUND = file not found
NO_REPLY = peer did not reply with data

View File

@@ -1,57 +1,83 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# Keys are from api.ApiError enum
ADDRESS_UNKNOWN = неизвестная учетная запись
BLOCKCHAIN_NEEDS_SYNC = блокчейн должен сначала синхронизироваться
# Blocks
BLOCK_UNKNOWN = неизвестный блок
CANNOT_MINT = аккаунт не может чеканить
GROUP_UNKNOWN = неизвестная группа
INVALID_ADDRESS = неизвестный адрес
# Assets
INVALID_ASSET_ID = неверный идентификатор актива
INVALID_CRITERIA = неверные критерии поиска
INVALID_DATA = неверные данные
INVALID_HEIGHT = недопустимая высота блока
INVALID_NETWORK_ADDRESS = неверный сетевой адрес
INVALID_ORDER_ID = неверный идентификатор заказа актива
INVALID_PRIVATE_KEY = неверный приватный ключ
INVALID_PUBLIC_KEY = недействительный открытый ключ
INVALID_REFERENCE = неверная ссылка
# Validation
INVALID_SIGNATURE = недействительная подпись
# "localeLang": "ru",
### Common ###
JSON = не удалось разобрать сообщение json
NAME_UNKNOWN = имя неизвестно
INSUFFICIENT_BALANCE = insufficient balance
NON_PRODUCTION = этот вызов API не разрешен для производственных систем
ORDER_UNKNOWN = неизвестный идентификатор заказа актива
PUBLIC_KEY_NOT_FOUND = открытый ключ не найден
UNAUTHORIZED = вызов API не авторизован
REPOSITORY_ISSUE = ошибка репозитория
TRANSACTION_INVALID = транзакция недействительна: %s (%s)
NON_PRODUCTION = этот вызов API не разрешен для производственных систем
TRANSACTION_UNKNOWN = транзакция неизвестна
BLOCKCHAIN_NEEDS_SYNC = блокчейн должен сначала синхронизироваться
NO_TIME_SYNC = no clock synchronization yet
### Validation ###
INVALID_SIGNATURE = недействительная подпись
INVALID_ADDRESS = неизвестный адрес
INVALID_PUBLIC_KEY = недействительный открытый ключ
INVALID_DATA = неверные данные
INVALID_NETWORK_ADDRESS = неверный сетевой адрес
ADDRESS_UNKNOWN = неизвестная учетная запись
INVALID_CRITERIA = неверные критерии поиска
INVALID_REFERENCE = неверная ссылка
TRANSFORMATION_ERROR = не удалось преобразовать JSON в транзакцию
UNAUTHORIZED = вызов API не авторизован
INVALID_PRIVATE_KEY = неверный приватный ключ
INVALID_HEIGHT = недопустимая высота блока
CANNOT_MINT = аккаунт не может чеканить
### Blocks ###
BLOCK_UNKNOWN = неизвестный блок
### Transactions ###
TRANSACTION_UNKNOWN = транзакция неизвестна
PUBLIC_KEY_NOT_FOUND = открытый ключ не найден
# this one is special in that caller expected to pass two additional strings, hence the two %s
TRANSACTION_INVALID = транзакция недействительна: %s (%s)
### Naming ###
NAME_UNKNOWN = имя неизвестно
### Asset ###
INVALID_ASSET_ID = неверный идентификатор актива
INVALID_ORDER_ID = неверный идентификатор заказа актива
ORDER_UNKNOWN = неизвестный идентификатор заказа актива
### Groups ###
GROUP_UNKNOWN = неизвестная группа
### Foreign Blockchain ###
FOREIGN_BLOCKCHAIN_NETWORK_ISSUE = foreign blokchain or ElectrumX network issue
FOREIGN_BLOCKCHAIN_BALANCE_ISSUE = insufficient balance on foreign blockchain
FOREIGN_BLOCKCHAIN_TOO_SOON = too soon to broadcast foreign blockchain transaction (LockTime/median block time)
### Trade Portal ###
ORDER_SIZE_TOO_SMALL = order amount too low
### Data ###
FILE_NOT_FOUND = file not found
NO_REPLY = peer did not reply with data

View File

@@ -1,12 +1,14 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
APPLYING_UPDATE_AND_RESTARTING = Applying automatic update and restarting...
AUTO_UPDATE = Auto Update
APPLYING_UPDATE_AND_RESTARTING = Applying automatic update and restarting...
BLOCK_HEIGHT = height
BUILD_VERSION = Build version
CHECK_TIME_ACCURACY = Check time accuracy
CONNECTING = Connecting
@@ -27,13 +29,6 @@ MINTING_DISABLED = NOT minting
MINTING_ENABLED = \u2714 Minting
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = Computer's clock is inaccurate!
NTP_NAG_TEXT_UNIX = Install NTP service to get an accurate clock.
NTP_NAG_TEXT_WINDOWS = Select "Synchronize clock" from menu to fix.
OPEN_UI = Open UI
PERFORMING_DB_CHECKPOINT = Saving uncommitted database changes...
@@ -42,4 +37,4 @@ SYNCHRONIZE_CLOCK = Synchronize clock
SYNCHRONIZING_BLOCKCHAIN = Synchronizing
SYNCHRONIZING_CLOCK = Synchronizing clock
SYNCHRONIZING_CLOCK = Synchronizing clock

View File

@@ -1,12 +1,14 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
APPLYING_UPDATE_AND_RESTARTING = Automaattinen päivitys käynnissä, uudelleenkäynnistys seuraa...
AUTO_UPDATE = Automaattinen päivitys
APPLYING_UPDATE_AND_RESTARTING = Automaattinen päivitys käynnissä, uudelleenkäynnistys seuraa...
BLOCK_HEIGHT = korkeus
BUILD_VERSION = Versio
CHECK_TIME_ACCURACY = Tarkista ajan tarkkuus
CONNECTING = Yhdistää
@@ -27,13 +29,6 @@ MINTING_DISABLED = EI lyö rahaa
MINTING_ENABLED = \u2714 Lyö rahaa
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = Tietokoneen kello on epätarkka!
NTP_NAG_TEXT_UNIX = Asennathan NTP-palvelun, jotta saat kellon tarkkuuden oikeaksi.
NTP_NAG_TEXT_WINDOWS = Valitse "Kellon synkronisointi" valikosta korjataksesi.
OPEN_UI = Avaa UI
PERFORMING_DB_CHECKPOINT = Tallentaa kommittoidut tietokantamuutokset...
@@ -42,4 +37,4 @@ SYNCHRONIZE_CLOCK = Synkronisoi kello
SYNCHRONIZING_BLOCKCHAIN = Synkronisoi
SYNCHRONIZING_CLOCK = Synkronisoi kelloa
SYNCHRONIZING_CLOCK = Synkronisoi kelloa

View File

@@ -0,0 +1,42 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
# Magyar myelvre forditotta: Szkíta (Scythian). 2021 Augusztus 7.
AUTO_UPDATE = Automatikus Frissítés
APPLYING_UPDATE_AND_RESTARTING = Automatikus frissítés és újraindítás alkalmazása...
BLOCK_HEIGHT = blokkmagasság
BUILD_VERSION = Verzió
CHECK_TIME_ACCURACY = Idő pontosság ellenőrzése
CONNECTING = Kapcsolódás
CONNECTION = kapcsolat
CONNECTIONS = kapcsolat
CREATING_BACKUP_OF_DB_FILES = Adatbázis fájlok biztonsági mentésének létrehozása...
DB_BACKUP = Adatbázis biztonsági mentése
DB_CHECKPOINT = Adatbázis-ellenőrzőpont
EXIT = Kilépés
MINTING_DISABLED = QORT-érmeverés jelenleg nincs folyamatban
MINTING_ENABLED = \u2714 QORT-érmeverés folyamatban
OPEN_UI = Felhasználói eszköz megnyitása
PERFORMING_DB_CHECKPOINT = Mentetlen adatbázis-módosítások mentése...
SYNCHRONIZE_CLOCK = Óra-szinkronizálás megkezdése
SYNCHRONIZING_BLOCKCHAIN = Szinkronizálás
SYNCHRONIZING_CLOCK = Óra-szinkronizálás folyamatban

View File

@@ -8,6 +8,8 @@ AUTO_UPDATE = Aggiornamento automatico
BLOCK_HEIGHT = altezza
BUILD_VERSION = Versione
CHECK_TIME_ACCURACY = Controlla la precisione dell'ora
CONNECTING = Collegando
@@ -28,13 +30,6 @@ MINTING_DISABLED = NON coniando
MINTING_ENABLED = \u2714 Coniando
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = L'orologio del computer è impreciso!
NTP_NAG_TEXT_UNIX = Installare servizio NTP per ottenere un orologio preciso.
NTP_NAG_TEXT_WINDOWS = Seleziona "Sincronizza orologio" dal menu per correggere.
OPEN_UI = Apri UI
PERFORMING_DB_CHECKPOINT = Salvataggio delle modifiche al database non salvate...
@@ -43,4 +38,4 @@ SYNCHRONIZE_CLOCK = Sincronizza orologio
SYNCHRONIZING_BLOCKCHAIN = Sincronizzando
SYNCHRONIZING_CLOCK = Sincronizzando orologio
SYNCHRONIZING_CLOCK = Sincronizzando orologio

View File

@@ -7,6 +7,8 @@ AUTO_UPDATE = Automatische Update
BLOCK_HEIGHT = hoogte
BUILD_VERSION = Versie
CHECK_TIME_ACCURACY = Controleer accuraatheid van de tijd
CONNECTING = Verbinden
@@ -27,13 +29,6 @@ MINTING_DISABLED = NIET muntend
MINTING_ENABLED = \u2714 Muntend
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = Klok van de computer is inaccuraat!
NTP_NAG_TEXT_UNIX = Installeer NTP service voor een accurate klok.
NTP_NAG_TEXT_WINDOWS = Selecteer "Synchronizeer klok" uit het menu om op te lossen.
OPEN_UI = Open UI
PERFORMING_DB_CHECKPOINT = Nieuwe veranderingen aan database worden opgeslagen...
@@ -42,4 +37,4 @@ SYNCHRONIZE_CLOCK = Synchronizeer klok
SYNCHRONIZING_BLOCKCHAIN = Aan het synchronizeren
SYNCHRONIZING_CLOCK = Klok wordt gesynchronizeerd
SYNCHRONIZING_CLOCK = Klok wordt gesynchronizeerd

View File

@@ -7,6 +7,8 @@ AUTO_UPDATE = Автоматическое обновление
BLOCK_HEIGHT = Высота блока
BUILD_VERSION = Build version
CHECK_TIME_ACCURACY = Проверка точного времени
CONNECTING = Подключение
@@ -25,17 +27,12 @@ MINTING_DISABLED = Чеканка отключена
MINTING_ENABLED = Чеканка активна
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = Часы компьютера неточны!
NTP_NAG_TEXT_UNIX = Установите службу NTP, чтобы получить точное время
NTP_NAG_TEXT_WINDOWS = Выберите "Синхронизация времени" из меню, чтобы исправить
OPEN_UI = Открыть пользовательский интерфейс
PERFORMING_DB_CHECKPOINT = Saving uncommitted database changes...
SYNCHRONIZE_CLOCK = Синхронизировать время
SYNCHRONIZING_BLOCKCHAIN = Синхронизация цепи
SYNCHRONIZING_CLOCK = Проверка времени
SYNCHRONIZING_CLOCK = Проверка времени

View File

@@ -1,31 +1,40 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
AUTO_UPDATE = Auto Update
APPLYING_UPDATE_AND_RESTARTING = Applying automatic update and restarting...
BLOCK_HEIGHT = 区块高度
BUILD_VERSION = Build version
CHECK_TIME_ACCURACY = 检查时间准确性
CONNECTING = Connecting
CONNECTION = 个链接
CONNECTIONS = 个链接
CREATING_BACKUP_OF_DB_FILES = Creating backup of database files...
DB_BACKUP = Database Backup
DB_CHECKPOINT = Database Checkpoint
EXIT = 退出核心
MINTING_DISABLED = 没有铸币
MINTING_ENABLED = ✔ 铸币
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = 电脑的时间不准确!
NTP_NAG_TEXT_UNIX = 安装NTP服务以获取准确的时间。
NTP_NAG_TEXT_WINDOWS = 从菜单中选择“同步时钟”进行修复。
OPEN_UI = 开启Qortal界面
PERFORMING_DB_CHECKPOINT = Saving uncommitted database changes...
SYNCHRONIZE_CLOCK = 同步时钟
SYNCHRONIZING_BLOCKCHAIN = 正在同步区块链
SYNCHRONIZING_CLOCK = 正在同步时钟
SYNCHRONIZING_CLOCK = 正在同步时钟

View File

@@ -1,31 +1,40 @@
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
# SysTray pop-up menu
AUTO_UPDATE = Auto Update
APPLYING_UPDATE_AND_RESTARTING = Applying automatic update and restarting...
BLOCK_HEIGHT = 區塊高度
BUILD_VERSION = Build version
CHECK_TIME_ACCURACY = 檢查時間準確性
CONNECTING = Connecting
CONNECTION = 個鏈接
CONNECTIONS = 個鏈接
CREATING_BACKUP_OF_DB_FILES = Creating backup of database files...
DB_BACKUP = Database Backup
DB_CHECKPOINT = Database Checkpoint
EXIT = 退出核心
MINTING_DISABLED = 沒有鑄幣
MINTING_ENABLED = ✔ 鑄幣
# Nagging about lack of NTP time sync
NTP_NAG_CAPTION = 電腦的時間不準確!
NTP_NAG_TEXT_UNIX = 安装NTP服務以獲取準確的時間。
NTP_NAG_TEXT_WINDOWS = 從菜單中選擇“同步時鐘”進行修復。
OPEN_UI = 開啓Qortal界面
PERFORMING_DB_CHECKPOINT = Saving uncommitted database changes...
SYNCHRONIZE_CLOCK = 同步時鐘
SYNCHRONIZING_BLOCKCHAIN = 正在同步區塊鏈
SYNCHRONIZING_CLOCK = 正在同步時鐘
SYNCHRONIZING_CLOCK = 正在同步時鐘

View File

@@ -1,161 +1,44 @@
ACCOUNT_ALREADY_EXISTS = account already exists
ACCOUNT_CANNOT_REWARD_SHARE = account cannot reward-share
ALREADY_GROUP_ADMIN = already group admin
ALREADY_GROUP_MEMBER = already group member
ALREADY_VOTED_FOR_THAT_OPTION = already voted for that option
ASSET_ALREADY_EXISTS = asset already exists
ASSET_DOES_NOT_EXIST = asset does not exist
ASSET_DOES_NOT_MATCH_AT = asset does not match AT's asset
ASSET_NOT_SPENDABLE = asset is not spendable
AT_ALREADY_EXISTS = AT already exists
AT_IS_FINISHED = AT has finished
AT_UNKNOWN = AT unknown
BANNED_FROM_GROUP = banned from group
BAN_EXISTS = ban already exists
BAN_UNKNOWN = ban unknown
BUYER_ALREADY_OWNER = buyer is already owner
CHAT = CHAT transactions are never valid for inclusion into blocks
CLOCK_NOT_SYNCED = clock not synchronized
DUPLICATE_OPTION = duplicate option
GROUP_ALREADY_EXISTS = group already exists
GROUP_APPROVAL_DECIDED = group-approval already decided
GROUP_APPROVAL_NOT_REQUIRED = group-approval not required
GROUP_DOES_NOT_EXIST = group does not exist
GROUP_ID_MISMATCH = group ID mismatch
GROUP_OWNER_CANNOT_LEAVE = group owner cannot leave group
HAVE_EQUALS_WANT = have-asset is the same as want-asset
INCORRECT_NONCE = incorrect PoW nonce
INSUFFICIENT_FEE = insufficient fee
OK = OK
INVALID_ADDRESS = invalid address
INVALID_AMOUNT = invalid amount
INVALID_ASSET_OWNER = invalid asset owner
INVALID_AT_TRANSACTION = invalid AT transaction
INVALID_AT_TYPE_LENGTH = invalid AT 'type' length
INVALID_CREATION_BYTES = invalid creation bytes
INVALID_DATA_LENGTH = invalid data length
INVALID_DESCRIPTION_LENGTH = invalid description length
INVALID_GROUP_APPROVAL_THRESHOLD = invalid group-approval threshold
INVALID_GROUP_BLOCK_DELAY = invalid group-approval block delay
INVALID_GROUP_ID = invalid group ID
INVALID_GROUP_OWNER = invalid group owner
INVALID_LIFETIME = invalid lifetime
INVALID_NAME_LENGTH = invalid name length
INVALID_NAME_OWNER = invalid name owner
INVALID_OPTIONS_COUNT = invalid options count
INVALID_OPTION_LENGTH = invalid options length
INVALID_ORDER_CREATOR = invalid order creator
INVALID_PAYMENTS_COUNT = invalid payments count
INVALID_PUBLIC_KEY = invalid public key
INVALID_QUANTITY = invalid quantity
INVALID_REFERENCE = invalid reference
INVALID_RETURN = invalid return
INVALID_REWARD_SHARE_PERCENT = invalid reward-share percent
INVALID_SELLER = invalid seller
INVALID_TAGS_LENGTH = invalid 'tags' length
INVALID_TX_GROUP_ID = invalid transaction group ID
INVALID_VALUE_LENGTH = invalid 'value' length
INVITE_UNKNOWN = group invite unknown
JOIN_REQUEST_EXISTS = group join request already exists
MAXIMUM_REWARD_SHARES = already at maximum number of reward-shares for this account
MISSING_CREATOR = missing creator
MULTIPLE_NAMES_FORBIDDEN = multiple registered names per account is forbidden
NAME_ALREADY_FOR_SALE = name already for sale
NAME_ALREADY_REGISTERED = name already registered
NAME_DOES_NOT_EXIST = name does not exist
NAME_NOT_FOR_SALE = name is not for sale
NAME_NOT_NORMALIZED = name not in Unicode 'normalized' form
NEGATIVE_AMOUNT = invalid/negative amount
NEGATIVE_FEE = invalid/negative fee
NEGATIVE_PRICE = invalid/negative price
NOT_GROUP_ADMIN = account is not a group admin
NOT_GROUP_MEMBER = account is not a group member
NOT_MINTING_ACCOUNT = account cannot mint
NOT_YET_RELEASED = feature not yet released
NO_BALANCE = insufficient balance
NO_BLOCKCHAIN_LOCK = node's blockchain currently busy
INVALID_REFERENCE = invalid reference
NO_FLAG_PERMISSION = account does not have that permission
INVALID_NAME_LENGTH = invalid name length
OK = OK
INVALID_VALUE_LENGTH = invalid 'value' length
ORDER_ALREADY_CLOSED = asset trade order is already closed
NAME_ALREADY_REGISTERED = name already registered
ORDER_DOES_NOT_EXIST = asset trade order does not exist
NAME_DOES_NOT_EXIST = name does not exist
INVALID_NAME_OWNER = invalid name owner
NAME_ALREADY_FOR_SALE = name already for sale
NAME_NOT_FOR_SALE = name is not for sale
BUYER_ALREADY_OWNER = buyer is already owner
INVALID_AMOUNT = invalid amount
INVALID_SELLER = invalid seller
NAME_NOT_NORMALIZED = name not in Unicode 'normalized' form
INVALID_DESCRIPTION_LENGTH = invalid description length
INVALID_OPTIONS_COUNT = invalid options count
INVALID_OPTION_LENGTH = invalid options length
DUPLICATE_OPTION = duplicate option
POLL_ALREADY_EXISTS = poll already exists
@@ -163,22 +46,146 @@ POLL_DOES_NOT_EXIST = poll does not exist
POLL_OPTION_DOES_NOT_EXIST = poll option does not exist
PUBLIC_KEY_UNKNOWN = public key unknown
ALREADY_VOTED_FOR_THAT_OPTION = already voted for that option
REWARD_SHARE_UNKNOWN = reward-share unknown
INVALID_DATA_LENGTH = invalid data length
SELF_SHARE_EXISTS = self-share (reward-share) already exists
INVALID_QUANTITY = invalid quantity
TIMESTAMP_TOO_NEW = timestamp too new
ASSET_DOES_NOT_EXIST = asset does not exist
INVALID_RETURN = invalid return
HAVE_EQUALS_WANT = have-asset is the same as want-asset
ORDER_DOES_NOT_EXIST = asset trade order does not exist
INVALID_ORDER_CREATOR = invalid order creator
INVALID_PAYMENTS_COUNT = invalid payments count
NEGATIVE_PRICE = invalid/negative price
INVALID_CREATION_BYTES = invalid creation bytes
INVALID_TAGS_LENGTH = invalid 'tags' length
INVALID_AT_TYPE_LENGTH = invalid AT 'type' length
INVALID_AT_TRANSACTION = invalid AT transaction
INSUFFICIENT_FEE = insufficient fee
ASSET_DOES_NOT_MATCH_AT = asset does not match AT's asset
ASSET_ALREADY_EXISTS = asset already exists
MISSING_CREATOR = missing creator
TIMESTAMP_TOO_OLD = timestamp too old
TIMESTAMP_TOO_NEW = timestamp too new
TOO_MANY_UNCONFIRMED = account has too many unconfirmed transactions pending
TRANSACTION_ALREADY_CONFIRMED = transaction has already confirmed
GROUP_ALREADY_EXISTS = group already exists
TRANSACTION_ALREADY_EXISTS = transaction already exists
GROUP_DOES_NOT_EXIST = group does not exist
INVALID_GROUP_OWNER = invalid group owner
ALREADY_GROUP_MEMBER = already group member
GROUP_OWNER_CANNOT_LEAVE = group owner cannot leave group
NOT_GROUP_MEMBER = account is not a group member
ALREADY_GROUP_ADMIN = already group admin
NOT_GROUP_ADMIN = account is not a group admin
INVALID_LIFETIME = invalid lifetime
INVITE_UNKNOWN = group invite unknown
BAN_EXISTS = ban already exists
BAN_UNKNOWN = ban unknown
BANNED_FROM_GROUP = banned from group
JOIN_REQUEST_EXISTS = group join request already exists
INVALID_GROUP_APPROVAL_THRESHOLD = invalid group-approval threshold
GROUP_ID_MISMATCH = group ID mismatch
INVALID_GROUP_ID = invalid group ID
TRANSACTION_UNKNOWN = transaction unknown
TRANSACTION_ALREADY_CONFIRMED = transaction has already confirmed
INVALID_TX_GROUP_ID = invalid transaction group ID
TX_GROUP_ID_MISMATCH = transaction's group ID does not match
MULTIPLE_NAMES_FORBIDDEN = multiple registered names per account is forbidden
INVALID_ASSET_OWNER = invalid asset owner
AT_IS_FINISHED = AT has finished
NO_FLAG_PERMISSION = account does not have that permission
NOT_MINTING_ACCOUNT = account cannot mint
REWARD_SHARE_UNKNOWN = reward-share unknown
INVALID_REWARD_SHARE_PERCENT = invalid reward-share percent
PUBLIC_KEY_UNKNOWN = public key unknown
INVALID_PUBLIC_KEY = invalid public key
AT_UNKNOWN = AT unknown
AT_ALREADY_EXISTS = AT already exists
GROUP_APPROVAL_NOT_REQUIRED = group-approval not required
GROUP_APPROVAL_DECIDED = group-approval already decided
MAXIMUM_REWARD_SHARES = already at maximum number of reward-shares for this account
TRANSACTION_ALREADY_EXISTS = transaction already exists
NO_BLOCKCHAIN_LOCK = node's blockchain currently busy
ORDER_ALREADY_CLOSED = asset trade order is already closed
CLOCK_NOT_SYNCED = clock not synchronized
ASSET_NOT_SPENDABLE = asset is not spendable
ACCOUNT_CANNOT_REWARD_SHARE = account cannot reward-share
SELF_SHARE_EXISTS = self-share (reward-share) already exists
ACCOUNT_ALREADY_EXISTS = account already exists
INVALID_GROUP_BLOCK_DELAY = invalid group-approval block delay
INCORRECT_NONCE = incorrect PoW nonce
INVALID_TIMESTAMP_SIGNATURE = invalid timestamp signature
ADDRESS_IN_BLACKLIST = this address is in your blacklist
ADDRESS_ABOVE_RATE_LIMIT = address reached specified rate limit
DUPLICATE_MESSAGE = address sent duplicate message
INVALID_BUT_OK = invalid but OK
NOT_YET_RELEASED = feature not yet released

View File

@@ -1,4 +1,3 @@
ACCOUNT_ALREADY_EXISTS = tili on jo olemassa
ACCOUNT_CANNOT_REWARD_SHARE = tili ei voi palkinto-jakaa
@@ -31,8 +30,6 @@ BAN_UNKNOWN = tuntematon eväys
BUYER_ALREADY_OWNER = ostaja on jo omistaja
CHAT = CHATin transaktiot eivät koskaan ole kelvollisia sisällytettäväksi lohkoihin
CLOCK_NOT_SYNCED = kello on synkronisoimatta
DUPLICATE_OPTION = kahdennettu valinta
@@ -182,3 +179,13 @@ TRANSACTION_ALREADY_EXISTS = transaktio on jo olemassa
TRANSACTION_UNKNOWN = tuntematon transaktio
TX_GROUP_ID_MISMATCH = transaktion ryhmä-ID:n vastaavuusvirhe
ADDRESS_IN_BLACKLIST = this address is in your blacklist
ADDRESS_ABOVE_RATE_LIMIT = address reached specified rate limit
DUPLICATE_MESSAGE = address sent duplicate message
INVALID_TIMESTAMP_SIGNATURE = Invalid timestamp signature
INVALID_BUT_OK = Invalid but OK

View File

@@ -0,0 +1,193 @@
# Magyar myelvre forditotta: Szkíta (Scythian). 2021 Augusztus 7.
OK = OK
INVALID_ADDRESS = érvénytelen név vagy cím
NEGATIVE_AMOUNT = negatív összeg
NEGATIVE_FEE = érvénytelen/negatív tranzakciós díj
NO_BALANCE = elégtelen egyenleg
INVALID_REFERENCE = érvénytelen hivatkozás
INVALID_NAME_LENGTH = érvénytelen névhossz
INVALID_VALUE_LENGTH = érvénytelen értékhossz
NAME_ALREADY_REGISTERED = ez a név már regisztrált
NAME_DOES_NOT_EXIST = ez a név nem létezik
INVALID_NAME_OWNER = érvénytelen név tulajdonos
NAME_ALREADY_FOR_SALE = ez a név már eladó
NAME_NOT_FOR_SALE = ez a név nem eladó
BUYER_ALREADY_OWNER = ez a vevő már a tulajdonos
INVALID_AMOUNT = érvénytelen összeg
INVALID_SELLER = érvénytelen eladó
NAME_NOT_NORMALIZED = ez a név nincs "normalizált" Unicode formátumban
INVALID_DESCRIPTION_LENGTH = érvénytelen leíráshossz
INVALID_OPTIONS_COUNT = invalid options count
INVALID_OPTION_LENGTH = érvénytelen opciókszám
DUPLICATE_OPTION = ez a lehetőség már létezik
POLL_ALREADY_EXISTS = ez a szavazás már létezik
POLL_DOES_NOT_EXIST = ez a szavazás nem létezik
POLL_OPTION_DOES_NOT_EXIST = ez a szavazási lehetőség nem létezik
ALREADY_VOTED_FOR_THAT_OPTION = erre a lehetőségre már szavaztál
INVALID_DATA_LENGTH = érvénytelen adathossz
INVALID_QUANTITY = érvénytelen mennyiség
ASSET_DOES_NOT_EXIST = tőke nem létezik
INVALID_RETURN = érvénytelen csere tőke
HAVE_EQUALS_WANT = saját tőke egyenlő a csere tőkével
ORDER_DOES_NOT_EXIST = tőke rendelés nem létezik
INVALID_ORDER_CREATOR = érvénytelen rendelés létrehozó
INVALID_PAYMENTS_COUNT = a kifizetések száma érvénytelen
NEGATIVE_PRICE = érvénytelen/negatív ár
INVALID_CREATION_BYTES = érvénytelen létrehozási bájtok
INVALID_TAGS_LENGTH = érvénytelen cimkehossz
INVALID_AT_TYPE_LENGTH = érvénytelen AT "típus" hossz
INVALID_AT_TRANSACTION = érvénytelen AT tranzakció
INSUFFICIENT_FEE = elégtelen díj
ASSET_DOES_NOT_MATCH_AT = a tőke nem egyezik az AT tőkéjével
ASSET_ALREADY_EXISTS = ez a tőke már létezik
MISSING_CREATOR = hiányzó létrehozó
TIMESTAMP_TOO_OLD = időbélyeg túl régi
TIMESTAMP_TOO_NEW = időbélyeg túl korai
TOO_MANY_UNCONFIRMED = ennek a fióknak túl sok meg nem erősített tranzakciója van folyamatban
GROUP_ALREADY_EXISTS = ez a csoport már létezik
GROUP_DOES_NOT_EXIST = ez a csoport nem létezik
INVALID_GROUP_OWNER = érvénytelen csoport tulajdonos
ALREADY_GROUP_MEMBER = már csoporttag
GROUP_OWNER_CANNOT_LEAVE = a csoport tulajdonos nem tudja elhagyni a csoportot
NOT_GROUP_MEMBER = ez a tag nem csoporttag
ALREADY_GROUP_ADMIN = már csoport adminisztrátor
NOT_GROUP_ADMIN = ez a tag nem csoport adminisztrátor
INVALID_LIFETIME = érvénytelen élettartam
INVITE_UNKNOWN = ismeretlen csoport meghívás
BAN_EXISTS = már ki van tiltva
BAN_UNKNOWN = kitiltás nem létezik
BANNED_FROM_GROUP = ki van tiltva a csoportból
JOIN_REQUEST_EXISTS = a csoporthoz való csatlakozási kérelem már megtöretént
INVALID_GROUP_APPROVAL_THRESHOLD = érvénytelen jóváhagyási küszöbérték
GROUP_ID_MISMATCH = csoportazonosító nem egyezik
INVALID_GROUP_ID = csoportazonosító érvénytelen
TRANSACTION_UNKNOWN = ismeretlen tranzakció
TRANSACTION_ALREADY_CONFIRMED = ez a tranzakció már meg van erősítve
INVALID_TX_GROUP_ID = a tranzakció csoportazonosítója érvénytelen
TX_GROUP_ID_MISMATCH = a tranzakció csoportazonosítója nem egyezik
MULTIPLE_NAMES_FORBIDDEN = fiókonként több név regisztrálása tilos
INVALID_ASSET_OWNER = érvénytelen tőke tulajdonos
AT_IS_FINISHED = az AT végzett
NO_FLAG_PERMISSION = ez a fiók nem rendelkezik ezzel az engedéllyel
NOT_MINTING_ACCOUNT = ez a fiók nem tud QORT-ot verni
REWARD_SHARE_UNKNOWN = ez a jutalék-megosztás ismeretlen
INVALID_REWARD_SHARE_PERCENT = ez a jutalék-megosztási arány érvénytelen
PUBLIC_KEY_UNKNOWN = ismeretlen nyilvános kulcs
INVALID_PUBLIC_KEY = érvénytelen nyilvános kulcs
AT_UNKNOWN = az AT ismeretlen
AT_ALREADY_EXISTS = az AT már létezik
GROUP_APPROVAL_NOT_REQUIRED = csoport általi jóváhagyás nem szükséges
GROUP_APPROVAL_DECIDED = csoport általi jóváhagyás el van döntve
MAXIMUM_REWARD_SHARES = ez a fiókcím már elérte a maximális lehetséges jutalék-megosztási részesedést
TRANSACTION_ALREADY_EXISTS = ez a tranzakció már létezik
NO_BLOCKCHAIN_LOCK = csomópont blokklánca jelenleg elfoglalt
ORDER_ALREADY_CLOSED = ez a tőke értékesítés már befejeződött
CLOCK_NOT_SYNCED = az óra nincs szinkronizálva
ASSET_NOT_SPENDABLE = ez a tőke nem értékesíthető
ACCOUNT_CANNOT_REWARD_SHARE = ez a fiók nem vehet részt jutalék-megosztásban
SELF_SHARE_EXISTS = önrészes jutalék-megosztás már létezik
ACCOUNT_ALREADY_EXISTS = ez a fiók már létezik
INVALID_GROUP_BLOCK_DELAY = invalid group-approval block delay
INCORRECT_NONCE = helytelen Proof-of-Work Nonce
INVALID_TIMESTAMP_SIGNATURE = érvénytelen időbélyeg aláírás
ADDRESS_IN_BLACKLIST = ez a fiókcím a fekete listádon van
ADDRESS_ABOVE_RATE_LIMIT = ez a cím elérte a megengedett mérték korlátot
DUPLICATE_MESSAGE = ez a cím duplikált üzenetet küldött
INVALID_BUT_OK = érvénytelen de elfogadva
NOT_YET_RELEASED = ez a funkció még nem került kiadásra

View File

@@ -32,8 +32,6 @@ BAN_UNKNOWN = divieto sconosciuto
BUYER_ALREADY_OWNER = l'acquirente è già proprietario
CHAT = Le transazioni CHAT non sono mai valide per l'inclusione nei blocchi
CLOCK_NOT_SYNCED = orologio non sincronizzato
DUPLICATE_OPTION = opzione duplicata
@@ -183,3 +181,13 @@ TRANSACTION_ALREADY_EXISTS = la transazione già esiste
TRANSACTION_UNKNOWN = transazione sconosciuta
TX_GROUP_ID_MISMATCH = identificazione di gruppo della transazione non corrisponde
ADDRESS_IN_BLACKLIST = this address is in your blacklist
ADDRESS_ABOVE_RATE_LIMIT = address reached specified rate limit
DUPLICATE_MESSAGE = address sent duplicate message
INVALID_TIMESTAMP_SIGNATURE = Invalid timestamp signature
INVALID_BUT_OK = Invalid but OK

View File

@@ -1,4 +1,3 @@
ACCOUNT_ALREADY_EXISTS = account bestaat al
ACCOUNT_CANNOT_REWARD_SHARE = account kan geen beloningen delen
@@ -31,8 +30,6 @@ BAN_UNKNOWN = ban onbekend
BUYER_ALREADY_OWNER = koper is al eigenaar
CHAT = CHAT transacties zijn nooit geldig voor opname in blokken
CLOCK_NOT_SYNCED = klok is niet gesynchronizeerd
DUPLICATE_OPTION = dubbele optie
@@ -182,3 +179,13 @@ TRANSACTION_ALREADY_EXISTS = transactie bestaat al
TRANSACTION_UNKNOWN = transactie onbekend
TX_GROUP_ID_MISMATCH = groep-ID van transactie matcht niet
ADDRESS_IN_BLACKLIST = this address is in your blacklist
ADDRESS_ABOVE_RATE_LIMIT = address reached specified rate limit
DUPLICATE_MESSAGE = address sent duplicate message
INVALID_TIMESTAMP_SIGNATURE = Invalid timestamp signature
INVALID_BUT_OK = Invalid but OK

View File

@@ -1,4 +1,3 @@
ACCOUNT_ALREADY_EXISTS = аккаунт уже существует
ACCOUNT_CANNOT_REWARD_SHARE = аккаунт не может делиться вознаграждением
@@ -174,3 +173,13 @@ TRANSACTION_ALREADY_EXISTS = транзакция существует
TRANSACTION_UNKNOWN = неизвестная транзакция
TX_GROUP_ID_MISMATCH = не соответствие идентификатора группы c хэш транзации
ADDRESS_IN_BLACKLIST = this address is in your blacklist
ADDRESS_ABOVE_RATE_LIMIT = address reached specified rate limit
DUPLICATE_MESSAGE = address sent duplicate message
INVALID_TIMESTAMP_SIGNATURE = Invalid timestamp signature
INVALID_BUT_OK = Invalid but OK

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 187 KiB

View File

@@ -14,9 +14,9 @@ public class CheckTranslations {
private static final String[] SUPPORTED_LANGS = new String[] { "en", "de", "zh", "ru" };
private static final Set<String> SYSTRAY_KEYS = Set.of("AUTO_UPDATE", "APPLYING_UPDATE_AND_RESTARTING", "BLOCK_HEIGHT",
"CHECK_TIME_ACCURACY", "CONNECTING", "CONNECTION", "CONNECTIONS", "CREATING_BACKUP_OF_DB_FILES", "DB_BACKUP", "EXIT",
"MINTING_DISABLED", "MINTING_ENABLED", "NTP_NAG_CAPTION", "NTP_NAG_TEXT_UNIX", "NTP_NAG_TEXT_WINDOWS",
"OPEN_UI", "SYNCHRONIZE_CLOCK", "SYNCHRONIZING_BLOCKCHAIN", "SYNCHRONIZING_CLOCK");
"BUILD_VERSION", "CHECK_TIME_ACCURACY", "CONNECTING", "CONNECTION", "CONNECTIONS", "CREATING_BACKUP_OF_DB_FILES",
"DB_BACKUP", "DB_CHECKPOINT", "EXIT", "MINTING_DISABLED", "MINTING_ENABLED", "OPEN_UI", "PERFORMING_DB_CHECKPOINT",
"SYNCHRONIZE_CLOCK", "SYNCHRONIZING_BLOCKCHAIN", "SYNCHRONIZING_CLOCK");
private static String failurePrefix;

View File

@@ -75,7 +75,7 @@ public class AtRepositoryTests extends Common {
Integer testHeight = maxHeight - 2;
// Trim AT state data
repository.getATRepository().prepareForAtStateTrimming();
repository.getATRepository().rebuildLatestAtStates();
repository.getATRepository().trimAtStates(2, maxHeight, 1000);
ATStateData atStateData = repository.getATRepository().getATStateAtHeight(atAddress, testHeight);
@@ -129,7 +129,7 @@ public class AtRepositoryTests extends Common {
Integer testHeight = blockchainHeight;
// Trim AT state data
repository.getATRepository().prepareForAtStateTrimming();
repository.getATRepository().rebuildLatestAtStates();
// COMMIT to check latest AT states persist / TEMPORARY table interaction
repository.saveChanges();
@@ -280,7 +280,7 @@ public class AtRepositoryTests extends Common {
Integer testHeight = maxHeight - 2;
// Trim AT state data
repository.getATRepository().prepareForAtStateTrimming();
repository.getATRepository().rebuildLatestAtStates();
repository.getATRepository().trimAtStates(2, maxHeight, 1000);
List<ATStateData> atStates = repository.getATRepository().getBlockATStatesAtHeight(testHeight);