From 5ffddd01692e2eef0411effa08e18db75bda76b5 Mon Sep 17 00:00:00 2001 From: catbref Date: Tue, 2 Jun 2020 10:42:45 +0100 Subject: [PATCH] Changes to block reward distribution Any reward leftover from ditributing to legacy QORA holders is reallocated to either: founders if any online or account-level-based reward candidates, if no founders online We should get pretty close to 100% block reward distribution, barring rounding artifacts. More documentation and tests. Removed BlockChain's founderShare as it is calculated in Block on a per-block basis instead. --- src/main/java/org/qortal/block/Block.java | 193 +++++++++++------- .../java/org/qortal/block/BlockChain.java | 27 +-- .../org/qortal/test/minting/RewardTests.java | 67 ++++++ .../test-chain-v2-leftover-reward.json | 70 +++++++ .../test-chain-v2-reward-scaling.json | 70 +++++++ .../test-settings-v2-leftover-reward.json | 7 + .../test-settings-v2-reward-scaling.json | 7 + 7 files changed, 354 insertions(+), 87 deletions(-) create mode 100644 src/test/resources/test-chain-v2-leftover-reward.json create mode 100644 src/test/resources/test-chain-v2-reward-scaling.json create mode 100644 src/test/resources/test-settings-v2-leftover-reward.json create mode 100644 src/test/resources/test-settings-v2-reward-scaling.json diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index ea4eee35..f17a0777 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -1620,14 +1620,91 @@ public class Block { } } - protected void distributeBlockReward(final long totalAmount) throws DataException { - LOGGER.trace(() -> String.format("Distributing: %s", Amounts.prettyAmount(totalAmount))); + protected void distributeBlockReward(long totalAmount) throws DataException { + final long totalAmountForLogging = totalAmount; + LOGGER.trace(() -> String.format("Distributing: %s", Amounts.prettyAmount(totalAmountForLogging))); + + final boolean isProcessingNotOrphaning = totalAmount >= 0; + + // How to distribute reward among groups, with ratio, IN ORDER + List rewardCandidates = determineBlockRewardCandidates(isProcessingNotOrphaning); + + // Now distribute to candidates + + // Collate all balance changes and then apply in one final step + Map balanceChanges = new HashMap<>(); + + long remainingAmount = totalAmount; + for (int r = 0; r < rewardCandidates.size(); ++r) { + BlockRewardCandidate rewardCandidate = rewardCandidates.get(r); + + // Distribute to these reward candidate accounts + final long distributionAmount = Amounts.roundDownScaledMultiply(totalAmount, rewardCandidate.share); + + long sharedAmount = rewardCandidate.distribute(distributionAmount, balanceChanges); + remainingAmount -= sharedAmount; + + // Reallocate any amount we didn't distribute, e.g. from maxxed legacy QORA holders + if (sharedAmount != distributionAmount) + totalAmount += Amounts.scaledDivide(distributionAmount - sharedAmount, 1_00000000 - rewardCandidate.share); + + final long remainingAmountForLogging = remainingAmount; + LOGGER.trace(() -> String.format("%s share: %s. Actually shared: %s. Remaining: %s", + rewardCandidate.description, + Amounts.prettyAmount(distributionAmount), + Amounts.prettyAmount(sharedAmount), + Amounts.prettyAmount(remainingAmountForLogging))); + } + + // Apply balance changes + for (Map.Entry balanceChange : balanceChanges.entrySet()) + balanceChange.getKey().modifyAssetBalance(Asset.QORT, balanceChange.getValue()); + } + + protected List determineBlockRewardCandidates(boolean isProcessingNotOrphaning) throws DataException { + // How to distribute reward among groups, with ratio, IN ORDER + List rewardCandidates = new ArrayList<>(); // All online accounts final List expandedAccounts = this.getExpandedAccounts(); - // How to distribute reward among groups, with ratio, IN ORDER - List rewardCandidates = new ArrayList<>(); + /* + * Distribution rules: + * + * Distribution is based on the minting account of 'online' reward-shares. + * + * If ANY founders are online, then they receive the leftover non-distributed reward. + * If NO founders are online, then account-level-based rewards are scaled up so 100% of reward is allocated. + * + * If ANY non-maxxed legacy QORA holders exist then they are always allocated their fixed share (e.g. 20%). + * + * There has to be either at least one 'online' account for blocks to be minted + * so there is always either one account-level-based or founder reward candidate. + * + * Examples: + * + * With at least one founder online: + * Level 1/2 accounts: 5% + * Legacy QORA holders: 20% + * Founders: ~75% + * + * No online founders: + * Level 1/2 accounts: 5% + * Level 5/6 accounts: 15% + * Legacy QORA holders: 20% + * Total: 40% + * + * After scaling account-level-based shares to fill 100%: + * Level 1/2 accounts: 20% + * Level 5/6 accounts: 60% + * Legacy QORA holders: 20% + * Total: 100% + */ + long totalShares = 0; + + // Determine whether we have any online founders + final List onlineFounderAccounts = expandedAccounts.stream().filter(expandedAccount -> expandedAccount.isMinterFounder).collect(Collectors.toList()); + final boolean haveFounders = !onlineFounderAccounts.isEmpty(); // Determine reward candidates based on account level List accountLevelShareBins = BlockChain.getInstance().getAccountLevelShareBins(); @@ -1644,86 +1721,64 @@ public class Block { String description = String.format("Bin %d", binIndex); BlockRewardDistributor accountLevelBinDistributor = (distributionAmount, balanceChanges) -> distributeBlockRewardShare(distributionAmount, binnedAccounts, balanceChanges); - BlockRewardCandidate rewardCandidate = new BlockRewardCandidate(description, accountLevelShareBins.get(binIndex).share, accountLevelBinDistributor); + BlockRewardCandidate rewardCandidate = new BlockRewardCandidate(description, accountLevelShareBin.share, accountLevelBinDistributor); rewardCandidates.add(rewardCandidate); + + totalShares += rewardCandidate.share; } - // Determine reward candidates based on legacy QORA held // Fetch list of legacy QORA holders who haven't reached their cap of QORT reward. - final boolean isProcessingNotOrphaning = totalAmount >= 0; List qoraHolders = this.repository.getAccountRepository().getEligibleLegacyQoraHolders(isProcessingNotOrphaning ? null : this.blockData.getHeight()); + final boolean haveQoraHolders = !qoraHolders.isEmpty(); + final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare(); - // Any eligible legacy QORA holders? - if (!qoraHolders.isEmpty()) { + // Perform account-level-based reward scaling if appropriate + if (!haveFounders) { + // Recalculate distribution ratios based on candidates + + // Nothing shared? This shouldn't happen + if (totalShares == 0) + throw new DataException("Unexpected lack of block reward candidates?"); + + // Re-scale individual reward candidate's share as if total shared was 100% - legacy QORA holders' share + long scalingFactor; + if (haveQoraHolders) + scalingFactor = Amounts.scaledDivide(totalShares, 1_00000000 - qoraHoldersShare); + else + scalingFactor = totalShares; + + for (BlockRewardCandidate rewardCandidate : rewardCandidates) + rewardCandidate.share = Amounts.scaledDivide(rewardCandidate.share, scalingFactor); + } + + // Add legacy QORA holders as reward candidate with fixed share (if appropriate) + if (haveQoraHolders) { // Yes: add to reward candidates list BlockRewardDistributor legacyQoraHoldersDistributor = (distributionAmount, balanceChanges) -> distributeBlockRewardToQoraHolders(distributionAmount, qoraHolders, balanceChanges, this); - BlockRewardCandidate rewardCandidate = new BlockRewardCandidate("Legacy QORA holders", BlockChain.getInstance().getQoraHoldersShare(), legacyQoraHoldersDistributor); - rewardCandidates.add(rewardCandidate); + BlockRewardCandidate rewardCandidate = new BlockRewardCandidate("Legacy QORA holders", qoraHoldersShare, legacyQoraHoldersDistributor); + + if (haveFounders) + // We have founders, so distribute legacy QORA holders just before founders so founders get any non-distributed + rewardCandidates.add(rewardCandidate); + else + // No founder, so distribute legacy QORA holders first, so all account-level-based rewards get share of any non-distributed + rewardCandidates.add(0, rewardCandidate); + + totalShares += rewardCandidate.share; } - // Determine whether we reward founders - final List onlineFounderAccounts = expandedAccounts.stream().filter(expandedAccount -> expandedAccount.isMinterFounder).collect(Collectors.toList()); - if (!onlineFounderAccounts.isEmpty()) { + // Add founders as reward candidate if appropriate + if (haveFounders) { // Yes: add to reward candidates list BlockRewardDistributor founderDistributor = (distributionAmount, balanceChanges) -> distributeBlockRewardShare(distributionAmount, onlineFounderAccounts, balanceChanges); - BlockRewardCandidate rewardCandidate = new BlockRewardCandidate("Founders", BlockChain.getInstance().getFoundersShare(), founderDistributor); + final long foundersShare = 1_00000000 - totalShares; + BlockRewardCandidate rewardCandidate = new BlockRewardCandidate("Founders", foundersShare, founderDistributor); rewardCandidates.add(rewardCandidate); } - // Recalculate distribution ratios based on candidates - long totalShared = 0; - for (BlockRewardCandidate rewardCandidate : rewardCandidates) - totalShared += rewardCandidate.share; - - // No eligible candidates? - // Example scenario: the only online accounts are legacy QORA holders and they've already reached their max capped QORT-from-QORA. - if (totalShared == 0) - return; - - // Re-scale individual reward candidate's share as if total shared was 100% - for (BlockRewardCandidate rewardCandidate : rewardCandidates) - rewardCandidate.share = Amounts.scaledDivide(rewardCandidate.share, totalShared); - - // Now distribute to candidates - - // Collate all balance changes and then apply in one final step - Map balanceChanges = new HashMap<>(); - - long remainingAmount = totalAmount; - for (int r = 0; r < rewardCandidates.size(); ++r) { - BlockRewardCandidate rewardCandidate = rewardCandidates.get(r); - - long distributionAmount; - if (r < rewardCandidates.size() - 1) - // Distribute according to sharePercent - distributionAmount = Amounts.roundDownScaledMultiply(totalAmount, rewardCandidate.share); - else - /* - * Last reward candidate gets full remaining amount. Typically this will be online founders. - * - * The only time the amount would differ from above sharePercent calculation is - * if the *previous* rewardCandidate was "legacy QORA holders" and not all of their - * allotment was distributed. - */ - distributionAmount = remainingAmount; - - // Distribute to these reward candidate accounts, reducing remainingAmount by how much was actually distributed - long sharedAmount = rewardCandidate.distribute(distributionAmount, balanceChanges); - remainingAmount -= sharedAmount; - - final long remainingAmountForLogging = remainingAmount; - LOGGER.trace(() -> String.format("%s share: %s. Actually shared: %s. Remaining: %s", - rewardCandidate.description, - Amounts.prettyAmount(distributionAmount), - Amounts.prettyAmount(sharedAmount), - Amounts.prettyAmount(remainingAmountForLogging))); - } - - // Apply balance changes - for (Map.Entry balanceChange : balanceChanges.entrySet()) - balanceChange.getKey().modifyAssetBalance(Asset.QORT, balanceChange.getValue()); + return rewardCandidates; } private static long distributeBlockRewardShare(long distributionAmount, List accounts, Map balanceChanges) { @@ -1768,14 +1823,14 @@ public class Block { long finalTotalQoraHeld = totalQoraHeld; LOGGER.trace(() -> String.format("Total legacy QORA held: %s", Amounts.prettyAmount(finalTotalQoraHeld))); - long sharedAmount = 0; if (totalQoraHeld <= 0) - return sharedAmount; + return 0; // Could do with a faster 128bit integer library, but until then... BigInteger qoraHoldersAmountBI = BigInteger.valueOf(qoraHoldersAmount); BigInteger totalQoraHeldBI = BigInteger.valueOf(totalQoraHeld); + long sharedAmount = 0; for (int h = 0; h < qoraHolders.size(); ++h) { AccountBalanceData qoraHolder = qoraHolders.get(h); BigInteger qoraHolderBalanceBI = BigInteger.valueOf(qoraHolder.getBalance()); diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 6e64be0d..7f21f014 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -109,9 +109,6 @@ public class BlockChain { @XmlJavaTypeAdapter(value = org.qortal.api.AmountTypeAdapter.class) private Long qoraPerQortReward; - /** Share of block reward/fees to founders. CALCULATED */ - private long foundersShare; - /** * Number of minted blocks required to reach next level from previous. *

