forked from Qortal/qortal
Compare commits
4 Commits
qora-holde
...
share-bin-
Author | SHA1 | Date | |
---|---|---|---|
|
97221a4449 | ||
|
508a34684b | ||
|
3d2144f303 | ||
|
35f3430687 |
@@ -199,6 +199,11 @@ public class Block {
|
||||
|
||||
}
|
||||
|
||||
public boolean hasShareBin(AccountLevelShareBin shareBin, int blockHeight) {
|
||||
AccountLevelShareBin ourShareBin = this.getShareBin(blockHeight);
|
||||
return ourShareBin != null && shareBin.id == ourShareBin.id;
|
||||
}
|
||||
|
||||
public long distribute(long accountAmount, Map<String, Long> balanceChanges) {
|
||||
if (this.isRecipientAlsoMinter) {
|
||||
// minter & recipient the same - simpler case
|
||||
@@ -1891,12 +1896,67 @@ public class Block {
|
||||
final boolean haveFounders = !onlineFounderAccounts.isEmpty();
|
||||
|
||||
// Determine reward candidates based on account level
|
||||
List<AccountLevelShareBin> accountLevelShareBins = BlockChain.getInstance().getAccountLevelShareBins();
|
||||
for (int binIndex = 0; binIndex < accountLevelShareBins.size(); ++binIndex) {
|
||||
// Find all accounts in share bin. getShareBin() returns null for minter accounts that are also founders, so they are effectively filtered out.
|
||||
// This needs a deep copy, so the shares can be modified when tiers aren't activated yet
|
||||
List<AccountLevelShareBin> accountLevelShareBins = new ArrayList<>();
|
||||
for (AccountLevelShareBin accountLevelShareBin : BlockChain.getInstance().getAccountLevelShareBins()) {
|
||||
accountLevelShareBins.add((AccountLevelShareBin) accountLevelShareBin.clone());
|
||||
}
|
||||
|
||||
Map<Integer, List<ExpandedAccount>> accountsForShareBin = new HashMap<>();
|
||||
|
||||
// We might need to combine some share bins if they haven't reached the minimum number of minters yet
|
||||
for (int binIndex = accountLevelShareBins.size()-1; binIndex >= 0; --binIndex) {
|
||||
AccountLevelShareBin accountLevelShareBin = accountLevelShareBins.get(binIndex);
|
||||
// Object reference compare is OK as all references are read-only from blockchain config.
|
||||
List<ExpandedAccount> binnedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.getShareBin(this.blockData.getHeight()) == accountLevelShareBin).collect(Collectors.toList());
|
||||
|
||||
// Find all accounts in share bin. getShareBin() returns null for minter accounts that are also founders, so they are effectively filtered out.
|
||||
List<ExpandedAccount> binnedAccounts = expandedAccounts.stream().filter(accountInfo -> accountInfo.hasShareBin(accountLevelShareBin, this.blockData.getHeight())).collect(Collectors.toList());
|
||||
// Add any accounts that have been moved down from a higher tier
|
||||
List<ExpandedAccount> existingBinnedAccounts = accountsForShareBin.get(binIndex);
|
||||
if (existingBinnedAccounts != null)
|
||||
binnedAccounts.addAll(existingBinnedAccounts);
|
||||
|
||||
// Logic below may only apply to higher levels, and only for share bins with a specific range of online accounts
|
||||
if (accountLevelShareBin.levels.get(0) < BlockChain.getInstance().getShareBinActivationMinLevel() ||
|
||||
binnedAccounts.isEmpty() || binnedAccounts.size() >= BlockChain.getInstance().getMinAccountsToActivateShareBin()) {
|
||||
// Add all accounts for this share bin to the accountsForShareBin list
|
||||
accountsForShareBin.put(binIndex, binnedAccounts);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Share bin contains more than one, but less than the minimum number of minters. We treat this share bin
|
||||
// as not activated yet. In these cases, the rewards and minters are combined and paid out to the previous
|
||||
// share bin, to prevent a single or handful of accounts receiving the entire rewards for a share bin.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// - Share bin for levels 5 and 6 has 100 minters
|
||||
// - Share bin for levels 7 and 8 has 10 minters
|
||||
//
|
||||
// This is below the minimum of 30, so share bins are reconstructed as follows:
|
||||
//
|
||||
// - Share bin for levels 5 and 6 now contains 110 minters
|
||||
// - Share bin for levels 7 and 8 now contains 0 minters
|
||||
// - Share bin for levels 5 and 6 now pays out rewards for levels 5, 6, 7, and 8
|
||||
// - Share bin for levels 7 and 8 pays zero rewards
|
||||
//
|
||||
// This process is iterative, so will combine several tiers if needed.
|
||||
|
||||
// Designate this share bin as empty
|
||||
accountsForShareBin.put(binIndex, new ArrayList<>());
|
||||
|
||||
// Move the accounts originally intended for this share bin to the previous one
|
||||
accountsForShareBin.put(binIndex - 1, binnedAccounts);
|
||||
|
||||
// Move the block reward from this share bin to the previous one
|
||||
AccountLevelShareBin previousShareBin = accountLevelShareBins.get(binIndex - 1);
|
||||
previousShareBin.share += accountLevelShareBin.share;
|
||||
accountLevelShareBin.share = 0L;
|
||||
}
|
||||
|
||||
// Now loop through (potentially modified) share bins and determine the reward candidates
|
||||
for (int binIndex = 0; binIndex < accountLevelShareBins.size(); ++binIndex) {
|
||||
AccountLevelShareBin accountLevelShareBin = accountLevelShareBins.get(binIndex);
|
||||
List<ExpandedAccount> binnedAccounts = accountsForShareBin.get(binIndex);
|
||||
|
||||
// No online accounts in this bin? Skip to next one
|
||||
if (binnedAccounts.isEmpty())
|
||||
@@ -1914,7 +1974,7 @@ public class Block {
|
||||
// Fetch list of legacy QORA holders who haven't reached their cap of QORT reward.
|
||||
List<EligibleQoraHolderData> qoraHolders = this.repository.getAccountRepository().getEligibleLegacyQoraHolders(isProcessingNotOrphaning ? null : this.blockData.getHeight());
|
||||
final boolean haveQoraHolders = !qoraHolders.isEmpty();
|
||||
final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShareAtHeight(this.blockData.getHeight());
|
||||
final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare();
|
||||
|
||||
// Perform account-level-based reward scaling if appropriate
|
||||
if (!haveFounders) {
|
||||
|
@@ -104,27 +104,42 @@ public class BlockChain {
|
||||
private List<RewardByHeight> rewardsByHeight;
|
||||
|
||||
/** Share of block reward/fees by account level */
|
||||
public static class AccountLevelShareBin {
|
||||
public static class AccountLevelShareBin implements Cloneable {
|
||||
public int id;
|
||||
public List<Integer> levels;
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
public long share;
|
||||
|
||||
public Object clone() {
|
||||
AccountLevelShareBin shareBinCopy = new AccountLevelShareBin();
|
||||
List<Integer> levelsCopy = new ArrayList<>();
|
||||
for (Integer level : this.levels) {
|
||||
levelsCopy.add(level);
|
||||
}
|
||||
shareBinCopy.id = this.id;
|
||||
shareBinCopy.levels = levelsCopy;
|
||||
shareBinCopy.share = this.share;
|
||||
return shareBinCopy;
|
||||
}
|
||||
}
|
||||
private List<AccountLevelShareBin> sharesByLevel;
|
||||
/** Generated lookup of share-bin by account level */
|
||||
private AccountLevelShareBin[] shareBinsByLevel;
|
||||
|
||||
/** Share of block reward/fees to legacy QORA coin holders, by block height */
|
||||
public static class ShareByHeight {
|
||||
public int height;
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
public long share;
|
||||
}
|
||||
private List<ShareByHeight> qoraHoldersShareByHeight;
|
||||
/** Share of block reward/fees to legacy QORA coin holders */
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
private Long qoraHoldersShare;
|
||||
|
||||
/** How many legacy QORA per 1 QORT of block reward. */
|
||||
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
|
||||
private Long qoraPerQortReward;
|
||||
|
||||
/** Minimum number of accounts before a share bin is considered activated */
|
||||
private int minAccountsToActivateShareBin;
|
||||
|
||||
/** Min level at which share bin activation takes place; lower levels allow less than minAccountsPerShareBin */
|
||||
private int shareBinActivationMinLevel;
|
||||
|
||||
/**
|
||||
* Number of minted blocks required to reach next level from previous.
|
||||
* <p>
|
||||
@@ -358,10 +373,22 @@ public class BlockChain {
|
||||
return this.cumulativeBlocksByLevel;
|
||||
}
|
||||
|
||||
public long getQoraHoldersShare() {
|
||||
return this.qoraHoldersShare;
|
||||
}
|
||||
|
||||
public long getQoraPerQortReward() {
|
||||
return this.qoraPerQortReward;
|
||||
}
|
||||
|
||||
public int getMinAccountsToActivateShareBin() {
|
||||
return this.minAccountsToActivateShareBin;
|
||||
}
|
||||
|
||||
public int getShareBinActivationMinLevel() {
|
||||
return this.shareBinActivationMinLevel;
|
||||
}
|
||||
|
||||
public int getMinAccountLevelToMint() {
|
||||
return this.minAccountLevelToMint;
|
||||
}
|
||||
@@ -468,15 +495,6 @@ public class BlockChain {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long getQoraHoldersShareAtHeight(int ourHeight) {
|
||||
// Scan through for QORA share at our height
|
||||
for (int i = qoraHoldersShareByHeight.size() - 1; i >= 0; --i)
|
||||
if (qoraHoldersShareByHeight.get(i).height <= ourHeight)
|
||||
return qoraHoldersShareByHeight.get(i).share;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/** Validate blockchain config read from JSON */
|
||||
private void validateConfig() {
|
||||
if (this.genesisInfo == null)
|
||||
@@ -488,8 +506,8 @@ public class BlockChain {
|
||||
if (this.sharesByLevel == null)
|
||||
Settings.throwValidationError("No \"sharesByLevel\" entry found in blockchain config");
|
||||
|
||||
if (this.qoraHoldersShareByHeight == null)
|
||||
Settings.throwValidationError("No \"qoraHoldersShareByHeight\" entry found in blockchain config");
|
||||
if (this.qoraHoldersShare == null)
|
||||
Settings.throwValidationError("No \"qoraHoldersShare\" entry found in blockchain config");
|
||||
|
||||
if (this.qoraPerQortReward == null)
|
||||
Settings.throwValidationError("No \"qoraPerQortReward\" entry found in blockchain config");
|
||||
@@ -527,7 +545,7 @@ public class BlockChain {
|
||||
Settings.throwValidationError(String.format("Missing feature trigger \"%s\" in blockchain config", featureTrigger.name()));
|
||||
|
||||
// Check block reward share bounds
|
||||
long totalShare = this.getQoraHoldersShareAtHeight(1);
|
||||
long totalShare = this.qoraHoldersShare;
|
||||
// Add share percents for account-level-based rewards
|
||||
for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevel)
|
||||
totalShare += accountLevelShareBin.share;
|
||||
@@ -565,7 +583,6 @@ public class BlockChain {
|
||||
this.blocksNeededByLevel = Collections.unmodifiableList(this.blocksNeededByLevel);
|
||||
this.cumulativeBlocksByLevel = Collections.unmodifiableList(this.cumulativeBlocksByLevel);
|
||||
this.blockTimingsByHeight = Collections.unmodifiableList(this.blockTimingsByHeight);
|
||||
this.qoraHoldersShareByHeight = Collections.unmodifiableList(this.qoraHoldersShareByHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -39,17 +39,16 @@
|
||||
{ "height": 3110401, "reward": 2.00 }
|
||||
],
|
||||
"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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 9999999, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 7200, 64800, 129600, 172800, 244000, 345600, 518400, 691200, 864000, 1036800 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -3,11 +3,9 @@ package org.qortal.test.minting;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.junit.After;
|
||||
@@ -15,7 +13,6 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.qortal.account.PrivateKeyAccount;
|
||||
import org.qortal.asset.Asset;
|
||||
import org.qortal.block.Block;
|
||||
import org.qortal.block.BlockChain;
|
||||
import org.qortal.block.BlockChain.RewardByHeight;
|
||||
import org.qortal.controller.BlockMinter;
|
||||
@@ -111,7 +108,7 @@ public class RewardTests extends Common {
|
||||
public void testLegacyQoraReward() throws DataException {
|
||||
Common.useSettings("test-settings-v2-qora-holder-extremes.json");
|
||||
|
||||
long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShareAtHeight(1);
|
||||
long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare();
|
||||
BigInteger qoraHoldersShareBI = BigInteger.valueOf(qoraHoldersShare);
|
||||
|
||||
long qoraPerQort = BlockChain.getInstance().getQoraPerQortReward();
|
||||
@@ -192,47 +189,6 @@ public class RewardTests extends Common {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLegacyQoraRewardReduction() throws DataException {
|
||||
Common.useSettings("test-settings-v2-qora-holder-extremes.json");
|
||||
|
||||
// Make sure that the QORA share reduces between blocks 4 and 5
|
||||
assertTrue(BlockChain.getInstance().getQoraHoldersShareAtHeight(5) < BlockChain.getInstance().getQoraHoldersShareAtHeight(4));
|
||||
|
||||
// Keep track of balance deltas at each height
|
||||
Map<Integer, Long> chloeQortBalanceDeltaAtEachHeight = new HashMap<>();
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
long chloeLastQortBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
|
||||
for (int i=2; i<=10; i++) {
|
||||
|
||||
Block block = BlockUtils.mintBlock(repository);
|
||||
|
||||
// Add to map of balance deltas at each height
|
||||
long chloeNewQortBalance = AccountUtils.getBalance(repository, "chloe", Asset.QORT);
|
||||
chloeQortBalanceDeltaAtEachHeight.put(block.getBlockData().getHeight(), chloeNewQortBalance - chloeLastQortBalance);
|
||||
chloeLastQortBalance = chloeNewQortBalance;
|
||||
}
|
||||
|
||||
// Ensure blocks 2-4 paid out the same rewards to Chloe
|
||||
assertEquals(chloeQortBalanceDeltaAtEachHeight.get(2), chloeQortBalanceDeltaAtEachHeight.get(4));
|
||||
|
||||
// Ensure block 5 paid a lower reward
|
||||
assertTrue(chloeQortBalanceDeltaAtEachHeight.get(5) < chloeQortBalanceDeltaAtEachHeight.get(4));
|
||||
|
||||
// Check that the reward was 20x lower
|
||||
assertTrue(chloeQortBalanceDeltaAtEachHeight.get(5) == chloeQortBalanceDeltaAtEachHeight.get(4) / 20);
|
||||
|
||||
// Orphan to block 4 and ensure that Chloe's balance hasn't been incorrectly affected by the reward reduction
|
||||
BlockUtils.orphanToBlock(repository, 4);
|
||||
long expectedChloeQortBalance = initialBalances.get("chloe").get(Asset.QORT) + chloeQortBalanceDeltaAtEachHeight.get(2) +
|
||||
chloeQortBalanceDeltaAtEachHeight.get(3) + chloeQortBalanceDeltaAtEachHeight.get(4);
|
||||
assertEquals(expectedChloeQortBalance, AccountUtils.getBalance(repository, "chloe", Asset.QORT));
|
||||
}
|
||||
}
|
||||
|
||||
/** Use Alice-Chloe reward-share to bump Chloe from level 0 to level 1, then check orphaning works as expected. */
|
||||
@Test
|
||||
public void testLevel1() throws DataException {
|
||||
@@ -338,7 +294,7 @@ public class RewardTests extends Common {
|
||||
* So Dilbert should receive 100% - legacy QORA holder's share.
|
||||
*/
|
||||
|
||||
final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShareAtHeight(1);
|
||||
final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare();
|
||||
final long remainingShare = 1_00000000 - qoraHoldersShare;
|
||||
|
||||
long dilbertExpectedBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
@@ -745,6 +701,15 @@ public class RewardTests extends Common {
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel7And8Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel7And8Reward);
|
||||
|
||||
// Orphan and ensure balances return to their previous values
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -830,6 +795,348 @@ public class RewardTests extends Common {
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel9And10Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel9And10Reward);
|
||||
|
||||
// Orphan and ensure balances return to their previous values
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 7 and 8 accounts, when the tier doesn't yet have enough minters in it */
|
||||
@Test
|
||||
public void testLevel7And8RewardsPreActivation() throws DataException, IllegalAccessException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
// Set minAccountsToActivateShareBin to 3 so that share bins 7-8 and 9-10 are considered inactive
|
||||
FieldUtils.writeField(BlockChain.getInstance(), "minAccountsToActivateShareBin", 3, true);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share NOT online
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint enough blocks to bump testAccount levels to 7 and 8
|
||||
final int minterBlocksNeeded = cumulativeBlocksByLevel.get(8) - 20; // 20 blocks before level 8, so that the test accounts reach the correct levels
|
||||
for (int bc = 0; bc < minterBlocksNeeded; ++bc)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(8, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Now that everyone is at level 7 or 8 (except Bob who has only just started minting, so is at level 1), we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
final long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are using the correct block reward value
|
||||
assertEquals(100000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Chloe, and Dilbert are 'online'.
|
||||
* Chloe is level 7; Dilbert is level 8.
|
||||
* One founder online (Alice, who is also level 7).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Level 7 and 8 is not yet activated, so its rewards are added to the level 5 and 6 share bin.
|
||||
* There are no level 5 and 6 online.
|
||||
* Chloe and Dilbert should receive equal shares of the 35% block reward for levels 5 to 8.
|
||||
* Alice should receive the remainder (65%).
|
||||
*/
|
||||
|
||||
final int level5To8SharePercent = 35_00; // 35% (combined 15% and 20%)
|
||||
final long level5To8ShareAmount = (blockReward * level5To8SharePercent) / 100L / 100L;
|
||||
final long expectedLevel5To8Reward = level5To8ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long expectedFounderReward = blockReward - level5To8ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel5To8Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel5To8Reward);
|
||||
|
||||
// Orphan and ensure balances return to their previous values
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 9 and 10 accounts, when the tier doesn't yet have enough minters in it.
|
||||
* Tier 7-8 isn't activated either, so the rewards and minters are all moved to tier 5-6. */
|
||||
@Test
|
||||
public void testLevel9And10RewardsPreActivation() throws DataException, IllegalAccessException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
// Set minAccountsToActivateShareBin to 3 so that share bins 7-8 and 9-10 are considered inactive
|
||||
FieldUtils.writeField(BlockChain.getInstance(), "minAccountsToActivateShareBin", 3, true);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share not initially online
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint enough blocks to bump testAccount levels to 9 and 10
|
||||
final int minterBlocksNeeded = cumulativeBlocksByLevel.get(10) - 20; // 20 blocks before level 10, so that the test accounts reach the correct levels
|
||||
for (int bc = 0; bc < minterBlocksNeeded; ++bc)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Bob self-share now comes online
|
||||
byte[] bobRewardSharePrivateKey = AccountUtils.rewardShare(repository, "bob", "bob", 0);
|
||||
PrivateKeyAccount bobRewardShareAccount = new PrivateKeyAccount(repository, bobRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(bobRewardShareAccount);
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(9, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(9, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(10, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Now that everyone is at level 7 or 8 (except Bob who has only just started minting, so is at level 1), we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
final long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are using the correct block reward value
|
||||
assertEquals(100000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Bob, Chloe, and Dilbert are 'online'.
|
||||
* Bob is level 1; Chloe is level 9; Dilbert is level 10.
|
||||
* One founder online (Alice, who is also level 9).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Levels 7+8, and 9+10 are not yet activated, so their rewards are added to the level 5 and 6 share bin.
|
||||
* There are no levels 5-8 online.
|
||||
* Chloe and Dilbert should receive equal shares of the 60% block reward for levels 5 to 10.
|
||||
* Alice should receive the remainder (40%).
|
||||
*/
|
||||
|
||||
final int level1And2SharePercent = 5_00; // 5%
|
||||
final int level5To10SharePercent = 60_00; // 60% (combined 15%, 20%, and 25%)
|
||||
final long level1And2ShareAmount = (blockReward * level1And2SharePercent) / 100L / 100L;
|
||||
final long level5To10ShareAmount = (blockReward * level5To10SharePercent) / 100L / 100L;
|
||||
final long expectedLevel1And2Reward = level1And2ShareAmount; // The reward is given entirely to Bob
|
||||
final long expectedLevel5To10Reward = level5To10ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long expectedFounderReward = blockReward - level1And2ShareAmount - level5To10ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance+expectedLevel1And2Reward);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel5To10Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel5To10Reward);
|
||||
|
||||
// Orphan and ensure balances return to their previous values
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/** Test rewards for level 7 and 8 accounts, when the tier reaches the minimum number of accounts */
|
||||
@Test
|
||||
public void testLevel7And8RewardsPreAndPostActivation() throws DataException, IllegalAccessException {
|
||||
Common.useSettings("test-settings-v2-reward-levels.json");
|
||||
|
||||
// Set minAccountsToActivateShareBin to 2 so that share bins 7-8 and 9-10 are considered inactive at first
|
||||
FieldUtils.writeField(BlockChain.getInstance(), "minAccountsToActivateShareBin", 2, true);
|
||||
|
||||
try (final Repository repository = RepositoryManager.getRepository()) {
|
||||
|
||||
List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
|
||||
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
|
||||
|
||||
// Alice self share online
|
||||
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
|
||||
mintingAndOnlineAccounts.add(aliceSelfShare);
|
||||
|
||||
// Bob self-share NOT online
|
||||
|
||||
// Chloe self share online
|
||||
byte[] chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
|
||||
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
|
||||
|
||||
// Dilbert self share online
|
||||
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
|
||||
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
|
||||
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
|
||||
|
||||
// Mint enough blocks to bump two of the testAccount levels to 7
|
||||
final int minterBlocksNeeded = cumulativeBlocksByLevel.get(7) - 12; // 12 blocks before level 7, so that dilbert and alice have reached level 7, but chloe will reach it in the next 2 blocks
|
||||
for (int bc = 0; bc < minterBlocksNeeded; ++bc)
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(6, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Now that dilbert has reached level 7, we can capture initial balances
|
||||
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
|
||||
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
|
||||
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint a block
|
||||
long blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure we are using the correct block reward value
|
||||
assertEquals(100000000L, blockReward);
|
||||
|
||||
/*
|
||||
* Alice, Chloe, and Dilbert are 'online'.
|
||||
* Chloe is level 6; Dilbert is level 7.
|
||||
* One founder online (Alice, who is also level 7).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Level 7 and 8 is not yet activated, so its rewards are added to the level 5 and 6 share bin.
|
||||
* There are no level 5 and 6 online.
|
||||
* Chloe and Dilbert should receive equal shares of the 35% block reward for levels 5 to 8.
|
||||
* Alice should receive the remainder (65%).
|
||||
*/
|
||||
|
||||
final int level5To8SharePercent = 35_00; // 35% (combined 15% and 20%)
|
||||
final long level5To8ShareAmount = (blockReward * level5To8SharePercent) / 100L / 100L;
|
||||
final long expectedLevel5To8Reward = level5To8ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long expectedFounderReward = blockReward - level5To8ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel5To8Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel5To8Reward);
|
||||
|
||||
// Ensure that the levels are as we expect
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(6, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
// Capture pre-activation balances
|
||||
Map<String, Map<Long, Long>> preActivationBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
|
||||
final long alicePreActivationBalance = preActivationBalances.get("alice").get(Asset.QORT);
|
||||
final long bobPreActivationBalance = preActivationBalances.get("bob").get(Asset.QORT);
|
||||
final long chloePreActivationBalance = preActivationBalances.get("chloe").get(Asset.QORT);
|
||||
final long dilbertPreActivationBalance = preActivationBalances.get("dilbert").get(Asset.QORT);
|
||||
|
||||
// Mint another block
|
||||
blockReward = BlockUtils.getNextBlockReward(repository);
|
||||
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
|
||||
|
||||
// Ensure that the levels are as we expect (chloe has now increased to level 7; level 7-8 is now activated)
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "alice").getLevel());
|
||||
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "chloe").getLevel());
|
||||
assertEquals(7, (int) Common.getTestAccount(repository, "dilbert").getLevel());
|
||||
|
||||
/*
|
||||
* Alice, Chloe, and Dilbert are 'online'.
|
||||
* Chloe and Dilbert are level 7.
|
||||
* One founder online (Alice, who is also level 7).
|
||||
* No legacy QORA holders.
|
||||
*
|
||||
* Level 7 and 8 is now activated, so its rewards are paid out in the normal way.
|
||||
* There are no level 5 and 6 online.
|
||||
* Chloe and Dilbert should receive equal shares of the 20% block reward for levels 7 to 8.
|
||||
* Alice should receive the remainder (80%).
|
||||
*/
|
||||
|
||||
final int level7To8SharePercent = 20_00; // 20%
|
||||
final long level7To8ShareAmount = (blockReward * level7To8SharePercent) / 100L / 100L;
|
||||
final long expectedLevel7To8Reward = level7To8ShareAmount / 2; // The reward is split between Chloe and Dilbert
|
||||
final long newExpectedFounderReward = blockReward - level7To8ShareAmount; // Alice should receive the remainder
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, alicePreActivationBalance+newExpectedFounderReward);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobPreActivationBalance); // Bob not online so his balance remains the same
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloePreActivationBalance+expectedLevel7To8Reward);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertPreActivationBalance+expectedLevel7To8Reward);
|
||||
|
||||
|
||||
// Orphan and ensure balances return to their pre-activation values
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, alicePreActivationBalance);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobPreActivationBalance);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloePreActivationBalance);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertPreActivationBalance);
|
||||
|
||||
|
||||
// Orphan again and ensure balances return to their initial values
|
||||
BlockUtils.orphanBlocks(repository, 1);
|
||||
|
||||
// Validate the balances
|
||||
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
|
||||
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,17 +20,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 },
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 5, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 1,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
@@ -24,17 +24,16 @@
|
||||
{ "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 }
|
||||
],
|
||||
"qoraHoldersShareByHeight": [
|
||||
{ "height": 1, "share": 0.20 },
|
||||
{ "height": 1000000, "share": 0.01 }
|
||||
{ "id": 1, "levels": [ 1, 2 ], "share": 0.05 },
|
||||
{ "id": 2, "levels": [ 3, 4 ], "share": 0.10 },
|
||||
{ "id": 3, "levels": [ 5, 6 ], "share": 0.15 },
|
||||
{ "id": 4, "levels": [ 7, 8 ], "share": 0.20 },
|
||||
{ "id": 5, "levels": [ 9, 10 ], "share": 0.25 }
|
||||
],
|
||||
"qoraHoldersShare": 0.20,
|
||||
"qoraPerQortReward": 250,
|
||||
"minAccountsToActivateShareBin": 30,
|
||||
"shareBinActivationMinLevel": 7,
|
||||
"blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ],
|
||||
"blockTimingsByHeight": [
|
||||
{ "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 }
|
||||
|
Reference in New Issue
Block a user