Compare commits

..

15 Commits

Author SHA1 Message Date
catbref
2ddb1fa23e Bump version to v1.0.6 2020-03-31 08:04:35 +01:00
catbref
82f6e38adb Add PID tracking to stop.sh (thanks IceBurst) 2020-03-30 17:39:36 +01:00
catbref
00ac26cf27 Add script to build release ZIP 2020-03-30 17:39:36 +01:00
catbref
fa1aa1c8b2 Show informative page instead of "Forbidden" when user tries to access API documentation when disabled. 2020-03-30 17:39:36 +01:00
catbref
9156325ffc Reduce minimum networking requires to relax CPU usage somewhat 2020-03-30 17:39:36 +01:00
catbref
70131914b2 Auto-updates: increase checking interval & TCP timeouts
Bumped TCP timeouts for fetching auto-update from 5s (connect) and
3s (read) to 30s (connect) and 10s (read) to allow for nodes with
slower internet connections.

Increased interval between checking for auto-updates from 5 minutes
to 20 minutes to reduce load on update sources and also to reduce
the number of nodes that restart at any one time.

Obviously this new checking interval will only apply after the NEXT
auto-update...
2020-03-30 17:39:36 +01:00
catbref
bd87e6cc1a Increase retry interval and count in ApplyUpdate.
Used when checking that node has shutdown and when replacing old JAR with new update.

ApplyUpdate previously waited 5 seconds between checks/retries, for up to 5 times: 25 seconds.
Now waits 10 seconds, for up to 12 times: 120 seconds.
Hopefully this will give slower nodes enough time to shut down and prevent errors like these on Windows installs:

2020-03-24 12:05:50 INFO  ApplyUpdate:114 - Unable to replace JAR: qortal.jar: The process cannot access the file because it is being used by another process.
2020-03-30 17:39:36 +01:00
catbref
6c8e96daae Turn off repository backups by default.
They can be re-enabled by setting "repositoryBackupInterval" to a
non-zero value in settings.json. Note the value is in milliseconds!
2020-03-30 17:39:36 +01:00
catbref
cfb8f53849 Reduce DB space taken up by Blocks 2020-03-30 17:39:36 +01:00
catbref
7bb2f841ad Rip out historic account balances as they take up too much DB space. 2020-03-30 17:39:36 +01:00
catbref
558263521c Minor fix to auto-update tx publish script 2020-03-30 17:39:36 +01:00
catbref
1db8c06291 Merge pull request #2 from IceBurst/patch-1
Java Checks
2020-03-30 11:32:04 +01:00
catbref
edee08a7b5 Merge pull request #1 from nitrokrypt/master
Fixed Images
2020-03-30 11:25:34 +01:00
IceBurst
83bce3ce52 Java Checks
Validates Java is available and version is 11 or greater
2020-03-14 10:52:54 -04:00
nitrokrypt
bf288dbfc2 Fixed Images
Previous images had a small hole in the icon (probably a result of a background removal), I just filled it back in with white like it's supposed to be. Also, the previous square icons were streched into a square aspect ratio, these are unstreched.
2020-03-12 12:01:24 -05:00
25 changed files with 181 additions and 268 deletions

View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.qortal</groupId>
<artifactId>qortal</artifactId>
<version>1.0.5</version>
<version>1.0.6</version>
<packaging>jar</packaging>
<properties>
<bitcoin.version>0.15.4</bitcoin.version>

17
run.sh
View File

@@ -6,6 +6,23 @@ if [ "$USER" = "root" ]; then
exit
fi
# Validate Java is installed and the minimum version is available
MIN_JAVA_VER='11'
if command -v java > /dev/null 2>&1; then
version=$(java -version 2>&1 | awk -F '"' '/version/ {print $2}')
version=$(echo $version | cut -d'.' -f1,2)
if [ `echo "${version}>=${MIN_JAVA_VER}" | bc` -eq 1 ]; then
echo 'Passed Java version check'
else
echo 'Please upgrade your Java to version 11 or greater'
exit 1
fi
else
echo 'Java is not available, please install Java 11 or greater'
exit 1
fi
# No qortal.jar but we have a Maven built one?
# Be helpful and copy across to correct location
if [ ! -e qortal.jar -a -f target/qortal*.jar ]; then

