mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-22 04:14:18 +00:00
Merge remote-tracking branch 'kenny/master' into master-kenny3
This commit is contained in:
commit
6a5013d378
@ -2089,7 +2089,7 @@ public String finalizeUpload(
|
|||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// Streaming errors should not rethrow — just log
|
// Streaming errors should not rethrow — just log
|
||||||
LOGGER.warn(String.format("Streaming error for %s %s: %s", service, name, e.getMessage()), e);
|
LOGGER.warn(String.format("Streaming error for %s %s: %s", service, name, e.getMessage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (IOException | ApiException | DataException e) {
|
} catch (IOException | ApiException | DataException e) {
|
||||||
|
@ -1092,25 +1092,4 @@ public class AdminResource {
|
|||||||
|
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
@GET
|
|
||||||
@Path("/dbstates")
|
|
||||||
@Operation(
|
|
||||||
summary = "Get DB States",
|
|
||||||
description = "Get DB States",
|
|
||||||
responses = {
|
|
||||||
@ApiResponse(
|
|
||||||
content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = DbConnectionInfo.class)))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
public List<DbConnectionInfo> getDbConnectionsStates() {
|
|
||||||
|
|
||||||
try {
|
|
||||||
return Controller.REPOSITORY_FACTORY.getDbConnectionsStates();
|
|
||||||
} catch (Exception e) {
|
|
||||||
LOGGER.error(e.getMessage(), e);
|
|
||||||
return new ArrayList<>(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -62,7 +62,17 @@ public enum Service {
|
|||||||
|
|
||||||
// Custom validation function to require an index HTML file in the root directory
|
// Custom validation function to require an index HTML file in the root directory
|
||||||
List<String> fileNames = ArbitraryDataRenderer.indexFiles();
|
List<String> fileNames = ArbitraryDataRenderer.indexFiles();
|
||||||
String[] files = path.toFile().list();
|
List<String> files;
|
||||||
|
|
||||||
|
// single files are paackaged differently
|
||||||
|
if( path.toFile().isFile() ) {
|
||||||
|
files = new ArrayList<>(1);
|
||||||
|
files.add(path.getFileName().toString());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
files = new ArrayList<>(Arrays.asList(path.toFile().list()));
|
||||||
|
}
|
||||||
|
|
||||||
if (files != null) {
|
if (files != null) {
|
||||||
for (String file : files) {
|
for (String file : files) {
|
||||||
Path fileName = Paths.get(file).getFileName();
|
Path fileName = Paths.get(file).getFileName();
|
||||||
|
@ -1735,7 +1735,7 @@ public class Block {
|
|||||||
|
|
||||||
for (int newLevel = maximumLevel; newLevel >= 0; --newLevel)
|
for (int newLevel = maximumLevel; newLevel >= 0; --newLevel)
|
||||||
if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) {
|
if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) {
|
||||||
if (newLevel > accountData.getLevel()) {
|
if (newLevel != accountData.getLevel()) {
|
||||||
// Account has increased in level!
|
// Account has increased in level!
|
||||||
accountData.setLevel(newLevel);
|
accountData.setLevel(newLevel);
|
||||||
bumpedAccounts.put(accountData.getAddress(), newLevel);
|
bumpedAccounts.put(accountData.getAddress(), newLevel);
|
||||||
@ -2141,7 +2141,7 @@ public class Block {
|
|||||||
|
|
||||||
int blocksMintedAdjustment
|
int blocksMintedAdjustment
|
||||||
=
|
=
|
||||||
(this.blockData.getHeight() > BlockChain.getInstance().getMintedBlocksAdjustmentRemovalHeight())
|
(this.blockData.getHeight() -1 > BlockChain.getInstance().getMintedBlocksAdjustmentRemovalHeight())
|
||||||
?
|
?
|
||||||
0
|
0
|
||||||
:
|
:
|
||||||
@ -2151,7 +2151,7 @@ public class Block {
|
|||||||
|
|
||||||
for (int newLevel = maximumLevel; newLevel >= 0; --newLevel)
|
for (int newLevel = maximumLevel; newLevel >= 0; --newLevel)
|
||||||
if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) {
|
if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) {
|
||||||
if (newLevel < accountData.getLevel()) {
|
if (newLevel != accountData.getLevel()) {
|
||||||
// Account has decreased in level!
|
// Account has decreased in level!
|
||||||
accountData.setLevel(newLevel);
|
accountData.setLevel(newLevel);
|
||||||
repository.getAccountRepository().setLevel(accountData);
|
repository.getAccountRepository().setLevel(accountData);
|
||||||
|
@ -76,8 +76,6 @@ import java.util.stream.Stream;
|
|||||||
|
|
||||||
public class Controller extends Thread {
|
public class Controller extends Thread {
|
||||||
|
|
||||||
public static HSQLDBRepositoryFactory REPOSITORY_FACTORY;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// This must go before any calls to LogManager/Logger
|
// This must go before any calls to LogManager/Logger
|
||||||
System.setProperty("log4j2.formatMsgNoLookups", "true");
|
System.setProperty("log4j2.formatMsgNoLookups", "true");
|
||||||
@ -411,8 +409,8 @@ public class Controller extends Thread {
|
|||||||
|
|
||||||
LOGGER.info("Starting repository");
|
LOGGER.info("Starting repository");
|
||||||
try {
|
try {
|
||||||
REPOSITORY_FACTORY = new HSQLDBRepositoryFactory(getRepositoryUrl());
|
HSQLDBRepositoryFactory repositoryFactory = new HSQLDBRepositoryFactory(getRepositoryUrl());
|
||||||
RepositoryManager.setRepositoryFactory(REPOSITORY_FACTORY);
|
RepositoryManager.setRepositoryFactory(repositoryFactory);
|
||||||
RepositoryManager.setRequestedCheckpoint(Boolean.TRUE);
|
RepositoryManager.setRequestedCheckpoint(Boolean.TRUE);
|
||||||
|
|
||||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
@ -47,15 +47,15 @@ public class ArbitraryDataStorageManager extends Thread {
|
|||||||
|
|
||||||
private static final long DIRECTORY_SIZE_CHECK_INTERVAL = 10 * 60 * 1000L; // 10 minutes
|
private static final long DIRECTORY_SIZE_CHECK_INTERVAL = 10 * 60 * 1000L; // 10 minutes
|
||||||
|
|
||||||
/** Treat storage as full at 90% usage, to reduce risk of going over the limit.
|
/** Treat storage as full at 80% usage, to reduce risk of going over the limit.
|
||||||
* This is necessary because we don't calculate total storage values before every write.
|
* This is necessary because we don't calculate total storage values before every write.
|
||||||
* It also helps avoid a fetch/delete loop, as we will stop fetching before the hard limit.
|
* It also helps avoid a fetch/delete loop, as we will stop fetching before the hard limit.
|
||||||
* This must be lower than DELETION_THRESHOLD. */
|
* This must be lower than DELETION_THRESHOLD. */
|
||||||
private static final double STORAGE_FULL_THRESHOLD = 0.90f; // 90%
|
private static final double STORAGE_FULL_THRESHOLD = 0.8f; // 80%
|
||||||
|
|
||||||
/** Start deleting files once we reach 98% usage.
|
/** Start deleting files once we reach 90% usage.
|
||||||
* This must be higher than STORAGE_FULL_THRESHOLD in order to avoid a fetch/delete loop. */
|
* This must be higher than STORAGE_FULL_THRESHOLD in order to avoid a fetch/delete loop. */
|
||||||
public static final double DELETION_THRESHOLD = 0.98f; // 98%
|
public static final double DELETION_THRESHOLD = 0.9f; // 90%
|
||||||
|
|
||||||
private static final long PER_NAME_STORAGE_MULTIPLIER = 4L;
|
private static final long PER_NAME_STORAGE_MULTIPLIER = 4L;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.qortal.repository;
|
package org.qortal.repository;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.sql.Connection;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
public interface Repository extends AutoCloseable {
|
public interface Repository extends AutoCloseable {
|
||||||
@ -62,4 +63,5 @@ public interface Repository extends AutoCloseable {
|
|||||||
|
|
||||||
public static void attemptRecovery(String connectionUrl, String name) throws DataException {}
|
public static void attemptRecovery(String connectionUrl, String name) throws DataException {}
|
||||||
|
|
||||||
|
public Connection getConnection();
|
||||||
}
|
}
|
||||||
|
@ -468,7 +468,7 @@ public class HSQLDBCacheUtils {
|
|||||||
|
|
||||||
Thread.currentThread().setName(DB_CACHE_TIMER_TASK);
|
Thread.currentThread().setName(DB_CACHE_TIMER_TASK);
|
||||||
|
|
||||||
try (final HSQLDBRepository respository = (HSQLDBRepository) Controller.REPOSITORY_FACTORY.getRepository()) {
|
try (final Repository respository = RepositoryManager.getRepository()) {
|
||||||
fillCache(ArbitraryResourceCache.getInstance(), respository);
|
fillCache(ArbitraryResourceCache.getInstance(), respository);
|
||||||
}
|
}
|
||||||
catch( DataException e ) {
|
catch( DataException e ) {
|
||||||
@ -611,7 +611,7 @@ public class HSQLDBCacheUtils {
|
|||||||
private static int recordCurrentBalances(ConcurrentHashMap<Integer, List<AccountBalanceData>> balancesByHeight) {
|
private static int recordCurrentBalances(ConcurrentHashMap<Integer, List<AccountBalanceData>> balancesByHeight) {
|
||||||
int currentHeight;
|
int currentHeight;
|
||||||
|
|
||||||
try (final HSQLDBRepository repository = (HSQLDBRepository) Controller.REPOSITORY_FACTORY.getRepository()) {
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
|
||||||
// get current balances
|
// get current balances
|
||||||
List<AccountBalanceData> accountBalances = getAccountBalances(repository);
|
List<AccountBalanceData> accountBalances = getAccountBalances(repository);
|
||||||
@ -675,7 +675,7 @@ public class HSQLDBCacheUtils {
|
|||||||
* @param cache the cache to fill
|
* @param cache the cache to fill
|
||||||
* @param repository the data source to fill the cache with
|
* @param repository the data source to fill the cache with
|
||||||
*/
|
*/
|
||||||
public static void fillCache(ArbitraryResourceCache cache, HSQLDBRepository repository) {
|
public static void fillCache(ArbitraryResourceCache cache, Repository repository) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// ensure all data is committed in, before we query it
|
// ensure all data is committed in, before we query it
|
||||||
@ -713,7 +713,7 @@ public class HSQLDBCacheUtils {
|
|||||||
*
|
*
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
private static void fillNamepMap(ConcurrentHashMap<String, Integer> levelByName, HSQLDBRepository repository ) throws SQLException {
|
private static void fillNamepMap(ConcurrentHashMap<String, Integer> levelByName, Repository repository ) throws SQLException {
|
||||||
|
|
||||||
StringBuilder sql = new StringBuilder(512);
|
StringBuilder sql = new StringBuilder(512);
|
||||||
|
|
||||||
@ -721,7 +721,7 @@ public class HSQLDBCacheUtils {
|
|||||||
sql.append("FROM NAMES ");
|
sql.append("FROM NAMES ");
|
||||||
sql.append("INNER JOIN ACCOUNTS on owner = account ");
|
sql.append("INNER JOIN ACCOUNTS on owner = account ");
|
||||||
|
|
||||||
Statement statement = repository.connection.createStatement();
|
Statement statement = repository.getConnection().createStatement();
|
||||||
|
|
||||||
ResultSet resultSet = statement.executeQuery(sql.toString());
|
ResultSet resultSet = statement.executeQuery(sql.toString());
|
||||||
|
|
||||||
@ -744,7 +744,7 @@ public class HSQLDBCacheUtils {
|
|||||||
* @return the resources
|
* @return the resources
|
||||||
* @throws SQLException
|
* @throws SQLException
|
||||||
*/
|
*/
|
||||||
private static List<ArbitraryResourceData> getResources( HSQLDBRepository repository) throws SQLException {
|
private static List<ArbitraryResourceData> getResources( Repository repository) throws SQLException {
|
||||||
|
|
||||||
List<ArbitraryResourceData> resources = new ArrayList<>();
|
List<ArbitraryResourceData> resources = new ArrayList<>();
|
||||||
|
|
||||||
@ -756,7 +756,7 @@ public class HSQLDBCacheUtils {
|
|||||||
sql.append("LEFT JOIN ArbitraryMetadataCache USING (service, name, identifier) WHERE name IS NOT NULL");
|
sql.append("LEFT JOIN ArbitraryMetadataCache USING (service, name, identifier) WHERE name IS NOT NULL");
|
||||||
|
|
||||||
List<ArbitraryResourceData> arbitraryResources = new ArrayList<>();
|
List<ArbitraryResourceData> arbitraryResources = new ArrayList<>();
|
||||||
Statement statement = repository.connection.createStatement();
|
Statement statement = repository.getConnection().createStatement();
|
||||||
|
|
||||||
ResultSet resultSet = statement.executeQuery(sql.toString());
|
ResultSet resultSet = statement.executeQuery(sql.toString());
|
||||||
|
|
||||||
@ -822,7 +822,7 @@ public class HSQLDBCacheUtils {
|
|||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<AccountBalanceData> getAccountBalances(HSQLDBRepository repository) {
|
public static List<AccountBalanceData> getAccountBalances(Repository repository) {
|
||||||
|
|
||||||
StringBuilder sql = new StringBuilder();
|
StringBuilder sql = new StringBuilder();
|
||||||
|
|
||||||
@ -836,7 +836,7 @@ public class HSQLDBCacheUtils {
|
|||||||
LOGGER.info( "Getting account balances ...");
|
LOGGER.info( "Getting account balances ...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Statement statement = repository.connection.createStatement();
|
Statement statement = repository.getConnection().createStatement();
|
||||||
|
|
||||||
ResultSet resultSet = statement.executeQuery(sql.toString());
|
ResultSet resultSet = statement.executeQuery(sql.toString());
|
||||||
|
|
||||||
|
@ -174,6 +174,11 @@ public class HSQLDBRepository implements Repository {
|
|||||||
|
|
||||||
// Transaction COMMIT / ROLLBACK / savepoints
|
// Transaction COMMIT / ROLLBACK / savepoints
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection getConnection() {
|
||||||
|
return this.connection;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void saveChanges() throws DataException {
|
public void saveChanges() throws DataException {
|
||||||
long beforeQuery = this.slowQueryThreshold == null ? 0 : System.currentTimeMillis();
|
long beforeQuery = this.slowQueryThreshold == null ? 0 : System.currentTimeMillis();
|
||||||
|
@ -756,14 +756,14 @@ public class Settings {
|
|||||||
private void setAdditionalDefaults() {
|
private void setAdditionalDefaults() {
|
||||||
// Populate defaults for maxThreadsPerMessageType. If any are specified in settings.json, they will take priority.
|
// Populate defaults for maxThreadsPerMessageType. If any are specified in settings.json, they will take priority.
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_DATA_FILE", 5));
|
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_DATA_FILE", 5));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_DATA_FILE", 5));
|
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_DATA_FILE", 20));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_DATA", 5));
|
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_DATA", 5));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_DATA", 5));
|
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_DATA", 5));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_DATA_FILE_LIST", 50));
|
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_DATA_FILE_LIST", 50));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_DATA_FILE_LIST", 50));
|
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_DATA_FILE_LIST", 50));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_SIGNATURES", 5));
|
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_SIGNATURES", 5));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_METADATA", 5));
|
maxThreadsPerMessageType.add(new ThreadLimit("ARBITRARY_METADATA", 5));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_METADATA", 50));
|
maxThreadsPerMessageType.add(new ThreadLimit("GET_ARBITRARY_METADATA", 100));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("GET_TRANSACTION", 50));
|
maxThreadsPerMessageType.add(new ThreadLimit("GET_TRANSACTION", 50));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("TRANSACTION_SIGNATURES", 50));
|
maxThreadsPerMessageType.add(new ThreadLimit("TRANSACTION_SIGNATURES", 50));
|
||||||
maxThreadsPerMessageType.add(new ThreadLimit("TRADE_PRESENCES", 50));
|
maxThreadsPerMessageType.add(new ThreadLimit("TRADE_PRESENCES", 50));
|
||||||
|
@ -102,7 +102,7 @@ public class BuyNameTransaction extends Transaction {
|
|||||||
return ValidationResult.INVALID_AMOUNT;
|
return ValidationResult.INVALID_AMOUNT;
|
||||||
|
|
||||||
// Check buyer has enough funds
|
// Check buyer has enough funds
|
||||||
if (buyer.getConfirmedBalance(Asset.QORT) < this.buyNameTransactionData.getFee())
|
if (buyer.getConfirmedBalance(Asset.QORT) < this.buyNameTransactionData.getFee() + this.buyNameTransactionData.getAmount())
|
||||||
return ValidationResult.NO_BALANCE;
|
return ValidationResult.NO_BALANCE;
|
||||||
|
|
||||||
return ValidationResult.OK;
|
return ValidationResult.OK;
|
||||||
|
@ -53,7 +53,7 @@ public class ArbitraryIndexUtils {
|
|||||||
try {
|
try {
|
||||||
fillCache(IndexCache.getInstance());
|
fillCache(IndexCache.getInstance());
|
||||||
} catch (IOException | DataException e) {
|
} catch (IOException | DataException e) {
|
||||||
LOGGER.error(e.getMessage(), e);
|
LOGGER.warn(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -111,6 +111,8 @@ public class ArbitraryIndexUtils {
|
|||||||
|
|
||||||
indexDetails.add( new ArbitraryDataIndexDetail(indexResource.name, rank, indices.get(rank - 1), indexResource.identifier ));
|
indexDetails.add( new ArbitraryDataIndexDetail(indexResource.name, rank, indices.get(rank - 1), indexResource.identifier ));
|
||||||
}
|
}
|
||||||
|
} catch (MissingDataException e) {
|
||||||
|
LOGGER.warn( e.getMessage() );
|
||||||
} catch (InvalidFormatException e) {
|
} catch (InvalidFormatException e) {
|
||||||
LOGGER.debug("invalid format, skipping: " + indexResource);
|
LOGGER.debug("invalid format, skipping: " + indexResource);
|
||||||
} catch (UnrecognizedPropertyException e) {
|
} catch (UnrecognizedPropertyException e) {
|
||||||
@ -191,7 +193,7 @@ public class ArbitraryIndexUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getJson(String name, String identifier) throws IOException {
|
public static String getJson(String name, String identifier) throws IOException, MissingDataException {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
ArbitraryDataReader arbitraryDataReader
|
ArbitraryDataReader arbitraryDataReader
|
||||||
@ -209,7 +211,7 @@ public class ArbitraryIndexUtils {
|
|||||||
} catch (MissingDataException e) {
|
} catch (MissingDataException e) {
|
||||||
if (attempts > maxAttempts) {
|
if (attempts > maxAttempts) {
|
||||||
// Give up after 5 attempts
|
// Give up after 5 attempts
|
||||||
throw new IOException("Data unavailable. Please try again later.");
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,10 @@ import org.junit.After;
|
|||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.qortal.account.PrivateKeyAccount;
|
import org.qortal.account.PrivateKeyAccount;
|
||||||
|
import org.qortal.block.BlockChain;
|
||||||
import org.qortal.controller.BlockMinter;
|
import org.qortal.controller.BlockMinter;
|
||||||
import org.qortal.controller.OnlineAccountsManager;
|
import org.qortal.controller.OnlineAccountsManager;
|
||||||
|
import org.qortal.data.account.AccountData;
|
||||||
import org.qortal.data.account.RewardShareData;
|
import org.qortal.data.account.RewardShareData;
|
||||||
import org.qortal.repository.DataException;
|
import org.qortal.repository.DataException;
|
||||||
import org.qortal.repository.Repository;
|
import org.qortal.repository.Repository;
|
||||||
@ -15,8 +17,9 @@ import org.qortal.test.common.BlockUtils;
|
|||||||
import org.qortal.test.common.Common;
|
import org.qortal.test.common.Common;
|
||||||
import org.qortal.test.common.TestAccount;
|
import org.qortal.test.common.TestAccount;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import java.util.List;
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
public class BlocksMintedCountTests extends Common {
|
public class BlocksMintedCountTests extends Common {
|
||||||
|
|
||||||
@ -85,6 +88,121 @@ public class BlocksMintedCountTests extends Common {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLevelSetting() {
|
||||||
|
|
||||||
|
boolean exceptionThrown = false;
|
||||||
|
|
||||||
|
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||||
|
|
||||||
|
// get the Alice's reward share account
|
||||||
|
PrivateKeyAccount aliceMintingAccount = Common.getTestAccount(repository, "alice-reward-share");
|
||||||
|
|
||||||
|
// give Alice an 8 blocks minted adjustment
|
||||||
|
int blocksMintedAdjustmentForAlice = 8;
|
||||||
|
adjustMintingData(repository, "alice", blocksMintedAdjustmentForAlice);
|
||||||
|
|
||||||
|
// Confirm reward-share exists
|
||||||
|
RewardShareData aliceRewardShareData = repository.getAccountRepository().getRewardShare(aliceMintingAccount.getPublicKey());
|
||||||
|
assertNotNull(aliceRewardShareData);
|
||||||
|
|
||||||
|
// mint 40 blocks
|
||||||
|
for( int i = 0; i < 40; i++ ) {
|
||||||
|
// Create signed timestamps
|
||||||
|
OnlineAccountsManager.getInstance().ensureTestingAccountsOnline(aliceMintingAccount);
|
||||||
|
|
||||||
|
// Mint another block
|
||||||
|
BlockMinter.mintTestingBlockRetainingTimestamps(repository, aliceMintingAccount);
|
||||||
|
|
||||||
|
// assert Alice's minting data after another block minted
|
||||||
|
assertMintingData(repository, "alice", blocksMintedAdjustmentForAlice);
|
||||||
|
|
||||||
|
// orphan the block
|
||||||
|
BlockUtils.orphanLastBlock(repository);
|
||||||
|
|
||||||
|
// assert the orphaning
|
||||||
|
assertMintingData(repository, "alice", blocksMintedAdjustmentForAlice);
|
||||||
|
|
||||||
|
// mint another block to reverse the orpaning
|
||||||
|
BlockMinter.mintTestingBlockRetainingTimestamps(repository, aliceMintingAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (DataException e) {
|
||||||
|
exceptionThrown = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertFalse(exceptionThrown);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert Minting Data
|
||||||
|
*
|
||||||
|
* @param repository the data repository
|
||||||
|
* @param name the name of the minting account
|
||||||
|
* @param adjustment the blocks minted adjustment
|
||||||
|
*
|
||||||
|
* @throws DataException
|
||||||
|
*/
|
||||||
|
private static void assertMintingData(Repository repository, String name, int adjustment ) throws DataException {
|
||||||
|
|
||||||
|
// get the test account data
|
||||||
|
TestAccount testAccount = Common.getTestAccount(repository, name);
|
||||||
|
AccountData testAccountData = repository.getAccountRepository().getAccount(testAccount.getAddress());
|
||||||
|
|
||||||
|
List<Integer> blocksNeededByLevel = BlockChain.getInstance().getBlocksNeededByLevel();
|
||||||
|
|
||||||
|
// determine current height and adjustment ability
|
||||||
|
int height = repository.getBlockRepository().getBlockchainHeight();
|
||||||
|
int adjustmentRemovalHeight = BlockChain.getInstance().getMintedBlocksAdjustmentRemovalHeight();
|
||||||
|
boolean isAdjustingEnabled = height <= adjustmentRemovalHeight;
|
||||||
|
|
||||||
|
// initialize loop variables
|
||||||
|
int blocksLeft;
|
||||||
|
|
||||||
|
if( isAdjustingEnabled )
|
||||||
|
blocksLeft = testAccountData.getBlocksMinted() + adjustment;
|
||||||
|
else
|
||||||
|
blocksLeft = testAccountData.getBlocksMinted();
|
||||||
|
|
||||||
|
int index = 0;
|
||||||
|
int expectedLevel = 0;
|
||||||
|
|
||||||
|
// update expected level based on the blocks needed by level list entries
|
||||||
|
while( blocksNeededByLevel.size() > index ) {
|
||||||
|
|
||||||
|
Integer blocksNeededByThisLevel = blocksNeededByLevel.get(index);
|
||||||
|
if( blocksNeededByThisLevel <= blocksLeft ) {
|
||||||
|
expectedLevel++;
|
||||||
|
blocksLeft -= blocksNeededByThisLevel;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// print and assert the expected and derived numbers
|
||||||
|
System.out.println(String.format("height = %s,expectedLevel = %s, adjustment = %s, blocksMinted = %s", height, expectedLevel, adjustment, testAccountData.getBlocksMinted()) );
|
||||||
|
assertEquals( expectedLevel, testAccountData.getLevel() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjust Minting Data
|
||||||
|
*
|
||||||
|
* @param repository the data repository
|
||||||
|
* @param name the name of the account to adjust
|
||||||
|
* @param blocksMintedAdjustment the number of blocks to adjust
|
||||||
|
*
|
||||||
|
* @throws DataException
|
||||||
|
*/
|
||||||
|
private static void adjustMintingData(Repository repository, String name, int blocksMintedAdjustment) throws DataException {
|
||||||
|
TestAccount testAccount = Common.getTestAccount(repository, name);
|
||||||
|
AccountData testAccountData = repository.getAccountRepository().getAccount(testAccount.getAddress());
|
||||||
|
testAccountData.setBlocksMintedAdjustment(blocksMintedAdjustment);
|
||||||
|
repository.getAccountRepository().setBlocksMintedAdjustment(testAccountData);
|
||||||
|
}
|
||||||
|
|
||||||
private void testRewardShare(Repository repository, PrivateKeyAccount testRewardShareAccount, int aliceDelta, int bobDelta) throws DataException {
|
private void testRewardShare(Repository repository, PrivateKeyAccount testRewardShareAccount, int aliceDelta, int bobDelta) throws DataException {
|
||||||
// Create signed timestamps
|
// Create signed timestamps
|
||||||
OnlineAccountsManager.getInstance().ensureTestingAccountsOnline(testRewardShareAccount);
|
OnlineAccountsManager.getInstance().ensureTestingAccountsOnline(testRewardShareAccount);
|
||||||
@ -124,5 +242,4 @@ public class BlocksMintedCountTests extends Common {
|
|||||||
TestAccount testAccount = Common.getTestAccount(repository, name);
|
TestAccount testAccount = Common.getTestAccount(repository, name);
|
||||||
return repository.getAccountRepository().getAccount(testAccount.getAddress()).getBlocksMinted();
|
return repository.getAccountRepository().getAccount(testAccount.getAddress()).getBlocksMinted();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@
|
|||||||
"nullGroupMembershipHeight": 20,
|
"nullGroupMembershipHeight": 20,
|
||||||
"adminQueryFixHeight": 9999999999999,
|
"adminQueryFixHeight": 9999999999999,
|
||||||
"multipleNamesPerAccountHeight": 10,
|
"multipleNamesPerAccountHeight": 10,
|
||||||
"mintedBlocksAdjustmentRemovalHeight": 9999999999999
|
"mintedBlocksAdjustmentRemovalHeight": 27
|
||||||
},
|
},
|
||||||
"genesisInfo": {
|
"genesisInfo": {
|
||||||
"version": 4,
|
"version": 4,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user