mirror of
https://github.com/Qortal/qortal.git
synced 2025-07-31 14:11:25 +00:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
5eafdf3c80 | ||
|
d7c26c27e1 | ||
|
2d18dd62eb | ||
|
51fd177d79 | ||
|
c4643538f1 | ||
|
0edadaf901 | ||
|
c05533fb71 | ||
|
db270f559f | ||
|
79f7f68b0c | ||
|
d30d61edab | ||
|
f7e2ee383e | ||
|
544fdbfbe9 | ||
|
c3d1ecb7e1 | ||
|
873a9d0cee | ||
|
95cb5f607b | ||
|
54d0b721c4 | ||
|
4a4678b331 | ||
|
12f9ecaaca | ||
|
1d3ee77fb8 | ||
|
64055e280d | ||
|
90e0f9dddc | ||
|
b0b0e2ac18 | ||
|
9db606af5a | ||
|
5bfc17bd64 | ||
|
a3c44428d3 | ||
|
450ff7318f |
BIN
lib/org/hsqldb/hsqldb/2.5.0-fixed/hsqldb-2.5.0-fixed-sources.jar
Normal file
BIN
lib/org/hsqldb/hsqldb/2.5.0-fixed/hsqldb-2.5.0-fixed-sources.jar
Normal file
Binary file not shown.
BIN
lib/org/hsqldb/hsqldb/2.5.0-fixed/hsqldb-2.5.0-fixed.jar
Normal file
BIN
lib/org/hsqldb/hsqldb/2.5.0-fixed/hsqldb-2.5.0-fixed.jar
Normal file
Binary file not shown.
9
lib/org/hsqldb/hsqldb/2.5.0-fixed/hsqldb-2.5.0-fixed.pom
Normal file
9
lib/org/hsqldb/hsqldb/2.5.0-fixed/hsqldb-2.5.0-fixed.pom
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<version>2.5.0-fixed</version>
|
||||
<description>POM was created from install:install-file</description>
|
||||
</project>
|
12
lib/org/hsqldb/hsqldb/maven-metadata-local.xml
Normal file
12
lib/org/hsqldb/hsqldb/maven-metadata-local.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>org.hsqldb</groupId>
|
||||
<artifactId>hsqldb</artifactId>
|
||||
<versioning>
|
||||
<release>2.5.0-fixed</release>
|
||||
<versions>
|
||||
<version>2.5.0-fixed</version>
|
||||
</versions>
|
||||
<lastUpdated>20200318133132</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
4
pom.xml
4
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.qortal</groupId>
|
||||
<artifactId>qortal</artifactId>
|
||||
<version>1.0</version>
|
||||
<version>1.0.4</version>
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<bitcoin.version>0.15.4</bitcoin.version>
|
||||
@@ -13,7 +13,7 @@
|
||||
<commons-text.version>1.8</commons-text.version>
|
||||
<dagger.version>1.2.2</dagger.version>
|
||||
<guava.version>28.1-jre</guava.version>
|
||||
<hsqldb.version>2.5.0</hsqldb.version>
|
||||
<hsqldb.version>2.5.0-fixed</hsqldb.version>
|
||||
<hsqldb-sqltool.version>2.5.0</hsqldb-sqltool.version>
|
||||
<jersey.version>2.29.1</jersey.version>
|
||||
<jetty.version>9.4.22.v20191022</jetty.version>
|
||||
|
15
src/main/java/org/qortal/api/model/NodeStatus.java
Normal file
15
src/main/java/org/qortal/api/model/NodeStatus.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package org.qortal.api.model;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public class NodeStatus {
|
||||
|
||||
public boolean isMintingPossible;
|
||||
public boolean isSynchronizing;
|
||||
|
||||
public NodeStatus() {
|
||||
}
|
||||
|
||||
}
|
@@ -45,6 +45,7 @@ import org.qortal.api.ApiExceptionFactory;
|
||||
import org.qortal.api.Security;
|
||||
import org.qortal.api.model.ActivitySummary;
|
||||
import org.qortal.api.model.NodeInfo;
|
||||
import org.qortal.api.model.NodeStatus;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.controller.Synchronizer.SynchronizationResult;
|
||||
@@ -120,6 +121,27 @@ public class AdminResource {
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/status")
|
||||
@Operation(
|
||||
summary = "Fetch node status",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = NodeStatus.class))
|
||||
)
|
||||
}
|
||||
)
|
||||
public NodeStatus status() {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
NodeStatus nodeStatus = new NodeStatus();
|
||||
|
||||
nodeStatus.isMintingPossible = Controller.getInstance().isMintingPossible();
|
||||
nodeStatus.isSynchronizing = Controller.getInstance().isSynchronizing();
|
||||
|
||||
return nodeStatus;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stop")
|
||||
@Operation(
|
||||
@@ -202,6 +224,8 @@ public class AdminResource {
|
||||
)
|
||||
@ApiErrors({ApiError.REPOSITORY_ISSUE})
|
||||
public List<MintingAccountData> getMintingAccounts() {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
List<MintingAccountData> mintingAccounts = repository.getAccountRepository().getMintingAccounts();
|
||||
|
||||
@@ -246,6 +270,8 @@ public class AdminResource {
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.REPOSITORY_ISSUE, ApiError.CANNOT_MINT})
|
||||
public String addMintingAccount(String seed58) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
byte[] seed = Base58.decode(seed58.trim());
|
||||
|
||||
@@ -296,6 +322,8 @@ public class AdminResource {
|
||||
)
|
||||
@ApiErrors({ApiError.INVALID_PRIVATE_KEY, ApiError.REPOSITORY_ISSUE})
|
||||
public String deleteMintingAccount(String seed58) {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
byte[] seed = Base58.decode(seed58.trim());
|
||||
|
||||
|
@@ -31,6 +31,7 @@ import org.qortal.network.PeerAddress;
|
||||
import org.qortal.repository.DataException;
|
||||
import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.utils.ExecuteProduceConsume;
|
||||
|
||||
@Path("/peers")
|
||||
@Tag(name = "Peers")
|
||||
@@ -108,6 +109,29 @@ public class PeersResource {
|
||||
return Network.getInstance().getSelfPeers();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/enginestats")
|
||||
@Operation(
|
||||
summary = "Fetch statistics snapshot for networking engine",
|
||||
responses = {
|
||||
@ApiResponse(
|
||||
content = @Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
array = @ArraySchema(
|
||||
schema = @Schema(
|
||||
implementation = ExecuteProduceConsume.StatsSnapshot.class
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
public ExecuteProduceConsume.StatsSnapshot getEngineStats() {
|
||||
Security.checkApiCallAllowed(request);
|
||||
|
||||
return Network.getInstance().getStatsSnapshot();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(
|
||||
summary = "Add new peer address",
|
||||
|
@@ -573,7 +573,7 @@ public class TransactionsResource {
|
||||
|
||||
public static ApiException createTransactionInvalidException(HttpServletRequest request, ValidationResult result) {
|
||||
String translatedResult = Translator.INSTANCE.translate("TransactionValidity", request.getLocale().getLanguage(), result.name());
|
||||
return ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSACTION_INVALID, null, translatedResult);
|
||||
return ApiExceptionFactory.INSTANCE.createException(request, ApiError.TRANSACTION_INVALID, null, translatedResult, result.name());
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -9,7 +9,6 @@ import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -126,31 +125,43 @@ public class Block {
|
||||
protected BigDecimal ourAtFees; // Generated locally
|
||||
|
||||
/** Lazy-instantiated expanded info on block's online accounts. */
|
||||
class ExpandedAccount {
|
||||
final RewardShareData rewardShareData;
|
||||
final boolean isRecipientAlsoMinter;
|
||||
static class ExpandedAccount {
|
||||
private static final BigDecimal oneHundred = BigDecimal.valueOf(100L);
|
||||
|
||||
final Account mintingAccount;
|
||||
final AccountData mintingAccountData;
|
||||
final boolean isMinterFounder;
|
||||
private final Repository repository;
|
||||
|
||||
final Account recipientAccount;
|
||||
final AccountData recipientAccountData;
|
||||
final boolean isRecipientFounder;
|
||||
private final RewardShareData rewardShareData;
|
||||
private final boolean isRecipientAlsoMinter;
|
||||
|
||||
private final Account mintingAccount;
|
||||
private final AccountData mintingAccountData;
|
||||
private final boolean isMinterFounder;
|
||||
|
||||
private final Account recipientAccount;
|
||||
private final AccountData recipientAccountData;
|
||||
private final boolean isRecipientFounder;
|
||||
|
||||
ExpandedAccount(Repository repository, int accountIndex) throws DataException {
|
||||
this.repository = repository;
|
||||
this.rewardShareData = repository.getAccountRepository().getRewardShareByIndex(accountIndex);
|
||||
|
||||
this.mintingAccount = new PublicKeyAccount(repository, this.rewardShareData.getMinterPublicKey());
|
||||
this.recipientAccount = new Account(repository, this.rewardShareData.getRecipient());
|
||||
|
||||
this.mintingAccountData = repository.getAccountRepository().getAccount(this.mintingAccount.getAddress());
|
||||
this.isMinterFounder = Account.isFounder(mintingAccountData.getFlags());
|
||||
|
||||
this.recipientAccountData = repository.getAccountRepository().getAccount(this.recipientAccount.getAddress());
|
||||
this.isRecipientFounder = Account.isFounder(recipientAccountData.getFlags());
|
||||
this.isRecipientAlsoMinter = this.rewardShareData.getRecipient().equals(this.mintingAccount.getAddress());
|
||||
|
||||
this.isRecipientAlsoMinter = this.mintingAccountData.getAddress().equals(this.recipientAccountData.getAddress());
|
||||
if (this.isRecipientAlsoMinter) {
|
||||
// Self-share: minter is also recipient
|
||||
this.recipientAccount = this.mintingAccount;
|
||||
this.recipientAccountData = this.mintingAccountData;
|
||||
this.isRecipientFounder = this.isMinterFounder;
|
||||
} else {
|
||||
// Recipient differs from minter
|
||||
this.recipientAccount = new Account(repository, this.rewardShareData.getRecipient());
|
||||
this.recipientAccountData = repository.getAccountRepository().getAccount(this.recipientAccount.getAddress());
|
||||
this.isRecipientFounder = Account.isFounder(recipientAccountData.getFlags());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,22 +187,26 @@ public class Block {
|
||||
}
|
||||
|
||||
void distribute(BigDecimal accountAmount) throws DataException {
|
||||
final BigDecimal oneHundred = BigDecimal.valueOf(100L);
|
||||
|
||||
if (this.mintingAccount.getAddress().equals(this.recipientAccount.getAddress())) {
|
||||
if (this.isRecipientAlsoMinter) {
|
||||
// minter & recipient the same - simpler case
|
||||
LOGGER.trace(() -> String.format("Minter/recipient account %s share: %s", this.mintingAccount.getAddress(), accountAmount.toPlainString()));
|
||||
this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(accountAmount));
|
||||
if (accountAmount.signum() != 0)
|
||||
// this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(accountAmount));
|
||||
this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, accountAmount);
|
||||
} else {
|
||||
// minter & recipient different - extra work needed
|
||||
BigDecimal recipientAmount = accountAmount.multiply(this.rewardShareData.getSharePercent()).divide(oneHundred, RoundingMode.DOWN);
|
||||
BigDecimal minterAmount = accountAmount.subtract(recipientAmount);
|
||||
|
||||
LOGGER.trace(() -> String.format("Minter account %s share: %s", this.mintingAccount.getAddress(), minterAmount.toPlainString()));
|
||||
this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(minterAmount));
|
||||
if (minterAmount.signum() != 0)
|
||||
// this.mintingAccount.setConfirmedBalance(Asset.QORT, this.mintingAccount.getConfirmedBalance(Asset.QORT).add(minterAmount));
|
||||
this.repository.getAccountRepository().modifyAssetBalance(this.mintingAccount.getAddress(), Asset.QORT, minterAmount);
|
||||
|
||||
LOGGER.trace(() -> String.format("Recipient account %s share: %s", this.recipientAccount.getAddress(), recipientAmount.toPlainString()));
|
||||
this.recipientAccount.setConfirmedBalance(Asset.QORT, this.recipientAccount.getConfirmedBalance(Asset.QORT).add(recipientAmount));
|
||||
if (recipientAmount.signum() != 0)
|
||||
// this.recipientAccount.setConfirmedBalance(Asset.QORT, this.recipientAccount.getConfirmedBalance(Asset.QORT).add(recipientAmount));
|
||||
this.repository.getAccountRepository().modifyAssetBalance(this.recipientAccount.getAddress(), Asset.QORT, recipientAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1256,8 +1271,9 @@ public class Block {
|
||||
AccountData accountData = getAccountData.apply(expandedAccount);
|
||||
|
||||
accountData.setBlocksMinted(accountData.getBlocksMinted() + 1);
|
||||
repository.getAccountRepository().setMintedBlockCount(accountData);
|
||||
LOGGER.trace(() -> String.format("Block minter %s up to %d minted block%s", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : "")));
|
||||
// repository.getAccountRepository().setMintedBlockCount(accountData); int rowCount = 1; // Until HSQLDB rev 6100 is fixed
|
||||
int rowCount = repository.getAccountRepository().modifyMintedBlockCount(accountData.getAddress(), +1);
|
||||
LOGGER.trace(() -> String.format("Block minter %s up to %d minted block%s (rowCount: %d)", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""), rowCount));
|
||||
}
|
||||
|
||||
// We are only interested in accounts that are NOT already highest level
|
||||
@@ -1425,35 +1441,40 @@ public class Block {
|
||||
public void orphan() throws DataException {
|
||||
LOGGER.trace(() -> String.format("Orphaning block %d", this.blockData.getHeight()));
|
||||
|
||||
// Return AT fees and delete AT states from repository
|
||||
orphanAtFeesAndStates();
|
||||
this.repository.setDebug(false);
|
||||
try {
|
||||
// Return AT fees and delete AT states from repository
|
||||
orphanAtFeesAndStates();
|
||||
|
||||
// Orphan, and unlink, transactions from this block
|
||||
orphanTransactionsFromBlock();
|
||||
// Orphan, and unlink, transactions from this block
|
||||
orphanTransactionsFromBlock();
|
||||
|
||||
// Undo any group-approval decisions that happen at this block
|
||||
orphanGroupApprovalTransactions();
|
||||
// Undo any group-approval decisions that happen at this block
|
||||
orphanGroupApprovalTransactions();
|
||||
|
||||
if (this.blockData.getHeight() > 1) {
|
||||
// Invalidate expandedAccounts as they may have changed due to orphaning TRANSFER_PRIVS transactions, etc.
|
||||
this.cachedExpandedAccounts = null;
|
||||
if (this.blockData.getHeight() > 1) {
|
||||
// Invalidate expandedAccounts as they may have changed due to orphaning TRANSFER_PRIVS transactions, etc.
|
||||
this.cachedExpandedAccounts = null;
|
||||
|
||||
// Deduct any transaction fees from minter/reward-share account(s)
|
||||
deductTransactionFees();
|
||||
// Deduct any transaction fees from minter/reward-share account(s)
|
||||
deductTransactionFees();
|
||||
|
||||
// Block rewards removed after transactions undone
|
||||
orphanBlockRewards();
|
||||
// Block rewards removed after transactions undone
|
||||
orphanBlockRewards();
|
||||
|
||||
// Decrease account levels
|
||||
decreaseAccountLevels();
|
||||
// Decrease account levels
|
||||
decreaseAccountLevels();
|
||||
}
|
||||
|
||||
// Delete orphaned balances
|
||||
this.repository.getAccountRepository().deleteBalancesFromHeight(this.blockData.getHeight());
|
||||
|
||||
// Delete block from blockchain
|
||||
this.repository.getBlockRepository().delete(this.blockData);
|
||||
this.blockData.setHeight(null);
|
||||
} finally {
|
||||
this.repository.setDebug(false);
|
||||
}
|
||||
|
||||
// Delete orphaned balances
|
||||
this.repository.getAccountRepository().deleteBalancesFromHeight(this.blockData.getHeight());
|
||||
|
||||
// Delete block from blockchain
|
||||
this.repository.getBlockRepository().delete(this.blockData);
|
||||
this.blockData.setHeight(null);
|
||||
}
|
||||
|
||||
protected void orphanTransactionsFromBlock() throws DataException {
|
||||
@@ -1571,8 +1592,9 @@ public class Block {
|
||||
AccountData accountData = getAccountData.apply(expandedAccount);
|
||||
|
||||
accountData.setBlocksMinted(accountData.getBlocksMinted() - 1);
|
||||
repository.getAccountRepository().setMintedBlockCount(accountData);
|
||||
LOGGER.trace(() -> String.format("Block minter %s down to %d minted block%s", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : "")));
|
||||
// repository.getAccountRepository().setMintedBlockCount(accountData); int rowCount = 1; // Until HSQLDB rev 6100 is fixed
|
||||
int rowCount = repository.getAccountRepository().modifyMintedBlockCount(accountData.getAddress(), -1);
|
||||
LOGGER.trace(() -> String.format("Block minter %s down to %d minted block%s (rowCount: %d)", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""), rowCount));
|
||||
}
|
||||
|
||||
// We are only interested in accounts that are NOT already lowest level
|
||||
@@ -1602,8 +1624,22 @@ public class Block {
|
||||
protected void distributeBlockReward(BigDecimal totalAmount) throws DataException {
|
||||
LOGGER.trace(() -> String.format("Distributing: %s", totalAmount.toPlainString()));
|
||||
|
||||
List<ShareByLevel> sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel();
|
||||
// Distribute according to account level
|
||||
BigDecimal sharedByLevelAmount = distributeBlockRewardByLevel(totalAmount);
|
||||
LOGGER.trace(() -> String.format("Shared %s of %s based on account levels", sharedByLevelAmount.toPlainString(), totalAmount.toPlainString()));
|
||||
|
||||
// Distribute amongst legacy QORA holders
|
||||
BigDecimal sharedByQoraHoldersAmount = distributeBlockRewardToQoraHolders(totalAmount);
|
||||
LOGGER.trace(() -> String.format("Shared %s of %s to legacy QORA holders", sharedByQoraHoldersAmount.toPlainString(), totalAmount.toPlainString()));
|
||||
|
||||
// Spread remainder across founder accounts
|
||||
BigDecimal foundersAmount = totalAmount.subtract(sharedByLevelAmount).subtract(sharedByQoraHoldersAmount);
|
||||
distributeBlockRewardToFounders(foundersAmount);
|
||||
}
|
||||
|
||||
private BigDecimal distributeBlockRewardByLevel(BigDecimal totalAmount) throws DataException {
|
||||
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
||||
List<ShareByLevel> sharesByLevel = BlockChain.getInstance().getBlockSharesByLevel();
|
||||
|
||||
// Distribute amount across bins
|
||||
BigDecimal sharedAmount = BigDecimal.ZERO;
|
||||
@@ -1628,36 +1664,17 @@ public class Block {
|
||||
}
|
||||
}
|
||||
|
||||
// Distribute share across legacy QORA holders
|
||||
return sharedAmount;
|
||||
}
|
||||
|
||||
private BigDecimal distributeBlockRewardToQoraHolders(BigDecimal totalAmount) throws DataException {
|
||||
BigDecimal qoraHoldersAmount = BlockChain.getInstance().getQoraHoldersShare().multiply(totalAmount).setScale(8, RoundingMode.DOWN);
|
||||
LOGGER.trace(() -> String.format("Legacy QORA holders share of %s: %s", totalAmount.toPlainString(), qoraHoldersAmount.toPlainString()));
|
||||
|
||||
List<AccountBalanceData> qoraHolders = this.repository.getAccountRepository().getAssetBalances(Asset.LEGACY_QORA, true);
|
||||
final boolean isProcessingNotOrphaning = totalAmount.signum() >= 0;
|
||||
|
||||
// Filter out qoraHolders who have received max QORT due to holding legacy QORA, (ratio from blockchain config)
|
||||
BigDecimal qoraPerQortReward = BlockChain.getInstance().getQoraPerQortReward();
|
||||
Iterator<AccountBalanceData> qoraHoldersIterator = qoraHolders.iterator();
|
||||
while (qoraHoldersIterator.hasNext()) {
|
||||
AccountBalanceData qoraHolder = qoraHoldersIterator.next();
|
||||
|
||||
Account qoraHolderAccount = new Account(repository, qoraHolder.getAddress());
|
||||
BigDecimal qortFromQora = qoraHolderAccount.getConfirmedBalance(Asset.QORT_FROM_QORA);
|
||||
|
||||
// If we're processing a block, then totalAmount will be positive
|
||||
if (totalAmount.signum() >= 0) {
|
||||
BigDecimal maxQortFromQora = qoraHolder.getBalance().divide(qoraPerQortReward, RoundingMode.DOWN);
|
||||
|
||||
// Disregard qora holders who have already received maximum qort from holding legacy qora
|
||||
if (qortFromQora.compareTo(maxQortFromQora) >= 0)
|
||||
qoraHoldersIterator.remove();
|
||||
} else {
|
||||
// We're orphaning a block
|
||||
// so disregard qora holders who have already had their final qort-from-qora reward (i.e. reward reward block is earlier than this one)
|
||||
QortFromQoraData qortFromQoraData = this.repository.getAccountRepository().getQortFromQoraInfo(qoraHolder.getAddress());
|
||||
if (qortFromQoraData != null && qortFromQoraData.getFinalBlockHeight() < this.blockData.getHeight())
|
||||
qoraHoldersIterator.remove();
|
||||
}
|
||||
}
|
||||
List<AccountBalanceData> qoraHolders = this.repository.getAccountRepository().getEligibleLegacyQoraHolders(isProcessingNotOrphaning ? null : this.blockData.getHeight());
|
||||
|
||||
BigDecimal totalQoraHeld = BigDecimal.ZERO;
|
||||
for (int i = 0; i < qoraHolders.size(); ++i)
|
||||
@@ -1666,6 +1683,7 @@ public class Block {
|
||||
BigDecimal finalTotalQoraHeld = totalQoraHeld;
|
||||
LOGGER.trace(() -> String.format("Total legacy QORA held: %s", finalTotalQoraHeld.toPlainString()));
|
||||
|
||||
BigDecimal sharedAmount = BigDecimal.ZERO;
|
||||
for (int h = 0; h < qoraHolders.size(); ++h) {
|
||||
AccountBalanceData qoraHolder = qoraHolders.get(h);
|
||||
|
||||
@@ -1674,12 +1692,16 @@ public class Block {
|
||||
LOGGER.trace(() -> String.format("QORA holder %s has %s / %s QORA so share: %s",
|
||||
qoraHolder.getAddress(), qoraHolder.getBalance().toPlainString(), finalTotalQoraHeld, finalHolderReward.toPlainString()));
|
||||
|
||||
// Too small to register this time?
|
||||
if (holderReward.signum() == 0)
|
||||
continue;
|
||||
|
||||
Account qoraHolderAccount = new Account(repository, qoraHolder.getAddress());
|
||||
|
||||
BigDecimal newQortFromQoraBalance = qoraHolderAccount.getConfirmedBalance(Asset.QORT_FROM_QORA).add(holderReward);
|
||||
|
||||
// If processing, make sure we don't overpay
|
||||
if (totalAmount.signum() >= 0) {
|
||||
if (isProcessingNotOrphaning) {
|
||||
BigDecimal maxQortFromQora = qoraHolder.getBalance().divide(qoraPerQortReward, RoundingMode.DOWN);
|
||||
|
||||
if (newQortFromQoraBalance.compareTo(maxQortFromQora) >= 0) {
|
||||
@@ -1689,7 +1711,7 @@ public class Block {
|
||||
holderReward = holderReward.subtract(adjustment);
|
||||
newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment);
|
||||
|
||||
// This is also qora holders final qort-from-qora block
|
||||
// This is also the QORA holder's final QORT-from-QORA block
|
||||
QortFromQoraData qortFromQoraData = new QortFromQoraData(qoraHolder.getAddress(), holderReward, this.blockData.getHeight());
|
||||
this.repository.getAccountRepository().save(qortFromQoraData);
|
||||
|
||||
@@ -1701,9 +1723,10 @@ public class Block {
|
||||
// Orphaning
|
||||
QortFromQoraData qortFromQoraData = this.repository.getAccountRepository().getQortFromQoraInfo(qoraHolder.getAddress());
|
||||
if (qortFromQoraData != null) {
|
||||
// Note use of negate() here as qortFromQora will be negative during orphaning,
|
||||
// but final qort-from-qora is stored in repository during processing (and hence positive).
|
||||
BigDecimal adjustment = holderReward.subtract(qortFromQoraData.getFinalQortFromQora().negate());
|
||||
// Final QORT-from-QORA amount from repository was stored during processing, and hence positive.
|
||||
// So we use add() here as qortFromQora is negative during orphaning.
|
||||
// More efficient than holderReward.subtract(final-qort-from-qora.negate())
|
||||
BigDecimal adjustment = holderReward.add(qortFromQoraData.getFinalQortFromQora());
|
||||
|
||||
holderReward = holderReward.subtract(adjustment);
|
||||
newQortFromQoraBalance = newQortFromQoraBalance.subtract(adjustment);
|
||||
@@ -1716,7 +1739,8 @@ public class Block {
|
||||
}
|
||||
}
|
||||
|
||||
qoraHolderAccount.setConfirmedBalance(Asset.QORT, qoraHolderAccount.getConfirmedBalance(Asset.QORT).add(holderReward));
|
||||
// qoraHolderAccount.setConfirmedBalance(Asset.QORT, qoraHolderAccount.getConfirmedBalance(Asset.QORT).add(holderReward));
|
||||
this.repository.getAccountRepository().modifyAssetBalance(qoraHolder.getAddress(), Asset.QORT, holderReward);
|
||||
|
||||
if (newQortFromQoraBalance.signum() > 0)
|
||||
qoraHolderAccount.setConfirmedBalance(Asset.QORT_FROM_QORA, newQortFromQoraBalance);
|
||||
@@ -1727,27 +1751,39 @@ public class Block {
|
||||
sharedAmount = sharedAmount.add(holderReward);
|
||||
}
|
||||
|
||||
// Spread remainder across founder accounts
|
||||
BigDecimal foundersAmount = totalAmount.subtract(sharedAmount);
|
||||
BigDecimal finalSharedAmount = sharedAmount;
|
||||
return sharedAmount;
|
||||
}
|
||||
|
||||
private void distributeBlockRewardToFounders(BigDecimal foundersAmount) throws DataException {
|
||||
// Remaining reward portion is spread across all founders, online or not
|
||||
List<AccountData> founderAccounts = this.repository.getAccountRepository().getFlaggedAccounts(Account.FOUNDER_FLAG);
|
||||
BigDecimal foundersCount = BigDecimal.valueOf(founderAccounts.size());
|
||||
BigDecimal perFounderAmount = foundersAmount.divide(foundersCount, RoundingMode.DOWN);
|
||||
|
||||
LOGGER.trace(() -> String.format("Shared %s of %s, remaining %s to %d founder%s, %s each",
|
||||
finalSharedAmount.toPlainString(), totalAmount.toPlainString(),
|
||||
LOGGER.trace(() -> String.format("Sharing remaining %s to %d founder%s, %s each",
|
||||
foundersAmount.toPlainString(), founderAccounts.size(), (founderAccounts.size() != 1 ? "s" : ""),
|
||||
perFounderAmount.toPlainString()));
|
||||
|
||||
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
||||
for (int a = 0; a < founderAccounts.size(); ++a) {
|
||||
Account founderAccount = new Account(this.repository, founderAccounts.get(a).getAddress());
|
||||
|
||||
// If founder is minter in any online reward-shares then founder's amount is spread across these, otherwise founder gets whole amount.
|
||||
|
||||
/* Fixed version:
|
||||
List<ExpandedAccount> founderExpandedAccounts = expandedAccounts.stream().filter(
|
||||
accountInfo -> accountInfo.isMinterFounder &&
|
||||
accountInfo.mintingAccountData.getAddress().equals(founderAccount.getAddress())
|
||||
).collect(Collectors.toList());
|
||||
*/
|
||||
|
||||
// Broken version:
|
||||
List<ExpandedAccount> founderExpandedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.isMinterFounder).collect(Collectors.toList());
|
||||
|
||||
if (founderExpandedAccounts.isEmpty()) {
|
||||
// Simple case: no founder-as-minter reward-shares online so founder gets whole amount.
|
||||
Account founderAccount = new Account(this.repository, founderAccounts.get(a).getAddress());
|
||||
founderAccount.setConfirmedBalance(Asset.QORT, founderAccount.getConfirmedBalance(Asset.QORT).add(perFounderAmount));
|
||||
// founderAccount.setConfirmedBalance(Asset.QORT, founderAccount.getConfirmedBalance(Asset.QORT).add(perFounderAmount));
|
||||
this.repository.getAccountRepository().modifyAssetBalance(founderAccount.getAddress(), Asset.QORT, perFounderAmount);
|
||||
} else {
|
||||
// Distribute over reward-shares
|
||||
BigDecimal perFounderRewardShareAmount = perFounderAmount.divide(BigDecimal.valueOf(founderExpandedAccounts.size()), RoundingMode.DOWN);
|
||||
|
@@ -137,19 +137,18 @@ public class BlockMinter extends Thread {
|
||||
// Disregard peers that have "misbehaved" recently
|
||||
peers.removeIf(Controller.hasMisbehaved);
|
||||
|
||||
// Don't mint if we don't have enough connected peers as where would the transactions/consensus come from?
|
||||
if (peers.size() < Settings.getInstance().getMinBlockchainPeers())
|
||||
continue;
|
||||
|
||||
// Disregard peers that don't have a recent block
|
||||
peers.removeIf(Controller.hasNoRecentBlock);
|
||||
|
||||
// If we have any peers with a recent block, but our latest block isn't recent
|
||||
// then we need to synchronize instead of minting.
|
||||
// Don't mint if we don't have enough up-to-date peers as where would the transactions/consensus come from?
|
||||
if (peers.size() < Settings.getInstance().getMinBlockchainPeers())
|
||||
continue;
|
||||
|
||||
// If our latest block isn't recent then we need to synchronize instead of minting.
|
||||
if (!peers.isEmpty() && lastBlockData.getTimestamp() < minLatestBlockTimestamp)
|
||||
continue;
|
||||
|
||||
// There are no peers with a recent block and/or our latest block is recent
|
||||
// There are enough peers with a recent block and our latest block is recent
|
||||
// so go ahead and mint a block if possible.
|
||||
isMintingPossible = true;
|
||||
|
||||
@@ -341,17 +340,19 @@ public class BlockMinter extends Thread {
|
||||
this.interrupt();
|
||||
}
|
||||
|
||||
public static void mintTestingBlock(Repository repository, PrivateKeyAccount mintingAccount) throws DataException {
|
||||
public static void mintTestingBlock(Repository repository, PrivateKeyAccount... mintingAndOnlineAccounts) throws DataException {
|
||||
if (!BlockChain.getInstance().isTestChain()) {
|
||||
LOGGER.warn("Ignoring attempt to mint testing block for non-test chain!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure mintingAccount is 'online' so blocks can be minted
|
||||
Controller.getInstance().ensureTestingAccountOnline(mintingAccount);
|
||||
Controller.getInstance().ensureTestingAccountsOnline(mintingAndOnlineAccounts);
|
||||
|
||||
BlockData previousBlockData = repository.getBlockRepository().getLastBlock();
|
||||
|
||||
PrivateKeyAccount mintingAccount = mintingAndOnlineAccounts[0];
|
||||
|
||||
Block newBlock = Block.mint(repository, previousBlockData, mintingAccount);
|
||||
|
||||
// Make sure we're the only thread modifying the blockchain
|
||||
|
@@ -231,6 +231,10 @@ public class AutoUpdate extends Thread {
|
||||
// JVM arguments
|
||||
javaCmd.addAll(ManagementFactory.getRuntimeMXBean().getInputArguments());
|
||||
|
||||
// Remove JNI options as they won't be supported by command-line 'java'
|
||||
// These are typically added by the AdvancedInstaller Java launcher EXE
|
||||
javaCmd.removeAll(Arrays.asList("abort", "exit", "vfprintf"));
|
||||
|
||||
// Call ApplyUpdate using new JAR
|
||||
javaCmd.addAll(Arrays.asList("-cp", NEW_JAR_FILENAME, ApplyUpdate.class.getCanonicalName()));
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package org.qortal.controller;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.SecureRandom;
|
||||
@@ -77,7 +78,6 @@ import org.qortal.transaction.ArbitraryTransaction;
|
||||
import org.qortal.transaction.Transaction;
|
||||
import org.qortal.transaction.Transaction.TransactionType;
|
||||
import org.qortal.transaction.Transaction.ValidationResult;
|
||||
import org.qortal.ui.UiService;
|
||||
import org.qortal.utils.Base58;
|
||||
import org.qortal.utils.ByteArray;
|
||||
import org.qortal.utils.NTP;
|
||||
@@ -100,7 +100,7 @@ public class Controller extends Thread {
|
||||
private static final long MISBEHAVIOUR_COOLOFF = 10 * 60 * 1000L; // ms
|
||||
private static final int MAX_BLOCKCHAIN_TIP_AGE = 5; // blocks
|
||||
private static final Object shutdownLock = new Object();
|
||||
private static final String repositoryUrlTemplate = "jdbc:hsqldb:file:%s/blockchain;create=true;hsqldb.full_log_replay=true";
|
||||
private static final String repositoryUrlTemplate = "jdbc:hsqldb:file:%s" + File.separator + "blockchain;create=true;hsqldb.full_log_replay=true";
|
||||
private static final long ARBITRARY_REQUEST_TIMEOUT = 5 * 1000L; // ms
|
||||
private static final long REPOSITORY_BACKUP_PERIOD = 123 * 60 * 1000L; // ms
|
||||
private static final long NTP_PRE_SYNC_CHECK_PERIOD = 5 * 1000L; // ms
|
||||
@@ -133,6 +133,9 @@ public class Controller extends Thread {
|
||||
/** Whether we can mint new blocks, as reported by BlockMinter. */
|
||||
private volatile boolean isMintingPossible = false;
|
||||
|
||||
/** Whether we are attempting to synchronize. */
|
||||
private volatile boolean isSynchronizing = false;
|
||||
|
||||
/** Latest block signatures from other peers that we know are on inferior chains. */
|
||||
List<ByteArray> inferiorChainSignatures = new ArrayList<>();
|
||||
|
||||
@@ -241,6 +244,19 @@ public class Controller extends Thread {
|
||||
return this.savedArgs;
|
||||
}
|
||||
|
||||
/* package */ static boolean isStopping() {
|
||||
return isStopping;
|
||||
}
|
||||
|
||||
// For API use
|
||||
public boolean isMintingPossible() {
|
||||
return this.isMintingPossible;
|
||||
}
|
||||
|
||||
public boolean isSynchronizing() {
|
||||
return this.isSynchronizing;
|
||||
}
|
||||
|
||||
// Entry point
|
||||
|
||||
public static void main(String[] args) {
|
||||
@@ -311,6 +327,7 @@ public class Controller extends Thread {
|
||||
network.start();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error("Unable to start networking", e);
|
||||
Controller.getInstance().shutdown();
|
||||
Gui.getInstance().fatalError("Networking failure", e);
|
||||
return; // Not System.exit() so that GUI can display error
|
||||
}
|
||||
@@ -344,20 +361,11 @@ public class Controller extends Thread {
|
||||
apiService.start();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Unable to start API", e);
|
||||
Controller.getInstance().shutdown();
|
||||
Gui.getInstance().fatalError("API failure", e);
|
||||
return; // Not System.exit() so that GUI can display error
|
||||
}
|
||||
|
||||
LOGGER.info(String.format("Starting node management UI on port %d", Settings.getInstance().getUiPort()));
|
||||
try {
|
||||
UiService uiService = UiService.getInstance();
|
||||
uiService.start();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Unable to start node management UI", e);
|
||||
Gui.getInstance().fatalError("Node management UI failure", e);
|
||||
return; // Not System.exit() so that GUI can display error
|
||||
}
|
||||
|
||||
// If GUI is enabled, we're no longer starting up but actually running now
|
||||
Gui.getInstance().notifyRunning();
|
||||
}
|
||||
@@ -501,7 +509,13 @@ public class Controller extends Thread {
|
||||
int index = new SecureRandom().nextInt(peers.size());
|
||||
Peer peer = peers.get(index);
|
||||
|
||||
isSynchronizing = true;
|
||||
updateSysTray();
|
||||
|
||||
actuallySynchronize(peer, false);
|
||||
|
||||
isSynchronizing = false;
|
||||
requestSysTrayUpdate = true;
|
||||
}
|
||||
|
||||
public SynchronizationResult actuallySynchronize(Peer peer, boolean force) throws InterruptedException {
|
||||
@@ -553,6 +567,10 @@ public class Controller extends Thread {
|
||||
LOGGER.debug(() -> String.format("Failed to synchronize with peer %s (%s)", peer, syncResult.name()));
|
||||
break;
|
||||
|
||||
case SHUTTING_DOWN:
|
||||
// Just quietly exit
|
||||
break;
|
||||
|
||||
case OK:
|
||||
requestSysTrayUpdate = true;
|
||||
// fall-through...
|
||||
@@ -601,9 +619,17 @@ public class Controller extends Thread {
|
||||
|
||||
String connectionsText = Translator.INSTANCE.translate("SysTray", numberOfPeers != 1 ? "CONNECTIONS" : "CONNECTION");
|
||||
String heightText = Translator.INSTANCE.translate("SysTray", "BLOCK_HEIGHT");
|
||||
String mintingText = Translator.INSTANCE.translate("SysTray", isMintingPossible ? "MINTING_ENABLED" : "MINTING_DISABLED");
|
||||
|
||||
String tooltip = String.format("%s - %d %s - %s %d", mintingText, numberOfPeers, connectionsText, heightText, height);
|
||||
String actionKey;
|
||||
if (isMintingPossible)
|
||||
actionKey = "MINTING_ENABLED";
|
||||
else if (isSynchronizing)
|
||||
actionKey = "SYNCHRONIZING_BLOCKCHAIN";
|
||||
else
|
||||
actionKey = "MINTING_DISABLED";
|
||||
String actionText = Translator.INSTANCE.translate("SysTray", actionKey);
|
||||
|
||||
String tooltip = String.format("%s - %d %s - %s %d", actionText, numberOfPeers, connectionsText, heightText, height);
|
||||
SysTray.getInstance().setToolTipText(tooltip);
|
||||
}
|
||||
|
||||
@@ -638,9 +664,6 @@ public class Controller extends Thread {
|
||||
if (!isStopping) {
|
||||
isStopping = true;
|
||||
|
||||
LOGGER.info("Shutting down node management UI");
|
||||
UiService.getInstance().stop();
|
||||
|
||||
LOGGER.info("Shutting down API");
|
||||
ApiService.getInstance().stop();
|
||||
|
||||
@@ -1345,7 +1368,7 @@ public class Controller extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
public void ensureTestingAccountOnline(PrivateKeyAccount mintingAccount) {
|
||||
public void ensureTestingAccountsOnline(PrivateKeyAccount... onlineAccounts) {
|
||||
if (!BlockChain.getInstance().isTestChain()) {
|
||||
LOGGER.warn("Ignoring attempt to ensure test account is online for non-test chain!");
|
||||
return;
|
||||
@@ -1355,19 +1378,21 @@ public class Controller extends Thread {
|
||||
if (now == null)
|
||||
return;
|
||||
|
||||
// Check mintingAccount is actually reward-share?
|
||||
|
||||
// Add reward-share & timestamp to online accounts
|
||||
final long onlineAccountsTimestamp = Controller.toOnlineAccountTimestamp(now);
|
||||
byte[] timestampBytes = Longs.toByteArray(onlineAccountsTimestamp);
|
||||
|
||||
byte[] signature = mintingAccount.sign(timestampBytes);
|
||||
byte[] publicKey = mintingAccount.getPublicKey();
|
||||
|
||||
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
||||
synchronized (this.onlineAccounts) {
|
||||
this.onlineAccounts.clear();
|
||||
this.onlineAccounts.add(ourOnlineAccountData);
|
||||
|
||||
for (PrivateKeyAccount onlineAccount : onlineAccounts) {
|
||||
// Check mintingAccount is actually reward-share?
|
||||
|
||||
byte[] signature = onlineAccount.sign(timestampBytes);
|
||||
byte[] publicKey = onlineAccount.getPublicKey();
|
||||
|
||||
OnlineAccountData ourOnlineAccountData = new OnlineAccountData(onlineAccountsTimestamp, signature, publicKey);
|
||||
this.onlineAccounts.add(ourOnlineAccountData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -3,7 +3,7 @@ package org.qortal.controller;
|
||||
import java.math.BigInteger;
|
||||
import java.text.DecimalFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -39,13 +39,13 @@ public class Synchronizer {
|
||||
|
||||
private static final int INITIAL_BLOCK_STEP = 8;
|
||||
private static final int MAXIMUM_BLOCK_STEP = 500;
|
||||
private static final int MAXIMUM_COMMON_DELTA = 1440; // XXX move to Settings?
|
||||
private static final int MAXIMUM_COMMON_DELTA = 240; // XXX move to Settings?
|
||||
private static final int SYNC_BATCH_SIZE = 200;
|
||||
|
||||
private static Synchronizer instance;
|
||||
|
||||
public enum SynchronizationResult {
|
||||
OK, NOTHING_TO_DO, GENESIS_ONLY, NO_COMMON_BLOCK, TOO_DIVERGENT, NO_REPLY, INFERIOR_CHAIN, INVALID_DATA, NO_BLOCKCHAIN_LOCK, REPOSITORY_ISSUE;
|
||||
OK, NOTHING_TO_DO, GENESIS_ONLY, NO_COMMON_BLOCK, TOO_DIVERGENT, NO_REPLY, INFERIOR_CHAIN, INVALID_DATA, NO_BLOCKCHAIN_LOCK, REPOSITORY_ISSUE, SHUTTING_DOWN;
|
||||
}
|
||||
|
||||
// Constructors
|
||||
@@ -93,15 +93,11 @@ public class Synchronizer {
|
||||
peerHeight, Base58.encode(peersLastBlockSignature), peer.getChainTipData().getLastBlockTimestamp(),
|
||||
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp()));
|
||||
|
||||
List<BlockSummaryData> peerBlockSummaries = fetchSummariesFromCommonBlock(repository, peer, ourInitialHeight);
|
||||
if (peerBlockSummaries == null) {
|
||||
LOGGER.info(String.format("Error while trying to find common block with peer %s", peer));
|
||||
return SynchronizationResult.NO_REPLY;
|
||||
}
|
||||
if (peerBlockSummaries.isEmpty()) {
|
||||
LOGGER.info(String.format("Failure to find common block with peer %s", peer));
|
||||
return SynchronizationResult.NO_COMMON_BLOCK;
|
||||
}
|
||||
List<BlockSummaryData> peerBlockSummaries = new ArrayList<>();
|
||||
SynchronizationResult findCommonBlockResult = fetchSummariesFromCommonBlock(repository, peer, ourInitialHeight, force, peerBlockSummaries);
|
||||
if (findCommonBlockResult != SynchronizationResult.OK)
|
||||
// Logging performed by fetchSummariesFromCommonBlock() above
|
||||
return findCommonBlockResult;
|
||||
|
||||
// First summary is common block
|
||||
final BlockData commonBlockData = repository.getBlockRepository().fromSignature(peerBlockSummaries.get(0).getSignature());
|
||||
@@ -129,13 +125,6 @@ public class Synchronizer {
|
||||
return SynchronizationResult.NOTHING_TO_DO;
|
||||
}
|
||||
|
||||
// If common block is too far behind us then we're on massively different forks so give up.
|
||||
int minCommonHeight = ourInitialHeight - MAXIMUM_COMMON_DELTA;
|
||||
if (!force && commonBlockHeight < minCommonHeight) {
|
||||
LOGGER.info(String.format("Blockchain too divergent with peer %s", peer));
|
||||
return SynchronizationResult.TOO_DIVERGENT;
|
||||
}
|
||||
|
||||
// Unless we're doing a forced sync, we might need to compare blocks after common block
|
||||
if (!force && ourInitialHeight > commonBlockHeight) {
|
||||
// If our latest block is very old, we're very behind and should ditch our fork.
|
||||
@@ -154,6 +143,9 @@ public class Synchronizer {
|
||||
int peerBlockCount = peerHeight - commonBlockHeight;
|
||||
|
||||
while (peerBlockSummaries.size() < peerBlockCount) {
|
||||
if (Controller.isStopping())
|
||||
return SynchronizationResult.SHUTTING_DOWN;
|
||||
|
||||
int lastSummaryHeight = commonBlockHeight + peerBlockSummaries.size();
|
||||
byte[] previousSignature;
|
||||
if (peerBlockSummaries.isEmpty())
|
||||
@@ -212,6 +204,9 @@ public class Synchronizer {
|
||||
LOGGER.debug(String.format("Orphaning blocks back to common block height %d, sig %.8s", commonBlockHeight, commonBlockSig58));
|
||||
|
||||
while (ourHeight > commonBlockHeight) {
|
||||
if (Controller.isStopping())
|
||||
return SynchronizationResult.SHUTTING_DOWN;
|
||||
|
||||
BlockData blockData = repository.getBlockRepository().fromHeight(ourHeight);
|
||||
Block block = new Block(repository, blockData);
|
||||
block.orphan();
|
||||
@@ -232,6 +227,9 @@ public class Synchronizer {
|
||||
List<byte[]> peerBlockSignatures = peerBlockSummaries.stream().map(BlockSummaryData::getSignature).collect(Collectors.toList());
|
||||
|
||||
while (ourHeight < peerHeight && ourHeight < maxBatchHeight) {
|
||||
if (Controller.isStopping())
|
||||
return SynchronizationResult.SHUTTING_DOWN;
|
||||
|
||||
// Do we need more signatures?
|
||||
if (peerBlockSignatures.isEmpty()) {
|
||||
int numberRequested = maxBatchHeight - ourHeight;
|
||||
@@ -320,45 +318,59 @@ public class Synchronizer {
|
||||
* @throws DataException
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
private List<BlockSummaryData> fetchSummariesFromCommonBlock(Repository repository, Peer peer, int ourHeight) throws DataException, InterruptedException {
|
||||
private SynchronizationResult fetchSummariesFromCommonBlock(Repository repository, Peer peer, int ourHeight, boolean force, List<BlockSummaryData> blockSummariesFromCommon) throws DataException, InterruptedException {
|
||||
// Start by asking for a few recent block hashes as this will cover a majority of reorgs
|
||||
// Failing that, back off exponentially
|
||||
int step = INITIAL_BLOCK_STEP;
|
||||
|
||||
List<BlockSummaryData> blockSummaries = null;
|
||||
|
||||
int testHeight = Math.max(ourHeight - step, 1);
|
||||
BlockData testBlockData = null;
|
||||
|
||||
List<BlockSummaryData> blockSummariesBatch = null;
|
||||
|
||||
while (testHeight >= 1) {
|
||||
// Are we shutting down?
|
||||
if (Controller.isStopping())
|
||||
return SynchronizationResult.SHUTTING_DOWN;
|
||||
|
||||
// Fetch our block signature at this height
|
||||
testBlockData = repository.getBlockRepository().fromHeight(testHeight);
|
||||
if (testBlockData == null) {
|
||||
// Not found? But we've locked the blockchain and height is below blockchain's tip!
|
||||
LOGGER.error("Failed to get block at height lower than blockchain tip during synchronization?");
|
||||
return null;
|
||||
return SynchronizationResult.REPOSITORY_ISSUE;
|
||||
}
|
||||
|
||||
// Ask for block signatures since test block's signature
|
||||
byte[] testSignature = testBlockData.getSignature();
|
||||
LOGGER.trace(String.format("Requesting %d summar%s after height %d", step, (step != 1 ? "ies": "y"), testHeight));
|
||||
blockSummaries = this.getBlockSummaries(peer, testSignature, step);
|
||||
blockSummariesBatch = this.getBlockSummaries(peer, testSignature, step);
|
||||
|
||||
if (blockSummaries == null)
|
||||
if (blockSummariesBatch == null) {
|
||||
// No response - give up this time
|
||||
return null;
|
||||
LOGGER.info(String.format("Error while trying to find common block with peer %s", peer));
|
||||
return SynchronizationResult.NO_REPLY;
|
||||
}
|
||||
|
||||
LOGGER.trace(String.format("Received %s summar%s", blockSummaries.size(), (blockSummaries.size() != 1 ? "ies" : "y")));
|
||||
LOGGER.trace(String.format("Received %s summar%s", blockSummariesBatch.size(), (blockSummariesBatch.size() != 1 ? "ies" : "y")));
|
||||
|
||||
// Empty list means remote peer is unaware of test signature OR has no new blocks after test signature
|
||||
if (!blockSummaries.isEmpty())
|
||||
if (!blockSummariesBatch.isEmpty())
|
||||
// We have entries so we have found a common block
|
||||
break;
|
||||
|
||||
// No blocks after genesis block?
|
||||
// We don't get called for a peer at genesis height so this means NO blocks in common
|
||||
if (testHeight == 1)
|
||||
return Collections.emptyList();
|
||||
if (testHeight == 1) {
|
||||
LOGGER.info(String.format("Failure to find common block with peer %s", peer));
|
||||
return SynchronizationResult.NO_COMMON_BLOCK;
|
||||
}
|
||||
|
||||
// If common block is too far behind us then we're on massively different forks so give up.
|
||||
if (!force && testHeight < ourHeight - MAXIMUM_COMMON_DELTA) {
|
||||
LOGGER.info(String.format("Blockchain too divergent with peer %s", peer));
|
||||
return SynchronizationResult.TOO_DIVERGENT;
|
||||
}
|
||||
|
||||
if (peer.getVersion() >= 2) {
|
||||
step <<= 1;
|
||||
@@ -373,20 +385,21 @@ public class Synchronizer {
|
||||
|
||||
// Prepend test block's summary as first block summary, as summaries returned are *after* test block
|
||||
BlockSummaryData testBlockSummary = new BlockSummaryData(testBlockData);
|
||||
blockSummaries.add(0, testBlockSummary);
|
||||
blockSummariesFromCommon.add(0, testBlockSummary);
|
||||
blockSummariesFromCommon.addAll(blockSummariesBatch);
|
||||
|
||||
// Trim summaries so that first summary is common block.
|
||||
// Currently we work back from the end until we hit a block we also have.
|
||||
// TODO: rewrite as modified binary search!
|
||||
for (int i = blockSummaries.size() - 1; i > 0; --i) {
|
||||
if (repository.getBlockRepository().exists(blockSummaries.get(i).getSignature())) {
|
||||
for (int i = blockSummariesFromCommon.size() - 1; i > 0; --i) {
|
||||
if (repository.getBlockRepository().exists(blockSummariesFromCommon.get(i).getSignature())) {
|
||||
// Note: index i isn't cleared: List.subList is fromIndex inclusive to toIndex exclusive
|
||||
blockSummaries.subList(0, i).clear();
|
||||
blockSummariesFromCommon.subList(0, i).clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return blockSummaries;
|
||||
return SynchronizationResult.OK;
|
||||
}
|
||||
|
||||
private List<BlockSummaryData> getBlockSummaries(Peer peer, byte[] parentSignature, int numberRequested) throws InterruptedException {
|
||||
|
@@ -10,11 +10,14 @@ import java.awt.event.WindowEvent;
|
||||
import java.awt.event.WindowFocusListener;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URL;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -30,7 +33,7 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.qortal.controller.Controller;
|
||||
import org.qortal.globalization.Translator;
|
||||
import org.qortal.settings.Settings;
|
||||
import org.qortal.ui.UiService;
|
||||
import org.qortal.utils.RandomizeList;
|
||||
import org.qortal.utils.URLViewer;
|
||||
|
||||
public class SysTray {
|
||||
@@ -144,15 +147,11 @@ public class SysTray {
|
||||
}
|
||||
});
|
||||
|
||||
JMenuItem openUi = new JMenuItem(Translator.INSTANCE.translate("SysTray", "OPEN_NODE_UI"));
|
||||
JMenuItem openUi = new JMenuItem(Translator.INSTANCE.translate("SysTray", "OPEN_UI"));
|
||||
openUi.addActionListener(actionEvent -> {
|
||||
destroyHiddenDialog();
|
||||
|
||||
try {
|
||||
URLViewer.openWebpage(new URL("http://localhost:" + Settings.getInstance().getUiPort()));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Unable to open node UI in browser");
|
||||
}
|
||||
new OpenUiWorker().execute();
|
||||
});
|
||||
menu.add(openUi);
|
||||
|
||||
@@ -174,7 +173,7 @@ public class SysTray {
|
||||
syncTime.addActionListener(actionEvent -> {
|
||||
destroyHiddenDialog();
|
||||
|
||||
new SynchronizeWorker().execute();
|
||||
new SynchronizeClockWorker().execute();
|
||||
});
|
||||
menu.add(syncTime);
|
||||
}
|
||||
@@ -190,11 +189,53 @@ public class SysTray {
|
||||
return menu;
|
||||
}
|
||||
|
||||
class SynchronizeWorker extends SwingWorker<Void, Void> {
|
||||
static class OpenUiWorker extends SwingWorker<Void, Void> {
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
List<String> uiServers = new ArrayList<>();
|
||||
|
||||
String[] remoteUiServers = Settings.getInstance().getRemoteUiServers();
|
||||
uiServers.addAll(Arrays.asList(remoteUiServers));
|
||||
// Randomize remote servers
|
||||
uiServers = RandomizeList.randomize(uiServers);
|
||||
|
||||
// Prepend local servers
|
||||
String[] localUiServers = Settings.getInstance().getLocalUiServers();
|
||||
uiServers.addAll(0, Arrays.asList(localUiServers));
|
||||
|
||||
// Check each server in turn before opening browser tab
|
||||
int uiPort = Settings.getInstance().getUiServerPort();
|
||||
for (String uiServer : uiServers) {
|
||||
InetSocketAddress socketAddress = new InetSocketAddress(uiServer, uiPort);
|
||||
|
||||
// If we couldn't resolve try next
|
||||
if (socketAddress.isUnresolved())
|
||||
continue;
|
||||
|
||||
try (SocketChannel socketChannel = SocketChannel.open()) {
|
||||
socketChannel.socket().connect(socketAddress, 100);
|
||||
|
||||
// If we reach here, then socket connected to UI server!
|
||||
URLViewer.openWebpage(new URL(String.format("http://%s:%d", uiServer, uiPort)));
|
||||
|
||||
return null;
|
||||
} catch (IOException e) {
|
||||
// try next server
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Unable to open UI website in browser");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static class SynchronizeClockWorker extends SwingWorker<Void, Void> {
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
// Extract reconfiguration script from resources
|
||||
String resourceName = "/" + UiService.DOWNLOADS_RESOURCE_PATH + "/" + NTP_SCRIPT;
|
||||
String resourceName = "/node-management/" + NTP_SCRIPT;
|
||||
Path scriptPath = Paths.get(NTP_SCRIPT);
|
||||
|
||||
try (InputStream in = SysTray.class.getResourceAsStream(resourceName)) {
|
||||
@@ -218,7 +259,7 @@ public class SysTray {
|
||||
}
|
||||
}
|
||||
|
||||
class ClosingWorker extends SwingWorker<Void, Void> {
|
||||
static class ClosingWorker extends SwingWorker<Void, Void> {
|
||||
@Override
|
||||
protected Void doInBackground() {
|
||||
Controller.getInstance().shutdown();
|
||||
|
@@ -56,6 +56,7 @@ import org.qortal.repository.Repository;
|
||||
import org.qortal.repository.RepositoryManager;
|
||||
import org.qortal.settings.Settings;
|
||||
import org.qortal.utils.ExecuteProduceConsume;
|
||||
import org.qortal.utils.ExecuteProduceConsume.StatsSnapshot;
|
||||
import org.qortal.utils.NTP;
|
||||
|
||||
// For managing peers
|
||||
@@ -88,7 +89,10 @@ public class Network {
|
||||
"node4.qortal.org",
|
||||
"node5.qortal.org",
|
||||
"node6.qortal.org",
|
||||
"node7.qortal.org"
|
||||
"node7.qortal.org",
|
||||
"node8.qortal.org",
|
||||
"node9.qortal.org",
|
||||
"node10.qortal.org"
|
||||
};
|
||||
|
||||
public static final int MAX_SIGNATURES_PER_REPLY = 500;
|
||||
@@ -196,6 +200,10 @@ public class Network {
|
||||
return this.maxMessageSize;
|
||||
}
|
||||
|
||||
public StatsSnapshot getStatsSnapshot() {
|
||||
return this.networkEPC.getStatsSnapshot();
|
||||
}
|
||||
|
||||
// Peer lists
|
||||
|
||||
public List<Peer> getConnectedPeers() {
|
||||
@@ -478,18 +486,20 @@ public class Network {
|
||||
|
||||
try {
|
||||
if (now == null) {
|
||||
LOGGER.debug(String.format("Connection discarded from peer %s due to lack of NTP sync", socketChannel.getRemoteAddress()));
|
||||
LOGGER.debug(() -> String.format("Connection discarded from peer %s due to lack of NTP sync", PeerAddress.fromSocket(socketChannel.socket())));
|
||||
socketChannel.close();
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (this.connectedPeers) {
|
||||
if (connectedPeers.size() >= maxPeers) {
|
||||
// We have enough peers
|
||||
LOGGER.debug(String.format("Connection discarded from peer %s", socketChannel.getRemoteAddress()));
|
||||
LOGGER.debug(() -> String.format("Connection discarded from peer %s", PeerAddress.fromSocket(socketChannel.socket())));
|
||||
socketChannel.close();
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.debug(String.format("Connection accepted from peer %s", socketChannel.getRemoteAddress()));
|
||||
LOGGER.debug(() -> String.format("Connection accepted from peer %s", PeerAddress.fromSocket(socketChannel.socket())));
|
||||
|
||||
newPeer = new Peer(socketChannel);
|
||||
this.connectedPeers.add(newPeer);
|
||||
|
@@ -61,6 +61,8 @@ public class Peer {
|
||||
private InetSocketAddress resolvedAddress = null;
|
||||
/** True if remote address is loopback/link-local/site-local, false otherwise. */
|
||||
private boolean isLocal;
|
||||
|
||||
private final Object byteBufferLock = new Object();
|
||||
private volatile ByteBuffer byteBuffer;
|
||||
private Map<Integer, BlockingQueue<Message>> replyQueues;
|
||||
private LinkedBlockingQueue<Message> pendingMessages;
|
||||
@@ -256,7 +258,7 @@ public class Peer {
|
||||
this.connectionTimestamp = NTP.getTime();
|
||||
this.socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
|
||||
this.socketChannel.configureBlocking(false);
|
||||
this.byteBuffer = ByteBuffer.allocate(Network.getInstance().getMaxMessageSize());
|
||||
this.byteBuffer = null; // Defer allocation to when we need it, to save memory. Sorry GC!
|
||||
this.replyQueues = Collections.synchronizedMap(new HashMap<Integer, BlockingQueue<Message>>());
|
||||
this.pendingMessages = new LinkedBlockingQueue<>();
|
||||
}
|
||||
@@ -292,11 +294,15 @@ public class Peer {
|
||||
* @throws IOException
|
||||
*/
|
||||
/* package */ void readChannel() throws IOException {
|
||||
synchronized (this.byteBuffer) {
|
||||
synchronized (this.byteBufferLock) {
|
||||
while(true) {
|
||||
if (!this.socketChannel.isOpen() || this.socketChannel.socket().isClosed())
|
||||
return;
|
||||
|
||||
// Do we need to allocate byteBuffer?
|
||||
if (this.byteBuffer == null)
|
||||
this.byteBuffer = ByteBuffer.allocate(Network.getInstance().getMaxMessageSize());
|
||||
|
||||
final int bytesRead = this.socketChannel.read(this.byteBuffer);
|
||||
if (bytesRead == -1) {
|
||||
this.disconnect("EOF");
|
||||
@@ -318,9 +324,15 @@ public class Peer {
|
||||
return;
|
||||
}
|
||||
|
||||
if (message == null && bytesRead == 0 && !wasByteBufferFull)
|
||||
if (message == null && bytesRead == 0 && !wasByteBufferFull) {
|
||||
// No complete message in buffer, no more bytes to read from socket even though there was room to read bytes
|
||||
|
||||
// If byteBuffer is empty then we can deallocate it, to save memory, albeit costing GC
|
||||
if (this.byteBuffer.remaining() == this.byteBuffer.capacity())
|
||||
this.byteBuffer = null;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (message == null)
|
||||
// No complete message in buffer, but maybe more bytes to read from socket
|
||||
@@ -452,7 +464,7 @@ public class Peer {
|
||||
}
|
||||
|
||||
/* package */ void startPings() {
|
||||
// Replacing initial null value allows pingCheck() to start sending pings.
|
||||
// Replacing initial null value allows getPingTask() to start sending pings.
|
||||
LOGGER.trace(() -> String.format("Enabling pings for peer %s", this));
|
||||
this.lastPingSent = System.currentTimeMillis();
|
||||
}
|
||||
@@ -477,6 +489,7 @@ public class Peer {
|
||||
final long after = System.currentTimeMillis();
|
||||
|
||||
if (message == null || message.getType() != MessageType.PING) {
|
||||
LOGGER.debug(() -> String.format("Didn't receive reply from %s for PING ID %d", this, pingMessage.getId()));
|
||||
this.disconnect("no ping received");
|
||||
return;
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package org.qortal.repository;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
import org.qortal.data.account.AccountBalanceData;
|
||||
@@ -82,6 +83,12 @@ public interface AccountRepository {
|
||||
*/
|
||||
public void setMintedBlockCount(AccountData accountData) throws DataException;
|
||||
|
||||
/** Modifies account's minted block count only.
|
||||
* <p>
|
||||
* @return 2 if minted block count updated, 1 if block count set to delta, 0 if address not found.
|
||||
*/
|
||||
public int modifyMintedBlockCount(String address, int delta) throws DataException;
|
||||
|
||||
/** Delete account from repository. */
|
||||
public void delete(String address) throws DataException;
|
||||
|
||||
@@ -105,6 +112,8 @@ public interface AccountRepository {
|
||||
|
||||
public List<AccountBalanceData> getAssetBalances(List<String> addresses, List<Long> assetIds, BalanceOrdering balanceOrdering, Boolean excludeZero, Integer limit, Integer offset, Boolean reverse) throws DataException;
|
||||
|
||||
public void modifyAssetBalance(String address, long assetId, BigDecimal deltaBalance) throws DataException;
|
||||
|
||||
public void save(AccountBalanceData accountBalanceData) throws DataException;
|
||||
|
||||
public void delete(String address, long assetId) throws DataException;
|
||||
@@ -155,6 +164,21 @@ public interface AccountRepository {
|
||||
|
||||
// Managing QORT from legacy QORA
|
||||
|
||||
/**
|
||||
* Returns balance data for accounts with legacy QORA asset that are eligible
|
||||
* for more block reward (block processing) or for block reward removal (block orphaning).
|
||||
* <p>
|
||||
* For block processing, accounts that have already received their final QORT reward for owning
|
||||
* legacy QORA are omitted from the results. <tt>blockHeight</tt> should be <tt>null</tt>.
|
||||
* <p>
|
||||
* For block orphaning, accounts that did not receive a QORT reward at <tt>blockHeight</tt>
|
||||
* are omitted from the results.
|
||||
*
|
||||
* @param blockHeight QORT reward must have be present at this height (for orphaning only)
|
||||
* @throws DataException
|
||||
*/
|
||||
public List<AccountBalanceData> getEligibleLegacyQoraHolders(Integer blockHeight) throws DataException;
|
||||
|
||||
public QortFromQoraData getQortFromQoraInfo(String address) throws DataException;
|
||||
|
||||
public void save(QortFromQoraData qortFromQoraData) throws DataException;
|
||||
|
@@ -4,9 +4,9 @@ import java.math.BigDecimal;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.data.account.AccountBalanceData;
|
||||
import org.qortal.data.account.AccountData;
|
||||
import org.qortal.data.account.MintingAccountData;
|
||||
@@ -144,6 +144,10 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
|
||||
@Override
|
||||
public void ensureAccount(AccountData accountData) throws DataException {
|
||||
/*
|
||||
* Why do we need to check/set the public_key?
|
||||
* Is there something that sets an account's balance which also needs to set the public key?
|
||||
|
||||
byte[] publicKey = accountData.getPublicKey();
|
||||
String sql = "SELECT public_key FROM Accounts WHERE account = ?";
|
||||
|
||||
@@ -168,6 +172,15 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to ensure minimal account in repository", e);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
String sql = "INSERT IGNORE INTO Accounts (account) VALUES (?)"; // MySQL syntax
|
||||
try {
|
||||
this.repository.checkedExecuteUpdateCount(sql, accountData.getAddress());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to ensure minimal account in repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -273,6 +286,18 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int modifyMintedBlockCount(String address, int delta) throws DataException {
|
||||
String sql = "INSERT INTO Accounts (account, blocks_minted) VALUES (?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE blocks_minted = blocks_minted + ?";
|
||||
|
||||
try {
|
||||
return this.repository.checkedExecuteUpdateCount(sql, address, delta, delta);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to modify account's minted block count in repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String address) throws DataException {
|
||||
// NOTE: Account balances are deleted automatically by the database thanks to "ON DELETE CASCADE" in AccountBalances' FOREIGN KEY
|
||||
@@ -470,6 +495,54 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyAssetBalance(String address, long assetId, BigDecimal deltaBalance) throws DataException {
|
||||
// If deltaBalance is zero then do nothing
|
||||
if (deltaBalance.signum() == 0)
|
||||
return;
|
||||
|
||||
// If deltaBalance is negative then we assume AccountBalances & parent Accounts rows exist
|
||||
if (deltaBalance.signum() < 0) {
|
||||
// Perform actual balance change
|
||||
String sql = "UPDATE AccountBalances set balance = balance + ? WHERE account = ? AND asset_id = ?";
|
||||
try {
|
||||
this.repository.checkedExecuteUpdateCount(sql, deltaBalance, address, assetId);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to reduce account balance in repository", e);
|
||||
}
|
||||
|
||||
// If balance is now zero, and there are no prior historic balances, then simply delete row for this address-assetId (typically during orphaning)
|
||||
String deleteWhereSql = "account = ? AND asset_id = ? AND balance = 0 " + // covers "if balance now zero"
|
||||
"AND (" +
|
||||
"SELECT TRUE FROM HistoricAccountBalances " +
|
||||
"WHERE account = ? AND asset_id = ? AND height < (SELECT height - 1 FROM NextBlockHeight) " +
|
||||
"LIMIT 1" +
|
||||
")";
|
||||
try {
|
||||
this.repository.delete("AccountBalances", deleteWhereSql, address, assetId, address, assetId);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to prune account balance in repository", e);
|
||||
}
|
||||
} else {
|
||||
// We have to ensure parent row exists to satisfy foreign key constraint
|
||||
try {
|
||||
String sql = "INSERT IGNORE INTO Accounts (account) VALUES (?)"; // MySQL syntax
|
||||
this.repository.checkedExecuteUpdateCount(sql, address);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to ensure minimal account in repository", e);
|
||||
}
|
||||
|
||||
// Perform actual balance change
|
||||
String sql = "INSERT INTO AccountBalances (account, asset_id, balance) VALUES (?, ?, ?) " +
|
||||
"ON DUPLICATE KEY UPDATE balance = balance + ?";
|
||||
try {
|
||||
this.repository.checkedExecuteUpdateCount(sql, address, assetId, deltaBalance, deltaBalance);
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to increase account balance in repository", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(AccountBalanceData accountBalanceData) throws DataException {
|
||||
// If balance is zero and there are no prior historic balance, then simply delete balances for this assetId (typically during orphaning)
|
||||
@@ -490,13 +563,17 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
throw new DataException("Unable to delete account balance from repository", e);
|
||||
}
|
||||
|
||||
// I don't think we need to do this as Block.orphan() would do this for us?
|
||||
/*
|
||||
* I don't think we need to do this as Block.orphan() would do this for us?
|
||||
|
||||
try {
|
||||
this.repository.delete("HistoricAccountBalances", "account = ? AND asset_id = ?", accountBalanceData.getAddress(), accountBalanceData.getAssetId());
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to delete historic account balances from repository", e);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -768,6 +845,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
|
||||
// Minting accounts used by BlockMinter
|
||||
|
||||
@Override
|
||||
public List<MintingAccountData> getMintingAccounts() throws DataException {
|
||||
List<MintingAccountData> mintingAccounts = new ArrayList<>();
|
||||
|
||||
@@ -787,6 +865,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(MintingAccountData mintingAccountData) throws DataException {
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("MintingAccounts");
|
||||
|
||||
@@ -799,6 +878,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(byte[] minterPrivateKey) throws DataException {
|
||||
try {
|
||||
return this.repository.delete("MintingAccounts", "minter_private_key = ?", minterPrivateKey);
|
||||
@@ -809,6 +889,42 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
|
||||
// Managing QORT from legacy QORA
|
||||
|
||||
@Override
|
||||
public List<AccountBalanceData> getEligibleLegacyQoraHolders(Integer blockHeight) throws DataException {
|
||||
StringBuilder sql = new StringBuilder(1024);
|
||||
sql.append("SELECT account, balance from AccountBalances ");
|
||||
sql.append("LEFT OUTER JOIN AccountQortFromQoraInfo USING (account) ");
|
||||
sql.append("WHERE asset_id = ");
|
||||
sql.append(Asset.LEGACY_QORA); // int is safe to use literally
|
||||
sql.append(" AND (final_block_height IS NULL");
|
||||
|
||||
if (blockHeight != null) {
|
||||
sql.append(" OR final_block_height >= ");
|
||||
sql.append(blockHeight);
|
||||
}
|
||||
|
||||
sql.append(")");
|
||||
|
||||
List<AccountBalanceData> accountBalances = new ArrayList<>();
|
||||
|
||||
try (ResultSet resultSet = this.repository.checkedExecute(sql.toString())) {
|
||||
if (resultSet == null)
|
||||
return accountBalances;
|
||||
|
||||
do {
|
||||
String address = resultSet.getString(1);
|
||||
BigDecimal balance = resultSet.getBigDecimal(2).setScale(8);
|
||||
|
||||
accountBalances.add(new AccountBalanceData(address, Asset.LEGACY_QORA, balance));
|
||||
} while (resultSet.next());
|
||||
|
||||
return accountBalances;
|
||||
} catch (SQLException e) {
|
||||
throw new DataException("Unable to fetch eligible legacy QORA holders from repository", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public QortFromQoraData getQortFromQoraInfo(String address) throws DataException {
|
||||
String sql = "SELECT final_qort_from_qora, final_block_height FROM AccountQortFromQoraInfo WHERE account = ?";
|
||||
|
||||
@@ -827,6 +943,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void save(QortFromQoraData qortFromQoraData) throws DataException {
|
||||
HSQLDBSaver saveHelper = new HSQLDBSaver("AccountQortFromQoraInfo");
|
||||
|
||||
@@ -841,6 +958,7 @@ public class HSQLDBAccountRepository implements AccountRepository {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int deleteQortFromQoraInfo(String address) throws DataException {
|
||||
try {
|
||||
return this.repository.delete("AccountQortFromQoraInfo", "account = ?", address);
|
||||
|
@@ -928,6 +928,11 @@ public class HSQLDBDatabaseUpdates {
|
||||
stmt.execute("CREATE INDEX IF NOT EXISTS HistoricAccountBalancesHeightIndex ON HistoricAccountBalances (height)");
|
||||
break;
|
||||
|
||||
case 66:
|
||||
// Add CHECK constraint to account balances
|
||||
stmt.execute("ALTER TABLE AccountBalances ADD CONSTRAINT CheckBalanceNotNegative CHECK (balance >= 0)");
|
||||
break;
|
||||
|
||||
default:
|
||||
// nothing to do
|
||||
return false;
|
||||
|
@@ -337,8 +337,8 @@ public class HSQLDBRepository implements Repository {
|
||||
Path oldRepoFilePath = oldRepoPath.getFileName();
|
||||
|
||||
// Try to open backup. We need to remove "create=true" and insert "backup" dir before final filename.
|
||||
String backupUrlTemplate = "jdbc:hsqldb:file:%s/backup/%s;create=false;hsqldb.full_log_replay=true";
|
||||
return String.format(backupUrlTemplate, oldRepoDirPath.toString(), oldRepoFilePath.toString());
|
||||
String backupUrlTemplate = "jdbc:hsqldb:file:%s%sbackup%s%s;create=false;hsqldb.full_log_replay=true";
|
||||
return String.format(backupUrlTemplate, oldRepoDirPath.toString(), File.separator, File.separator, oldRepoFilePath.toString());
|
||||
}
|
||||
|
||||
/* package */ static void attemptRecovery(String connectionUrl) throws DataException {
|
||||
@@ -361,8 +361,8 @@ public class HSQLDBRepository implements Repository {
|
||||
.forEach(File::delete);
|
||||
|
||||
try (Statement stmt = connection.createStatement()) {
|
||||
// Now "backup" the backup back to original repository location (the parent)
|
||||
// NOTE: trailing / is OK because HSQLDB checks for both / and O/S-specific separator
|
||||
// Now "backup" the backup back to original repository location (the parent).
|
||||
// NOTE: trailing / is OK because HSQLDB checks for both / and O/S-specific separator.
|
||||
// textdb.allow_full_path connection property is required to be able to use '..'
|
||||
stmt.execute("BACKUP DATABASE TO '../' BLOCKING AS FILES");
|
||||
} catch (SQLException e) {
|
||||
|
@@ -31,9 +31,6 @@ public class Settings {
|
||||
private static final int MAINNET_API_PORT = 12391;
|
||||
private static final int TESTNET_API_PORT = 62391;
|
||||
|
||||
private static final int MAINNET_UI_PORT = 12390;
|
||||
private static final int TESTNET_UI_PORT = 62390;
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(Settings.class);
|
||||
private static final String SETTINGS_FILENAME = "settings.json";
|
||||
|
||||
@@ -43,14 +40,17 @@ public class Settings {
|
||||
// Settings, and other config files
|
||||
private String userPath;
|
||||
|
||||
// Common to all networking (UI/API/P2P)
|
||||
// Common to all networking (API/P2P)
|
||||
private String bindAddress = "::"; // Use IPv6 wildcard to listen on all local addresses
|
||||
|
||||
// Node management UI
|
||||
private boolean uiEnabled = true;
|
||||
private Integer uiPort;
|
||||
private String[] uiWhitelist = new String[] {
|
||||
"::1", "127.0.0.1"
|
||||
// UI servers
|
||||
private int uiPort = 12388;
|
||||
private String[] uiLocalServers = new String[] {
|
||||
"localhost", "172.24.1.1", "qor.tal"
|
||||
};
|
||||
private String[] uiRemoteServers = new String[] {
|
||||
"node1.qortal.org", "node2.qortal.org", "node3.qortal.org", "node4.qortal.org", "node5.qortal.org",
|
||||
"node6.qortal.org", "node7.qortal.org", "node8.qortal.org", "node9.qortal.org", "node10.qortal.org"
|
||||
};
|
||||
|
||||
// API-related
|
||||
@@ -77,11 +77,11 @@ public class Settings {
|
||||
/** Port number for inbound peer-to-peer connections. */
|
||||
private Integer listenPort;
|
||||
/** Minimum number of peers to allow block minting / synchronization. */
|
||||
private int minBlockchainPeers = 5;
|
||||
private int minBlockchainPeers = 10;
|
||||
/** Target number of outbound connections to peers we should make. */
|
||||
private int minOutboundPeers = 20;
|
||||
private int minOutboundPeers = 40;
|
||||
/** Maximum number of peer connections we allow. */
|
||||
private int maxPeers = 50;
|
||||
private int maxPeers = 80;
|
||||
|
||||
// Which blockchains this node is running
|
||||
private String blockchainConfig = null; // use default from resources
|
||||
@@ -244,19 +244,16 @@ public class Settings {
|
||||
return this.userPath;
|
||||
}
|
||||
|
||||
public boolean isUiEnabled() {
|
||||
return this.uiEnabled;
|
||||
public int getUiServerPort() {
|
||||
return this.uiPort;
|
||||
}
|
||||
|
||||
public int getUiPort() {
|
||||
if (this.uiPort != null)
|
||||
return this.uiPort;
|
||||
|
||||
return this.isTestNet ? TESTNET_UI_PORT : MAINNET_UI_PORT;
|
||||
public String[] getLocalUiServers() {
|
||||
return this.uiLocalServers;
|
||||
}
|
||||
|
||||
public String[] getUiWhitelist() {
|
||||
return this.uiWhitelist;
|
||||
public String[] getRemoteUiServers() {
|
||||
return this.uiRemoteServers;
|
||||
}
|
||||
|
||||
public boolean isApiEnabled() {
|
||||
|
@@ -241,6 +241,7 @@ public abstract class Transaction {
|
||||
ASSET_NOT_SPENDABLE(89),
|
||||
ACCOUNT_CANNOT_REWARD_SHARE(90),
|
||||
SELF_SHARE_EXISTS(91),
|
||||
ACCOUNT_ALREADY_EXISTS(92),
|
||||
NOT_YET_RELEASED(1000);
|
||||
|
||||
public final int value;
|
||||
|
@@ -83,6 +83,10 @@ public class TransferPrivsTransaction extends Transaction {
|
||||
if (!Crypto.isValidAddress(this.transferPrivsTransactionData.getRecipient()))
|
||||
return ValidationResult.INVALID_ADDRESS;
|
||||
|
||||
// Check recipient is new account
|
||||
if (this.repository.getAccountRepository().accountExists(this.transferPrivsTransactionData.getRecipient()))
|
||||
return ValidationResult.ACCOUNT_ALREADY_EXISTS;
|
||||
|
||||
return ValidationResult.OK;
|
||||
}
|
||||
|
||||
@@ -183,8 +187,11 @@ public class TransferPrivsTransaction extends Transaction {
|
||||
accountRepository.setFlags(senderData);
|
||||
|
||||
// Restore recipient's flags
|
||||
recipientData.setFlags(this.transferPrivsTransactionData.getPreviousRecipientFlags());
|
||||
accountRepository.setFlags(recipientData);
|
||||
Integer previousRecipientFlags = this.transferPrivsTransactionData.getPreviousRecipientFlags();
|
||||
if (previousRecipientFlags != null) {
|
||||
recipientData.setFlags(previousRecipientFlags);
|
||||
accountRepository.setFlags(recipientData);
|
||||
}
|
||||
|
||||
// Clean values in transaction data
|
||||
this.transferPrivsTransactionData.setPreviousSenderFlags(null);
|
||||
|
@@ -1,46 +0,0 @@
|
||||
package org.qortal.ui;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpContent;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.server.ResourceService;
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
/**
|
||||
* Replace ResourceService that delivers content as "attachments", typically forcing download instead of rendering.
|
||||
* <p>
|
||||
* Sets <tt>Content-Type</tt> header to <tt>application/octet-stream</tt><br>
|
||||
* Sets <tt>Content-Disposition</tt> header to <tt>attachment; filename="<i>basename</i>"</tt><br>
|
||||
* where <i>basename</i> is that last component of requested URI path.
|
||||
* <p>
|
||||
* Example usage:<br>
|
||||
* <br>
|
||||
* <tt>... = new ServletHolder("servlet-name", new DefaultServlet(new DownloadResourceService()));</tt>
|
||||
*/
|
||||
public class DownloadResourceService extends ResourceService {
|
||||
|
||||
@Override
|
||||
protected boolean sendData(HttpServletRequest request, HttpServletResponse response, boolean include, final HttpContent content, Enumeration<String> reqRanges) throws IOException {
|
||||
final boolean _pathInfoOnly = super.isPathInfoOnly();
|
||||
String servletPath = _pathInfoOnly ? "/" : request.getServletPath();
|
||||
String pathInfo = request.getPathInfo();
|
||||
String pathInContext = URIUtil.addPaths(servletPath,pathInfo);
|
||||
|
||||
// Find basename of requested content
|
||||
final int slashIndex = pathInContext.lastIndexOf(URIUtil.SLASH);
|
||||
if (slashIndex != -1)
|
||||
pathInContext = pathInContext.substring(slashIndex + 1);
|
||||
|
||||
// Add appropriate headers
|
||||
response.setHeader(HttpHeader.CONTENT_TYPE.asString(), "application/octet-stream");
|
||||
response.setHeader("Content-Disposition", "attachment; filename=\"" + pathInContext + "\"");
|
||||
|
||||
return super.sendData(request, response, include, content, reqRanges);
|
||||
}
|
||||
|
||||
}
|
@@ -1,101 +0,0 @@
|
||||
package org.qortal.ui;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
|
||||
import org.eclipse.jetty.rewrite.handler.RedirectPatternRule;
|
||||
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.handler.InetAccessHandler;
|
||||
import org.eclipse.jetty.servlet.DefaultServlet;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||
import org.qortal.settings.Settings;
|
||||
|
||||
public class UiService {
|
||||
|
||||
public static final String DOWNLOADS_RESOURCE_PATH = "node-ui-downloads";
|
||||
private static UiService instance;
|
||||
|
||||
private Server server;
|
||||
|
||||
private UiService() {
|
||||
}
|
||||
|
||||
public static UiService getInstance() {
|
||||
if (instance == null)
|
||||
instance = new UiService();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void start() {
|
||||
try {
|
||||
// Create node management UI server
|
||||
InetAddress bindAddr = InetAddress.getByName(Settings.getInstance().getBindAddress());
|
||||
InetSocketAddress endpoint = new InetSocketAddress(bindAddr, Settings.getInstance().getUiPort());
|
||||
this.server = new Server(endpoint);
|
||||
|
||||
// IP address based access control
|
||||
InetAccessHandler accessHandler = new InetAccessHandler();
|
||||
for (String pattern : Settings.getInstance().getUiWhitelist()) {
|
||||
accessHandler.include(pattern);
|
||||
}
|
||||
this.server.setHandler(accessHandler);
|
||||
|
||||
// URL rewriting
|
||||
RewriteHandler rewriteHandler = new RewriteHandler();
|
||||
accessHandler.setHandler(rewriteHandler);
|
||||
|
||||
// Context
|
||||
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
|
||||
context.setContextPath("/");
|
||||
rewriteHandler.setHandler(context);
|
||||
|
||||
// Cross-origin resource sharing
|
||||
FilterHolder corsFilterHolder = new FilterHolder(CrossOriginFilter.class);
|
||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
|
||||
corsFilterHolder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET, POST, DELETE");
|
||||
corsFilterHolder.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");
|
||||
context.addFilter(corsFilterHolder, "/*", null);
|
||||
|
||||
ClassLoader loader = this.getClass().getClassLoader();
|
||||
|
||||
// Node management UI download servlet
|
||||
ServletHolder uiDownloadServlet = new ServletHolder("node-ui-download", new DefaultServlet(new DownloadResourceService()));
|
||||
uiDownloadServlet.setInitParameter("resourceBase", loader.getResource(DOWNLOADS_RESOURCE_PATH + "/").toString());
|
||||
uiDownloadServlet.setInitParameter("dirAllowed", "true");
|
||||
uiDownloadServlet.setInitParameter("pathInfoOnly", "true");
|
||||
context.addServlet(uiDownloadServlet, "/downloads/*");
|
||||
|
||||
// Node management UI static content servlet
|
||||
ServletHolder uiServlet = new ServletHolder("node-management-ui", DefaultServlet.class);
|
||||
uiServlet.setInitParameter("resourceBase", loader.getResource("node-management-ui/").toString());
|
||||
uiServlet.setInitParameter("dirAllowed", "true");
|
||||
uiServlet.setInitParameter("pathInfoOnly", "true");
|
||||
context.addServlet(uiServlet, "/*");
|
||||
|
||||
rewriteHandler.addRule(new RedirectPatternRule("", "/index.html")); // node management UI start page
|
||||
|
||||
// Start server
|
||||
this.server.start();
|
||||
} catch (Exception e) {
|
||||
// Failed to start
|
||||
throw new RuntimeException("Failed to start node management UI", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
try {
|
||||
// Stop server
|
||||
this.server.stop();
|
||||
} catch (Exception e) {
|
||||
// Failed to stop
|
||||
}
|
||||
|
||||
this.server = null;
|
||||
}
|
||||
|
||||
}
|
@@ -5,11 +5,26 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAccessType;
|
||||
import javax.xml.bind.annotation.XmlAccessorType;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public abstract class ExecuteProduceConsume implements Runnable {
|
||||
|
||||
@XmlAccessorType(XmlAccessType.FIELD)
|
||||
public static class StatsSnapshot {
|
||||
public int activeThreadCount = 0;
|
||||
public int greatestActiveThreadCount = 0;
|
||||
public int consumerCount = 0;
|
||||
public int tasksProduced = 0;
|
||||
public int tasksConsumed = 0;
|
||||
|
||||
public StatsSnapshot() {
|
||||
}
|
||||
}
|
||||
|
||||
private final String className;
|
||||
private final Logger logger;
|
||||
|
||||
@@ -51,28 +66,18 @@ public abstract class ExecuteProduceConsume implements Runnable {
|
||||
return this.executor.awaitTermination(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
public int getActiveThreadCount() {
|
||||
synchronized (this) {
|
||||
return this.activeThreadCount;
|
||||
}
|
||||
}
|
||||
public StatsSnapshot getStatsSnapshot() {
|
||||
StatsSnapshot snapshot = new StatsSnapshot();
|
||||
|
||||
public int getGreatestActiveThreadCount() {
|
||||
synchronized (this) {
|
||||
return this.greatestActiveThreadCount;
|
||||
snapshot.activeThreadCount = this.activeThreadCount;
|
||||
snapshot.greatestActiveThreadCount = this.greatestActiveThreadCount;
|
||||
snapshot.consumerCount = this.consumerCount;
|
||||
snapshot.tasksProduced = this.tasksProduced;
|
||||
snapshot.tasksConsumed = this.tasksConsumed;
|
||||
}
|
||||
}
|
||||
|
||||
public int getTasksProduced() {
|
||||
synchronized (this) {
|
||||
return this.tasksProduced;
|
||||
}
|
||||
}
|
||||
|
||||
public int getTasksConsumed() {
|
||||
synchronized (this) {
|
||||
return this.tasksConsumed;
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
/**
|
||||
|
29
src/main/java/org/qortal/utils/RandomizeList.java
Normal file
29
src/main/java/org/qortal/utils/RandomizeList.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package org.qortal.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class RandomizeList {
|
||||
private static final Random random = new Random();
|
||||
|
||||
public static <T> List<T> randomize(List<T> inputList) {
|
||||
List<T> outputList = new ArrayList<T>();
|
||||
|
||||
Iterator<T> inputIterator = inputList.iterator();
|
||||
while (inputIterator.hasNext()) {
|
||||
T element = inputIterator.next();
|
||||
|
||||
if (outputList.isEmpty()) {
|
||||
outputList.add(element);
|
||||
} else {
|
||||
int outputIndex = random.nextInt(outputList.size() + 1);
|
||||
outputList.add(outputIndex, element);
|
||||
}
|
||||
}
|
||||
|
||||
return outputList;
|
||||
}
|
||||
|
||||
}
|
@@ -57,7 +57,7 @@
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
"timestamp": "1583250000000",
|
||||
"timestamp": "1583870000000",
|
||||
"transactions": [
|
||||
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORT", "description": "QORTAL coin", "quantity": 0, "isDivisible": true, "reference": "28u54WRcMfGujtQMZ9dNKFXVqucY7XfPihXAqPFsnx853NPUwfDJy1sMH5boCkahFgjUNYqc5fkduxdBhQTKgUsC", "data": "{}" },
|
||||
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||
@@ -72,6 +72,7 @@
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "Qd453ewoyESrEgUab6dTFe2pufWkD94Tsm", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QfjoMGib4trpZHzxUSMdmtiRnsrLNf74zp", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "Qgfh143pRJyxpS92JoazjXNMH1uZueQBZ2", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "Qi3N6fNRrs15EHmkxYyWHyh4z3Dp2rVU2i", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QicRwDhfk8M2CGNvpMEmYzQEjESvF7WrFY", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QLwMaXmDDUvh7aN5MdpY28rqTKE8U1Cepc", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QP3J3GHgjqP69neTAprpYe4co33eKQiQpS", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
@@ -87,21 +88,24 @@
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QSJ5cDLWivGcn9ym21azufBfiqeuGH1maq", "andMask": -1, "orMask": 0, "xorMask": 0 },
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QhPXuFFRa9s91Q2qYKpSN5LVCUTqYkgRLz", "andMask": -1, "orMask": 0, "xorMask": 0 },
|
||||
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QagyUZdmnKJA9LyEqcxJFFf2ehmcqVZsKb", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVhboSLD1VmX2YvAnfAXkbzvsmXkDJZTNR", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qg3TKLhPvn7bKVrT9x37wJiJ7YZ4jBuqQW", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcCYuXos5xBXXHbRg1RTfSdxiZEkGa3N2P", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPFM4xX2826MuBEhMtdReW1QR3vRYrQff3", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVhboSLD1VmX2YvAnfAXkbzvsmXkDJZTNR", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QagyUZdmnKJA9LyEqcxJFFf2ehmcqVZsKb", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcCYuXos5xBXXHbRg1RTfSdxiZEkGa3N2P", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qg3TKLhPvn7bKVrT9x37wJiJ7YZ4jBuqQW", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QiQUssukhoo1ft4G9Mxa8JpViqFW4PdBjJ", "level": 5 },
|
||||
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTVLRwoopfg7qSG9CMfCPJz3UydnT3jDxD", "level": 4 },
|
||||
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPLoqpwAoytvpQKwvJ6GRsaRcVZ3xnYgVB", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVxGkDgXt4nHj4MAd1afV9AxT1XCUVLGja", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTrosCDj7oCdJc8K7sJZW6ssHU3StVoY7s", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QeoFPT4xPxLp2P42yKVo22wx2V8kydfNmr", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qata5oApMShnD4F1kcgSJMTiYsxTPSFW4F", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXHmtFXzf4D7PEu73NfBm3sZyeuGrm3QC5", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRPhK2FwijTaMQ4PeJ9gFK1vvGBaYaht2P", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRk5TG57SQGLkybXUqxBnobADTFGj9GR3Z", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTrosCDj7oCdJc8K7sJZW6ssHU3StVoY7s", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVgX5FVNvAvESmeVD67vz3xE7BQTQARLLM", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVxGkDgXt4nHj4MAd1afV9AxT1XCUVLGja", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXHmtFXzf4D7PEu73NfBm3sZyeuGrm3QC5", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qata5oApMShnD4F1kcgSJMTiYsxTPSFW4F", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QeoFPT4xPxLp2P42yKVo22wx2V8kydfNmr", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qi7Lbvz1UdWgzgbKzfpRisLsdLR4smVoVh", "level": 3 },
|
||||
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "HFDmuc4HAAoVs9Siea3MugjBHasbotgVz2gsRDuLAAcB", "recipient": "QY82MasqEH6ChwXaETH4piMtE8Pk4NBWD3", "rewardSharePublicKey": "F35TbQXmgzz32cALj29jxzpdYSUKQvssqThLsZSabSXx", "sharePercent": 0 },
|
||||
@@ -124,8 +128,8 @@
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcrnYL6yNwHKuEzYLXQ8LewG3m2B5k9K5f", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPYfRd1uhnAgqkZNmjNCjgPhkguMnHWuc4", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QM9zVbXXnfrtQ1X7zPQ5zxPYPAWTaVMXqZ", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qft1ktvJ14eBFjpJaphT24ks4WRcN3K6tB", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcHF9YogbuzZhG4fK4116pgE2qrmbkGh2n", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qft1ktvJ14eBFjpJaphT24ks4WRcN3K6tB", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QaUciVnbQDXdNygJadEY31PuDEBLi6Spmu", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWe1iPDudLU189BggPykbH1DrAeaFEgX6W", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgECFJiiri2dDN4zA32URvbdDid2cFrJwM", "level": 5 },
|
||||
@@ -134,25 +138,26 @@
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXan6JJ1WuRi7GigvFDjtTzJY1rfYEqEqv", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQa3MTgdnru5B7wSqPcq7qXcZcpbDQ7oyE", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQoHo1x4hvZcSFbj2uQAXQhq2ZXBUgPeNj", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQEZEGWt3sAPwEWYD2RQ6tMwnpkayG81dY", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUvoLFfkuVuRe1KGMLQS4nUHry6CBTuTYz", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTEE4ZJXv68ke4841HWjTLAAU8mfccxwbE", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQEZEGWt3sAPwEWYD2RQ6tMwnpkayG81dY", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVurebcEbe4USR4xcS3Mbk12mhxsjRX31u", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QfmM8dgfikTB2FYVuJ9owzQXVm8wP7T4QT", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPUMyJ59kkrp75tDzDPxSyw1GWCrbC2cS2", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qc9dZchoYfc1eRJhSLXR9rxSHcqNB47Dex", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTMTFswUU83XVmk6T4Gez7qUJCccbAad7S", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjrC8NXwR8gFkEauvRwCPxqHroPFqAJbhK", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPcTWoAhYWmwjmWbQAS8muisrQVaLJMbg7", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjrC8NXwR8gFkEauvRwCPxqHroPFqAJbhK", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXv4WiqgryFPi8BgX7RU7gqtAgrVutmU4S", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QSkiGy3v3MwvGy3aACuyLDv3Xy2AWAYfPS", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgpAW6uqwNR58gNYWRCVXNLm4F5TuckAw4", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QLpLY9o3X21q7q4L6u5JdDuMskYA838iYE", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QguNJ4evpN9i76dYHysTzLiKoWKFhE4B4U", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWpJxitn53ovwknQfrCaqivvoUNuPrX2sb", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QdGbhtkFHUqd9nK9UegxxGXD1eSRYSoKjt", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXv4WiqgryFPi8BgX7RU7gqtAgrVutmU4S", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QP8xG56L8b28h1mguSk9LuzNhxbHgAoL9b", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QfjL32jLsxtumbfx6ufmfCFCBccVCQFkrh", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QguNJ4evpN9i76dYHysTzLiKoWKFhE4B4U", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QSkiGy3v3MwvGy3aACuyLDv3Xy2AWAYfPS", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWLpsGYrkF2cy3tH6DCxso7kXZpZJvv13e", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbwgBD6LWdk1hZsb8EwdVVmDZdvpxMzyGT", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qbcy4uyMkQF2JXYqGkueDiFNZ4tHjRg8CR", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QdXe21sjY8smjVmiAUgZY8xWVzwgxMgK5A", "level": 5 },
|
||||
@@ -161,7 +166,6 @@
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXKmtkHHwaUQzGeHHG2dFiHUnKAp815Mzq", "level": 5 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZkThgFfExognAbxLjYZGVHpL7X6g3EG4A", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbH8srDKeS5VcsQsgsaF3nqCzGT1NqfsTx", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWLpsGYrkF2cy3tH6DCxso7kXZpZJvv13e", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QdtAQm1EGNgM7QDSaC2qvV9WdpRHwpApUT", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbqRyFw7Xu6Nsb4FraaUSe7nUPukuUpekG", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRFHr4jnVgvAsPTubeSrh8bPy1yzwzYaWD", "level": 4 },
|
||||
@@ -170,25 +174,25 @@
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQPYyoE3Bm2vh8Wr5aaBNyirC8dd3BhBGH", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUzUCfoakDqBaL5zBgfvTKLHcuxbUfB38Q", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QSBJuNoCAFUTcevuCTcMi3i5nzNPhC5R4b", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTGeQqn3XEFdnnCqvifCFXYdKym7SaHzTd", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVSqUrNFR4mPTMa7UdVmNKZTSaDVAv8XXF", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMLjFiPiPa46YBRoushe6a227kSrXnXyKb", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTGeQqn3XEFdnnCqvifCFXYdKym7SaHzTd", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNJ8jDx6Mni2GKLHEY1BMh9xDumT3vnJQM", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMLjFiPiPa46YBRoushe6a227kSrXnXyKb", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgVZb632eqF1eLQm9gBGuBtyp9Dyz2FKUK", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPREQjU2defiYdgA33HDiLNGBpxtuebeqE", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QiBYApdEYRwsFYjt59UJqZV55wcwykvhsh", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMsKXQAYKmR7dBH4P3kMLiKzYatK3h1CeS", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRCtc67FTNKS5zVXM8omw8F55h9DP7herL", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZvHW7amu5DNktsBgaMrR1brHZhhhVwKLW", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRCtc67FTNKS5zVXM8omw8F55h9DP7herL", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgqM5bKs3tNqKNAnVeaQp4oaYMXCmX6YJr", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QhMEWhZCufhKLkfuNU2DAzj1mmWoAxX147", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qgwb5SLVGAperXAMoVNBDvGpVAxTVY7f6F", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbvxC3ENqomXp11833APchdjeyCNd49nLj", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qj5ncwncQY4KPYgKHD1eYpXHbR717PeLcJ", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QY1RFZTD2ogRohf3UrdT4g1Qo9D122AZDN", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcU4VhU9ohDXU4k4AUMapgJRYSzEpizjLN", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QY1RFZTD2ogRohf3UrdT4g1Qo9D122AZDN", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZsygC1chppsJK1cnaHG4fEsNaDYfLF7ZJ", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYGcPZcRhGaY1MsiDr3VtwTXmB9TAbLFSn", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qgwb5SLVGAperXAMoVNBDvGpVAxTVY7f6F", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QaFN7DWcGF7keNACJpwCVnegePbfsAoFCw", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYGNMWBmqWgVtMWGHypAsKhDVQw5mrFZww", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcNmqT8CZ6zSZwuRm5LahRZnuGBJRnPY8o", "level": 4 },
|
||||
@@ -199,300 +203,318 @@
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgCQq4cFaGrJhwvKs4XwvccKiLZ8GVMCXR", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRtRELSSASzqiYy2FtNcrePH6TVnqJkv9B", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMtm8wVPHGE3qHg2hMaj6SZ78D5eXw3VWZ", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QY6ZGZdi8h5op2VrRXkG1W5Jp3feLwp7ZD", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QY6ZGZdi8h5op2VrRXkG1W5Jp3feLwp7ZD", "level": 4 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVrvy4ac2jBTfxyCKB7MLimqJooTDBApmS", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcCBVfL35rxSyQ416L2MBz14FYbNrbeNPx", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNHdGeFJmPcDdN8prPzPL4bk2dpnJ2ZZFr", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRWEbcRnLoGccAndtLcGgpeQFH2ZBcMqHo", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcCBVfL35rxSyQ416L2MBz14FYbNrbeNPx", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qce2Djqrk2WzG1QhMZ3BqFok9HGsz4wtM3", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRWEbcRnLoGccAndtLcGgpeQFH2ZBcMqHo", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QeDhJRqTyeDmcgGJoms6FHK49ZGVpVahxg", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUXga5K8nzd9EqYtvEesZWEYuA688h6D3d", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QfbX8JJupEw5ckNtU4upQgET35oLTr5e6v", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRaDef6H2zYfefqLwYGmUg7T6DAqo6DDqc", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QibuD4c6gvXgS4iut7q3sXuVb23rgFJq2M", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QawSgZ7i2LLFTKyPxQptk9gN526ihy5yZi", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMozpRT9aUunfmPh7EtQ6LPoth2JFJWBXC", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTpYQqRyMekaEuECziirzy3HvCVofZS1wJ", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QawSgZ7i2LLFTKyPxQptk9gN526ihy5yZi", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMuWNAJ2tbeViHtBUN3yD2KARrrzcanLAd", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUGo9SErgc6ceB5aBzcSJDNqBkQ9eaCKZS", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QdFZk74skMUu4rKMPEmcSVwR87LNDe6o3Y", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qgp16aMcdiS2EUkxCm5NSZgB8DixGK51zT", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QScBgSw74MquesXmVJxerX3YgyhtShRr4q", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgcF6KgVZ9eDAMHJdSEeAtp91t931VKZMv", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjgGeEkyiXa43pyqkXxZbvAChQpVYfUyKz", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRWEbzH4niUcu9dL3Yq42X4j89aqQk3qWw", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYD3kXchZ86vUyJBXNCVQ4LUvTAd6PUZW3", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPqfuZpmyA6cK6WUFwcGeKH2Te1aegkHBM", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QhQdzLn36SDgrgoMfvdZAkoWtTUHpB3acJ", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgcF6KgVZ9eDAMHJdSEeAtp91t931VKZMv", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRWEbzH4niUcu9dL3Yq42X4j89aqQk3qWw", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWL7kZp6Pdd1bhxZ6SXPhVf5g7GParG9CC", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbxJvwrEHZs7MDE8rbqBwZAZkcywue5F3W", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QhQdzLn36SDgrgoMfvdZAkoWtTUHpB3acJ", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPqfuZpmyA6cK6WUFwcGeKH2Te1aegkHBM", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUKKwug9PNai3DBggXUXP8Ag7WmR5SVUR4", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWwrtjBL4ah965XPXHYJhymreC9jyryNLZ", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjEaMxcBKMsj91ytKe6GdTBJP8Mu1Ru3r4", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QeDhJRqTyeDmcgGJoms6FHK49ZGVpVahxg", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbYTowTHCr9WzfrR6b8uDfJKwL41nG1vyr", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPusqAVBVFGAAeE7RdospttA18AuyLP7sB", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qd1Px9vhWuEmF2SbLx3Ez7HhGtifGMa8TJ", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QSq8y4ZrSbF55ZddWNcw1ett2LDtjQEvNn", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNw9xAm9TUerin9QsapCPL9mV6zmoXyJrh", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qd1Px9vhWuEmF2SbLx3Ez7HhGtifGMa8TJ", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbYTowTHCr9WzfrR6b8uDfJKwL41nG1vyr", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QU8XJyQEZxSHgrS1XyooWUo5MmruXg36Pe", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYuMATkLjz7YB6s4EG1aWCmmmrAPj3W9Ce", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNw9xAm9TUerin9QsapCPL9mV6zmoXyJrh", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNiTnonHpXTeUrgNdyYWVDPP4ZdjkLpW72", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTtXS6fZGThRLq4qgkwM4ngBYkLoFyZ3bK", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjrCFCi6dqvka4UELg2SHhM2oWnQWepd1o", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYuMATkLjz7YB6s4EG1aWCmmmrAPj3W9Ce", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QREtYDhP4HkpeCCZroemuGXMGVFoZHH3Lp", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgesuKa3zwx8VAseF1oHZAFHMf29k8ergq", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjrCFCi6dqvka4UELg2SHhM2oWnQWepd1o", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTtXS6fZGThRLq4qgkwM4ngBYkLoFyZ3bK", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjEAs2or122weKppv5zALzoQzXxbsDjy3f", "level": 3 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QdXdUxnyKGGo7eEfTcx85oEikNe5nYnuwa", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgesuKa3zwx8VAseF1oHZAFHMf29k8ergq", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWBFK5h61ZxGfqQpEkwwKTcLAo8t9VWe4K", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjEAs2or122weKppv5zALzoQzXxbsDjy3f", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZ19JRpSsgvm4z6EjnbhdxJBoUYzDGvP3x", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUZQPWhrxpze32vGiux6wa85kg9iwuhCDx", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZ19JRpSsgvm4z6EjnbhdxJBoUYzDGvP3x", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QdwSxr3t4hdGHjQFy6EVGR9yGMipefsTuo", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZ2gi6BhUNpGmrErgJLFuY1WHy6xK1J7qX", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWinRb65f2g3yBoaZvTrQKQk7CW7vfBgGX", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQaKBSjAt9RK2bqJoSriR77X4ULstGzrFQ", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QisSQZ7Et7Rfzx2SCC2o9UDSeRZWMyFKWc", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWinRb65f2g3yBoaZvTrQKQk7CW7vfBgGX", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXYk68x2tiUrDBv8eq6wd4KtBmLHYiC4zR", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QisSQZ7Et7Rfzx2SCC2o9UDSeRZWMyFKWc", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTyokTJrR4b2y76An3BFUEbqQy5vvg76iN", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPyx2bNiAnJEjitfeAh8jZXzQVKio2B7Mi", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMJwdufHY9dMoARHCUyGbMPAqUB4BcqGKm", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNffmqjCUQsLaHLZXBaA47tQW7bERdpP4R", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QeUc5NfzubZD4eA4eJ7bfXAFraAWc7jVz5", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QfPcwetW3BErP4ySTurxFJSHpNkNXPEhGk", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMMh94Pfs5LVE4xJee1yggViqP1YDdQHT4", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQHWFSJpzuDupPfcTvGMRNJp2UGz98Kb7j", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QdepeWWLMD3LiDRKkKBria29rDhZP9V5RJ", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QeSh3t1AnaRcRThkkUTvvdMEouixCADeVh", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMMh94Pfs5LVE4xJee1yggViqP1YDdQHT4", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWez1VBfYVJ8KFoZ6MhJDzYVLbn5mr38VT", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZiYh4m4Uh3FH52cnow8MrNyXhSH88bp2H", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYsh2NB6TogqV1iXHmHXcVaWw25WEYA94o", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QU9dnN47Nc5KaH7JwNoCZ7TANwCW5VX9iG", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZjCgcSVvSRsFZeLJz9C5dTa36s3cSKqvB", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXoW5M113C3SDUeFdjVVs2m1RT9XtzXS3z", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QfhJM5CpX5MhfjCcopwfw7pgS6w1hVJ49E", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZjCgcSVvSRsFZeLJz9C5dTa36s3cSKqvB", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QjsD7pMh2LpmgYudaaRM8gmzhBC4c9uwyj", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZxcntxfJimHus9pgfvVVPbpHs5yU7ZMhB", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRBVE5gHumH6RUgEUxJdQ5417NUvc14k3F", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QakZtV3nZeRp5UEoEUNX7p8Qz5VbVXTQid", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXeiJa7ftN6dAYNZcdqKDBhmAFyGjX2zkm", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qc2eRuYXtpATrg87Pr1WDXkUBCsgNRYcQF", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbEJ5tfeskASVnmLFeKzspuZJJb6cJVPZ4", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qc2eRuYXtpATrg87Pr1WDXkUBCsgNRYcQF", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYpYshfVzMMVU71KqAV4erX3NJAUCnJXgD", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QViKVZa3M3ar7RBRSBMTx8FdzLh1zxUhN8", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWvTdm9LU1GSX9q6Rrvgx7xjo2iuV2Gxn1", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMb2cAKb2BUxknneuoXQynJ7uzosJ57Top", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZw7tgMttSySNMKfcMrEbdtnqHVrQ9w9fT", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbGwYRV3UDek4VNpzoAVVQWfoKjZmm4qPb", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWvTdm9LU1GSX9q6Rrvgx7xjo2iuV2Gxn1", "level": 2 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QYM76r3tuf6FJWz2USqVjwXJrV9tLMz1cj", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QfnbnWrRQ4HNDQvtg3wG2B1eC4ycUsFqZz", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUdjqijDoyc83K4WcMW1sCn7zLd2t1WTqn", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QbGwYRV3UDek4VNpzoAVVQWfoKjZmm4qPb", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QRUbzEbLd7fRjAx2fBdXAH4QS1WQyetvDc", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QU9K6pWhkok9mokkPDoCwgSu3j62ECuKLD", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QaZs97g4Mbq9tXMoBWbhw3jFvBBVkWKS5F", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcJwVCyzraPy51uB4xd4f94n2UFYAsznGC", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QMb2cAKb2BUxknneuoXQynJ7uzosJ57Top", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZbKMgYBwWGyTDJzoDir2aNqXsFdDp6fS9", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZ7wvWAUcHKRhvQ3ijdrqM4zucQKCgQ1hQ", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgmEtScSZWJmTUAidCZKj6gDr3LznZ6rr4", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVuksgNt3QAr7KCrkxtE5FWrczfgLKxs4H", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQXgH4CnQCB76BbXhsApu6ShhohFfvoXv7", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQzMut6erjgSKCpZ1dHDcjKcj9KAce7cug", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZJc1V32oFm8tufB4bk7fa3aepu4EdkeDU", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZbKMgYBwWGyTDJzoDir2aNqXsFdDp6fS9", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QaZs97g4Mbq9tXMoBWbhw3jFvBBVkWKS5F", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QSkicapNH35a3UebSxxSMCfntBhwwi6veW", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QLqZD8eiNk8nrzyWcURfQDS9T5NWx98vrz", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQzMut6erjgSKCpZ1dHDcjKcj9KAce7cug", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUdjqijDoyc83K4WcMW1sCn7zLd2t1WTqn", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QfnbnWrRQ4HNDQvtg3wG2B1eC4ycUsFqZz", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QU9K6pWhkok9mokkPDoCwgSu3j62ECuKLD", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QcJwVCyzraPy51uB4xd4f94n2UFYAsznGC", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVuksgNt3QAr7KCrkxtE5FWrczfgLKxs4H", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZJc1V32oFm8tufB4bk7fa3aepu4EdkeDU", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZ7wvWAUcHKRhvQ3ijdrqM4zucQKCgQ1hQ", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQXgH4CnQCB76BbXhsApu6ShhohFfvoXv7", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXpCzKnANyh8jrRRxemvsXHAeFRHrDvdXF", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qd1Xw41BzN1CgASqsh2PcrrkTKyDs2MVYF", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVEfMEeEHP768c9rEyi3WcH2JQwqheVDe3", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QehM6hQZVkrYAst8WrBVdQiMFfTtDtbKQu", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QY7gQwZKiPYMHYjYASSDBhhAoyYaxmex17", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXqAtZJsLU6HFGTzuegjeUbmEW8cGEnYt6", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QLqZD8eiNk8nrzyWcURfQDS9T5NWx98vrz", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QPE3EGR8LTYSLtWBq52SFte66oud6cxchF", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qa9NXeLAvmaTsKackfDRmm1A9zy7JeEjgV", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXqAtZJsLU6HFGTzuegjeUbmEW8cGEnYt6", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXpCzKnANyh8jrRRxemvsXHAeFRHrDvdXF", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qd1Xw41BzN1CgASqsh2PcrrkTKyDs2MVYF", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVb7P596imucSwDqb5HHEjqpnAre615PbS", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZgDpQ1Vyi5pNNe2ZzBRhcUgSU13yeWvmH", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QTgfJFaEj5wuFvufwjUmYPhQ8eVfHR4u22", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNMtHRjEWPgZUVCpiC2qE2LyJsxq1BC1Yj", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUNTxxwr9f47RcwY48ZUP6FmmKnWCsxes5", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QiQernJdiP4zCECB93v247FUtovkfJyTYm", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QSTDdguUfKe6TaQrik2zq4Xrbu6unxNa9o", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QVb7P596imucSwDqb5HHEjqpnAre615PbS", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQfFiVWkyAmfs1hDUcWU6XBfonxLdz6RSN", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgsxpySDm1qja6D2EyKuHPiUhyTM1RMk6c", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgJDnJF1zfoFBxB1LRvi5Rxb8p345UmVos", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWZwi5NUzHuJfg5fh9HzjWRpQc1fmMknAh", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QP9vU5yTsBjuTSFxH5Cb9VXYNRHKhMNAJ4", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QXm5e16Lq6dnYwpZJ8Rn2cME3ziHZfRRnp", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWkLaA4fYK51m4CszSgBd7efoDsSKamdf1", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QZgDpQ1Vyi5pNNe2ZzBRhcUgSU13yeWvmH", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QiQernJdiP4zCECB93v247FUtovkfJyTYm", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QUNTxxwr9f47RcwY48ZUP6FmmKnWCsxes5", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "Qa4KygekpW1Brr3D66K6rGtS5AY8qkhvD3", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QQfFiVWkyAmfs1hDUcWU6XBfonxLdz6RSN", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QgJDnJF1zfoFBxB1LRvi5Rxb8p345UmVos", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QWZwi5NUzHuJfg5fh9HzjWRpQc1fmMknAh", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QNMtHRjEWPgZUVCpiC2qE2LyJsxq1BC1Yj", "level": 1 },
|
||||
{ "type": "ACCOUNT_LEVEL", "target": "QP9vU5yTsBjuTSFxH5Cb9VXYNRHKhMNAJ4", "level": 1 },
|
||||
|
||||
{ "type": "GENESIS", "recipient": "QS2k8PMpmvUHzFDp2JfbVxRo8SiGiV72xx", "amount": "637557960.49687541", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUM95jekFMye3Lmkg2gj7CdhFreYye4DaA", "amount": "400671036.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaYAUoT2SzYA6kqBEW6W11brkYpSf7g4Wy", "amount": "352652351.30705076", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdoLZqB3pY4NgbSpvbsFzBDq2LcShjXsoq", "amount": "263574650.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTnPQVphizFhBFtSbTQzAC4Kubj4uqpv96", "amount": "232356835.62157121", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMtx2UmUuRZckCmRJRyxdzSAazHP8hU5rA", "amount": "160672815.43629771", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVz1rKM9QQrGX7gkxR9EV8a2T4AuD1QT87", "amount": "128506517.58266672", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUUcETWLi7EcjQ9M83Aur3SwmESFBFuQye", "amount": "126998534.24906898", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qa15fMdEiY7WqK2xwvdHrdhU2TE6yk3V3L", "amount": "122690068.33700012", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QM4LF9EzQnXJ9VyFwnJbJzskJLrybuSzsw", "amount": "114361793.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQKeokRiFCgAhBSdu1DUf5e1LCkgApvrxZ", "amount": "100000078.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QiaaoNZ54wKoaUMXxW72UsPt1MiPpeUTWm", "amount": "100000031.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYoRWAxw6CVMeYeWHKJh3csmTVkVzjpdBo", "amount": "100000026.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPwLkxemtGsrdcPJSA4iMgKbBPdecL7dwZ", "amount": "99999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMkgf9Y6Ac2TUrynDvyhX69ekpC3P3GQmN", "amount": "99008835.47860426", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUnfm6t9SrhgFB8YqCV1vQhmqFzhncxioK", "amount": "80000207.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWZm17rRXeUehcM4TprVNNRSTHWQmG2bME", "amount": "62663714.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMWSbqWDC1eHU7fCGJ3UwABHiTxBgBJGRC", "amount": "60036435.28601510", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXdcbgN6vdQRBKJzHSB2JaSDDzPEsTUi14", "amount": "49450000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QczMrVLEnEAXwvucwrfasH9H7QHNMPxLKu", "amount": "46488501.53473695", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qaa5hs9dj1gPxhqigH7tVyZ3oRuVcMULXB", "amount": "44766868.70000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQMswzmv65MZSdafqzCACm3tbLj23ixkVR", "amount": "42447489.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qa95hURaNK4kPhDhbdmDFm2wMkkoWFZ4Zz", "amount": "40976709.97984710", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVEde9kJt2ejqXavBzK35zY3eztnkSXTwN", "amount": "38172492.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZjDFrMrgeR9SGL8bprqyiXVtAZMVCXESZ", "amount": "37291276.42323117", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPmmPjdDXW35ykF557KbaVy5jYBbaM3pRp", "amount": "33333332.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaWReiLe5tAsTNoq7hMiSGNasJEXTX5bmn", "amount": "31465829.00562504", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLrErVJvpRdYAt4dGwk8i8rG3PMUSgpp3i", "amount": "26593505.67231102", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYsLsfwMRBPnunmuWmFkM4hvGsfooY8ssU", "amount": "25303386.66951814", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgULJXk3qyLDr4EKkz48rxwwvWK7BDL8Ux", "amount": "22476856.86000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNwedkYs6hToBRPZvBb6naKvDEgB7BeaKi", "amount": "21967101.30582773", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQ2DsD3fKWy88dQ6PMmdaNkAwSQrGof9aX", "amount": "20555555.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgP7vQABSDJwuE8VTdVkiNnbkorqWWctQm", "amount": "20442665.63310002", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QM5Fg3kBzj9BqsGAJKHhsEtgRgf4x2Yn5G", "amount": "20000006.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLdC7wm9PYC1KjgZaeVdGzH4M88BYhEeEw", "amount": "19927901.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLgBTDkt7t7WwbTd1RM8iGxu9FexPZcn7C", "amount": "19000025.40000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQShonrAdJXg4CzhegtynzaBP7S631vKGq", "amount": "16577946.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QM5gE4YYAL6byjpbbGH26TvuCUPmjcksY9", "amount": "16000000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMFJGxGLCbegHL3NZ7HRo3UYaCXhweJVKS", "amount": "14977498.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QcAVj3i3drVkiC9P8FtDgVdYz8Zeu61uNg", "amount": "13142764.62938834", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTDB2mmxEU7L4oUnmBh9F3qhCxfVpJUzaA", "amount": "12960806.75574690", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLgewDr48AZxWbDbRZ12xEvCePwPj9bMBE", "amount": "12684967.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPEoMF2dA7NHrHhsSG9zczCFwx9wFdWvzT", "amount": "10033147.61257500", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQSiZAgdMjadXZRVTHa252AAY2yjZfQKR1", "amount": "10000000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWLhUT55hZHgdD7ayEqwHnF9h62dHV45G1", "amount": "9999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QjnHejJPyuyTRs7huzcqXtbM6sufWyLf9o", "amount": "9999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QjJjjBUJSZAMuYiwTyfJTFthH6SrofjG6d", "amount": "8871800.22712502", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRh9zNPmz5McxeWx3iCy2xnBRrBoYDZfMJ", "amount": "8739995.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWGwGBzZG8UeNXJYZq9tjzN6ceriqTgMRd", "amount": "7825760.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRj4VNEthakckhYpCJMEBhEFk12pa7GPJT", "amount": "7810001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbdHzyw6q2hMAhg12JFbXSEcbrj43xNnYG", "amount": "7593814.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNFWWhmsun3HVUChjck8XqhsQW2tckNxxM", "amount": "7239123.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QeosmemBtE7aosv2QZhq1PTjqpKurKXKzy", "amount": "6185646.80750010", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QU8SzA12SVKPgqpTzRna5itKktGhvEdmgP", "amount": "5931486.64530000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNifyiT4LRxtkTp97VgJ23LuZ3qqTgLc33", "amount": "5580161.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdxuYD638XE84UGdKnb8QPoss1shkMRKYw", "amount": "5389659.68181819", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQM9B3ipQcSvATsxb4vgVuYs9kuxgnuBwG", "amount": "5032347.69657146", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLvdNNMagFRS3B25pAknRH1LMv5urwufHL", "amount": "4996995.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLu6X6CVhr6tovNR6k9pQijxN4DHiuLeJS", "amount": "4918400.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZgmtZrVPHd1rbHaRjF7zbYdnnS8a8mVZ3", "amount": "4418544.42625000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQ44LmLWzkWWe2UJfdvsySkd4ZRs1is3sX", "amount": "4384750.69674618", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNK7x1kgiA45Zvc5jZFaoDeDzrBrxivwYd", "amount": "4135907.02294526", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPdkHcN8DBLuxaFf4Q4zXNn3rq2G78VgNt", "amount": "4128493.31116798", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMNdPz11XubtvxXLGeiG3PHKaQW67LkZMp", "amount": "4056950.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QhUWSWWFt6vDy2qNFn68JPTPLjyDrzrh4D", "amount": "3220564.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQ7wKkXyQtkwrgG6zeSHNrnDhXQ6fGUsYS", "amount": "3088411.29750001", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QcxXT317FQi3mq9hrQggw7xZujPX63XCq7", "amount": "2779141.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMTcDA7v1dbRXHkryn4ppiM9Q7LGZFNkVT", "amount": "2752011.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTekAqCoXPy5R7NE78GXxjSaXBuekZge88", "amount": "2740816.93000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVQErjTVBkPTphbyDhPvhHKGX2etNXZsgy", "amount": "1999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNPfWoyuvYaScXuCdiCM2PEzvD7PWybBvh", "amount": "1749402.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaGhALQm9h3G6m728ZM9L2rrE7Y63nk9e8", "amount": "1637480.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QV96D15Y3qM1r55odiDf7j2TTYY8jCMJPs", "amount": "1572208.70562500", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLsEy8syg8EXttzajE8AWGg3CxccNR6YML", "amount": "1300001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLeNqYjzid2oJFL2iVtcHeAkyunuTsHae4", "amount": "1185185.44326193", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaUhQiyBZpZWSmFo56zRu2XXfZVL4D2xii", "amount": "1033451.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQYnwDmw6Kar4HzoSEG1m2p83mqnY3div7", "amount": "997720.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMrECKgkohx6ZXEMdLzikqBmAkdyHeQDqL", "amount": "997498.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QR1GoYcR3iasGNWtJjeNcZFPf9BH4TjCeC", "amount": "858153.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNsdyTKN7MVsTYqSyZNPatzRTgNyWDqPZW", "amount": "832163.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMK8v74dCqJ3d3p4JMZZknvQ1wEJovHEjd", "amount": "805444.62885757", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMFysMKXLubP21BCYeYiSNP3v5mLhp81Cd", "amount": "676881.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMdfkckSTcCMKnRRve51SEfZQPVLZg9S7b", "amount": "333086.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QN2UFyKfUsJBHmM5hZrrD2wrwZ1SctSfwQ", "amount": "296438.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMrSxECG64fp2JaoXkw9DYEp6jFPYmh5Jr", "amount": "124684.50000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQRUVwMGifreiLv3FuPTTcA1WbsvhH7Mux", "amount": "102825.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMQAG28pyv2aZVjWbKoRn39Ytir6rLPnTK", "amount": "100745.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWY9HkkCvbKJaqWMEkLbbAJcncywJgy31Q", "amount": "100004.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQDpKmaKa4uz61CSGVD2zSFaQzeRjaQ8FT", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRQvk1PCt7pNm6JSohtEQQdMEu7BrWjmtr", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTmKWUXtuQ3ihfigtuLSeFddskmSy3fGN5", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QV6rVM4Ywh7TzdN1hgSWVrArKxyEUMuMfs", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTBjfXU6ctuRFBTGkcJySZLqB3L9BdxNbb", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVdEoLEamBDg8MC8uoMe6fsWrpbd9J3mTL", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QW7o4Cd6tvFLe2WryK4eNmFCxz7a9Bs5pJ", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWCEJXDjfZLUfgCJ5V6Uy16KkdigLqEvvo", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWCTesfyodV4CnKF5cP1RCnksEonZhyJEj", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVjAVUkKjFHX9N2UQntuPWqHe3PGdBgKgx", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVEuVYqdLfkAyTU5paKkSj2kGid5X7upPi", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QV1FfiEGhYRfNSN5HeXL6ZB1t2NeQJ761N", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUqDXJzwKrte2YNpR4b44ttKWwnSk6pRTZ", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQEBW8LU48cuV4uiwQUJ94k3UkJo5eszeg", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgkNZyrj4zAEAwCDZHTLQ7RcbcCTwcNzPL", "amount": "99999.09400001", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLecTtNrkgGFAwsSPVpJLgkqdjmjLmsT3F", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVuKDbWVeM92yEta1fagk79NGQsLkvKdCf", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QW1NHhKUUPGg2WwMxNyV8VbzmEJJfBTXvy", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUVkDZ2ercosZrRBnC8YZt7et9sXfwfKP4", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQginD86frBSnoNjHEk7JE4zaBWbTqnHk6", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPNuGywKCewuy66DXj7GAJrev5HkcVWjuu", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QR2cz8ErrLtcos58iKrQA9aPa8T8kaGc5w", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QM1KcVDtpkfsrSyDx6v9okdigbXrNTDKmZ", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVvq5dE352hhujmDcoj6CaEajp7mBuAuKK", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWHinsRV49Nf2ue2ZBumK8jWHWrSFhHr91", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSYyv5yJBhkrGeB33NV3NbRYTys9mfkzze", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMHjTaHRi1zTRgXq1vddRiuMBxZpHEJFeb", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVxFXg9QMJp7FFD4XPU2BXR4hySCB9u9Zm", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQa6YP9ocmaMBZPEAPnY7dDekdqo9os4gr", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRdmMSmXk65qRYD8r1yzMBSKFdZf89nmZ8", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLm1JSR6mph8vXrJ4ZHj5PjchAP7UoN9zm", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPSEbEbncD9aUrSzNie658DuoFgex641Sd", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QU54p4EUyEr6EDEK7tmWEgwMBkr5MHNJr9", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRMbQS8Tz9tEH5Wxg6GcoqJ22NqVEcVLFY", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSu3hoF3n9qjMSBfmEn6bGVKNXpn4SrtiX", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQodoJ4PdvwWbPwLErJsXjxtrW2BDZ52Gd", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLpBVjVAxhygwbvHyGLLnKbyvsZ1jUqaWC", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPbWwDxBK3m1LU7uPv55Rq8bwvqLsSjXiE", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVeQRaWcPUNfT3yPWtm6xmuy1hM4N6b1yg", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QU6Ac4sVimeehkuK8XcbpFUBVatZ2qgd47", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSCdg6rRRm4jb3r4XNKVytyQAgiTxkAjTw", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQMFd2LfNJCyH6jkQkrK7mdKpjWiJhhFty", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRSeGbHVKqhbUGNzF33DhoJQiPpf9WxZWR", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQ5jfYBm2tPH4xCdfWDccnzUxhNPHL3VS3", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QW4jfDfsVWHLeLLvdapCG23q5MGajh8mKr", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQAiKxdyRuKT7vPTQGbTitiN4E1WVnWZFa", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTrMWWXU6WyuBRW3WU6duNXpe7AbNog4kP", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQszEg4sqt2o5oN4pdyjxrAwGD3TY5QNfZ", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQsuUfQg57meYToLCx7GTH2DYfAfe4KgQx", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMAsEHJcNAk6Vkzik2bEV6nJ7JZjWAksvi", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QP9tgb3pN6FzU3UCmHGW95s9Fmgubzm8md", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMdEATyg41BFbWuMZx829uzBZ8fqYDDERw", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVKtHgZutURJdqnMCRRewsi2WsFNmYGoEE", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVfFNmHCgWFHhUwLBNBYp32JjL2uusyfsW", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMMsPuLhww36VQAphKyo29BfhHLeekf8ST", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTAth8ahCmt7ZchLA2N6S5jbF5wCMzyC8H", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLzhvyT3LHzE9DqFrSwNivKMzkB6T1g2DD", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVfru93ZBLd5XR9YzRBbFHCuYiNV1hJhEw", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNG1SXqh2uJ4FAy2yxEzUXbRh9Z5KsTvq5", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRMpfSZ3gcX4gZKCXHam36qpaCC49F2cEt", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSqcH6aiqnGnP8iGG1cRxMSqtypsqgWprm", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPJbzQTJ7T8fRFu5NbGu9goLZyeLq5Kg3i", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVXchvzB4PaaXSwhyZcayiTTSe5cje3DE8", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMTN4ALHmKeJkbVFQQyceEAideDTb1eXdq", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWR8npVGu3w75p5WtgxMikcnujVHEuPBxL", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRPAESx3b4pWw9C4Q6YLdNYUcCQsWbzEfE", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRWVqUTDFQRRAKiWjkFCk5pdE1iDw2Cz8s", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMrodSbGSU3zsJVi8BN5X5gPE2xvXGt1ja", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNLsfHsFcS5xc7BR1fD6aNSZLPt8CEKLuG", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QM66cjqmF3WCbbiac6tVU1pLupv2AvU4ev", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRPnVABwiqT3qBqC6bscfWU6R79RjhUfp9", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QR3mxAS6xdwsRGQXiMVngYmL953gdKcaoa", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTHLtnDuCz85Xa2jkPuZKhdvyXh8yZH6NJ", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWA1V7vrRLuhibXjRCSG54y4mkTLf3a4T7", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLnyXdhGDVEBmByTtjTZihVE3UnReghNyd", "amount": "78296.75000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUcPy91Rx5kgbzrZCbR1BgbCLEZx2u3Tqc", "amount": "49996.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTHQ9pFMoraWTCS1tuFzuwWSXu7uVJ18VF", "amount": "40395.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfNkfy7kZ2gwue4Vsoz3YfARwSCS13cwXN", "amount": "14997.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QN9QJxXAT2ceDwgXvSvjkndeUGXW84xfhQ", "amount": "8505.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVZAQrQHPD8rePZ5hWKx4uj7Ty4GEBe15D", "amount": "2989.50000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPZTxWtCmH6Y6zwwntjnPDfKG6zNKRivqJ", "amount": "1282.61375000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qgwuvmkqf1wTAxqyFpvUswDqgzWrC6towa", "amount": "0.66600000", "assetId": 1 }
|
||||
{ "type": "GENESIS", "recipient": "QYgVi26jUqMzJo4ahZV9yekQNnYKHBaX8r", "amount": "637557960.49687541", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWe1iPDudLU189BggPykbH1DrAeaFEgX6W", "amount": "400671036.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVurebcEbe4USR4xcS3Mbk12mhxsjRX31u", "amount": "352652351.30705076", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfhJM5CpX5MhfjCcopwfw7pgS6w1hVJ49E", "amount": "263574650.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QeSh3t1AnaRcRThkkUTvvdMEouixCADeVh", "amount": "232356835.62157121", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QM9zVbXXnfrtQ1X7zPQ5zxPYPAWTaVMXqZ", "amount": "160672815.43629771", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZkThgFfExognAbxLjYZGVHpL7X6g3EG4A", "amount": "128506517.58266672", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVSqUrNFR4mPTMa7UdVmNKZTSaDVAv8XXF", "amount": "126998534.24906898", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXKmtkHHwaUQzGeHHG2dFiHUnKAp815Mzq", "amount": "122690068.33700012", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qft1ktvJ14eBFjpJaphT24ks4WRcN3K6tB", "amount": "114361793.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QcrnYL6yNwHKuEzYLXQ8LewG3m2B5k9K5f", "amount": "100000078.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qe9VPzQp3h4Kg3DHSHBUQ3AM3AiRBfCDfX", "amount": "100000031.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPYfRd1uhnAgqkZNmjNCjgPhkguMnHWuc4", "amount": "100000026.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QREtYDhP4HkpeCCZroemuGXMGVFoZHH3Lp", "amount": "99999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaUciVnbQDXdNygJadEY31PuDEBLi6Spmu", "amount": "99008835.47860426", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QjrC8NXwR8gFkEauvRwCPxqHroPFqAJbhK", "amount": "80000207.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QiBYApdEYRwsFYjt59UJqZV55wcwykvhsh", "amount": "62663714.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTTrv8SWR8huV8TFYUEQhfZ1j1JmtL5p8G", "amount": "60036435.28601510", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaPKuyyQtXJcsVhKLKgxCcYewxwaawxLrB", "amount": "49450000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbVGj7U4C2tQfipTss4tSzC22bbrF6fNnL", "amount": "46488501.53473695", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qd1Xw41BzN1CgASqsh2PcrrkTKyDs2MVYF", "amount": "44766868.70000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdXe21sjY8smjVmiAUgZY8xWVzwgxMgK5A", "amount": "42447489.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYsh2NB6TogqV1iXHmHXcVaWw25WEYA94o", "amount": "40976709.97984710", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZiYh4m4Uh3FH52cnow8MrNyXhSH88bp2H", "amount": "38172492.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWomBbcXNTdkyuPFUafwtBfpbxHzmUZzqi", "amount": "37291276.42323117", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QisSQZ7Et7Rfzx2SCC2o9UDSeRZWMyFKWc", "amount": "33333332.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QP8xG56L8b28h1mguSk9LuzNhxbHgAoL9b", "amount": "31465829.00562504", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfmM8dgfikTB2FYVuJ9owzQXVm8wP7T4QT", "amount": "26593505.67231102", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWvTdm9LU1GSX9q6Rrvgx7xjo2iuV2Gxn1", "amount": "25303386.66951814", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYhvqnVFapMA3s74Mu3D1DwPkEkcy1oKPn", "amount": "22476856.86000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUzUCfoakDqBaL5zBgfvTKLHcuxbUfB38Q", "amount": "21967101.30582773", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgJDnJF1zfoFBxB1LRvi5Rxb8p345UmVos", "amount": "20555555.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRBVE5gHumH6RUgEUxJdQ5417NUvc14k3F", "amount": "20442665.63310002", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbH8srDKeS5VcsQsgsaF3nqCzGT1NqfsTx", "amount": "20000006.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMuWNAJ2tbeViHtBUN3yD2KARrrzcanLAd", "amount": "19927901.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSBJuNoCAFUTcevuCTcMi3i5nzNPhC5R4b", "amount": "19000025.40000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdGbhtkFHUqd9nK9UegxxGXD1eSRYSoKjt", "amount": "16577946.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QakZtV3nZeRp5UEoEUNX7p8Qz5VbVXTQid", "amount": "16000000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZvHW7amu5DNktsBgaMrR1brHZhhhVwKLW", "amount": "14977498.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUNYcKorTAjcFEFH2kLuGzTHDSXHbTm9n4", "amount": "13142764.62938834", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXUWuZ2oAUodMU8EAQkAkDwkQHS1SFxpts", "amount": "12960806.75574690", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZw7tgMttSySNMKfcMrEbdtnqHVrQ9w9fT", "amount": "12684967.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXoW5M113C3SDUeFdjVVs2m1RT9XtzXS3z", "amount": "11583150.66666671", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdepeWWLMD3LiDRKkKBria29rDhZP9V5RJ", "amount": "10051008.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qi74UrbLEsCD9D98rRQrsszNtRA4pWbXaN", "amount": "10033147.61257500", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QawB5MesBratjs2d9EMnXnrN4EC7gw7LRw", "amount": "10000000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYpYshfVzMMVU71KqAV4erX3NJAUCnJXgD", "amount": "10000000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QeaDGU85fpffwsw9ngmd98QsT6NaFyFFed", "amount": "9999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qeq85FoJpxtzoDM93WiNQQCXiuiFynRQzm", "amount": "9999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbwgBD6LWdk1hZsb8EwdVVmDZdvpxMzyGT", "amount": "8871800.22712502", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaFN7DWcGF7keNACJpwCVnegePbfsAoFCw", "amount": "8739995.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfjL32jLsxtumbfx6ufmfCFCBccVCQFkrh", "amount": "7825760.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNMtHRjEWPgZUVCpiC2qE2LyJsxq1BC1Yj", "amount": "7810001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdtAQm1EGNgM7QDSaC2qvV9WdpRHwpApUT", "amount": "7593814.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbEJ5tfeskASVnmLFeKzspuZJJb6cJVPZ4", "amount": "7239123.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZfctgpdTkoCcLzpeWSSq7nYQwCLPsoFsL", "amount": "6185646.80750010", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZsygC1chppsJK1cnaHG4fEsNaDYfLF7ZJ", "amount": "5931486.64530000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXmYaDzKQdGiAMncJCr1FqXy6tX3avMRm9", "amount": "5580161.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgpAW6uqwNR58gNYWRCVXNLm4F5TuckAw4", "amount": "5389659.68181819", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLqeoXwo7RMJUsTv2sKbBffjDvbSSQN7vH", "amount": "5032347.69657146", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QhjovpBRoW2BJ1vRf1xmBcEqENYzexryM6", "amount": "4996995.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQNigUeJxK9WMJoGhydKpfa5y2mQp6bX7v", "amount": "4918400.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMfWg9oJg49izXMeRWrsErgNnBD6mJcKiX", "amount": "4418544.42625000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZjCgcSVvSRsFZeLJz9C5dTa36s3cSKqvB", "amount": "4384750.69674618", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXeiJa7ftN6dAYNZcdqKDBhmAFyGjX2zkm", "amount": "4135907.02294526", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QLsQg11ipfS4ZaD3QTFwncEgCBPsr1r2hj", "amount": "4128493.31116798", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QY7gQwZKiPYMHYjYASSDBhhAoyYaxmex17", "amount": "4056950.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfPcwetW3BErP4ySTurxFJSHpNkNXPEhGk", "amount": "3220564.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUJhiF8h6ubsXhyATKHccLwD9cz1ECDpaD", "amount": "3088411.29750001", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPtJ6prBJ1NR5NbtuEj7kMaM1a9FALueHA", "amount": "2779141.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QhAkKZZF1ZuC9ADgpfZ8BMZRVV43BiQhuZ", "amount": "2752011.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZxcntxfJimHus9pgfvVVPbpHs5yU7ZMhB", "amount": "2740816.93000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXHEZ4axuNq91K5wW9zaNSvtLzsdsQ1yVz", "amount": "1999999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPhNJXXc97CRBP4JMu2cgiumyfKciBHHyu", "amount": "1749402.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZu4Ka4XHKgTJMc6Y8Kr9cjF12sMKQaPQP", "amount": "1637480.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVEfMEeEHP768c9rEyi3WcH2JQwqheVDe3", "amount": "1572208.70562500", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QhdoF3Kt3dV5DkuPgTmvH3RNzgZrSK9o6W", "amount": "1300001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQ5qnof5pUgJem8NPsAPgYdENL88cNqSj9", "amount": "1185185.44326193", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdceL6F94xZCUmXgrGPKCrsvtPtKmFprDp", "amount": "1033451.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qf2KXaB68Ca6E3uYty14DrEVXGJ6RMg8Tw", "amount": "997720.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QiFUUj4GvfHTTuAhFseuoWZm3wYemqxSDn", "amount": "997498.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXjRdpB4gMjixwP7cv5yeyn8RQR9BuDhet", "amount": "858153.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWez1VBfYVJ8KFoZ6MhJDzYVLbn5mr38VT", "amount": "832163.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPE3EGR8LTYSLtWBq52SFte66oud6cxchF", "amount": "805444.62885757", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZh58Nw9KfPHyhTbVa37iuqAsPGDSzJtka", "amount": "676881.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdUkv47jHcpCB5mZvqEn47XZPzBnxfr8V5", "amount": "333086.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVCzURyACQvd5sYkmMbg35YJvtZjsr2uGC", "amount": "296438.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfVQpcqzK6RVRWESrrdgyVNmM9sPuxjBC5", "amount": "124684.50000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRMSN3DDrfGcBpqAUfYJb5xz5DovCL2Qy1", "amount": "102825.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWKYjxBUt2c6BHm26c4k7U8iF9eUEEAeQy", "amount": "100745.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qbcy4uyMkQF2JXYqGkueDiFNZ4tHjRg8CR", "amount": "100004.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMtm8wVPHGE3qHg2hMaj6SZ78D5eXw3VWZ", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPqfuZpmyA6cK6WUFwcGeKH2Te1aegkHBM", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNMDKE7XTujNQkuQorcHXw6hL7qRvyaTjr", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTpYQqRyMekaEuECziirzy3HvCVofZS1wJ", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWL7kZp6Pdd1bhxZ6SXPhVf5g7GParG9CC", "amount": "100002.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgcF6KgVZ9eDAMHJdSEeAtp91t931VKZMv", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMLjFiPiPa46YBRoushe6a227kSrXnXyKb", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qgp16aMcdiS2EUkxCm5NSZgB8DixGK51zT", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQPYyoE3Bm2vh8Wr5aaBNyirC8dd3BhBGH", "amount": "100001.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRtRELSSASzqiYy2FtNcrePH6TVnqJkv9B", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWwrtjBL4ah965XPXHYJhymreC9jyryNLZ", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMJwdufHY9dMoARHCUyGbMPAqUB4BcqGKm", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qce2Djqrk2WzG1QhMZ3BqFok9HGsz4wtM3", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QcCBVfL35rxSyQ416L2MBz14FYbNrbeNPx", "amount": "100000.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QaSL21TX9QH3NTBTXbNCysB2zWRk3vkm6z", "amount": "99999.09400001", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUXga5K8nzd9EqYtvEesZWEYuA688h6D3d", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYD3kXchZ86vUyJBXNCVQ4LUvTAd6PUZW3", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QY1RFZTD2ogRohf3UrdT4g1Qo9D122AZDN", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSGB4Rd2xhd6UmA9LALTQ4f89Tfsz5VajU", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QY6ZGZdi8h5op2VrRXkG1W5Jp3feLwp7ZD", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRKRk5HVADsN1LHygK7q2pA7dWnYKnPpCT", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUZQPWhrxpze32vGiux6wa85kg9iwuhCDx", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUKKwug9PNai3DBggXUXP8Ag7WmR5SVUR4", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgCQq4cFaGrJhwvKs4XwvccKiLZ8GVMCXR", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZ19JRpSsgvm4z6EjnbhdxJBoUYzDGvP3x", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QjEaMxcBKMsj91ytKe6GdTBJP8Mu1Ru3r4", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgqM5bKs3tNqKNAnVeaQp4oaYMXCmX6YJr", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYGNMWBmqWgVtMWGHypAsKhDVQw5mrFZww", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qd1Px9vhWuEmF2SbLx3Ez7HhGtifGMa8TJ", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRWEbzH4niUcu9dL3Yq42X4j89aqQk3qWw", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QScBgSw74MquesXmVJxerX3YgyhtShRr4q", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNHdGeFJmPcDdN8prPzPL4bk2dpnJ2ZZFr", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdXdUxnyKGGo7eEfTcx85oEikNe5nYnuwa", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgVZb632eqF1eLQm9gBGuBtyp9Dyz2FKUK", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXYk68x2tiUrDBv8eq6wd4KtBmLHYiC4zR", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRCtc67FTNKS5zVXM8omw8F55h9DP7herL", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPEbvVBWDG7qgy4smY8nWiie78Vec8qiT9", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdFZk74skMUu4rKMPEmcSVwR87LNDe6o3Y", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRaDef6H2zYfefqLwYGmUg7T6DAqo6DDqc", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPREQjU2defiYdgA33HDiLNGBpxtuebeqE", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWBFK5h61ZxGfqQpEkwwKTcLAo8t9VWe4K", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QjgGeEkyiXa43pyqkXxZbvAChQpVYfUyKz", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QcNmqT8CZ6zSZwuRm5LahRZnuGBJRnPY8o", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSq8y4ZrSbF55ZddWNcw1ett2LDtjQEvNn", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPusqAVBVFGAAeE7RdospttA18AuyLP7sB", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNiTnonHpXTeUrgNdyYWVDPP4ZdjkLpW72", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNw9xAm9TUerin9QsapCPL9mV6zmoXyJrh", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbvxC3ENqomXp11833APchdjeyCNd49nLj", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QRWEbcRnLoGccAndtLcGgpeQFH2ZBcMqHo", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTtXS6fZGThRLq4qgkwM4ngBYkLoFyZ3bK", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QgesuKa3zwx8VAseF1oHZAFHMf29k8ergq", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QhQdzLn36SDgrgoMfvdZAkoWtTUHpB3acJ", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qj5ncwncQY4KPYgKHD1eYpXHbR717PeLcJ", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QjrCFCi6dqvka4UELg2SHhM2oWnQWepd1o", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNVKrjEq5bZdiDtgo64m5kz87rTHqCwvCP", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMozpRT9aUunfmPh7EtQ6LPoth2JFJWBXC", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUGo9SErgc6ceB5aBzcSJDNqBkQ9eaCKZS", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbYTowTHCr9WzfrR6b8uDfJKwL41nG1vyr", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QQaKBSjAt9RK2bqJoSriR77X4ULstGzrFQ", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QZ2gi6BhUNpGmrErgJLFuY1WHy6xK1J7qX", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWinRb65f2g3yBoaZvTrQKQk7CW7vfBgGX", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTyokTJrR4b2y76An3BFUEbqQy5vvg76iN", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QMsKXQAYKmR7dBH4P3kMLiKzYatK3h1CeS", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QibuD4c6gvXgS4iut7q3sXuVb23rgFJq2M", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfbX8JJupEw5ckNtU4upQgET35oLTr5e6v", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbxJvwrEHZs7MDE8rbqBwZAZkcywue5F3W", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYGcPZcRhGaY1MsiDr3VtwTXmB9TAbLFSn", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPyx2bNiAnJEjitfeAh8jZXzQVKio2B7Mi", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYuMATkLjz7YB6s4EG1aWCmmmrAPj3W9Ce", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QcU4VhU9ohDXU4k4AUMapgJRYSzEpizjLN", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QVrvy4ac2jBTfxyCKB7MLimqJooTDBApmS", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QawSgZ7i2LLFTKyPxQptk9gN526ihy5yZi", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdwSxr3t4hdGHjQFy6EVGR9yGMipefsTuo", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTGeQqn3XEFdnnCqvifCFXYdKym7SaHzTd", "amount": "99999.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXm5e16Lq6dnYwpZJ8Rn2cME3ziHZfRRnp", "amount": "78296.75000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QeAa7yawpJqQYk7PNisVD89HezskBRecH6", "amount": "49996.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfjNGq3SQt17Wi3e1Qp3cHJVeHMUHTe3Ar", "amount": "40395.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYogZKQbwwQV2ym46L53RYNGp3WfACCufj", "amount": "37998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdyyAwnpPEXtHmcz6GMpDoJZmP7rR2EwF1", "amount": "35998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QdceyQF75uirtzG9s7ZD2EeTMUAhWsoyFz", "amount": "35998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSsCvzpJhynqbNceqh2AYiLRHRZnCWcR9b", "amount": "35998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXRzrQyhShKoVUUwzGEMJK8p5RZqps8wa4", "amount": "34998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QSzaKcGZscNAGR476Bi5VBKqv5meNPAZQK", "amount": "23998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPLPWkys8cyZicdacFreT7TT3a2Jg6dzL6", "amount": "22998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QbBfYeGxPXpNkRMF5TrHA7tccjxCBeX5Vg", "amount": "22998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QXKaS5ydZ4VRGrEvMr94FvNzf7onEbxetE", "amount": "22998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPfJ3FU6jnamrxZJDH3ijH5Tze8UoEmtGu", "amount": "21998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QPZdi91axpQ9mkPbJdhGGjssmT5jaaZNX2", "amount": "20998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QhUWEeKX8EAxX2KomuaVmV3NvZrAV4Eznk", "amount": "14997.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QUg3gHTxunZzZMv5j9BmNoGrDzQbC3tNtN", "amount": "13997.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QNryEH4ryCCMNqdpnSG7qdj1Yr1NN4dRgU", "amount": "9998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QhyZ1DUi2vg4JmG6aLHxqsx2TfzhqCs7L6", "amount": "8998.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "Qa9SH9rEKGDvJakQJiiNp4J1P8XnzYRCAn", "amount": "8505.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QYFxjNYARmD7d3VGwXmfcdejyZ4pvUUs13", "amount": "2989.50000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QWZwi5NUzHuJfg5fh9HzjWRpQc1fmMknAh", "amount": "1282.61375000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QTKFuqgfLCn2JxQq7A2WvLjoYGGMWmr9dj", "amount": "997.00000000", "assetId": 1 },
|
||||
{ "type": "GENESIS", "recipient": "QfwU5G7SHXNaGNmK2YvwieGpG2UsoPibB8", "amount": "0.66600000", "assetId": 1 }
|
||||
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -48,7 +48,7 @@ PUBLIC_KEY_NOT_FOUND = public key not found
|
||||
|
||||
REPOSITORY_ISSUE = repository error
|
||||
|
||||
TRANSACTION_INVALID = transaction invalid: %s
|
||||
TRANSACTION_INVALID = transaction invalid: %s (%s)
|
||||
|
||||
TRANSACTION_UNKNOWN = transaction unknown
|
||||
|
||||
|
@@ -22,8 +22,10 @@ NTP_NAG_TEXT_UNIX = Install NTP service to get an accurate clock.
|
||||
|
||||
NTP_NAG_TEXT_WINDOWS = Select "Synchronize clock" from menu to fix.
|
||||
|
||||
OPEN_NODE_UI = Open Node UI
|
||||
OPEN_UI = Open UI
|
||||
|
||||
SYNCHRONIZE_CLOCK = Synchronize clock
|
||||
|
||||
SYNCHRONIZING_BLOCKCHAIN = Synchronizing
|
||||
|
||||
SYNCHRONIZING_CLOCK = Synchronizing clock
|
||||
|
@@ -22,8 +22,10 @@ NTP_NAG_TEXT_UNIX = \u5B89\u88C5NTP\u670D\u52A1\u4EE5\u83B7\u5F97\u51C6\u786E\u7
|
||||
|
||||
NTP_NAG_TEXT_WINDOWS = \u4ECE\u83DC\u5355\u4E2D\u9009\u62E9\u201C\u540C\u6B65\u65F6\u949F\u201D\u8FDB\u884C\u4FEE\u590D\u3002
|
||||
|
||||
OPEN_NODE_UI = \u5F00\u542F\u754C\u9762
|
||||
OPEN_UI = \u5F00\u542F\u754C\u9762
|
||||
|
||||
SYNCHRONIZE_CLOCK = \u540C\u6B65\u65F6\u949F
|
||||
|
||||
SYNCHRONIZING_BLOCKCHAIN = \u540C\u6B65\u533A\u5757\u94FE
|
||||
|
||||
SYNCHRONIZING_CLOCK = \u540C\u6B65\u7740\u65F6\u949F
|
||||
|
@@ -1,4 +1,6 @@
|
||||
|
||||
ACCOUNT_ALREADY_EXISTS = account already exists
|
||||
|
||||
ACCOUNT_CANNOT_REWARD_SHARE = account cannot reward-share
|
||||
|
||||
ALREADY_GROUP_ADMIN = already group admin
|
||||
@@ -139,7 +141,7 @@ NOT_YET_RELEASED = NOT_YET_RELEASED
|
||||
|
||||
NO_BALANCE = NO_BALANCE
|
||||
|
||||
NO_BLOCKCHAIN_LOCK = NO_BLOCKCHAIN_LOCK
|
||||
NO_BLOCKCHAIN_LOCK = node's blockchain currently busy
|
||||
|
||||
NO_FLAG_PERMISSION = NO_FLAG_PERMISSION
|
||||
|
||||
|
@@ -1 +0,0 @@
|
||||
Node UI goes here!
|
@@ -11,6 +11,7 @@ import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.qortal.utils.ExecuteProduceConsume;
|
||||
import org.qortal.utils.ExecuteProduceConsume.StatsSnapshot;
|
||||
|
||||
public class EPCTests {
|
||||
|
||||
@@ -60,13 +61,12 @@ public class EPCTests {
|
||||
ScheduledExecutorService statusExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
statusExecutor.scheduleAtFixedRate(() -> {
|
||||
synchronized (testEPC) {
|
||||
final long seconds = (System.currentTimeMillis() - start) / 1000L;
|
||||
System.out.println(String.format("After %d second%s, active threads: %d, greatest thread count: %d, tasks produced: %d, tasks consumed: %d",
|
||||
seconds, (seconds != 1 ? "s" : ""),
|
||||
testEPC.getActiveThreadCount(), testEPC.getGreatestActiveThreadCount(),
|
||||
testEPC.getTasksProduced(), testEPC.getTasksConsumed()));
|
||||
}
|
||||
StatsSnapshot snapshot = testEPC.getStatsSnapshot();
|
||||
final long seconds = (System.currentTimeMillis() - start) / 1000L;
|
||||
System.out.println(String.format("After %d second%s, active threads: %d, greatest thread count: %d, tasks produced: %d, tasks consumed: %d",
|
||||
seconds, (seconds != 1 ? "s" : ""),
|
||||
snapshot.activeThreadCount, snapshot.greatestActiveThreadCount,
|
||||
snapshot.tasksProduced, snapshot.tasksConsumed));
|
||||
}, 1L, 1L, TimeUnit.SECONDS);
|
||||
|
||||
// Let it run for a minute
|
||||
@@ -78,10 +78,10 @@ public class EPCTests {
|
||||
final long after = System.currentTimeMillis();
|
||||
|
||||
System.out.println(String.format("Shutdown took %d milliseconds", after - before));
|
||||
System.out.println(String.format("Greatest thread count: %d", testEPC.getGreatestActiveThreadCount()));
|
||||
|
||||
System.out.println(String.format("Tasks produced: %d", testEPC.getTasksProduced()));
|
||||
System.out.println(String.format("Tasks consumed: %d", testEPC.getTasksConsumed()));
|
||||
StatsSnapshot snapshot = testEPC.getStatsSnapshot();
|
||||
System.out.println(String.format("Greatest thread count: %d, tasks produced: %d, tasks consumed: %d",
|
||||
snapshot.greatestActiveThreadCount, snapshot.tasksProduced, snapshot.tasksConsumed));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@@ -5,6 +5,7 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.Account;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.account.PublicKeyAccount;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.block.BlockMinter;
|
||||
import org.qortal.data.account.AccountData;
|
||||
@@ -19,11 +20,13 @@ import org.qortal.test.common.BlockUtils;
|
||||
import org.qortal.test.common.Common;
|
||||
import org.qortal.test.common.TestAccount;
|
||||
import org.qortal.test.common.TransactionUtils;
|
||||
import org.qortal.transform.Transformer;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
public class TransferPrivsTests extends Common {
|
||||
|
||||
@@ -42,6 +45,27 @@ public class TransferPrivsTests extends Common {
|
||||
Common.orphanCheck();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAliceIntoNewAccountTransferPrivs() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
TestAccount alice = Common.getTestAccount(repository, "alice");
|
||||
assertTrue(alice.canMint());
|
||||
|
||||
PrivateKeyAccount aliceMintingAccount = Common.getTestAccount(repository, "alice-reward-share");
|
||||
|
||||
byte[] randomPublicKey = new byte[Transformer.PUBLIC_KEY_LENGTH];
|
||||
Random random = new Random();
|
||||
random.nextBytes(randomPublicKey);
|
||||
|
||||
Account randomAccount = new PublicKeyAccount(repository, randomPublicKey);
|
||||
|
||||
combineAccounts(repository, alice, randomAccount, aliceMintingAccount);
|
||||
|
||||
assertFalse(alice.canMint());
|
||||
assertTrue(randomAccount.canMint());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAliceIntoDilbertTransferPrivs() throws DataException {
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
@@ -14,7 +14,8 @@ public class CheckTranslations {
|
||||
|
||||
private static final String[] SUPPORTED_LANGS = new String[] { "en", "de", "zh", "ru" };
|
||||
private static final Set<String> SYSTRAY_KEYS = Set.of("BLOCK_HEIGHT", "CHECK_TIME_ACCURACY", "CONNECTION", "CONNECTIONS",
|
||||
"EXIT", "MINTING_DISABLED", "MINTING_ENABLED", "OPEN_NODE_UI", "SYNCHRONIZE_CLOCK", "SYNCHRONIZING_CLOCK");
|
||||
"EXIT", "MINTING_DISABLED", "MINTING_ENABLED", "NTP_NAG_CAPTION", "NTP_NAG_TEXT_UNIX", "NTP_NAG_TEXT_WINDOWS",
|
||||
"OPEN_UI", "SYNCHRONIZE_CLOCK", "SYNCHRONIZING_BLOCKCHAIN", "SYNCHRONIZING_CLOCK");
|
||||
|
||||
private static String failurePrefix;
|
||||
|
||||
|
@@ -4,9 +4,11 @@ import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.bitcoinj.core.Base58;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -204,4 +206,55 @@ public class RewardTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards to founders, one in reward-share, the other is self-share. */
|
||||
@Test
|
||||
public void testFounderRewards() throws DataException {
|
||||
Common.useSettings("test-settings-v2-founder-rewards.json");
|
||||
|
||||
BigDecimal perHundred = BigDecimal.valueOf(100L);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
BigDecimal blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice to mint, therefore online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share NOT online
|
||||
|
||||
// Chloe self-share and reward-share with Dilbert both online
|
||||
PrivateKeyAccount chloeSelfShare = Common.getTestAccount(repository, "chloe-reward-share");
|
||||
mintingAndOnlineAccounts.add(chloeSelfShare);
|
||||
PrivateKeyAccount chloeDilbertRewardShare = new PrivateKeyAccount(repository, Base58.decode("HuiyqLipUN1V9p1HZfLhyEwmEA6BTaT2qEfjgkwPViV4"));
|
||||
mintingAndOnlineAccounts.add(chloeDilbertRewardShare);
|
||||
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// 3 founders (online or not) so blockReward divided by 3
|
||||
BigDecimal founderCount = BigDecimal.valueOf(3L);
|
||||
BigDecimal perFounderReward = blockReward.divide(founderCount, RoundingMode.DOWN);
|
||||
|
||||
// Alice simple self-share so her reward is perFounderReward
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, perFounderReward);
|
||||
|
||||
// Bob not online so his reward is simply perFounderReward
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, perFounderReward);
|
||||
|
||||
// Chloe has two reward-shares, so her reward is divided by 2
|
||||
BigDecimal chloeSharesCount = BigDecimal.valueOf(2L);
|
||||
BigDecimal chloePerShareReward = perFounderReward.divide(chloeSharesCount, RoundingMode.DOWN);
|
||||
// Her self-share gets chloePerShareReward
|
||||
BigDecimal chloeExpectedBalance = chloePerShareReward;
|
||||
// Her reward-share with Dilbert: 25% goes to Dilbert
|
||||
BigDecimal dilbertSharePercent = BigDecimal.valueOf(25L);
|
||||
BigDecimal dilbertExpectedBalance = chloePerShareReward.multiply(dilbertSharePercent).divide(perHundred, RoundingMode.DOWN);
|
||||
// The remaining 75% goes to Chloe
|
||||
BigDecimal rewardShareRemaining = chloePerShareReward.subtract(dilbertExpectedBalance);
|
||||
chloeExpectedBalance = chloeExpectedBalance.add(rewardShareRemaining);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeExpectedBalance);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
63
src/test/resources/test-chain-v2-founder-rewards.json
Normal file
63
src/test/resources/test-chain-v2-founder-rewards.json
Normal file
@@ -0,0 +1,63 @@
|
||||
{
|
||||
"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 }
|
||||
],
|
||||
"featureTriggers": {
|
||||
"messageHeight": 0,
|
||||
"atHeight": 0,
|
||||
"assetsTimestamp": 0,
|
||||
"votingTimestamp": 0,
|
||||
"arbitraryTimestamp": 0,
|
||||
"powfixTimestamp": 0,
|
||||
"qortalTimestamp": 0,
|
||||
"newAssetPricingTimestamp": 0,
|
||||
"groupApprovalTimestamp": 0
|
||||
},
|
||||
"genesisInfo": {
|
||||
"version": 4,
|
||||
"timestamp": 0,
|
||||
"transactions": [
|
||||
{ "type": "ISSUE_ASSET", "owner": "QcFmNxSArv5tWEzCtTKb2Lqc5QkKuQ7RNs", "assetName": "QORT", "description": "QORT native coin", "data": "", "quantity": 0, "isDivisible": true, "fee": 0, "reference": "3Verk6ZKBJc3WTTVfxFC9icSjKdM8b92eeJEpJP8qNizG4ZszNFq8wdDYdSjJXq2iogDFR1njyhsBdVpbvDfjzU7" },
|
||||
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||
{ "type": "ISSUE_ASSET", "owner": "QUwGVHPPxJNJ2dq95abQNe79EyBN2K26zM", "assetName": "QORT-from-QORA", "description": "QORT gained from holding legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
|
||||
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 },
|
||||
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "rewardSharePublicKey": "CcABzvk26TFEHG7Yok84jxyd4oBtLkx8RJdGFVz2csvp", "sharePercent": 100 },
|
||||
|
||||
{ "type": "ACCOUNT_FLAGS", "target": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "andMask": -1, "orMask": 1, "xorMask": 0 },
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "7KNBj2MnEb6zq1vvKY1q8G2Voctcc2Z1X4avFyEH2eJC", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "rewardSharePublicKey": "6bnEKqZbsCSWryUQnbBT9Umufdu3CapFvxfAni6afhFb", "sharePercent": 100 },
|
||||
{ "type": "REWARD_SHARE", "minterPublicKey": "7KNBj2MnEb6zq1vvKY1q8G2Voctcc2Z1X4avFyEH2eJC", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "rewardSharePublicKey": "Hebh14YXUdJA66Vq8KyffNXHx3NSDUAZaNH9qbfEvf5M", "sharePercent": 25 }
|
||||
|
||||
]
|
||||
}
|
||||
}
|
6
src/test/resources/test-settings-v2-founder-rewards.json
Normal file
6
src/test/resources/test-settings-v2-founder-rewards.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"restrictedApi": false,
|
||||
"blockchainConfig": "src/test/resources/test-chain-v2-founder-rewards.json",
|
||||
"wipeUnconfirmedOnStart": false,
|
||||
"minPeers": 0
|
||||
}
|
Reference in New Issue
Block a user