Merge branch 'shares-by-level-rework'

This commit is contained in:
CalDescent 2022-09-09 11:35:56 +01:00
commit 5807d6e0dc
18 changed files with 668 additions and 68 deletions

View File

@ -10,7 +10,6 @@ import java.math.BigInteger;
import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.MessageFormat;
import java.text.NumberFormat;
import java.util.*;
import java.util.stream.Collectors;
@ -185,8 +184,11 @@ public class Block {
if (accountLevel <= 0)
return null; // level 0 isn't included in any share bins
// Select the correct set of share bins based on block height
final BlockChain blockChain = BlockChain.getInstance();
final AccountLevelShareBin[] shareBinsByLevel = blockChain.getShareBinsByAccountLevel();
final AccountLevelShareBin[] shareBinsByLevel = (blockHeight >= blockChain.getSharesByLevelV2Height()) ?
blockChain.getShareBinsByAccountLevelV2() : blockChain.getShareBinsByAccountLevelV1();
if (accountLevel > shareBinsByLevel.length)
return null;
@ -1896,10 +1898,14 @@ public class Block {
final List<ExpandedAccount> onlineFounderAccounts = expandedAccounts.stream().filter(expandedAccount -> expandedAccount.isMinterFounder).collect(Collectors.toList());
final boolean haveFounders = !onlineFounderAccounts.isEmpty();
// Select the correct set of share bins based on block height
List<AccountLevelShareBin> accountLevelShareBinsForBlock = (this.blockData.getHeight() >= BlockChain.getInstance().getSharesByLevelV2Height()) ?
BlockChain.getInstance().getAccountLevelShareBinsV2() : BlockChain.getInstance().getAccountLevelShareBinsV1();
// Determine reward candidates based on account level
// 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()) {
for (AccountLevelShareBin accountLevelShareBin : accountLevelShareBinsForBlock) {
accountLevelShareBins.add((AccountLevelShareBin) accountLevelShareBin.clone());
}
@ -1975,7 +1981,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().getQoraHoldersShare();
final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShareAtHeight(this.blockData.getHeight());
// Perform account-level-based reward scaling if appropriate
if (!haveFounders) {

View File

@ -68,6 +68,7 @@ public class BlockChain {
atFindNextTransactionFix,
newBlockSigHeight,
shareBinFix,
sharesByLevelV2Height,
rewardShareLimitTimestamp,
calcChainWeightTimestamp,
transactionV5Timestamp,
@ -122,13 +123,19 @@ public class BlockChain {
return shareBinCopy;
}
}
private List<AccountLevelShareBin> sharesByLevel;
private List<AccountLevelShareBin> sharesByLevelV1;
private List<AccountLevelShareBin> sharesByLevelV2;
/** Generated lookup of share-bin by account level */
private AccountLevelShareBin[] shareBinsByLevel;
private AccountLevelShareBin[] shareBinsByLevelV1;
private AccountLevelShareBin[] shareBinsByLevelV2;
/** Share of block reward/fees to legacy QORA coin holders */
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
private Long qoraHoldersShare;
/** 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;
/** How many legacy QORA per 1 QORT of block reward. */
@XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class)
@ -366,12 +373,20 @@ public class BlockChain {
return this.rewardsByHeight;
}
public List<AccountLevelShareBin> getAccountLevelShareBins() {
return this.sharesByLevel;
public List<AccountLevelShareBin> getAccountLevelShareBinsV1() {
return this.sharesByLevelV1;
}
public AccountLevelShareBin[] getShareBinsByAccountLevel() {
return this.shareBinsByLevel;
public List<AccountLevelShareBin> getAccountLevelShareBinsV2() {
return this.sharesByLevelV2;
}
public AccountLevelShareBin[] getShareBinsByAccountLevelV1() {
return this.shareBinsByLevelV1;
}
public AccountLevelShareBin[] getShareBinsByAccountLevelV2() {
return this.shareBinsByLevelV2;
}
public List<Integer> getBlocksNeededByLevel() {
@ -382,10 +397,6 @@ public class BlockChain {
return this.cumulativeBlocksByLevel;
}
public long getQoraHoldersShare() {
return this.qoraHoldersShare;
}
public long getQoraPerQortReward() {
return this.qoraPerQortReward;
}
@ -444,6 +455,10 @@ public class BlockChain {
return this.featureTriggers.get(FeatureTrigger.shareBinFix.name()).intValue();
}
public int getSharesByLevelV2Height() {
return this.featureTriggers.get(FeatureTrigger.sharesByLevelV2Height.name()).intValue();
}
public long getRewardShareLimitTimestamp() {
return this.featureTriggers.get(FeatureTrigger.rewardShareLimitTimestamp.name()).longValue();
}
@ -504,6 +519,15 @@ 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)
@ -512,11 +536,14 @@ public class BlockChain {
if (this.rewardsByHeight == null)
Settings.throwValidationError("No \"rewardsByHeight\" entry found in blockchain config");
if (this.sharesByLevel == null)
Settings.throwValidationError("No \"sharesByLevel\" entry found in blockchain config");
if (this.sharesByLevelV1 == null)
Settings.throwValidationError("No \"sharesByLevelV1\" entry found in blockchain config");
if (this.qoraHoldersShare == null)
Settings.throwValidationError("No \"qoraHoldersShare\" entry found in blockchain config");
if (this.sharesByLevelV2 == null)
Settings.throwValidationError("No \"sharesByLevelV2\" entry found in blockchain config");
if (this.qoraHoldersShareByHeight == null)
Settings.throwValidationError("No \"qoraHoldersShareByHeight\" entry found in blockchain config");
if (this.qoraPerQortReward == null)
Settings.throwValidationError("No \"qoraPerQortReward\" entry found in blockchain config");
@ -553,13 +580,22 @@ public class BlockChain {
if (!this.featureTriggers.containsKey(featureTrigger.name()))
Settings.throwValidationError(String.format("Missing feature trigger \"%s\" in blockchain config", featureTrigger.name()));
// Check block reward share bounds
long totalShare = this.qoraHoldersShare;
// Check block reward share bounds (V1)
long totalShareV1 = this.qoraHoldersShareByHeight.get(0).share;
// Add share percents for account-level-based rewards
for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevel)
totalShare += accountLevelShareBin.share;
for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevelV1)
totalShareV1 += accountLevelShareBin.share;
if (totalShare < 0 || totalShare > 1_00000000L)
if (totalShareV1 < 0 || totalShareV1 > 1_00000000L)
Settings.throwValidationError("Total non-founder share out of bounds (0<x<1e8)");
// Check block reward share bounds (V2)
long totalShareV2 = this.qoraHoldersShareByHeight.get(1).share;
// Add share percents for account-level-based rewards
for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevelV2)
totalShareV2 += accountLevelShareBin.share;
if (totalShareV2 < 0 || totalShareV2 > 1_00000000L)
Settings.throwValidationError("Total non-founder share out of bounds (0<x<1e8)");
}
@ -575,23 +611,34 @@ public class BlockChain {
cumulativeBlocks += this.blocksNeededByLevel.get(level);
}
// Generate lookup-array for account-level share bins
AccountLevelShareBin lastAccountLevelShareBin = this.sharesByLevel.get(this.sharesByLevel.size() - 1);
final int lastLevel = lastAccountLevelShareBin.levels.get(lastAccountLevelShareBin.levels.size() - 1);
this.shareBinsByLevel = new AccountLevelShareBin[lastLevel];
for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevel)
// Generate lookup-array for account-level share bins (V1)
AccountLevelShareBin lastAccountLevelShareBinV1 = this.sharesByLevelV1.get(this.sharesByLevelV1.size() - 1);
final int lastLevelV1 = lastAccountLevelShareBinV1.levels.get(lastAccountLevelShareBinV1.levels.size() - 1);
this.shareBinsByLevelV1 = new AccountLevelShareBin[lastLevelV1];
for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevelV1)
for (int level : accountLevelShareBin.levels)
// level 1 stored at index 0, level 2 stored at index 1, etc.
// level 0 not allowed
this.shareBinsByLevel[level - 1] = accountLevelShareBin;
this.shareBinsByLevelV1[level - 1] = accountLevelShareBin;
// Generate lookup-array for account-level share bins (V2)
AccountLevelShareBin lastAccountLevelShareBinV2 = this.sharesByLevelV2.get(this.sharesByLevelV2.size() - 1);
final int lastLevelV2 = lastAccountLevelShareBinV2.levels.get(lastAccountLevelShareBinV2.levels.size() - 1);
this.shareBinsByLevelV2 = new AccountLevelShareBin[lastLevelV2];
for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevelV2)
for (int level : accountLevelShareBin.levels)
// level 1 stored at index 0, level 2 stored at index 1, etc.
// level 0 not allowed
this.shareBinsByLevelV2[level - 1] = accountLevelShareBin;
// Convert collections to unmodifiable form
this.rewardsByHeight = Collections.unmodifiableList(this.rewardsByHeight);
this.sharesByLevel = Collections.unmodifiableList(this.sharesByLevel);
this.sharesByLevelV1 = Collections.unmodifiableList(this.sharesByLevelV1);
this.sharesByLevelV2 = Collections.unmodifiableList(this.sharesByLevelV2);
this.blocksNeededByLevel = Collections.unmodifiableList(this.blocksNeededByLevel);
this.cumulativeBlocksByLevel = Collections.unmodifiableList(this.cumulativeBlocksByLevel);
this.blockTimingsByHeight = Collections.unmodifiableList(this.blockTimingsByHeight);
this.qoraHoldersShareByHeight = Collections.unmodifiableList(this.qoraHoldersShareByHeight);
}
/**

View File

@ -39,14 +39,24 @@
{ "height": 2851201, "reward": 2.25 },
{ "height": 3110401, "reward": 2.00 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 9999999, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -64,6 +74,7 @@
"atFindNextTransactionFix": 275000,
"newBlockSigHeight": 320000,
"shareBinFix": 399000,
"sharesByLevelV2Height": 9999999,
"rewardShareLimitTimestamp": 1657382400000,
"calcChainWeightTimestamp": 1620579600000,
"transactionV5Timestamp": 1642176000000,

View File

@ -13,6 +13,7 @@ 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;
@ -108,7 +109,7 @@ public class RewardTests extends Common {
public void testLegacyQoraReward() throws DataException {
Common.useSettings("test-settings-v2-qora-holder-extremes.json");
long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare();
long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShareAtHeight(1);
BigInteger qoraHoldersShareBI = BigInteger.valueOf(qoraHoldersShare);
long qoraPerQort = BlockChain.getInstance().getQoraPerQortReward();
@ -189,6 +190,47 @@ public class RewardTests extends Common {
}
}
@Test
public void testLegacyQoraRewardReduction() throws DataException {
Common.useSettings("test-settings-v2-qora-holder-reduction.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 {
@ -294,7 +336,7 @@ public class RewardTests extends Common {
* So Dilbert should receive 100% - legacy QORA holder's share.
*/
final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare();
final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShareAtHeight(1);
final long remainingShare = 1_00000000 - qoraHoldersShare;
long dilbertExpectedBalance = initialBalances.get("dilbert").get(Asset.QORT);
@ -1140,6 +1182,250 @@ public class RewardTests extends Common {
}
}
/** Test rewards for level 1 and 2 accounts with V2 share-bin layout (post QORA reduction) */
@Test
public void testLevel1And2RewardsShareBinsV2() throws DataException {
Common.useSettings("test-settings-v2-reward-levels.json");
try (final Repository repository = RepositoryManager.getRepository()) {
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
// Alice self share online
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
mintingAndOnlineAccounts.add(aliceSelfShare);
byte[] chloeRewardSharePrivateKey;
// Bob self-share NOT online
// Mint some blocks, to get us close to the V2 activation, but with some room for Chloe and Dilbert to start minting some blocks
for (int i=0; i<990; i++)
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
// Chloe self share comes online
try {
chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
} catch (IllegalArgumentException ex) {
LOGGER.error("FAILED {}", ex.getLocalizedMessage(), ex);
throw ex;
}
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
// Dilbert self share comes online
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
// Mint 6 more blocks, so that V2 share bins are nearly activated
for (int i=0; i<6; i++)
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
// Ensure that the levels are as we expect
assertEquals(10, (int) Common.getTestAccount(repository, "alice").getLevel());
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
assertEquals(1, (int) Common.getTestAccount(repository, "chloe").getLevel());
assertEquals(2, (int) Common.getTestAccount(repository, "dilbert").getLevel());
// Ensure that only Alice is a founder
assertEquals(1, getFlags(repository, "alice"));
assertEquals(0, getFlags(repository, "bob"));
assertEquals(0, getFlags(repository, "chloe"));
assertEquals(0, getFlags(repository, "dilbert"));
// Now that everyone is at level 1 or 2, we can capture initial balances
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
// Mint a block
final long blockReward = BlockUtils.getNextBlockReward(repository);
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
// Ensure we are at the correct height and block reward value
assertEquals(1000, (int) repository.getBlockRepository().getLastBlock().getHeight());
assertEquals(100000000L, blockReward);
// We are past the sharesByLevelV2Height feature trigger, so we expect level 1 and 2 to share the increased reward (6%)
final int level1And2SharePercent = 6_00; // 6%
final long level1And2ShareAmount = (blockReward * level1And2SharePercent) / 100L / 100L;
final long expectedLevel1And2RewardV2 = level1And2ShareAmount / 2; // The reward is split between Chloe and Dilbert
final long expectedFounderRewardV2 = blockReward - level1And2ShareAmount; // Alice should receive the remainder
// Validate the balances
assertEquals(6000000, level1And2ShareAmount);
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderRewardV2);
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance+expectedLevel1And2RewardV2);
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel1And2RewardV2);
// Now orphan the latest block. This brings us to the threshold of the sharesByLevelV2Height feature trigger
BlockUtils.orphanBlocks(repository, 1);
assertEquals(999, (int) repository.getBlockRepository().getLastBlock().getHeight());
// Ensure the latest block rewards have been subtracted and they have returned to their initial values
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
// Orphan another block. This time, the block that was orphaned was prior to the sharesByLevelV2Height feature trigger.
BlockUtils.orphanBlocks(repository, 1);
assertEquals(998, (int) repository.getBlockRepository().getLastBlock().getHeight());
// In V1 of share-bins, level 1-2 pays out 5% instead of 6%
final int level1And2SharePercentV1 = 5_00; // 5%
final long level1And2ShareAmountV1 = (blockReward * level1And2SharePercentV1) / 100L / 100L;
final long expectedLevel1And2RewardV1 = level1And2ShareAmountV1 / 2; // The reward is split between Chloe and Dilbert
final long expectedFounderRewardV1 = blockReward - level1And2ShareAmountV1; // Alice should receive the remainder
// Validate the share amounts and balances
assertEquals(5000000, level1And2ShareAmountV1);
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance-expectedFounderRewardV1);
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance-expectedLevel1And2RewardV1);
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance-expectedLevel1And2RewardV1);
// Orphan the latest block one last time
BlockUtils.orphanBlocks(repository, 1);
assertEquals(997, (int) repository.getBlockRepository().getLastBlock().getHeight());
// Validate balances
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance-(expectedFounderRewardV1*2));
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance-(expectedLevel1And2RewardV1*2));
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance-(expectedLevel1And2RewardV1*2));
}
}
/** Test rewards for level 1 and 2 accounts with V2 share-bin layout (post QORA reduction)
* plus some legacy QORA holders */
@Test
public void testLevel1And2RewardsShareBinsV2WithQoraHolders() throws DataException {
Common.useSettings("test-settings-v2-qora-holder-extremes.json");
try (final Repository repository = RepositoryManager.getRepository()) {
List<PrivateKeyAccount> mintingAndOnlineAccounts = new ArrayList<>();
// Some legacy QORA holders exist (Bob and Chloe)
// Alice self share online
PrivateKeyAccount aliceSelfShare = Common.getTestAccount(repository, "alice-reward-share");
mintingAndOnlineAccounts.add(aliceSelfShare);
byte[] chloeRewardSharePrivateKey;
// Bob self-share NOT online
// Mint some blocks, to get us close to the V2 activation, but with some room for Chloe and Dilbert to start minting some blocks
for (int i=0; i<990; i++)
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
// Chloe self share comes online
try {
chloeRewardSharePrivateKey = AccountUtils.rewardShare(repository, "chloe", "chloe", 0);
} catch (IllegalArgumentException ex) {
LOGGER.error("FAILED {}", ex.getLocalizedMessage(), ex);
throw ex;
}
PrivateKeyAccount chloeRewardShareAccount = new PrivateKeyAccount(repository, chloeRewardSharePrivateKey);
mintingAndOnlineAccounts.add(chloeRewardShareAccount);
// Dilbert self share comes online
byte[] dilbertRewardSharePrivateKey = AccountUtils.rewardShare(repository, "dilbert", "dilbert", 0);
PrivateKeyAccount dilbertRewardShareAccount = new PrivateKeyAccount(repository, dilbertRewardSharePrivateKey);
mintingAndOnlineAccounts.add(dilbertRewardShareAccount);
// Mint 6 more blocks, so that V2 share bins are nearly activated
for (int i=0; i<6; i++)
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
// Ensure that the levels are as we expect
assertEquals(10, (int) Common.getTestAccount(repository, "alice").getLevel());
assertEquals(1, (int) Common.getTestAccount(repository, "bob").getLevel());
assertEquals(1, (int) Common.getTestAccount(repository, "chloe").getLevel());
assertEquals(2, (int) Common.getTestAccount(repository, "dilbert").getLevel());
// Ensure that only Alice is a founder
assertEquals(1, getFlags(repository, "alice"));
assertEquals(0, getFlags(repository, "bob"));
assertEquals(0, getFlags(repository, "chloe"));
assertEquals(0, getFlags(repository, "dilbert"));
// Now that everyone is at level 1 or 2, we can capture initial balances
Map<String, Map<Long, Long>> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA);
final long aliceInitialBalance = initialBalances.get("alice").get(Asset.QORT);
final long bobInitialBalance = initialBalances.get("bob").get(Asset.QORT);
final long chloeInitialBalance = initialBalances.get("chloe").get(Asset.QORT);
final long dilbertInitialBalance = initialBalances.get("dilbert").get(Asset.QORT);
// Mint a block
final long blockReward = BlockUtils.getNextBlockReward(repository);
BlockMinter.mintTestingBlock(repository, mintingAndOnlineAccounts.toArray(new PrivateKeyAccount[0]));
// Ensure we are at the correct height and block reward value
assertEquals(1000, (int) repository.getBlockRepository().getLastBlock().getHeight());
assertEquals(100000000L, blockReward);
// We are past the sharesByLevelV2Height feature trigger, so we expect level 1 and 2 to share the increased reward (6%)
// and the QORA share will be 1%
final int level1And2SharePercent = 6_00; // 6%
final int qoraSharePercentV2 = 1_00; // 1%
final long qoraShareAmountV2 = (blockReward * qoraSharePercentV2) / 100L / 100L;
final long level1And2ShareAmount = (blockReward * level1And2SharePercent) / 100L / 100L;
final long expectedLevel1And2RewardV2 = level1And2ShareAmount / 2; // The reward is split between Chloe and Dilbert
final long expectedFounderRewardV2 = blockReward - level1And2ShareAmount - qoraShareAmountV2; // Alice should receive the remainder
// Validate the balances
assertEquals(6000000, level1And2ShareAmount);
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance+expectedFounderRewardV2);
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
// Chloe is a QORA holder and will receive additional QORT, so it's not easy to pre-calculate her balance
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance+expectedLevel1And2RewardV2);
// Now orphan the latest block. This brings us to the threshold of the sharesByLevelV2Height feature trigger
BlockUtils.orphanBlocks(repository, 1);
assertEquals(999, (int) repository.getBlockRepository().getLastBlock().getHeight());
// Ensure the latest block rewards have been subtracted and they have returned to their initial values
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance);
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
AccountUtils.assertBalance(repository, "chloe", Asset.QORT, chloeInitialBalance);
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance);
// Orphan another block. This time, the block that was orphaned was prior to the sharesByLevelV2Height feature trigger.
BlockUtils.orphanBlocks(repository, 1);
assertEquals(998, (int) repository.getBlockRepository().getLastBlock().getHeight());
// In V1 of share-bins, level 1-2 pays out 5% instead of 6%, and the QORA share is higher at 20%
final int level1And2SharePercentV1 = 5_00; // 5%
final int qoraSharePercentV1 = 20_00; // 20%
final long qoraShareAmountV1 = (blockReward * qoraSharePercentV1) / 100L / 100L;
final long level1And2ShareAmountV1 = (blockReward * level1And2SharePercentV1) / 100L / 100L;
final long expectedLevel1And2RewardV1 = level1And2ShareAmountV1 / 2; // The reward is split between Chloe and Dilbert
final long expectedFounderRewardV1 = blockReward - level1And2ShareAmountV1 - qoraShareAmountV1; // Alice should receive the remainder
// Validate the share amounts and balances
assertEquals(5000000, level1And2ShareAmountV1);
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance-expectedFounderRewardV1);
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
// Chloe is a QORA holder and will receive additional QORT, so it's not easy to pre-calculate her balance
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance-expectedLevel1And2RewardV1);
// Orphan the latest block one last time
BlockUtils.orphanBlocks(repository, 1);
assertEquals(997, (int) repository.getBlockRepository().getLastBlock().getHeight());
// Validate balances
AccountUtils.assertBalance(repository, "alice", Asset.QORT, aliceInitialBalance-(expectedFounderRewardV1*2));
AccountUtils.assertBalance(repository, "bob", Asset.QORT, bobInitialBalance); // Bob not online so his balance remains the same
// Chloe is a QORA holder and will receive additional QORT, so it's not easy to pre-calculate her balance
AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertInitialBalance-(expectedLevel1And2RewardV1*2));
}
}
private int getFlags(Repository repository, String name) throws DataException {
TestAccount testAccount = Common.getTestAccount(repository, name);

View File

@ -19,14 +19,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -54,6 +64,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -23,14 +23,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -57,6 +67,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -58,6 +68,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -58,6 +68,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -58,6 +68,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -20,12 +20,19 @@
{ "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 }
"sharesByLevelV1": [
{ "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 }
],
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShare": 0.20,
"qoraPerQortReward": 250,
@ -52,6 +59,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -57,7 +67,8 @@
"groupApprovalTimestamp": 0,
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"shareBinFix": 0,
"sharesByLevelV2Height": 1000,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,
@ -90,7 +101,10 @@
{ "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 },
{ "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 },
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 8 }
{ "type": "ACCOUNT_LEVEL", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "level": 1 },
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 2 },
{ "type": "ACCOUNT_LEVEL", "target": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "level": 1 },
{ "type": "ACCOUNT_LEVEL", "target": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "level": 1 }
]
}
}