@@ -345,10 +342,6 @@ public class BlockChain { return this.qoraPerQortReward; } - public long getFoundersShare() { - return this.foundersShare; - } - public int getMinAccountLevelToMint() { return this.minAccountLevelToMint; } @@ -446,6 +439,15 @@ public class BlockChain { for (FeatureTrigger featureTrigger : FeatureTrigger.values()) 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; + // Add share percents for account-level-based rewards + for (AccountLevelShareBin accountLevelShareBin : this.sharesByLevel) + totalShare += accountLevelShareBin.share; + + if (totalShare < 0 || totalShare > 1_00000000L) + Settings.throwValidationError("Total non-founder share out of bounds (0 1_00000000L) - Settings.throwValidationError("Total non-founder share out of bounds (0> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA); + + long blockReward = BlockUtils.getNextBlockReward(repository); + + BlockMinter.mintTestingBlock(repository, dilbertSelfShareAccount); + + /* + * Dilbert is only account 'online'. + * No founders online. + * Some legacy QORA holders. + * + * So Dilbert should receive 100% - legacy QORA holder's share. + */ + + final long qoraHoldersShare = BlockChain.getInstance().getQoraHoldersShare(); + final long remainingShare = 1_00000000 - qoraHoldersShare; + + long dilbertExpectedBalance = initialBalances.get("dilbert").get(Asset.QORT); + dilbertExpectedBalance += Amounts.roundDownScaledMultiply(blockReward, remainingShare); + + AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertExpectedBalance); + + // After several blocks, the legacy QORA holder should be maxxed out + for (int i = 0; i < 10; ++i) + BlockUtils.mintBlock(repository); + + // Now Dilbert should be receiving 100% of block reward + blockReward = BlockUtils.getNextBlockReward(repository); + + BlockMinter.mintTestingBlock(repository, dilbertSelfShareAccount); + + AccountUtils.assertBalance(repository, "dilbert", Asset.QORT, dilbertExpectedBalance + blockReward); + } + } + + /** Check leftover legacy QORA reward goes to online founders. */ + @Test + public void testLeftoverReward() throws DataException { + Common.useSettings("test-settings-v2-leftover-reward.json"); + + try (final Repository repository = RepositoryManager.getRepository()) { + Map> initialBalances = AccountUtils.getBalances(repository, Asset.QORT, Asset.LEGACY_QORA, Asset.QORT_FROM_QORA); + + long blockReward = BlockUtils.getNextBlockReward(repository); + + BlockUtils.mintBlock(repository); // Block minted by Alice self-share + + // Chloe maxxes out her legacy QORA reward so some is leftover to reward to Alice. + + TestAccount chloe = Common.getTestAccount(repository, "chloe"); + final long chloeQortFromQora = chloe.getConfirmedBalance(Asset.QORT_FROM_QORA); + + long expectedBalance = initialBalances.get("alice").get(Asset.QORT) + blockReward - chloeQortFromQora; + AccountUtils.assertBalance(repository, "alice", Asset.QORT, expectedBalance); + } + } + } \ No newline at end of file diff --git a/src/test/resources/test-chain-v2-leftover-reward.json b/src/test/resources/test-chain-v2-leftover-reward.json new file mode 100644 index 00000000..d402aa95 --- /dev/null +++ b/src/test/resources/test-chain-v2-leftover-reward.json @@ -0,0 +1,70 @@ +{ + "isTestChain": true, + "blockTimestampMargin": 500, + "transactionExpiryPeriod": 86400000, + "maxBlockSize": 2097152, + "maxBytesPerUnitFee": 1024, + "unitFee": "0.1", + "requireGroupForApproval": false, + "minAccountLevelToRewardShare": 5, + "maxRewardSharesPerMintingAccount": 20, + "founderEffectiveMintingLevel": 10, + "onlineAccountSignaturesMinLifetime": 3600000, + "onlineAccountSignaturesMaxLifetime": 86400000, + "rewardsByHeight": [ + { "height": 1, "reward": 100 }, + { "height": 11, "reward": 10 }, + { "height": 21, "reward": 1 } + ], + "sharesByLevel": [ + { "levels": [ 1, 2 ], "share": 0.05 }, + { "levels": [ 3, 4 ], "share": 0.10 }, + { "levels": [ 5, 6 ], "share": 0.15 }, + { "levels": [ 7, 8 ], "share": 0.20 }, + { "levels": [ 9, 10 ], "share": 0.25 } + ], + "qoraHoldersShare": 0.20, + "qoraPerQortReward": 250, + "blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ], + "blockTimingsByHeight": [ + { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } + ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, + "featureTriggers": { + "messageHeight": 0, + "atHeight": 0, + "assetsTimestamp": 0, + "votingTimestamp": 0, + "arbitraryTimestamp": 0, + "powfixTimestamp": 0, + "qortalTimestamp": 0, + "newAssetPricingTimestamp": 0, + "groupApprovalTimestamp": 0 + }, + "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": "4000", "assetId": 1 }, + + { "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-reward-scaling.json b/src/test/resources/test-chain-v2-reward-scaling.json new file mode 100644 index 00000000..e454d8e7 --- /dev/null +++ b/src/test/resources/test-chain-v2-reward-scaling.json @@ -0,0 +1,70 @@ +{ + "isTestChain": true, + "blockTimestampMargin": 500, + "transactionExpiryPeriod": 86400000, + "maxBlockSize": 2097152, + "maxBytesPerUnitFee": 1024, + "unitFee": "0.1", + "requireGroupForApproval": false, + "minAccountLevelToRewardShare": 5, + "maxRewardSharesPerMintingAccount": 20, + "founderEffectiveMintingLevel": 10, + "onlineAccountSignaturesMinLifetime": 3600000, + "onlineAccountSignaturesMaxLifetime": 86400000, + "rewardsByHeight": [ + { "height": 1, "reward": 100 }, + { "height": 11, "reward": 10 }, + { "height": 21, "reward": 1 } + ], + "sharesByLevel": [ + { "levels": [ 1, 2 ], "share": 0.05 }, + { "levels": [ 3, 4 ], "share": 0.10 }, + { "levels": [ 5, 6 ], "share": 0.15 }, + { "levels": [ 7, 8 ], "share": 0.20 }, + { "levels": [ 9, 10 ], "share": 0.25 } + ], + "qoraHoldersShare": 0.20, + "qoraPerQortReward": 250, + "blocksNeededByLevel": [ 10, 20, 30, 40, 50, 60, 70, 80, 90, 100 ], + "blockTimingsByHeight": [ + { "height": 1, "target": 60000, "deviation": 30000, "power": 0.2 } + ], + "ciyamAtSettings": { + "feePerStep": "0.0001", + "maxStepsPerRound": 500, + "stepsPerFunctionCall": 10, + "minutesPerBlock": 1 + }, + "featureTriggers": { + "messageHeight": 0, + "atHeight": 0, + "assetsTimestamp": 0, + "votingTimestamp": 0, + "arbitraryTimestamp": 0, + "powfixTimestamp": 0, + "qortalTimestamp": 0, + "newAssetPricingTimestamp": 0, + "groupApprovalTimestamp": 0 + }, + "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": "40000", "assetId": 1 }, + + { "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-settings-v2-leftover-reward.json b/src/test/resources/test-settings-v2-leftover-reward.json new file mode 100644 index 00000000..bdbc1d52 --- /dev/null +++ b/src/test/resources/test-settings-v2-leftover-reward.json @@ -0,0 +1,7 @@ +{ + "restrictedApi": false, + "blockchainConfig": "src/test/resources/test-chain-v2-leftover-reward.json", + "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, + "minPeers": 0 +} diff --git a/src/test/resources/test-settings-v2-reward-scaling.json b/src/test/resources/test-settings-v2-reward-scaling.json new file mode 100644 index 00000000..262938b7 --- /dev/null +++ b/src/test/resources/test-settings-v2-reward-scaling.json @@ -0,0 +1,7 @@ +{ + "restrictedApi": false, + "blockchainConfig": "src/test/resources/test-chain-v2-reward-scaling.json", + "wipeUnconfirmedOnStart": false, + "testNtpOffset": 0, + "minPeers": 0 +}