From 8b6124771285c57115b248b1c4ceaa3c55786710 Mon Sep 17 00:00:00 2001 From: CalDescent <caldescent@protonmail.com> Date: Fri, 2 Sep 2022 16:43:46 +0100 Subject: [PATCH] Added shareBinsByLevelV2. This allows for different share bin distribution starting at an undecided future block height. This height will correspond with the QORA reduction. New values decided in recent community vote. --- src/main/java/org/qortal/block/Block.java | 12 +- .../java/org/qortal/block/BlockChain.java | 79 ++++-- src/main/resources/blockchain.json | 10 +- .../org/qortal/test/minting/RewardTests.java | 246 +++++++++++++++++- .../test-chain-v2-block-timestamps.json | 10 +- .../test-chain-v2-disable-reference.json | 10 +- .../test-chain-v2-founder-rewards.json | 10 +- .../test-chain-v2-leftover-reward.json | 10 +- src/test/resources/test-chain-v2-minting.json | 10 +- .../resources/test-chain-v2-no-sig-agg.json | 20 +- .../test-chain-v2-qora-holder-extremes.json | 19 +- .../test-chain-v2-qora-holder-reduction.json | 107 ++++++++ .../resources/test-chain-v2-qora-holder.json | 10 +- .../test-chain-v2-reward-levels.json | 12 +- .../test-chain-v2-reward-scaling.json | 10 +- .../test-chain-v2-reward-shares.json | 10 +- src/test/resources/test-chain-v2.json | 10 +- ...est-settings-v2-qora-holder-reduction.json | 11 + 18 files changed, 559 insertions(+), 47 deletions(-) create mode 100644 src/test/resources/test-chain-v2-qora-holder-reduction.json create mode 100644 src/test/resources/test-settings-v2-qora-holder-reduction.json diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index e1ad84ff..2bd6dda0 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -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()); } diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 793c5210..a95af2b9 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -68,6 +68,7 @@ public class BlockChain { atFindNextTransactionFix, newBlockSigHeight, shareBinFix, + sharesByLevelV2Height, rewardShareLimitTimestamp, calcChainWeightTimestamp, transactionV5Timestamp, @@ -122,9 +123,11 @@ 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, by block height */ public static class ShareByHeight { @@ -370,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() { @@ -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(); } @@ -521,8 +536,11 @@ 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.sharesByLevelV2 == null) + Settings.throwValidationError("No \"sharesByLevelV2\" entry found in blockchain config"); if (this.qoraHoldersShareByHeight == null) Settings.throwValidationError("No \"qoraHoldersShareByHeight\" entry found in blockchain config"); @@ -562,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.getQoraHoldersShareAtHeight(1); + // 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)"); } @@ -584,20 +611,30 @@ 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); diff --git a/src/main/resources/blockchain.json b/src/main/resources/blockchain.json index 6d38ffef..b0336ce4 100644 --- a/src/main/resources/blockchain.json +++ b/src/main/resources/blockchain.json @@ -39,13 +39,20 @@ { "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 } ], + "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 } @@ -67,6 +74,7 @@ "atFindNextTransactionFix": 275000, "newBlockSigHeight": 320000, "shareBinFix": 399000, + "sharesByLevelV2Height": 9999999, "rewardShareLimitTimestamp": 1657382400000, "calcChainWeightTimestamp": 1620579600000, "transactionV5Timestamp": 1642176000000, diff --git a/src/test/java/org/qortal/test/minting/RewardTests.java b/src/test/java/org/qortal/test/minting/RewardTests.java index 5e2e2463..1689db5b 100644 --- a/src/test/java/org/qortal/test/minting/RewardTests.java +++ b/src/test/java/org/qortal/test/minting/RewardTests.java @@ -192,7 +192,7 @@ public class RewardTests extends Common { @Test public void testLegacyQoraRewardReduction() throws DataException { - Common.useSettings("test-settings-v2-qora-holder-extremes.json"); + 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)); @@ -1182,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); diff --git a/src/test/resources/test-chain-v2-block-timestamps.json b/src/test/resources/test-chain-v2-block-timestamps.json index d041463f..e073f1b2 100644 --- a/src/test/resources/test-chain-v2-block-timestamps.json +++ b/src/test/resources/test-chain-v2-block-timestamps.json @@ -19,13 +19,20 @@ { "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 } ], + "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 } @@ -57,6 +64,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-disable-reference.json b/src/test/resources/test-chain-v2-disable-reference.json index 8a4a58cc..a151c5c1 100644 --- a/src/test/resources/test-chain-v2-disable-reference.json +++ b/src/test/resources/test-chain-v2-disable-reference.json @@ -23,13 +23,20 @@ { "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 } ], + "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 } @@ -60,6 +67,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-founder-rewards.json b/src/test/resources/test-chain-v2-founder-rewards.json index e8f1e52b..b6db1b0c 100644 --- a/src/test/resources/test-chain-v2-founder-rewards.json +++ b/src/test/resources/test-chain-v2-founder-rewards.json @@ -24,13 +24,20 @@ { "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 } ], + "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 } @@ -61,6 +68,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-leftover-reward.json b/src/test/resources/test-chain-v2-leftover-reward.json index 233f6aa0..730cf35e 100644 --- a/src/test/resources/test-chain-v2-leftover-reward.json +++ b/src/test/resources/test-chain-v2-leftover-reward.json @@ -24,13 +24,20 @@ { "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 } ], + "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 } @@ -61,6 +68,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-minting.json b/src/test/resources/test-chain-v2-minting.json index b1a25e86..2080beb7 100644 --- a/src/test/resources/test-chain-v2-minting.json +++ b/src/test/resources/test-chain-v2-minting.json @@ -24,13 +24,20 @@ { "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 } ], + "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 } @@ -61,6 +68,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-no-sig-agg.json b/src/test/resources/test-chain-v2-no-sig-agg.json index 71e1cc3d..b671d8f5 100644 --- a/src/test/resources/test-chain-v2-no-sig-agg.json +++ b/src/test/resources/test-chain-v2-no-sig-agg.json @@ -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, diff --git a/src/test/resources/test-chain-v2-qora-holder-extremes.json b/src/test/resources/test-chain-v2-qora-holder-extremes.json index a3bd1921..d90635f8 100644 --- a/src/test/resources/test-chain-v2-qora-holder-extremes.json +++ b/src/test/resources/test-chain-v2-qora-holder-extremes.json @@ -24,16 +24,23 @@ { "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 } ], + "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 } + { "height": 1000, "share": 0.01 } ], "qoraPerQortReward": 250, "minAccountsToActivateShareBin": 30, @@ -60,7 +67,8 @@ "groupApprovalTimestamp": 0, "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, - "shareBinFix": 999999, + "shareBinFix": 0, + "sharesByLevelV2Height": 1000, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, @@ -93,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 } ] } } diff --git a/src/test/resources/test-chain-v2-qora-holder-reduction.json b/src/test/resources/test-chain-v2-qora-holder-reduction.json new file mode 100644 index 00000000..75858057 --- /dev/null +++ b/src/test/resources/test-chain-v2-qora-holder-reduction.json @@ -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 } + ] + } +} diff --git a/src/test/resources/test-chain-v2-qora-holder.json b/src/test/resources/test-chain-v2-qora-holder.json index 76d10b0d..14712dae 100644 --- a/src/test/resources/test-chain-v2-qora-holder.json +++ b/src/test/resources/test-chain-v2-qora-holder.json @@ -24,13 +24,20 @@ { "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 } ], + "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 } @@ -61,6 +68,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 1000, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-reward-levels.json b/src/test/resources/test-chain-v2-reward-levels.json index 5434345d..31390285 100644 --- a/src/test/resources/test-chain-v2-reward-levels.json +++ b/src/test/resources/test-chain-v2-reward-levels.json @@ -24,16 +24,23 @@ { "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 } ], + "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 } + { "height": 1000, "share": 0.01 } ], "qoraPerQortReward": 250, "minAccountsToActivateShareBin": 1, @@ -61,6 +68,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 6, + "sharesByLevelV2Height": 1000, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-reward-scaling.json b/src/test/resources/test-chain-v2-reward-scaling.json index 97736e7e..b02207a6 100644 --- a/src/test/resources/test-chain-v2-reward-scaling.json +++ b/src/test/resources/test-chain-v2-reward-scaling.json @@ -24,13 +24,20 @@ { "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 } ], + "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 } @@ -61,6 +68,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-chain-v2-reward-shares.json b/src/test/resources/test-chain-v2-reward-shares.json index 74ce003c..dd86e03d 100644 --- a/src/test/resources/test-chain-v2-reward-shares.json +++ b/src/test/resources/test-chain-v2-reward-shares.json @@ -23,13 +23,20 @@ { "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 } ], + "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 } @@ -60,6 +67,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 0, "calcChainWeightTimestamp": 0, "newConsensusTimestamp": 0, diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 138dd5d9..7f58fc11 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -24,13 +24,20 @@ { "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 } ], + "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 } @@ -61,6 +68,7 @@ "atFindNextTransactionFix": 0, "newBlockSigHeight": 999999, "shareBinFix": 999999, + "sharesByLevelV2Height": 999999, "rewardShareLimitTimestamp": 9999999999999, "calcChainWeightTimestamp": 0, "transactionV5Timestamp": 0, diff --git a/src/test/resources/test-settings-v2-qora-holder-reduction.json b/src/test/resources/test-settings-v2-qora-holder-reduction.json new file mode 100644 index 00000000..a489cc12 --- /dev/null +++ b/src/test/resources/test-settings-v2-qora-holder-reduction.json @@ -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 +}