mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-31 06:01:22 +00:00
Compare commits
18 Commits
blocksig
...
block-rewa
Author | SHA1 | Date | |
---|---|---|---|
|
16453ed602 | ||
|
fde68dc598 | ||
|
847e81e95c | ||
|
7918622e2e | ||
|
427fa1816d | ||
|
0c7e388463 | ||
|
be3af53011 | ||
|
414399b2a0 | ||
|
c592051a80 | ||
|
33a8f311e5 | ||
|
018c3cdcd4 | ||
|
384dffbf9a | ||
|
0306ecb03d | ||
|
e5ce732557 | ||
|
f19e0498bf | ||
|
32ec02225a | ||
|
3920933fc7 | ||
|
1fdd7f156c |
@@ -2,6 +2,7 @@
|
||||
|
||||
## TL;DR: how-to
|
||||
|
||||
* Prepare new release version (see way below for details)
|
||||
* Assuming you are in git 'master' branch, at HEAD
|
||||
* Shutdown local node if running
|
||||
* Build auto-update download: `tools/build-auto-update.sh` - uploads auto-update file into new git branch
|
||||
@@ -59,4 +60,12 @@ $ java -cp qortal.jar org.qortal.XorUpdate
|
||||
usage: XorUpdate <input-file> <output-file>
|
||||
$ java -cp qortal.jar org.qortal.XorUpdate qortal.jar qortal.update
|
||||
$
|
||||
```
|
||||
```
|
||||
|
||||
## Preparing new release version
|
||||
|
||||
* Shutdown local node
|
||||
* Modify `pom.xml` and increase version inside `<version>` tag
|
||||
* Commit new `pom.xml` and push to github, e.g. `git commit -m 'Bumped to v1.4.2' -- pom.xml; git push`
|
||||
* Tag this new commit with same version: `git tag v1.4.2`
|
||||
* Push tag up to github: `git push origin v1.4.2`
|
||||
|
Binary file not shown.
@@ -19,10 +19,10 @@
|
||||
<ROW Property="Manufacturer" Value="Qortal"/>
|
||||
<ROW Property="MsiLogging" MultiBuildValue="DefaultBuild:vp"/>
|
||||
<ROW Property="NTP_GOOD" Value="false"/>
|
||||
<ROW Property="ProductCode" Value="1033:{9BC18BE1-7E1F-4103-9B2B-EC26CF4CE974} 1049:{6DD00C11-C69C-49FD-BCAF-BE2F0A7175AA} 2052:{9898B095-DAA0-49D5-B190-0B9A14C80E90} 2057:{FAD15601-2960-4759-9CF4-047B69FF6D17} " Type="16"/>
|
||||
<ROW Property="ProductCode" Value="1033:{9EA6B58D-641E-442E-8F16-5D35B92B9F9B} 1049:{B16722F6-C2FA-418D-A9DA-69707FE2034B} 2052:{459AC873-98DC-43AC-8787-B23BAF976FF5} 2057:{CD923B63-65A0-4F2A-93B6-6362AAC8608E} " Type="16"/>
|
||||
<ROW Property="ProductLanguage" Value="2057"/>
|
||||
<ROW Property="ProductName" Value="Qortal"/>
|
||||
<ROW Property="ProductVersion" Value="1.4.1" Type="32"/>
|
||||
<ROW Property="ProductVersion" Value="1.4.2" Type="32"/>
|
||||
<ROW Property="RECONFIG_NTP" Value="true"/>
|
||||
<ROW Property="REMOVE_BLOCKCHAIN" Value="YES" Type="4"/>
|
||||
<ROW Property="REPAIR_BLOCKCHAIN" Value="YES" Type="4"/>
|
||||
@@ -174,7 +174,7 @@
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_97" ComponentId="{D5544706-E2A7-424F-AEA5-3963E355AA29}" Directory_="jdk.crypto.mscapi_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_97" Type="0"/>
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_98" ComponentId="{104DBCE8-A458-4B3E-9EFA-2D8613561619}" Directory_="jdk.dynalink_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_98" Type="0"/>
|
||||
<ROW Component="ADDITIONAL_LICENSE_INFO_99" ComponentId="{D02E3C37-E81A-48FA-9E28-B26B728AECD9}" Directory_="jdk.httpserver_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_99" Type="0"/>
|
||||
<ROW Component="AI_CustomARPName" ComponentId="{23EC1B2A-D6F8-459B-B5CA-6974919022A6}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
||||
<ROW Component="AI_CustomARPName" ComponentId="{F9375D31-26C0-4E23-948D-3570B43B7FA2}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
|
||||
<ROW Component="AI_ExePath" ComponentId="{3644948D-AE0B-41BB-9FAF-A79E70490A08}" Directory_="APPDIR" Attributes="260" KeyPath="AI_ExePath"/>
|
||||
<ROW Component="APPDIR" ComponentId="{680DFDDE-3FB4-47A5-8FF5-934F576C6F91}" Directory_="APPDIR" Attributes="0"/>
|
||||
<ROW Component="DATA_PATH" ComponentId="{EE0B6107-E244-4CDB-B195-E9038D2F1E0E}" Directory_="DATA_PATH" Attributes="0"/>
|
||||
|
2
pom.xml
2
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.qortal</groupId>
|
||||
<artifactId>qortal</artifactId>
|
||||
<version>1.4.1</version>
|
||||
<version>1.4.3</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<skipTests>true</skipTests>
|
||||
|
@@ -176,19 +176,26 @@ public class Block {
|
||||
*
|
||||
* @return account-level share "bin" from blockchain config, or null if founder / none found
|
||||
*/
|
||||
public AccountLevelShareBin getShareBin() {
|
||||
public AccountLevelShareBin getShareBin(int blockHeight) {
|
||||
if (this.isMinterFounder)
|
||||
return null;
|
||||
|
||||
final int accountLevel = this.mintingAccountData.getLevel();
|
||||
if (accountLevel <= 0)
|
||||
return null;
|
||||
return null; // level 0 isn't included in any share bins
|
||||
|
||||
final AccountLevelShareBin[] shareBinsByLevel = BlockChain.getInstance().getShareBinsByAccountLevel();
|
||||
final BlockChain blockChain = BlockChain.getInstance();
|
||||
final AccountLevelShareBin[] shareBinsByLevel = blockChain.getShareBinsByAccountLevel();
|
||||
if (accountLevel > shareBinsByLevel.length)
|
||||
return null;
|
||||
|
||||
return shareBinsByLevel[accountLevel];
|
||||
if (blockHeight < blockChain.getShareBinFixHeight())
|
||||
// Off-by-one bug still in effect
|
||||
return shareBinsByLevel[accountLevel];
|
||||
|
||||
// level 1 stored at index 0, level 2 stored at index 1, etc.
|
||||
return shareBinsByLevel[accountLevel-1];
|
||||
|
||||
}
|
||||
|
||||
public long distribute(long accountAmount, Map<String, Long> balanceChanges) {
|
||||
@@ -1783,7 +1790,7 @@ public class Block {
|
||||
// Find all accounts in share bin. getShareBin() returns null for minter accounts that are also founders, so they are effectively filtered out.
|
||||
AccountLevelShareBin accountLevelShareBin = accountLevelShareBins.get(binIndex);
|
||||
// Object reference compare is OK as all references are read-only from blockchain config.
|
||||
List<ExpandedAccount> binnedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.getShareBin() == accountLevelShareBin).collect(Collectors.toList());
|
||||
List<ExpandedAccount> binnedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.getShareBin(this.blockData.getHeight()) == accountLevelShareBin).collect(Collectors.toList());
|
||||
|
||||
// No online accounts in this bin? Skip to next one
|
||||
if (binnedAccounts.isEmpty())
|
||||
|
@@ -71,7 +71,8 @@ public class BlockChain {
|
||||
|
||||
public enum FeatureTrigger {
|
||||
atFindNextTransactionFix,
|
||||
newBlockSigHeight;
|
||||
newBlockSigHeight,
|
||||
shareBinFix;
|
||||
}
|
||||
|
||||
/** Map of which blockchain features are enabled when (height/timestamp) */
|
||||
@@ -381,6 +382,10 @@ public class BlockChain {
|
||||
return this.featureTriggers.get(FeatureTrigger.newBlockSigHeight.name()).intValue();
|
||||
}
|
||||
|
||||
public int getShareBinFixHeight() {
|
||||
return this.featureTriggers.get(FeatureTrigger.shareBinFix.name()).intValue();
|
||||
}
|
||||
|
||||
// More complex getters for aspects that change by height or timestamp
|
||||
|
||||
public long getRewardAtHeight(int ourHeight) {
|
||||
|
@@ -143,7 +143,6 @@ public class Controller extends Thread {
|
||||
private ExecutorService callbackExecutor = Executors.newFixedThreadPool(3);
|
||||
private volatile boolean notifyGroupMembershipChange = false;
|
||||
|
||||
private static final int BLOCK_CACHE_SIZE = 10; // To cover typical Synchronizer request + a few spare
|
||||
/** Latest blocks on our chain. Note: tail/last is the latest block. */
|
||||
private final Deque<BlockData> latestBlocks = new LinkedList<>();
|
||||
|
||||
@@ -152,7 +151,7 @@ public class Controller extends Thread {
|
||||
private final LinkedHashMap<ByteArray, BlockMessage> blockMessageCache = new LinkedHashMap<>() {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<ByteArray, BlockMessage> eldest) {
|
||||
return this.size() > BLOCK_CACHE_SIZE;
|
||||
return this.size() > Settings.getInstance().getBlockCacheSize();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -319,11 +318,12 @@ public class Controller extends Thread {
|
||||
// Set initial chain height/tip
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BlockData blockData = repository.getBlockRepository().getLastBlock();
|
||||
int blockCacheSize = Settings.getInstance().getBlockCacheSize();
|
||||
|
||||
synchronized (this.latestBlocks) {
|
||||
this.latestBlocks.clear();
|
||||
|
||||
for (int i = 0; i < BLOCK_CACHE_SIZE && blockData != null; ++i) {
|
||||
for (int i = 0; i < blockCacheSize && blockData != null; ++i) {
|
||||
this.latestBlocks.addFirst(blockData);
|
||||
blockData = repository.getBlockRepository().fromHeight(blockData.getHeight() - 1);
|
||||
}
|
||||
@@ -806,7 +806,10 @@ public class Controller extends Thread {
|
||||
|
||||
repository.saveChanges();
|
||||
} catch (DataException e) {
|
||||
LOGGER.error("Repository issue while deleting expired unconfirmed transactions", e);
|
||||
if (RepositoryManager.isDeadlockRelated(e))
|
||||
LOGGER.info("Couldn't delete some expired, unconfirmed transactions this round");
|
||||
else
|
||||
LOGGER.error("Repository issue while deleting expired unconfirmed transactions", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -930,6 +933,7 @@ public class Controller extends Thread {
|
||||
public void onNewBlock(BlockData latestBlockData) {
|
||||
// Protective copy
|
||||
BlockData blockDataCopy = new BlockData(latestBlockData);
|
||||
int blockCacheSize = Settings.getInstance().getBlockCacheSize();
|
||||
|
||||
synchronized (this.latestBlocks) {
|
||||
BlockData cachedChainTip = this.latestBlocks.peekLast();
|
||||
@@ -939,7 +943,7 @@ public class Controller extends Thread {
|
||||
this.latestBlocks.addLast(latestBlockData);
|
||||
|
||||
// Trim if necessary
|
||||
if (this.latestBlocks.size() >= BLOCK_CACHE_SIZE)
|
||||
if (this.latestBlocks.size() >= blockCacheSize)
|
||||
this.latestBlocks.pollFirst();
|
||||
} else {
|
||||
if (cachedChainTip != null)
|
||||
@@ -1148,6 +1152,7 @@ public class Controller extends Thread {
|
||||
ByteArray signatureAsByteArray = new ByteArray(signature);
|
||||
|
||||
BlockMessage cachedBlockMessage = this.blockMessageCache.get(signatureAsByteArray);
|
||||
int blockCacheSize = Settings.getInstance().getBlockCacheSize();
|
||||
|
||||
// Check cached latest block message
|
||||
if (cachedBlockMessage != null) {
|
||||
@@ -1190,7 +1195,7 @@ public class Controller extends Thread {
|
||||
peer.disconnect("failed to send block");
|
||||
|
||||
// If request is for a recent block, cache it
|
||||
if (getChainHeight() - blockData.getHeight() <= BLOCK_CACHE_SIZE) {
|
||||
if (getChainHeight() - blockData.getHeight() <= blockCacheSize) {
|
||||
this.stats.getBlockMessageStats.cacheFills.incrementAndGet();
|
||||
|
||||
this.blockMessageCache.put(new ByteArray(blockData.getSignature()), blockMessage);
|
||||
|
@@ -488,7 +488,9 @@ public class Synchronizer {
|
||||
peerBlockSignatures.remove(0);
|
||||
++ourHeight;
|
||||
|
||||
LOGGER.trace(String.format("Fetching block %d, sig %.8s from %s", ourHeight, Base58.encode(latestPeerSignature), peer));
|
||||
Block newBlock = this.fetchBlock(repository, peer, latestPeerSignature);
|
||||
LOGGER.trace(String.format("Fetched block %d, sig %.8s from %s", ourHeight, Base58.encode(latestPeerSignature), peer));
|
||||
|
||||
if (newBlock == null) {
|
||||
LOGGER.info(String.format("Peer %s failed to respond with block for height %d, sig %.8s", peer,
|
||||
|
@@ -23,7 +23,7 @@ public interface AcctTradeBot {
|
||||
public ResponseResult startResponse(Repository repository, ATData atData, ACCT acct,
|
||||
CrossChainTradeData crossChainTradeData, String foreignKey, String receivingAddress) throws DataException;
|
||||
|
||||
public boolean canDelete(Repository repository, TradeBotData tradeBotData);
|
||||
public boolean canDelete(Repository repository, TradeBotData tradeBotData) throws DataException;
|
||||
|
||||
public void progress(Repository repository, TradeBotData tradeBotData) throws DataException, ForeignBlockchainException;
|
||||
|
||||
|
@@ -345,11 +345,15 @@ public class BitcoinACCTv1TradeBot implements AcctTradeBot {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDelete(Repository repository, TradeBotData tradeBotData) {
|
||||
public boolean canDelete(Repository repository, TradeBotData tradeBotData) throws DataException {
|
||||
State tradeBotState = State.valueOf(tradeBotData.getStateValue());
|
||||
if (tradeBotState == null)
|
||||
return true;
|
||||
|
||||
// If the AT doesn't exist then we might as well let the user tidy up
|
||||
if (!repository.getATRepository().exists(tradeBotData.getAtAddress()))
|
||||
return true;
|
||||
|
||||
switch (tradeBotState) {
|
||||
case BOB_WAITING_FOR_AT_CONFIRM:
|
||||
case ALICE_DONE:
|
||||
@@ -378,7 +382,16 @@ public class BitcoinACCTv1TradeBot implements AcctTradeBot {
|
||||
// Attempt to fetch AT data
|
||||
atData = repository.getATRepository().fromATAddress(tradeBotData.getAtAddress());
|
||||
if (atData == null) {
|
||||
LOGGER.warn(() -> String.format("Unable to fetch trade AT %s from repository", tradeBotData.getAtAddress()));
|
||||
LOGGER.debug(() -> String.format("Unable to fetch trade AT %s from repository", tradeBotData.getAtAddress()));
|
||||
|
||||
// If it has been over 24 hours since we last updated this trade-bot entry then assume AT is never coming back
|
||||
// and so wipe the trade-bot entry
|
||||
if (tradeBotData.getTimestamp() + MAX_AT_CONFIRMATION_PERIOD > NTP.getTime()) {
|
||||
LOGGER.info(() -> String.format("AT %s has been gone for too long - deleting trade-bot entry", tradeBotData.getAtAddress()));
|
||||
repository.getCrossChainRepository().delete(tradeBotData.getTradePrivateKey());
|
||||
repository.saveChanges();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -343,11 +343,15 @@ public class LitecoinACCTv1TradeBot implements AcctTradeBot {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDelete(Repository repository, TradeBotData tradeBotData) {
|
||||
public boolean canDelete(Repository repository, TradeBotData tradeBotData) throws DataException {
|
||||
State tradeBotState = State.valueOf(tradeBotData.getStateValue());
|
||||
if (tradeBotState == null)
|
||||
return true;
|
||||
|
||||
// If the AT doesn't exist then we might as well let the user tidy up
|
||||
if (!repository.getATRepository().exists(tradeBotData.getAtAddress()))
|
||||
return true;
|
||||
|
||||
switch (tradeBotState) {
|
||||
case BOB_WAITING_FOR_AT_CONFIRM:
|
||||
case ALICE_DONE:
|
||||
@@ -376,7 +380,16 @@ public class LitecoinACCTv1TradeBot implements AcctTradeBot {
|
||||
// Attempt to fetch AT data
|
||||
atData = repository.getATRepository().fromATAddress(tradeBotData.getAtAddress());
|
||||
if (atData == null) {
|
||||
LOGGER.warn(() -> String.format("Unable to fetch trade AT %s from repository", tradeBotData.getAtAddress()));
|
||||
LOGGER.debug(() -> String.format("Unable to fetch trade AT %s from repository", tradeBotData.getAtAddress()));
|
||||
|
||||
// If it has been over 24 hours since we last updated this trade-bot entry then assume AT is never coming back
|
||||
// and so wipe the trade-bot entry
|
||||
if (tradeBotData.getTimestamp() + MAX_AT_CONFIRMATION_PERIOD > NTP.getTime()) {
|
||||
LOGGER.info(() -> String.format("AT %s has been gone for too long - deleting trade-bot entry", tradeBotData.getAtAddress()));
|
||||
repository.getCrossChainRepository().delete(tradeBotData.getTradePrivateKey());
|
||||
repository.saveChanges();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package org.qortal.repository;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public interface RepositoryFactory {
|
||||
|
||||
public boolean wasPristineAtOpen();
|
||||
@@ -12,4 +14,7 @@ public interface RepositoryFactory {
|
||||
|
||||
public void close() throws DataException;
|
||||
|
||||
// Not ideal place for this but implementating class will know the answer without having to open a new DB session
|
||||
public boolean isDeadlockException(SQLException e);
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
package org.qortal.repository;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
public abstract class RepositoryManager {
|
||||
|
||||
private static RepositoryFactory repositoryFactory = null;
|
||||
@@ -69,4 +71,10 @@ public abstract class RepositoryManager {
|
||||
repositoryFactory = oldRepositoryFactory.reopen();
|
||||
}
|
||||
|
||||
public static boolean isDeadlockRelated(Throwable e) {
|
||||
Throwable cause = e.getCause();
|
||||
|
||||
return SQLException.class.isInstance(cause) && repositoryFactory.isDeadlockException((SQLException) cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package org.qortal.repository;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -251,6 +252,14 @@ public interface TransactionRepository {
|
||||
*/
|
||||
public List<TransactionData> getUnconfirmedTransactions(TransactionType txType, byte[] creatorPublicKey) throws DataException;
|
||||
|
||||
/**
|
||||
* Returns list of unconfirmed transactions excluding specified type(s).
|
||||
*
|
||||
* @return list of transactions, or empty if none.
|
||||
* @throws DataException
|
||||
*/
|
||||
public List<TransactionData> getUnconfirmedTransactions(EnumSet<TransactionType> excludedTxTypes) throws DataException;
|
||||
|
||||
/**
|
||||
* Remove transaction from unconfirmed transactions pile.
|
||||
*
|
||||
|
@@ -59,6 +59,9 @@ public class HSQLDBRepository implements Repository {
|
||||
|
||||
private static final Object CHECKPOINT_LOCK = new Object();
|
||||
|
||||
// "serialization failure"
|
||||
private static final Integer DEADLOCK_ERROR_CODE = Integer.valueOf(-4861);
|
||||
|
||||
protected Connection connection;
|
||||
protected final Deque<Savepoint> savepoints = new ArrayDeque<>(3);
|
||||
protected boolean debugState = false;
|
||||
@@ -708,7 +711,16 @@ public class HSQLDBRepository implements Repository {
|
||||
|
||||
long beforeQuery = this.slowQueryThreshold == null ? 0 : System.currentTimeMillis();
|
||||
|
||||
int[] updateCounts = preparedStatement.executeBatch();
|
||||
int[] updateCounts = null;
|
||||
try {
|
||||
updateCounts = preparedStatement.executeBatch();
|
||||
} catch (SQLException e) {
|
||||
if (isDeadlockException(e))
|
||||
// We want more info on what other DB sessions are doing to cause this
|
||||
examineException(e);
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
if (this.slowQueryThreshold != null) {
|
||||
long queryTime = System.currentTimeMillis() - beforeQuery;
|
||||
@@ -1000,4 +1012,8 @@ public class HSQLDBRepository implements Repository {
|
||||
return Crypto.toAddress(publicKey);
|
||||
}
|
||||
|
||||
/*package*/ static boolean isDeadlockException(SQLException e) {
|
||||
return DEADLOCK_ERROR_CODE.equals(e.getErrorCode());
|
||||
}
|
||||
|
||||
}
|
@@ -148,4 +148,9 @@ public class HSQLDBRepositoryFactory implements RepositoryFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDeadlockException(SQLException e) {
|
||||
return HSQLDBRepository.isDeadlockException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -9,6 +9,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumMap;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -1181,6 +1182,51 @@ public class HSQLDBTransactionRepository implements TransactionRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TransactionData> getUnconfirmedTransactions(EnumSet<TransactionType> excludedTxTypes) throws DataException {
|
||||
StringBuilder sql = new StringBuilder(1024);
|
||||
sql.append("SELECT signature FROM UnconfirmedTransactions ");
|
||||
sql.append("JOIN Transactions USING (signature) ");
|
||||
sql.append("WHERE type NOT IN (");
|
||||
|
||||
boolean firstTxType = true;
|
||||
for (TransactionType txType : excludedTxTypes) {
|
||||
if (firstTxType)
|
||||
firstTxType = false;
|
||||
else
|
||||
sql.append(", ");
|
||||
|
||||
sql.append(txType.value);
|
||||
}
|
||||
|
||||
sql.append(")");
|
||||
sql.append("ORDER BY created_when, signature");
|
||||
|
||||
List<TransactionData> transactions = new ArrayList<>();
|
||||
|
||||
// Find transactions with no corresponding row in BlockTransactions
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql.toString())) {
|
||||
if (resultSet == null)
|
||||
return transactions;
|
||||
|
||||
do {
|
||||
byte[] signature = resultSet.getBytes(1);
|
||||
|
||||
TransactionData transactionData = this.fromSignature(signature);
|
||||
|
||||
if (transactionData == null)
|
||||
// Something inconsistent with the repository
|
||||
throw new DataException(String.format("Unable to fetch unconfirmed transaction %s from repository?", Base58.encode(signature)));
|
||||
|
||||
transactions.add(transactionData);
|
||||
} while (resultSet.next());
|
||||
|
||||
return transactions;
|
||||
} catch (SQLException | DataException e) {
|
||||
throw new DataException("Unable to fetch unconfirmed transactions from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void confirmTransaction(byte[] signature) throws DataException {
|
||||
try {
|
||||
|
@@ -89,6 +89,8 @@ public class Settings {
|
||||
private long repositoryCheckpointInterval = 60 * 60 * 1000L; // 1 hour (ms) default
|
||||
/** Whether to show a notification when we perform repository 'checkpoint'. */
|
||||
private boolean showCheckpointNotification = false;
|
||||
/* How many blocks to cache locally. Defaulted to 10, which covers a typical Synchronizer request + a few spare */
|
||||
private int blockCacheSize = 10;
|
||||
|
||||
/** How long to keep old, full, AT state data (ms). */
|
||||
private long atStatesMaxLifetime = 2 * 7 * 24 * 60 * 60 * 1000L; // milliseconds
|
||||
@@ -361,6 +363,10 @@ public class Settings {
|
||||
return this.maxTransactionTimestampFuture;
|
||||
}
|
||||
|
||||
public int getBlockCacheSize() {
|
||||
return this.blockCacheSize;
|
||||
}
|
||||
|
||||
public boolean isTestNet() {
|
||||
return this.isTestNet;
|
||||
}
|
||||
|
@@ -4,6 +4,7 @@ import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.EnumSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -605,7 +606,8 @@ public abstract class Transaction {
|
||||
public static List<TransactionData> getUnconfirmedTransactions(Repository repository) throws DataException {
|
||||
BlockData latestBlockData = repository.getBlockRepository().getLastBlock();
|
||||
|
||||
List<TransactionData> unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions();
|
||||
EnumSet<TransactionType> excludedTxTypes = EnumSet.of(TransactionType.CHAT, TransactionType.PRESENCE);
|
||||
List<TransactionData> unconfirmedTransactions = repository.getTransactionRepository().getUnconfirmedTransactions(excludedTxTypes);
|
||||
|
||||
unconfirmedTransactions.sort(getDataComparator());
|
||||
|
||||
|
@@ -49,7 +49,8 @@
|
||||
},
|
||||
"featureTriggers": {
|
||||
"atFindNextTransactionFix": 275000,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 320000,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
72
src/main/resources/i18n/ApiError_it.properties
Normal file
72
src/main/resources/i18n/ApiError_it.properties
Normal file
@@ -0,0 +1,72 @@
|
||||
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
|
||||
# Keys are from api.ApiError enum
|
||||
# Italian translation by Pabs 2021
|
||||
|
||||
# La modifica della lingua dell'UI è fatta nel file Settings.json
|
||||
#
|
||||
# "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
|
||||
|
||||
JSON = Impossibile analizzare il messaggio JSON
|
||||
|
||||
NAME_UNKNOWN = nome sconosciuto
|
||||
|
||||
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
|
||||
|
||||
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)
|
||||
|
||||
TRANSACTION_UNKNOWN = transazione sconosciuta
|
||||
|
||||
TRANSFORMATION_ERROR = non è stato possibile trasformare JSON in transazione
|
||||
|
||||
UNAUTHORIZED = Chiamata API non autorizzata
|
46
src/main/resources/i18n/SysTray_it.properties
Normal file
46
src/main/resources/i18n/SysTray_it.properties
Normal file
@@ -0,0 +1,46 @@
|
||||
#Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
|
||||
# SysTray pop-up menu
|
||||
# Italian translation by Pabs 2021
|
||||
|
||||
APPLYING_UPDATE_AND_RESTARTING = Applicando aggiornamento automatico e riavviando...
|
||||
|
||||
AUTO_UPDATE = Aggiornamento automatico
|
||||
|
||||
BLOCK_HEIGHT = altezza
|
||||
|
||||
CHECK_TIME_ACCURACY = Controlla la precisione dell'ora
|
||||
|
||||
CONNECTING = Collegando
|
||||
|
||||
CONNECTION = connessione
|
||||
|
||||
CONNECTIONS = connessioni
|
||||
|
||||
CREATING_BACKUP_OF_DB_FILES = Creazione di backup dei file di database...
|
||||
|
||||
DB_BACKUP = Backup del database
|
||||
|
||||
DB_CHECKPOINT = Punto di controllo del database
|
||||
|
||||
EXIT = Uscita
|
||||
|
||||
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...
|
||||
|
||||
SYNCHRONIZE_CLOCK = Sincronizza orologio
|
||||
|
||||
SYNCHRONIZING_BLOCKCHAIN = Sincronizzando
|
||||
|
||||
SYNCHRONIZING_CLOCK = Sincronizzando orologio
|
185
src/main/resources/i18n/TransactionValidity_it.properties
Normal file
185
src/main/resources/i18n/TransactionValidity_it.properties
Normal file
@@ -0,0 +1,185 @@
|
||||
# Italian translation by Pabs 2021
|
||||
|
||||
ACCOUNT_ALREADY_EXISTS = l'account gia esiste
|
||||
|
||||
ACCOUNT_CANNOT_REWARD_SHARE = l'account non può fare la condivisione di ricompensa
|
||||
|
||||
ALREADY_GROUP_ADMIN = è già amministratore del gruppo
|
||||
|
||||
ALREADY_GROUP_MEMBER = è già membro del gruppo
|
||||
|
||||
ALREADY_VOTED_FOR_THAT_OPTION = già votato per questa opzione
|
||||
|
||||
ASSET_ALREADY_EXISTS = risorsa già esistente
|
||||
|
||||
ASSET_DOES_NOT_EXIST = risorsa non esistente
|
||||
|
||||
ASSET_DOES_NOT_MATCH_AT = l'asset non corrisponde all'asset di AT
|
||||
|
||||
ASSET_NOT_SPENDABLE = la risorsa non è spendibile
|
||||
|
||||
AT_ALREADY_EXISTS = AT gia esiste
|
||||
|
||||
AT_IS_FINISHED = AT ha finito
|
||||
|
||||
AT_UNKNOWN = AT sconosciuto
|
||||
|
||||
BANNED_FROM_GROUP = divietato dal gruppo
|
||||
|
||||
BAN_EXISTS = il divieto esiste già
|
||||
|
||||
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
|
||||
|
||||
GROUP_ALREADY_EXISTS = gruppo già esistente
|
||||
|
||||
GROUP_APPROVAL_DECIDED = approvazione di gruppo già decisa
|
||||
|
||||
GROUP_APPROVAL_NOT_REQUIRED = approvazione di gruppo non richiesto
|
||||
|
||||
GROUP_DOES_NOT_EXIST = gruppo non esiste
|
||||
|
||||
GROUP_ID_MISMATCH = identificazione di gruppo non corrispondente
|
||||
|
||||
GROUP_OWNER_CANNOT_LEAVE = il proprietario del gruppo non può lasciare il gruppo
|
||||
|
||||
HAVE_EQUALS_WANT = la risorsa avere è uguale a la risorsa volere
|
||||
|
||||
INCORRECT_NONCE = PoW nonce sbagliato
|
||||
|
||||
INSUFFICIENT_FEE = tariffa insufficiente
|
||||
|
||||
INVALID_ADDRESS = indirizzo non valido
|
||||
|
||||
INVALID_AMOUNT = importo non valido
|
||||
|
||||
INVALID_ASSET_OWNER = proprietario della risorsa non valido
|
||||
|
||||
INVALID_AT_TRANSACTION = transazione AT non valida
|
||||
|
||||
INVALID_AT_TYPE_LENGTH = lunghezza di "tipo" AT non valida
|
||||
|
||||
INVALID_CREATION_BYTES = byte di creazione non validi
|
||||
|
||||
INVALID_DATA_LENGTH = lunghezza di dati non valida
|
||||
|
||||
INVALID_DESCRIPTION_LENGTH = lunghezza della descrizione non valida
|
||||
|
||||
INVALID_GROUP_APPROVAL_THRESHOLD = soglia di approvazione del gruppo non valida
|
||||
|
||||
INVALID_GROUP_BLOCK_DELAY = ritardo del blocco di approvazione del gruppo non valido
|
||||
|
||||
INVALID_GROUP_ID = identificazione di gruppo non valida
|
||||
|
||||
INVALID_GROUP_OWNER = proprietario di gruppo non valido
|
||||
|
||||
INVALID_LIFETIME = durata della vita non valida
|
||||
|
||||
INVALID_NAME_LENGTH = lunghezza del nome non valida
|
||||
|
||||
INVALID_NAME_OWNER = proprietario del nome non valido
|
||||
|
||||
INVALID_OPTIONS_COUNT = conteggio di opzioni non validi
|
||||
|
||||
INVALID_OPTION_LENGTH = lunghezza di opzioni non valida
|
||||
|
||||
INVALID_ORDER_CREATOR = creatore dell'ordine non valido
|
||||
|
||||
INVALID_PAYMENTS_COUNT = conteggio pagamenti non validi
|
||||
|
||||
INVALID_PUBLIC_KEY = chiave pubblica non valida
|
||||
|
||||
INVALID_QUANTITY = quantità non valida
|
||||
|
||||
INVALID_REFERENCE = riferimento non valido
|
||||
|
||||
INVALID_RETURN = ritorno non valido
|
||||
|
||||
INVALID_REWARD_SHARE_PERCENT = percentuale condivisione di ricompensa non valida
|
||||
|
||||
INVALID_SELLER = venditore non valido
|
||||
|
||||
INVALID_TAGS_LENGTH = lunghezza dei "tag" non valida
|
||||
|
||||
INVALID_TX_GROUP_ID = identificazione di gruppo di transazioni non valida
|
||||
|
||||
INVALID_VALUE_LENGTH = lunghezza "valore" non valida
|
||||
|
||||
INVITE_UNKNOWN = invito di gruppo sconosciuto
|
||||
|
||||
JOIN_REQUEST_EXISTS = la richiesta di iscrizione al gruppo già esiste
|
||||
|
||||
MAXIMUM_REWARD_SHARES = numero massimo di condivisione di ricompensa raggiunto per l'account
|
||||
|
||||
MISSING_CREATOR = creatore mancante
|
||||
|
||||
MULTIPLE_NAMES_FORBIDDEN = è vietata la registrazione di multipli nomi per account
|
||||
|
||||
NAME_ALREADY_FOR_SALE = nome già in vendita
|
||||
|
||||
NAME_ALREADY_REGISTERED = nome già registrato
|
||||
|
||||
NAME_DOES_NOT_EXIST = il nome non esiste
|
||||
|
||||
NAME_NOT_FOR_SALE = il nome non è in vendita
|
||||
|
||||
NAME_NOT_NORMALIZED = il nome non è in forma "normalizzata" Unicode
|
||||
|
||||
NEGATIVE_AMOUNT = importo non valido / negativo
|
||||
|
||||
NEGATIVE_FEE = tariffa non valida / negativa
|
||||
|
||||
NEGATIVE_PRICE = prezzo non valido / negativo
|
||||
|
||||
NOT_GROUP_ADMIN = l'account non è un amministratore di gruppo
|
||||
|
||||
NOT_GROUP_MEMBER = l'account non è un membro del gruppo
|
||||
|
||||
NOT_MINTING_ACCOUNT = l'account non può coniare
|
||||
|
||||
NOT_YET_RELEASED = funzione non ancora rilasciata
|
||||
|
||||
NO_BALANCE = equilibrio insufficiente
|
||||
|
||||
NO_BLOCKCHAIN_LOCK = nodo di blockchain attualmente occupato
|
||||
|
||||
NO_FLAG_PERMISSION = l'account non dispone di questa autorizzazione
|
||||
|
||||
OK = OK
|
||||
|
||||
ORDER_ALREADY_CLOSED = l'ordine di scambio di risorsa è già chiuso
|
||||
|
||||
ORDER_DOES_NOT_EXIST = l'ordine di scambio di risorsa non esiste
|
||||
|
||||
POLL_ALREADY_EXISTS = il sondaggio già esiste
|
||||
|
||||
POLL_DOES_NOT_EXIST = il sondaggio non esiste
|
||||
|
||||
POLL_OPTION_DOES_NOT_EXIST = le opzioni di sondaggio non esistono
|
||||
|
||||
PUBLIC_KEY_UNKNOWN = chiave pubblica sconosciuta
|
||||
|
||||
REWARD_SHARE_UNKNOWN = condivisione di ricompensa sconosciuta
|
||||
|
||||
SELF_SHARE_EXISTS = condivisione di sé (condivisione di ricompensa) già esiste
|
||||
|
||||
TIMESTAMP_TOO_NEW = timestamp troppo nuovo
|
||||
|
||||
TIMESTAMP_TOO_OLD = timestamp troppo vecchio
|
||||
|
||||
TOO_MANY_UNCONFIRMED = l'account ha troppe transazioni non confermate in sospeso
|
||||
|
||||
TRANSACTION_ALREADY_CONFIRMED = la transazione è già confermata
|
||||
|
||||
TRANSACTION_ALREADY_EXISTS = la transazione già esiste
|
||||
|
||||
TRANSACTION_UNKNOWN = transazione sconosciuta
|
||||
|
||||
TX_GROUP_ID_MISMATCH = identificazione di gruppo della transazione non corrisponde
|
@@ -336,4 +336,457 @@ public class RewardTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 1 and 2 accounts both pre and post the shareBinFix, including orphaning back through the feature trigger block */
|
||||
@Test
|
||||
public void testLevel1And2Rewards() throws DataException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share NOT online
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint a couple of blocks so that we are able to orphan them later
|
||||
for (int i=0; i<2; i++)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(2, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Ensure that only Alice is a founder
|
||||
assertEquals(1, getFlags(repository, "alice"));
|
||||
assertEquals(0, getFlags(repository, "bob"));
|
||||
assertEquals(0, getFlags(repository, "chloe"));
|
||||
assertEquals(0, getFlags(repository, "dilbert"));
|
||||
|
||||
// Now that everyone is at level 1 or 2, we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
final long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are at the correct height and block reward value
|
||||
assertEquals(6, (int) repository.getBlockRepository().getLastBlock().getHeight());
|
||||
assertEquals(10000000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Chloe, and Dilbert are 'online'. Bob is offline.
|
||||
* Chloe is level 1, Dilbert is level 2.
|
||||
* One founder online (Alice, who is also level 1).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Chloe and Dilbert should receive equal shares of the 5% block reward for Level 1 and 2
|
||||
* Alice should receive the remainder (95%)
|
||||
*/
|
||||
|
||||
// We are after the shareBinFix feature trigger, so we expect level 1 and 2 to share the same reward (5%)
|
||||
final int level1And2SharePercent = 5_00; // 5%
|
||||
final long level1And2ShareAmount = (blockReward * level1And2SharePercent) / 100L / 100L;
|
||||
final long expectedReward = level1And2ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long expectedFounderReward = blockReward - level1And2ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances to ensure that the correct post-shareBinFix distribution is being applied
|
||||
assertEquals(500000000, level1And2ShareAmount);
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedReward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedReward);
|
||||
|
||||
// Now orphan the latest block. This brings us to the threshold of the shareBinFix feature trigger.
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
assertEquals(5, (int) repository.getBlockRepository().getLastBlock().getHeight());
|
||||
|
||||
// Ensure the latest post-fix block rewards have been subtracted and they have returned to their initial values
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
|
||||
|
||||
// Orphan another block. This time, the block that was orphaned was prior to the shareBinFix feature trigger.
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
assertEquals(4, (int) repository.getBlockRepository().getLastBlock().getHeight());
|
||||
|
||||
// Prior to the fix, the levels were incorrectly grouped
|
||||
// Chloe should receive 100% of the level 1 reward, and Dilbert should receive 100% of the level 2+3 reward
|
||||
final int level1SharePercent = 5_00; // 5%
|
||||
final int level2And3SharePercent = 10_00; // 10%
|
||||
final long level1ShareAmountBeforeFix = (blockReward * level1SharePercent) / 100L / 100L;
|
||||
final long level2And3ShareAmountBeforeFix = (blockReward * level2And3SharePercent) / 100L / 100L;
|
||||
final long expectedFounderRewardBeforeFix = blockReward - level1ShareAmountBeforeFix - level2And3ShareAmountBeforeFix; // Alice should receive the remainder
|
||||
|
||||
// Validate the share amounts and balances
|
||||
assertEquals(500000000, level1ShareAmountBeforeFix);
|
||||
assertEquals(1000000000, level2And3ShareAmountBeforeFix);
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance-expectedFounderRewardBeforeFix);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance-level1ShareAmountBeforeFix);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance-level2And3ShareAmountBeforeFix);
|
||||
|
||||
// Orphan the latest block one last time
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
assertEquals(3, (int) repository.getBlockRepository().getLastBlock().getHeight());
|
||||
|
||||
// Validate balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance-(expectedFounderRewardBeforeFix*2));
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance-(level1ShareAmountBeforeFix*2));
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance-(level2And3ShareAmountBeforeFix*2));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 3 and 4 accounts */
|
||||
@Test
|
||||
public void testLevel3And4Rewards() throws DataException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share online
|
||||
byte[] bobRewardSharePrivateKey = AccountUtils.rewardShare(repository, "bob", "bob", 0);
|
||||
PrivateKeyAccount bobRewardShareAccount = new PrivateKeyAccount(repository, bobRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(bobRewardShareAccount);
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint enough blocks to bump testAccount levels to 3 and 4
|
||||
final int minterBlocksNeeded = cumulativeBlocksByLevel.get(4) - 20; // 20 blocks before level 4, so that the test accounts reach the correct levels
|
||||
for (int bc = 0; bc < minterBlocksNeeded; ++bc)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(3, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(3, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(3, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(4, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Now that everyone is at level 3 or 4, we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
final long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are using the correct block reward value
|
||||
assertEquals(100000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Bob, Chloe, and Dilbert are 'online'.
|
||||
* Bob and Chloe are level 3; Dilbert is level 4.
|
||||
* One founder online (Alice, who is also level 3).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Chloe, Bob and Dilbert should receive equal shares of the 10% block reward for level 3 and 4
|
||||
* Alice should receive the remainder (90%)
|
||||
*/
|
||||
|
||||
// We are after the shareBinFix feature trigger, so we expect level 3 and 4 to share the same reward (10%)
|
||||
final int level3And4SharePercent = 10_00; // 10%
|
||||
final long level3And4ShareAmount = (blockReward * level3And4SharePercent) / 100L / 100L;
|
||||
final long expectedReward = level3And4ShareAmount / 3; // The reward is split between Bob, Chloe, and Dilbert
|
||||
final long expectedFounderReward = blockReward - level3And4ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances to ensure that the correct post-shareBinFix distribution is being applied
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance+expectedReward);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedReward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedReward);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 5 and 6 accounts */
|
||||
@Test
|
||||
public void testLevel5And6Rewards() throws DataException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share not initially online
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint enough blocks to bump testAccount levels to 5 and 6
|
||||
final int minterBlocksNeeded = cumulativeBlocksByLevel.get(6) - 20; // 20 blocks before level 6, so that the test accounts reach the correct levels
|
||||
for (int bc = 0; bc < minterBlocksNeeded; ++bc)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Bob self-share now comes online
|
||||
byte[] bobRewardSharePrivateKey = AccountUtils.rewardShare(repository, "bob", "bob", 0);
|
||||
PrivateKeyAccount bobRewardShareAccount = new PrivateKeyAccount(repository, bobRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(bobRewardShareAccount);
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(5, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(5, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(6, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Now that everyone is at level 5 or 6 (except Bob who has only just started minting, so is at level 1), we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
final long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are using the correct block reward value
|
||||
assertEquals(100000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Bob, Chloe, and Dilbert are 'online'.
|
||||
* Bob is level 1; Chloe is level 5; Dilbert is level 6.
|
||||
* One founder online (Alice, who is also level 5).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Chloe and Dilbert should receive equal shares of the 15% block reward for level 5 and 6
|
||||
* Bob should receive all of the level 1 and 2 reward (5%)
|
||||
* Alice should receive the remainder (80%)
|
||||
*/
|
||||
|
||||
// We are after the shareBinFix feature trigger, so we expect level 5 and 6 to share the same reward (15%)
|
||||
final int level1And2SharePercent = 5_00; // 5%
|
||||
final int level5And6SharePercent = 15_00; // 10%
|
||||
final long level1And2ShareAmount = (blockReward * level1And2SharePercent) / 100L / 100L;
|
||||
final long level5And6ShareAmount = (blockReward * level5And6SharePercent) / 100L / 100L;
|
||||
final long expectedLevel1And2Reward = level1And2ShareAmount; // The reward is given entirely to Bob
|
||||
final long expectedLevel5And6Reward = level5And6ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long expectedFounderReward = blockReward - level1And2ShareAmount - level5And6ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances to ensure that the correct post-shareBinFix distribution is being applied
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance+expectedLevel1And2Reward);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel5And6Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel5And6Reward);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 7 and 8 accounts */
|
||||
@Test
|
||||
public void testLevel7And8Rewards() throws DataException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share NOT online
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint enough blocks to bump testAccount levels to 7 and 8
|
||||
final int minterBlocksNeeded = cumulativeBlocksByLevel.get(8) - 20; // 20 blocks before level 8, so that the test accounts reach the correct levels
|
||||
for (int bc = 0; bc < minterBlocksNeeded; ++bc)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(8, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Now that everyone is at level 7 or 8 (except Bob who has only just started minting, so is at level 1), we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
final long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are using the correct block reward value
|
||||
assertEquals(100000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Chloe, and Dilbert are 'online'.
|
||||
* Chloe is level 7; Dilbert is level 8.
|
||||
* One founder online (Alice, who is also level 7).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Chloe and Dilbert should receive equal shares of the 20% block reward for level 7 and 8
|
||||
* Alice should receive the remainder (80%)
|
||||
*/
|
||||
|
||||
// We are after the shareBinFix feature trigger, so we expect level 7 and 8 to share the same reward (20%)
|
||||
final int level7And8SharePercent = 20_00; // 20%
|
||||
final long level7And8ShareAmount = (blockReward * level7And8SharePercent) / 100L / 100L;
|
||||
final long expectedLevel7And8Reward = level7And8ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long expectedFounderReward = blockReward - level7And8ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances to ensure that the correct post-shareBinFix distribution is being applied
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel7And8Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel7And8Reward);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 9 and 10 accounts */
|
||||
@Test
|
||||
public void testLevel9And10Rewards() throws DataException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share not initially online
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint enough blocks to bump testAccount levels to 9 and 10
|
||||
final int minterBlocksNeeded = cumulativeBlocksByLevel.get(10) - 20; // 20 blocks before level 10, so that the test accounts reach the correct levels
|
||||
for (int bc = 0; bc < minterBlocksNeeded; ++bc)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Bob self-share now comes online
|
||||
byte[] bobRewardSharePrivateKey = AccountUtils.rewardShare(repository, "bob", "bob", 0);
|
||||
PrivateKeyAccount bobRewardShareAccount = new PrivateKeyAccount(repository, bobRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(bobRewardShareAccount);
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(9, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(9, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(10, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Now that everyone is at level 7 or 8 (except Bob who has only just started minting, so is at level 1), we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
final long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are using the correct block reward value
|
||||
assertEquals(100000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Bob, Chloe, and Dilbert are 'online'.
|
||||
* Bob is level 1; Chloe is level 9; Dilbert is level 10.
|
||||
* One founder online (Alice, who is also level 9).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Chloe and Dilbert should receive equal shares of the 25% block reward for level 9 and 10
|
||||
* Bob should receive all of the level 1 and 2 reward (5%)
|
||||
* Alice should receive the remainder (70%)
|
||||
*/
|
||||
|
||||
// We are after the shareBinFix feature trigger, so we expect level 9 and 10 to share the same reward (25%)
|
||||
final int level1And2SharePercent = 5_00; // 5%
|
||||
final int level9And10SharePercent = 25_00; // 25%
|
||||
final long level1And2ShareAmount = (blockReward * level1And2SharePercent) / 100L / 100L;
|
||||
final long level9And10ShareAmount = (blockReward * level9And10SharePercent) / 100L / 100L;
|
||||
final long expectedLevel1And2Reward = level1And2ShareAmount; // The reward is given entirely to Bob
|
||||
final long expectedLevel9And10Reward = level9And10ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long expectedFounderReward = blockReward - level1And2ShareAmount - level9And10ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances to ensure that the correct post-shareBinFix distribution is being applied
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance+expectedLevel1And2Reward);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel9And10Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel9And10Reward);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private int getFlags(Repository repository, String name) throws DataException {
|
||||
TestAccount testAccount = Common.getTestAccount(repository, name);
|
||||
return repository.getAccountRepository().getAccount(testAccount.getAddress()).getFlags();
|
||||
}
|
||||
|
||||
}
|
@@ -46,7 +46,8 @@
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@@ -46,7 +46,8 @@
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@@ -46,7 +46,8 @@
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@@ -46,7 +46,8 @@
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@@ -46,7 +46,8 @@
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
74
src/test/resources/test-chain-v2-reward-levels.json
Normal file
74
src/test/resources/test-chain-v2-reward-levels.json
Normal file
@@ -0,0 +1,74 @@
|
||||
{
|
||||
"isTestChain": true,
|
||||
"blockTimestampMargin": 500,
|
||||
"transactionExpiryPeriod": 86400000,
|
||||
"maxBlockSize": 2097152,
|
||||
"maxBytesPerUnitFee": 1024,
|
||||
"unitFee": "0.1",
|
||||
"requireGroupForApproval": false,
|
||||
"minAccountLevelToRewardShare": 5,
|
||||
"maxRewardSharesPerMintingAccount": 20,
|
||||
"founderEffectiveMintingLevel": 10,
|
||||
"onlineAccountSignaturesMinLifetime": 3600000,
|
||||
"onlineAccountSignaturesMaxLifetime": 86400000,
|
||||
"rewardsByHeight": [
|
||||
{ "height": 1, "reward": 100 },
|
||||
{ "height": 11, "reward": 10 },
|
||||
{ "height": 21, "reward": 1 }
|
||||
],
|
||||
"sharesByLevel": [
|
||||
{ "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
],
|
||||
"ciyamAtSettings": {
|
||||
"feePerStep": "0.0001",
|
||||
"maxStepsPerRound": 500,
|
||||
"stepsPerFunctionCall": 10,
|
||||
"minutesPerBlock": 1
|
||||
},
|
||||
"featureTriggers": {
|
||||
"messageHeight": 0,
|
||||
"atHeight": 0,
|
||||
"assetsTimestamp": 0,
|
||||
"votingTimestamp": 0,
|
||||
"arbitraryTimestamp": 0,
|
||||
"powfixTimestamp": 0,
|
||||
"qortalTimestamp": 0,
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 6
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
"timestamp": 0,
|
||||
"transactions": [
|
||||
{ "type": "ISSUE_ASSET", "assetName": "QORT", "description": "QORT native coin", "data": "", "quantity": 0, "isDivisible": true, "fee": 0 },
|
||||
{ "type": "ISSUE_ASSET", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||
{ "type": "ISSUE_ASSET", "assetName": "QORT-from-QORA", "description": "QORT gained from holding legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||
|
||||
{ "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000" },
|
||||
{ "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000" },
|
||||
{ "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000" },
|
||||
{ "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000" },
|
||||
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 },
|
||||
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 2 }
|
||||
]
|
||||
}
|
||||
}
|
@@ -46,7 +46,8 @@
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
@@ -46,7 +46,8 @@
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0,
|
||||
"atFindNextTransactionFix": 0,
|
||||
"newBlockSigHeight": 999999
|
||||
"newBlockSigHeight": 999999,
|
||||
"shareBinFix": 999999
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
|
7
src/test/resources/test-settings-v2-reward-levels.json
Normal file
7
src/test/resources/test-settings-v2-reward-levels.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"restrictedApi": false,
|
||||
"blockchainConfig": "src/test/resources/test-chain-v2-reward-levels.json",
|
||||
"wipeUnconfirmedOnStart": false,
|
||||
"testNtpOffset": 0,
|
||||
"minPeers": 0
|
||||
}
|
10
stop.sh
10
stop.sh
@@ -21,13 +21,19 @@ fi
|
||||
read pid 2>/dev/null <run.pid
|
||||
is_pid_valid=$?
|
||||
|
||||
# Swap out the API port if the --testnet (or -t) argument is specified
|
||||
api_port=12391
|
||||
if [[ "$@" = *"--testnet"* ]] || [[ "$@" = *"-t"* ]]; then
|
||||
api_port=62391
|
||||
fi
|
||||
|
||||
# Ensure curl is installed
|
||||
curl_path=$(which curl)
|
||||
|
||||
if [[ -f $curl_path ]]; then
|
||||
|
||||
echo 'Calling GET /admin/stop on local Qortal node'
|
||||
if curl --url http://localhost:12391/admin/stop 1>/dev/null 2>&1; then
|
||||
if curl --url "http://localhost:${api_port}/admin/stop" 1>/dev/null 2>&1; then
|
||||
echo "Qortal node responded and should be shutting down"
|
||||
|
||||
if [ "${is_pid_valid}" -eq 0 ]; then
|
||||
@@ -42,7 +48,7 @@ if [[ -f $curl_path ]]; then
|
||||
fi
|
||||
exit 0
|
||||
else
|
||||
echo "${red}No response from Qortal node - not running?${normal}"
|
||||
echo "${red}No response from Qortal node - not running on port ${api_port}?${normal}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
Reference in New Issue
Block a user