View File

@ -0,0 +1,107 @@
{
"isTestChain": true,
"blockTimestampMargin": 500,
"transactionExpiryPeriod": 86400000,
"maxBlockSize": 2097152,
"maxBytesPerUnitFee": 1024,
"unitFee": "0.1",
"nameRegistrationUnitFees": [
{ "timestamp": 1645372800000, "fee": "5" }
],
"requireGroupForApproval": false,
"minAccountLevelToRewardShare": 5,
"maxRewardSharesPerFounderMintingAccount": 6,
"maxRewardSharesByTimestamp": [
{ "timestamp": 0, "maxShares": 6 },
{ "timestamp": 9999999999999, "maxShares": 3 }
],
"founderEffectiveMintingLevel": 10,
"onlineAccountSignaturesMinLifetime": 3600000,
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 9999999999999,
"rewardsByHeight": [
{ "height": 1, "reward": 100 },
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevelV1": [
{ "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 }
],
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 5, "share": 0.01 }
],
"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 }
],
"ciyamAtSettings": {
"feePerStep": "0.0001",
"maxStepsPerRound": 500,
"stepsPerFunctionCall": 10,
"minutesPerBlock": 1
},
"featureTriggers": {
"messageHeight": 0,
"atHeight": 0,
"assetsTimestamp": 0,
"votingTimestamp": 0,
"arbitraryTimestamp": 0,
"powfixTimestamp": 0,
"qortalTimestamp": 0,
"newAssetPricingTimestamp": 0,
"groupApprovalTimestamp": 0,
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 5,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,
"transactionV6Timestamp": 0,
"disableReferenceTimestamp": 9999999999999,
"aggregateSignatureTimestamp": 0
},
"genesisInfo": {
"version": 4,
"timestamp": 0,
"transactions": [
{ "type": "ISSUE_ASSET", "assetName": "QORT", "description": "QORT native coin", "data": "", "quantity": 0, "isDivisible": true, "fee": 0 },
{ "type": "ISSUE_ASSET", "assetName": "Legacy-QORA", "description": "Representative legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
{ "type": "ISSUE_ASSET", "assetName": "QORT-from-QORA", "description": "QORT gained from holding legacy QORA", "quantity": 0, "isDivisible": true, "data": "{}", "isUnspendable": true },
{ "type": "GENESIS", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "amount": "1000000000" },
{ "type": "GENESIS", "recipient": "QixPbJUwsaHsVEofJdozU9zgVqkK6aYhrK", "amount": "1000000" },
{ "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "1000000" },
{ "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "1000000" },
{ "type": "GENESIS", "recipient": "QaUpHNhT3Ygx6avRiKobuLdusppR5biXjL", "amount": "637557960.49687541", "assetId": 1 },
{ "type": "GENESIS", "recipient": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "amount": "0.666", "assetId": 1 },
{ "type": "CREATE_GROUP", "creatorPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "groupName": "dev-group", "description": "developer group", "isOpen": false, "approvalThreshold": "PCT100", "minimumBlockDelay": 0, "maximumBlockDelay": 1440 },
{ "type": "ISSUE_ASSET", "issuerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "assetName": "TEST", "description": "test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
{ "type": "ISSUE_ASSET", "issuerPublicKey": "C6wuddsBV3HzRrXUtezE7P5MoRXp5m3mEDokRDGZB6ry", "assetName": "OTHER", "description": "other test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
{ "type": "ISSUE_ASSET", "issuerPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "assetName": "GOLD", "description": "gold test asset", "data": "", "quantity": 1000000, "isDivisible": true, "fee": 0 },
{ "type": "ACCOUNT_FLAGS", "target": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "andMask": -1, "orMask": 1, "xorMask": 0 },
{ "type": "REWARD_SHARE", "minterPublicKey": "2tiMr5LTpaWCgbRvkPK8TFd7k63DyHJMMFFsz9uBf1ZP", "recipient": "QgV4s3xnzLhVBEJxcYui4u4q11yhUHsd9v", "rewardSharePublicKey": "7PpfnvLSG7y4HPh8hE7KoqAjLCkv7Ui6xw4mKAkbZtox", "sharePercent": 100 },
{ "type": "ACCOUNT_LEVEL", "target": "Qci5m9k4rcwe4ruKrZZQKka4FzUUMut3er", "level": 8 }
]
}
}

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -58,6 +68,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 1000,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 1,
"shareBinActivationMinLevel": 7,
@ -58,6 +68,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 6,
"sharesByLevelV2Height": 1000,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -58,6 +68,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -23,14 +23,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -57,6 +67,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 0,
"calcChainWeightTimestamp": 0,
"newConsensusTimestamp": 0,

View File

@ -24,14 +24,24 @@
{ "height": 11, "reward": 10 },
{ "height": 21, "reward": 1 }
],
"sharesByLevel": [
"sharesByLevelV1": [
{ "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,
"sharesByLevelV2": [
{ "id": 1, "levels": [ 1, 2 ], "share": 0.06 },
{ "id": 2, "levels": [ 3, 4 ], "share": 0.13 },
{ "id": 3, "levels": [ 5, 6 ], "share": 0.19 },
{ "id": 4, "levels": [ 7, 8 ], "share": 0.26 },
{ "id": 5, "levels": [ 9, 10 ], "share": 0.32 }
],
"qoraHoldersShareByHeight": [
{ "height": 1, "share": 0.20 },
{ "height": 1000000, "share": 0.01 }
],
"qoraPerQortReward": 250,
"minAccountsToActivateShareBin": 30,
"shareBinActivationMinLevel": 7,
@ -58,6 +68,7 @@
"atFindNextTransactionFix": 0,
"newBlockSigHeight": 999999,
"shareBinFix": 999999,
"sharesByLevelV2Height": 999999,
"rewardShareLimitTimestamp": 9999999999999,
"calcChainWeightTimestamp": 0,
"transactionV5Timestamp": 0,

View File

@ -0,0 +1,11 @@
{
"repositoryPath": "testdb",
"restrictedApi": false,
"blockchainConfig": "src/test/resources/test-chain-v2-qora-holder-reduction.json",
"exportPath": "qortal-backup-test",
"bootstrap": false,
"wipeUnconfirmedOnStart": false,
"testNtpOffset": 0,
"minPeers": 0,
"pruneBlockLimit": 100
}