View File

@@ -36,8 +36,8 @@ public class ApplyUpdate {
private static final String NEW_JAR_FILENAME = AutoUpdate.NEW_JAR_FILENAME;
private static final String WINDOWS_EXE_LAUNCHER = "qortal.exe";
private static final long CHECK_INTERVAL = 5 * 1000L; // ms
private static final int MAX_ATTEMPTS = 5;
private static final long CHECK_INTERVAL = 10 * 1000L; // ms
private static final int MAX_ATTEMPTS = 12;
public static void main(String[] args) {
Security.insertProviderAt(new BouncyCastleProvider(), 0);

View File

@@ -53,8 +53,8 @@ public class Account {
// Balance manipulations - assetId is 0 for QORT
public BigDecimal getBalance(long assetId, int height) throws DataException {
AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.address, assetId, height);
public BigDecimal getBalance(long assetId) throws DataException {
AccountBalanceData accountBalanceData = this.repository.getAccountRepository().getBalance(this.address, assetId);
if (accountBalanceData == null)
return BigDecimal.ZERO.setScale(8);

View File

@@ -149,8 +149,8 @@ public class ApiRequest {
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
con.setConnectTimeout(5000);
con.setReadTimeout(3000);
con.setConnectTimeout(30000);
con.setReadTimeout(10000);
ApiRequest.setConnectionSSL(con, ipAddress);
int status = con.getResponseCode();

View File

@@ -108,8 +108,19 @@ public class ApiService {
swaggerUIServlet.setInitParameter("pathInfoOnly", "true");
context.addServlet(swaggerUIServlet, "/api-documentation/*");
rewriteHandler.addRule(new RedirectPatternRule("", "/api-documentation/")); // redirect to Swagger UI start page
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/")); // redirect to Swagger UI start page
rewriteHandler.addRule(new RedirectPatternRule("", "/api-documentation/")); // redirect empty path to API docs
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/")); // redirect to add trailing slash if missing
} else {
// Simple pages that explains that API documentation is disabled
ClassLoader loader = this.getClass().getClassLoader();
ServletHolder swaggerUIServlet = new ServletHolder("api-docs-disabled", DefaultServlet.class);
swaggerUIServlet.setInitParameter("resourceBase", loader.getResource("api-docs-disabled/").toString());
swaggerUIServlet.setInitParameter("dirAllowed", "true");
swaggerUIServlet.setInitParameter("pathInfoOnly", "true");
context.addServlet(swaggerUIServlet, "/api-documentation/*");
rewriteHandler.addRule(new RedirectPatternRule("", "/api-documentation/")); // redirect empty path to API docs
rewriteHandler.addRule(new RedirectPatternRule("/api-documentation", "/api-documentation/")); // redirect to add trailing slash if missing
}
// Start server

View File

@@ -192,7 +192,7 @@ public class AddressesResource {
@Path("/balance/{address}")
@Operation(
summary = "Returns account balance",
description = "Returns account's balance, optionally of given asset and at given height",
description = "Returns account's QORT balance, or of other specified asset",
responses = {
@ApiResponse(
description = "the balance",
@@ -202,8 +202,7 @@ public class AddressesResource {
)
@ApiErrors({ApiError.INVALID_ADDRESS, ApiError.INVALID_ASSET_ID, ApiError.INVALID_HEIGHT, ApiError.REPOSITORY_ISSUE})
public BigDecimal getBalance(@PathParam("address") String address,
@QueryParam("assetId") Long assetId,
@QueryParam("height") Integer height) {
@QueryParam("assetId") Long assetId) {
if (!Crypto.isValidAddress(address))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ADDRESS);
@@ -215,12 +214,7 @@ public class AddressesResource {
else if (!repository.getAssetRepository().assetExists(assetId))
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_ASSET_ID);
if (height == null)
height = repository.getBlockRepository().getBlockchainHeight();
else if (height <= 0)
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.INVALID_HEIGHT);
return account.getBalance(assetId, height);
return account.getBalance(assetId);
} catch (ApiException e) {
throw e;
} catch (DataException e) {

View File

@@ -1466,9 +1466,6 @@ public class Block {
decreaseAccountLevels();
}
// Delete orphaned balances
this.repository.getAccountRepository().deleteBalancesFromHeight(this.blockData.getHeight());
// Delete block from blockchain
this.repository.getBlockRepository().delete(this.blockData);
this.blockData.setHeight(null);

View File

@@ -41,7 +41,7 @@ public class AutoUpdate extends Thread {
public static final String NEW_JAR_FILENAME = "new-" + JAR_FILENAME;
private static final Logger LOGGER = LogManager.getLogger(AutoUpdate.class);
private static final long CHECK_INTERVAL = 5 * 60 * 1000L; // ms
private static final long CHECK_INTERVAL = 20 * 60 * 1000L; // ms
private static final int DEV_GROUP_ID = 1;
private static final int UPDATE_SERVICE = 1;
@@ -210,7 +210,7 @@ public class AutoUpdate extends Thread {
return false; // failed - try another repo
}
} catch (IOException e) {
LOGGER.warn(String.format("Failed to fetch update from %s", repoUri));
LOGGER.warn(String.format("Failed to fetch update from %s: %s", repoUri, e.getMessage()));
return false; // failed - try another repo
}

View File

@@ -104,7 +104,6 @@ public class Controller extends Thread {
private static final Object shutdownLock = new Object();
private static final String repositoryUrlTemplate = "jdbc:hsqldb:file:%s" + File.separator + "blockchain;create=true;hsqldb.full_log_replay=true";
private static final long ARBITRARY_REQUEST_TIMEOUT = 5 * 1000L; // ms
private static final long REPOSITORY_BACKUP_PERIOD = 123 * 60 * 1000L; // ms
private static final long NTP_PRE_SYNC_CHECK_PERIOD = 5 * 1000L; // ms
private static final long NTP_POST_SYNC_CHECK_PERIOD = 5 * 60 * 1000L; // ms
private static final long DELETE_EXPIRED_INTERVAL = 5 * 60 * 1000L; // ms
@@ -127,7 +126,7 @@ public class Controller extends Thread {
private volatile BlockData chainTip = null;
private long repositoryBackupTimestamp = startTime + REPOSITORY_BACKUP_PERIOD; // ms
private long repositoryBackupTimestamp = startTime; // ms
private long ntpCheckTimestamp = startTime; // ms
private long deleteExpiredTimestamp = startTime + DELETE_EXPIRED_INTERVAL; // ms
private long onlineAccountsTasksTimestamp = startTime + ONLINE_ACCOUNTS_TASKS_INTERVAL; // ms
@@ -388,6 +387,8 @@ public class Controller extends Thread {
public void run() {
Thread.currentThread().setName("Controller");
final long repositoryBackupInterval = Settings.getInstance().getRepositoryBackupInterval();
try {
while (!isStopping) {
// Maybe update SysTray
@@ -428,9 +429,9 @@ public class Controller extends Thread {
final long requestMinimumTimestamp = now - ARBITRARY_REQUEST_TIMEOUT;
arbitraryDataRequests.entrySet().removeIf(entry -> entry.getValue().getC() < requestMinimumTimestamp);
// Give repository a chance to backup
if (now >= repositoryBackupTimestamp) {
repositoryBackupTimestamp = now + REPOSITORY_BACKUP_PERIOD;
// Give repository a chance to backup (if enabled)
if (repositoryBackupInterval > 0 && now >= repositoryBackupTimestamp + repositoryBackupInterval) {
repositoryBackupTimestamp = now + repositoryBackupInterval;
if (Settings.getInstance().getShowBackupNotification())
SysTray.getInstance().showMessage(Translator.INSTANCE.translate("SysTray", "DB_BACKUP"),

View File

@@ -96,12 +96,6 @@ public interface AccountRepository {
public AccountBalanceData getBalance(String address, long assetId) throws DataException;
/** Returns account balance data for address & assetId at (or before) passed block height. */
public AccountBalanceData getBalance(String address, long assetId, int height) throws DataException;
/** Returns per-height historic balance for address & assetId. */
public List<AccountBalanceData> getHistoricBalances(String address, long assetId) throws DataException;
public enum BalanceOrdering {
ASSET_BALANCE_ACCOUNT,
ACCOUNT_ASSET,
@@ -118,9 +112,6 @@ public interface AccountRepository {
public void delete(String address, long assetId) throws DataException;
/** Deletes orphaned balances at block height >= <tt>height</tt>. */
public int deleteBalancesFromHeight(int height) throws DataException;
// Reward-shares
public RewardShareData getRewardShare(byte[] mintingAccountPublicKey, String recipientAccount) throws DataException;

View File

@@ -327,44 +327,6 @@ public class HSQLDBAccountRepository implements AccountRepository {
}
}
@Override
public AccountBalanceData getBalance(String address, long assetId, int height) throws DataException {
String sql = "SELECT IFNULL(balance, 0) FROM HistoricAccountBalances WHERE account = ? AND asset_id = ? AND height <= ? ORDER BY height DESC LIMIT 1";
try (ResultSet resultSet = this.repository.checkedExecute(sql, address, assetId, height)) {
if (resultSet == null)
return null;
BigDecimal balance = resultSet.getBigDecimal(1).setScale(8);
return new AccountBalanceData(address, assetId, balance);
} catch (SQLException e) {
throw new DataException("Unable to fetch account balance from repository", e);
}
}
@Override
public List<AccountBalanceData> getHistoricBalances(String address, long assetId) throws DataException {
String sql = "SELECT height, balance FROM HistoricAccountBalances WHERE account = ? AND asset_id = ? ORDER BY height DESC";
List<AccountBalanceData> historicBalances = new ArrayList<>();
try (ResultSet resultSet = this.repository.checkedExecute(sql, address, assetId)) {
if (resultSet == null)
return historicBalances;
do {
int height = resultSet.getInt(1);
BigDecimal balance = resultSet.getBigDecimal(2);
historicBalances.add(new AccountBalanceData(address, assetId, balance, height));
} while (resultSet.next());
return historicBalances;
} catch (SQLException e) {
throw new DataException("Unable to fetch historic account balances from repository", e);
}
}
@Override
public List<AccountBalanceData> getAssetBalances(long assetId, Boolean excludeZero) throws DataException {
StringBuilder sql = new StringBuilder(1024);
@@ -510,19 +472,6 @@ public class HSQLDBAccountRepository implements AccountRepository {
} catch (SQLException e) {
throw new DataException("Unable to reduce account balance in repository", e);
}
// If balance is now zero, and there are no prior historic balances, then simply delete row for this address-assetId (typically during orphaning)
String deleteWhereSql = "account = ? AND asset_id = ? AND balance = 0 " + // covers "if balance now zero"
"AND (" +
"SELECT TRUE FROM HistoricAccountBalances " +
"WHERE account = ? AND asset_id = ? AND height < (SELECT height - 1 FROM NextBlockHeight) " +
"LIMIT 1" +
")";
try {
this.repository.delete("AccountBalances", deleteWhereSql, address, assetId, address, assetId);
} catch (SQLException e) {
throw new DataException("Unable to prune account balance in repository", e);
}
} else {
// We have to ensure parent row exists to satisfy foreign key constraint
try {
@@ -545,47 +494,12 @@ public class HSQLDBAccountRepository implements AccountRepository {
@Override
public void save(AccountBalanceData accountBalanceData) throws DataException {
// If balance is zero and there are no prior historic balance, then simply delete balances for this assetId (typically during orphaning)
if (accountBalanceData.getBalance().signum() == 0) {
String existsSql = "account = ? AND asset_id = ? AND height < (SELECT height - 1 FROM NextBlockHeight)"; // height prior to current block. no matches (obviously) prior to genesis block
boolean hasPriorBalances;
try {
hasPriorBalances = this.repository.exists("HistoricAccountBalances", existsSql, accountBalanceData.getAddress(), accountBalanceData.getAssetId());
} catch (SQLException e) {
throw new DataException("Unable to check for historic account balances in repository", e);
}
if (!hasPriorBalances) {
try {
this.repository.delete("AccountBalances", "account = ? AND asset_id = ?", accountBalanceData.getAddress(), accountBalanceData.getAssetId());
} catch (SQLException e) {
throw new DataException("Unable to delete account balance from repository", e);
}
/*
* I don't think we need to do this as Block.orphan() would do this for us?
try {
this.repository.delete("HistoricAccountBalances", "account = ? AND asset_id = ?", accountBalanceData.getAddress(), accountBalanceData.getAssetId());
} catch (SQLException e) {
throw new DataException("Unable to delete historic account balances from repository", e);
}
*/
return;
}
}
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountBalances");
saveHelper.bind("account", accountBalanceData.getAddress()).bind("asset_id", accountBalanceData.getAssetId()).bind("balance",
accountBalanceData.getBalance());
try {
// HistoricAccountBalances auto-updated via trigger
saveHelper.execute(this.repository);
} catch (SQLException e) {
throw new DataException("Unable to save account balance into repository", e);
@@ -599,21 +513,6 @@ public class HSQLDBAccountRepository implements AccountRepository {
} catch (SQLException e) {
throw new DataException("Unable to delete account balance from repository", e);
}
try {
this.repository.delete("HistoricAccountBalances", "account = ? AND asset_id = ?", address, assetId);
} catch (SQLException e) {
throw new DataException("Unable to delete historic account balances from repository", e);
}
}
@Override
public int deleteBalancesFromHeight(int height) throws DataException {
try {
return this.repository.delete("HistoricAccountBalances", "height >= ?", height);
} catch (SQLException e) {
throw new DataException("Unable to delete historic account balances from repository", e);
}
}
// Reward-Share

View File

@@ -956,6 +956,25 @@ public class HSQLDBDatabaseUpdates {
stmt.execute("SET FILES WRITE DELAY 5"); // only fsync() every 5 seconds
break;
case 69:
// Get rid of historic account balances as they simply use up way too much space
stmt.execute("DROP TRIGGER Historic_Account_Balance_Insert_Trigger");
stmt.execute("DROP TRIGGER Historic_Account_Balance_Update_Trigger");
stmt.execute("DROP TABLE HistoricAccountBalances");
// Reclaim space
stmt.execute("CHECKPOINT");
stmt.execute("CHECKPOINT DEFRAG");
break;
case 70:
// Reduce space used for storing online account in Blocks
stmt.execute("ALTER TABLE Blocks ALTER COLUMN online_accounts BLOB(1M)");
stmt.execute("ALTER TABLE Blocks ALTER COLUMN online_accounts_signatures BLOB(1M)");
// Reclaim space
stmt.execute("CHECKPOINT");
stmt.execute("CHECKPOINT DEFRAG");
break;
default:
// nothing to do
return false;

View File

@@ -71,6 +71,8 @@ public class Settings {
private int maxTransactionTimestampFuture = 24 * 60 * 60 * 1000; // milliseconds
/** Whether we check, fetch and install auto-updates */
private boolean autoUpdateEnabled = true;
/** How long between repository backups (ms), or 0 if disabled. */
private long repositoryBackupInterval = 0; // ms
/** Whether to show a notification when we backup repository. */
private boolean showBackupNotification = false;
@@ -79,11 +81,11 @@ public class Settings {
/** Port number for inbound peer-to-peer connections. */
private Integer listenPort;
/** Minimum number of peers to allow block minting / synchronization. */
private int minBlockchainPeers = 10;
private int minBlockchainPeers = 8;
/** Target number of outbound connections to peers we should make. */
private int minOutboundPeers = 25;
private int minOutboundPeers = 16;
/** Maximum number of peer connections we allow. */
private int maxPeers = 50;
private int maxPeers = 32;
/** Maximum number of threads for network engine. */
private int maxNetworkThreadPoolSize = 20;
@@ -371,6 +373,10 @@ public class Settings {
return this.testNtpOffset;
}
public long getRepositoryBackupInterval() {
return this.repositoryBackupInterval;
}
public boolean getShowBackupNotification() {
return this.showBackupNotification;
}

View File

@@ -0,0 +1,13 @@
<!doctype html>
<html>
<head>
<title>API Documentation disabled</title>
</head>
<body>
API documentation pages are currently disabled.
<p>
To enable, add an entry like this to your <tt>settings.json</tt> file:
<p>
<code>"apiDocumentationEnabled": true,</code>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 770 B

After

Width:  |  Height:  |  Size: 852 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

After

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 77 KiB

View File

@@ -14,15 +14,10 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.qortal.account.Account;
import org.qortal.account.PrivateKeyAccount;
import org.qortal.account.PublicKeyAccount;
import org.qortal.asset.Asset;
import org.qortal.block.BlockChain;
import org.qortal.data.account.AccountBalanceData;
import org.qortal.data.account.AccountData;
import org.qortal.data.transaction.BaseTransactionData;
import org.qortal.data.transaction.PaymentTransactionData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.RepositoryManager;
@@ -30,7 +25,6 @@ import org.qortal.repository.AccountRepository.BalanceOrdering;
import org.qortal.test.common.BlockUtils;
import org.qortal.test.common.Common;
import org.qortal.test.common.TestAccount;
import org.qortal.test.common.TransactionUtils;
public class AccountBalanceTests extends Common {
@@ -88,123 +82,6 @@ public class AccountBalanceTests extends Common {
}
}
/** Tests we can fetch initial balance when newer balance exists. */
@Test
public void testGetBalanceAtHeight() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
TestAccount alice = Common.getTestAccount(repository, "alice");
BigDecimal initialBalance = testNewerBalance(repository, alice);
// Fetch all historic balances
List<AccountBalanceData> historicBalances = repository.getAccountRepository().getHistoricBalances(alice.getAddress(), Asset.QORT);
for (AccountBalanceData historicBalance : historicBalances)
System.out.println(String.format("Balance at height %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString()));
// Fetch balance at height 1, even though newer balance exists
AccountBalanceData accountBalanceData = repository.getAccountRepository().getBalance(alice.getAddress(), Asset.QORT, 1);
BigDecimal genesisBalance = accountBalanceData.getBalance();
// Confirm genesis balance is same as initial
assertEqualBigDecimals("Genesis balance should match initial", initialBalance, genesisBalance);
}
}
/** Tests we can fetch balance with a height where no balance change occurred. */
@Test
public void testGetBalanceAtNearestHeight() throws DataException {
Random random = new Random();
byte[] publicKey = new byte[32];
random.nextBytes(publicKey);
try (final Repository repository = RepositoryManager.getRepository()) {
PublicKeyAccount recipientAccount = new PublicKeyAccount(repository, publicKey);
System.out.println(String.format("Test recipient: %s", recipientAccount.getAddress()));
// Mint a few blocks
for (int i = 0; i < 10; ++i)
BlockUtils.mintBlock(repository);
// Confirm recipient balance is zero
BigDecimal balance = recipientAccount.getConfirmedBalance(Asset.QORT);
assertEqualBigDecimals("recipient's balance should be zero", BigDecimal.ZERO, balance);
// Confirm recipient has no historic balances
List<AccountBalanceData> historicBalances = repository.getAccountRepository().getHistoricBalances(recipientAccount.getAddress(), Asset.QORT);
for (AccountBalanceData historicBalance : historicBalances)
System.err.println(String.format("Block %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString()));
assertTrue("recipient should not have historic balances yet", historicBalances.isEmpty());
// Send 1 QORT to recipient
TestAccount sendingAccount = Common.getTestAccount(repository, "alice");
pay(repository, sendingAccount, recipientAccount, BigDecimal.ONE);
// Mint some more blocks
for (int i = 0; i < 10; ++i)
BlockUtils.mintBlock(repository);
// Send more QORT to recipient
BigDecimal amount = BigDecimal.valueOf(random.nextInt(123456));
pay(repository, sendingAccount, recipientAccount, amount);
BigDecimal totalAmount = BigDecimal.ONE.add(amount);
// Mint some more blocks
for (int i = 0; i < 10; ++i)
BlockUtils.mintBlock(repository);
// Confirm recipient balance is as expected
balance = recipientAccount.getConfirmedBalance(Asset.QORT);
assertEqualBigDecimals("recipient's balance incorrect", totalAmount, balance);
historicBalances = repository.getAccountRepository().getHistoricBalances(recipientAccount.getAddress(), Asset.QORT);
for (AccountBalanceData historicBalance : historicBalances)
System.out.println(String.format("Block %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString()));
// Confirm balance as of 2 blocks ago
int height = repository.getBlockRepository().getBlockchainHeight();
balance = repository.getAccountRepository().getBalance(recipientAccount.getAddress(), Asset.QORT, height - 2).getBalance();
assertEqualBigDecimals("recipient's historic balance incorrect", totalAmount, balance);
// Confirm balance prior to last payment
balance = repository.getAccountRepository().getBalance(recipientAccount.getAddress(), Asset.QORT, height - 15).getBalance();
assertEqualBigDecimals("recipient's historic balance incorrect", BigDecimal.ONE, balance);
// Orphan blocks to before last payment
BlockUtils.orphanBlocks(repository, 10 + 5);
// Re-check balance from (now) invalid height
AccountBalanceData accountBalanceData = repository.getAccountRepository().getBalance(recipientAccount.getAddress(), Asset.QORT, height - 2);
balance = accountBalanceData.getBalance();
assertEqualBigDecimals("recipient's invalid-height balance should be one", BigDecimal.ONE, balance);
// Orphan blocks to before initial 1 QORT payment
BlockUtils.orphanBlocks(repository, 10 + 5);
// Re-check balance from (now) invalid height
accountBalanceData = repository.getAccountRepository().getBalance(recipientAccount.getAddress(), Asset.QORT, height - 2);
assertNull("recipient's invalid-height balance data should be null", accountBalanceData);
// Confirm recipient has no historic balances
historicBalances = repository.getAccountRepository().getHistoricBalances(recipientAccount.getAddress(), Asset.QORT);
for (AccountBalanceData historicBalance : historicBalances)
System.err.println(String.format("Block %d: %s", historicBalance.getHeight(), historicBalance.getBalance().toPlainString()));
assertTrue("recipient should have no remaining historic balances", historicBalances.isEmpty());
}
}
private void pay(Repository repository, PrivateKeyAccount sendingAccount, Account recipientAccount, BigDecimal amount) throws DataException {
byte[] reference = sendingAccount.getLastReference();
long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1;
int txGroupId = 0;
BigDecimal fee = BlockChain.getInstance().getUnitFee();
BaseTransactionData baseTransactionData = new BaseTransactionData(timestamp, txGroupId, reference, sendingAccount.getPublicKey(), fee, null);
TransactionData transactionData = new PaymentTransactionData(baseTransactionData, recipientAccount.getAddress(), amount);
TransactionUtils.signAndMint(repository, transactionData, sendingAccount);
}
/** Tests SQL query speed for account balance fetches. */
@Test
public void testRepositorySpeed() throws DataException, SQLException {

35
stop.sh
View File

@@ -1,10 +1,41 @@
#!/bin/sh
#!/usr/bin/env bash
# Check for color support
if [ -t 1 ]; then
ncolors=$( tput colors )
if [ -n "${ncolors}" -a "${ncolors}" -ge 8 ]; then
if normal="$( tput sgr0 )"; then
# use terminfo names
red="$( tput setaf 1 )"
green="$( tput setaf 2)"
else
# use termcap names for FreeBSD compat
normal="$( tput me )"
red="$( tput AF 1 )"
green="$( tput AF 2)"
fi
fi
fi
# Track the pid if we can find it
read pid 2>/dev/null <run.pid
is_pid_valid=$?
echo 'Calling GET /admin/stop on local Qortal node'
if curl --url http://localhost:12391/admin/stop 1>/dev/null 2>&1; then
echo "Qortal node responded and should be shutting down"
if [ "${is_pid_valid}" -eq 0 ]; then
echo -n "Monitoring for Qortal node to end"
while s=`ps -p $pid -o stat=` && [[ "$s" && "$s" != 'Z' ]]; do
echo -n .
sleep 1
done
echo
echo "${green}Qortal ended gracefully${normal}"
rm -f run.pid
fi
exit 0
else
echo "No response from Qortal node - not running?"
echo "${red}No response from Qortal node - not running?${normal}"
exit 1
fi

57
tools/build-zip.sh Executable file
View File

@@ -0,0 +1,57 @@
#!/usr/bin/env bash
set -e
saved_pwd=$PWD
# Check we are within a git repo
git_dir=$( git rev-parse --show-toplevel )
if [ -z "${git_dir}" ]; then
echo "Cannot determine top-level directory for git repo"
exit 1
fi
# Change to git top-level
cd ${git_dir}
# Check we are in 'master' branch
branch_name=$( git symbolic-ref -q HEAD )
branch_name=${branch_name##refs/heads/}
echo "Current git branch: ${branch_name}"
if [ "${branch_name}" != "master" ]; then
echo "Unexpected current branch '${branch_name}' - expecting 'master'"
exit 1
fi
# Determine project name
project=$( perl -n -e 'if (m/<artifactId>(\w+)<.artifactId>/) { print $1; exit }' pom.xml $)
if [ -z "${project}" ]; then
echo "Unable to determine project name from pom.xml?"
exit 1
fi
# Extract git tag
git_tag=$( git tag --points-at HEAD )
if [ -z "${git_tag}" ]; then
echo "Unable to extract git tag"
exit 1
fi
build_dir=/tmp/${project}
commit_ts=$( git show --no-patch --format=%cI )
/bin/rm -fr ${build_dir}
mkdir -p ${build_dir}
cp target/${project}*.jar ${build_dir}/${project}.jar
git show HEAD:log4j2.properties > ${build_dir}/log4j2.properties
git show HEAD:run.sh > ${build_dir}/run.sh
printf "{\n}\n" > ${build_dir}/settings.json
touch -d ${commit_ts%%+??:??} ${build_dir} ${build_dir}/*
rm -f ${saved_pwd}/${project}.zip
(cd ${build_dir}/..; 7z a -r -tzip ${saved_pwd}/${project}-${git_tag#v}.zip ${project}/)

View File

@@ -86,11 +86,11 @@ die("Can't convert base58 public key to hex:\n$pubkey_hex\n") unless $pubkey_hex
printf "Public key hex: %s\n", $pubkey_hex;
my $address = `curl --silent --url http://localhost:${port}/addresses/convert/${pubkey}`;
die("Can't convert base58 public key to address:\n$address\n") unless $address =~ m/^\w{34}$/;
die("Can't convert base58 public key to address:\n$address\n") unless $address =~ m/^\w{33,34}$/;
printf "Address: %s\n", $address;
my $reference = `curl --silent --url http://localhost:${port}/addresses/lastreference/${address}`;
die("Can't fetch last reference for $address:\n$reference\n") unless $reference =~ m/^\w{88}$/;
die("Can't fetch last reference for $address:\n$reference\n") unless $reference =~ m/^\w{87,88}$/;
printf "Last reference: %s\n", $reference;
my $reference_hex = `curl --silent --url http://localhost:${port}/utils/frombase58 --data ${reference}`;