diff --git a/src/main/java/org/qora/account/Account.java b/src/main/java/org/qora/account/Account.java index 4664fc1e..91acf47f 100644 --- a/src/main/java/org/qora/account/Account.java +++ b/src/main/java/org/qora/account/Account.java @@ -21,11 +21,6 @@ public class Account { private static final Logger LOGGER = LogManager.getLogger(Account.class); - public static final int TIER1_FORGING_MASK = 0x1; - public static final int TIER2_FORGING_MASK = 0x2; - public static final int TIER3_FORGING_MASK = 0x4; - public static final int FORGING_MASK = TIER1_FORGING_MASK | TIER2_FORGING_MASK | TIER3_FORGING_MASK; - public static final int ADDRESS_LENGTH = 25; protected Repository repository; diff --git a/src/main/java/org/qora/account/Forging.java b/src/main/java/org/qora/account/Forging.java new file mode 100644 index 00000000..5f9102b7 --- /dev/null +++ b/src/main/java/org/qora/account/Forging.java @@ -0,0 +1,19 @@ +package org.qora.account; + +import org.qora.block.BlockChain; +import org.qora.repository.DataException; + +/** Relating to whether accounts can forge. */ +public class Forging { + + /** Returns mask for account flags for forging bits. */ + public static int getForgingMask() { + return (1 << BlockChain.getInstance().getForgingTiers().size()) - 1; + } + + public static boolean canForge(Account account) throws DataException { + Integer flags = account.getFlags(); + return flags != null && (flags & getForgingMask()) != 0; + } + +} diff --git a/src/main/java/org/qora/api/model/BlockForgeSummary.java b/src/main/java/org/qora/api/model/BlockForgeSummary.java new file mode 100644 index 00000000..3b2d3d55 --- /dev/null +++ b/src/main/java/org/qora/api/model/BlockForgeSummary.java @@ -0,0 +1,20 @@ +package org.qora.api.model; + +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; + +@XmlAccessorType(XmlAccessType.FIELD) +public class BlockForgeSummary { + + public String address; + public int blockCount; + + protected BlockForgeSummary() { + } + + public BlockForgeSummary(String address, int blockCount) { + this.address = address; + this.blockCount = blockCount; + } + +} diff --git a/src/main/java/org/qora/api/resource/BlocksResource.java b/src/main/java/org/qora/api/resource/BlocksResource.java index 62b8e445..797cc690 100644 --- a/src/main/java/org/qora/api/resource/BlocksResource.java +++ b/src/main/java/org/qora/api/resource/BlocksResource.java @@ -28,6 +28,7 @@ import org.qora.api.ApiError; import org.qora.api.ApiErrors; import org.qora.api.ApiException; import org.qora.api.ApiExceptionFactory; +import org.qora.api.model.BlockForgeSummary; import org.qora.block.Block; import org.qora.crypto.Crypto; import org.qora.data.account.AccountData; @@ -570,6 +571,36 @@ public class BlocksResource { } } + @GET + @Path("/forgers") + @Operation( + summary = "Show summary of block forgers", + responses = { + @ApiResponse( + content = @Content( + array = @ArraySchema( + schema = @Schema( + implementation = BlockForgeSummary.class + ) + ) + ) + ) + } + ) + public List getBlockForgers(@Parameter( + ref = "limit" + ) @QueryParam("limit") Integer limit, @Parameter( + ref = "offset" + ) @QueryParam("offset") Integer offset, @Parameter( + ref = "reverse" + ) @QueryParam("reverse") Boolean reverse) { + try (final Repository repository = RepositoryManager.getRepository()) { + return repository.getBlockRepository().getBlockForgers(limit, offset, reverse); + } catch (DataException e) { + throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); + } + } + @POST @Path("/enableforging") @Operation( diff --git a/src/main/java/org/qora/block/Block.java b/src/main/java/org/qora/block/Block.java index b54707f4..e52020ef 100644 --- a/src/main/java/org/qora/block/Block.java +++ b/src/main/java/org/qora/block/Block.java @@ -19,7 +19,7 @@ import org.qora.account.PrivateKeyAccount; import org.qora.account.PublicKeyAccount; import org.qora.asset.Asset; import org.qora.at.AT; -import org.qora.block.BlockChain.RewardsByHeight; +import org.qora.block.BlockChain.RewardByHeight; import org.qora.crypto.Crypto; import org.qora.data.account.ProxyForgerData; import org.qora.data.at.ATData; @@ -1191,7 +1191,7 @@ public class Block { } protected BigDecimal getRewardAtHeight(int ourHeight) { - List rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight(); + List rewardsByHeight = BlockChain.getInstance().getBlockRewardsByHeight(); // No rewards configured? if (rewardsByHeight == null) diff --git a/src/main/java/org/qora/block/BlockChain.java b/src/main/java/org/qora/block/BlockChain.java index 258b644b..63321108 100644 --- a/src/main/java/org/qora/block/BlockChain.java +++ b/src/main/java/org/qora/block/BlockChain.java @@ -90,11 +90,20 @@ public class BlockChain { private boolean oneNamePerAccount = false; /** Block rewards by block height */ - public static class RewardsByHeight { + public static class RewardByHeight { public int height; public BigDecimal reward; } - List rewardsByHeight; + List rewardsByHeight; + + /** Forging right tiers */ + public static class ForgingTier { + /** Minimum number of blocks forged before account can enable minting on other accounts. */ + public int minBlocks; + /** Maximum number of other accounts that can be enabled. */ + public int maxSubAccounts; + } + List forgingTiers; // Constructors, etc. @@ -230,10 +239,14 @@ public class BlockChain { return this.oneNamePerAccount; } - public List getBlockRewardsByHeight() { + public List getBlockRewardsByHeight() { return this.rewardsByHeight; } + public List getForgingTiers() { + return this.forgingTiers; + } + // Convenience methods for specific blockchain feature triggers public long getMessageReleaseHeight() { diff --git a/src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java b/src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java index 2e76d0da..4c4f8dd8 100644 --- a/src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java +++ b/src/main/java/org/qora/data/transaction/EnableForgingTransactionData.java @@ -30,6 +30,10 @@ public class EnableForgingTransactionData extends TransactionData { this.target = target; } + public EnableForgingTransactionData(long timestamp, int groupId, byte[] reference, byte[] creatorPublicKey, String target, BigDecimal fee) { + this(timestamp, groupId, reference, creatorPublicKey, target, fee, null); + } + // Getters / setters public String getTarget() { diff --git a/src/main/java/org/qora/repository/BlockRepository.java b/src/main/java/org/qora/repository/BlockRepository.java index 4d39dc38..2d622f40 100644 --- a/src/main/java/org/qora/repository/BlockRepository.java +++ b/src/main/java/org/qora/repository/BlockRepository.java @@ -2,6 +2,7 @@ package org.qora.repository; import java.util.List; +import org.qora.api.model.BlockForgeSummary; import org.qora.data.block.BlockData; import org.qora.data.block.BlockTransactionData; import org.qora.data.transaction.TransactionData; @@ -101,6 +102,11 @@ public interface BlockRepository { */ public int countForgedBlocks(byte[] publicKey) throws DataException; + /** + * Returns summaries of block forgers. + */ + public List getBlockForgers(Integer limit, Integer offset, Boolean reverse) throws DataException; + /** * Returns blocks with passed generator public key. */ diff --git a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java index 4f42ec4d..ee9e4579 100644 --- a/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java +++ b/src/main/java/org/qora/repository/hsqldb/HSQLDBBlockRepository.java @@ -6,6 +6,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.List; +import org.qora.api.model.BlockForgeSummary; +import org.qora.crypto.Crypto; import org.qora.data.block.BlockData; import org.qora.data.block.BlockTransactionData; import org.qora.data.transaction.TransactionData; @@ -158,6 +160,33 @@ public class HSQLDBBlockRepository implements BlockRepository { } } + @Override + public List getBlockForgers(Integer limit, Integer offset, Boolean reverse) throws DataException { + String sql = "SELECT generator, COUNT(signature) FROM Blocks GROUP BY generator ORDER BY COUNT(signature) "; + if (reverse != null && reverse) + sql += " DESC"; + + sql += HSQLDBRepository.limitOffsetSql(limit, offset); + + List summaries = new ArrayList<>(); + + try (ResultSet resultSet = this.repository.checkedExecute(sql)) { + if (resultSet == null) + return summaries; + + do { + byte[] generator = resultSet.getBytes(1); + int count = resultSet.getInt(2); + + summaries.add(new BlockForgeSummary(Crypto.toAddress(generator), count)); + } while (resultSet.next()); + + return summaries; + } catch (SQLException e) { + throw new DataException("Unable to fetch generator's blocks from repository", e); + } + } + @Override public List getBlocksWithGenerator(byte[] generatorPublicKey, Integer limit, Integer offset, Boolean reverse) throws DataException { String sql = "SELECT " + BLOCK_DB_COLUMNS + " FROM Blocks WHERE generator = ? ORDER BY height "; diff --git a/src/main/java/org/qora/transaction/EnableForgingTransaction.java b/src/main/java/org/qora/transaction/EnableForgingTransaction.java index 4a9b0804..46b34494 100644 --- a/src/main/java/org/qora/transaction/EnableForgingTransaction.java +++ b/src/main/java/org/qora/transaction/EnableForgingTransaction.java @@ -6,8 +6,11 @@ import java.util.Collections; import java.util.List; import org.qora.account.Account; +import org.qora.account.Forging; import org.qora.account.PublicKeyAccount; import org.qora.asset.Asset; +import org.qora.block.BlockChain; +import org.qora.block.BlockChain.ForgingTier; import org.qora.data.transaction.EnableForgingTransactionData; import org.qora.data.transaction.TransactionData; import org.qora.repository.DataException; @@ -15,12 +18,6 @@ import org.qora.repository.Repository; public class EnableForgingTransaction extends Transaction { - public static final int TIER1_MIN_FORGED_BLOCKS = 50; - public static final int TIER1_MAX_ENABLED_ACCOUNTS = 5; - - public static final int TIER2_MIN_FORGED_BLOCKS = 5; - public static final int TIER2_MAX_ENABLED_ACCOUNTS = 5; - // Properties private EnableForgingTransactionData enableForgingTransactionData; @@ -80,39 +77,45 @@ public class EnableForgingTransaction extends Transaction { if (creatorFlags == null) return ValidationResult.INVALID_ADDRESS; - if ((creatorFlags & Account.FORGING_MASK) == 0) + if ((creatorFlags & Forging.getForgingMask()) == 0) return ValidationResult.NO_FORGING_PERMISSION; - // Tier3 forgers can't enable further accounts - if ((creatorFlags & Account.TIER3_FORGING_MASK) != 0) + int forgingTierLevel = 0; + ForgingTier forgingTier = null; + + List forgingTiers = BlockChain.getInstance().getForgingTiers(); + for (forgingTierLevel = 0; forgingTierLevel < forgingTiers.size(); ++forgingTierLevel) + if ((creatorFlags & (1 << forgingTierLevel)) != 0) { + forgingTier = forgingTiers.get(forgingTierLevel); + break; + } + + // forgingTier should not be null at this point + if (forgingTier == null) + return ValidationResult.NO_FORGING_PERMISSION; + + // Final tier forgers can't enable further accounts + if (forgingTierLevel == forgingTiers.size() - 1) return ValidationResult.FORGING_ENABLE_LIMIT; Account target = getTarget(); // Target needs to NOT have ANY forging-enabled account flags set - Integer targetFlags = target.getFlags(); - if (targetFlags != null && (targetFlags & Account.FORGING_MASK) != 0) + if (Forging.canForge(target)) return ValidationResult.FORGING_ALREADY_ENABLED; // Has creator reached minimum requirements? - int numberForgedBlocks = this.repository.getBlockRepository().countForgedBlocks(creator.getPublicKey()); + + // Already gifted maximum number of forging rights? int numberEnabledAccounts = this.repository.getAccountRepository().countForgingAccountsEnabledByAddress(creator.getAddress()); + if (numberEnabledAccounts >= forgingTier.maxSubAccounts) + return ValidationResult.FORGING_ENABLE_LIMIT; - if ((creatorFlags & Account.TIER1_FORGING_MASK) != 0) { - // Tier1: minimum 2,500 forged blocks & max 50 accounts - if (numberForgedBlocks < TIER1_MIN_FORGED_BLOCKS) - return ValidationResult.FORGE_MORE_BLOCKS; + // Not enough forged blocks to gift forging rights? + int numberForgedBlocks = this.repository.getBlockRepository().countForgedBlocks(creator.getPublicKey()); + if (numberForgedBlocks < forgingTier.minBlocks) + return ValidationResult.FORGE_MORE_BLOCKS; - if (numberEnabledAccounts >= TIER1_MAX_ENABLED_ACCOUNTS) - return ValidationResult.FORGING_ENABLE_LIMIT; - } else if ((creatorFlags & Account.TIER2_FORGING_MASK) != 0) { - // Tier2: minimum 50 forged blocks & max 50 accounts - if (numberForgedBlocks < TIER2_MIN_FORGED_BLOCKS) - return ValidationResult.FORGE_MORE_BLOCKS; - - if (numberEnabledAccounts >= TIER2_MAX_ENABLED_ACCOUNTS) - return ValidationResult.FORGING_ENABLE_LIMIT; - } // Check fee is zero or positive if (enableForgingTransactionData.getFee().compareTo(BigDecimal.ZERO) < 0) @@ -135,19 +138,16 @@ public class EnableForgingTransaction extends Transaction { int creatorFlags = creator.getFlags(); - int forgeBit = 0; - - if ((creatorFlags & Account.TIER1_FORGING_MASK) != 0) - forgeBit = Account.TIER2_FORGING_MASK; - else - forgeBit = Account.TIER3_FORGING_MASK; + int forgeBit = creatorFlags & Forging.getForgingMask(); + // Target's forging bit is next level from creator's + int targetForgeBit = forgeBit << 1; Account target = getTarget(); Integer targetFlags = target.getFlags(); if (targetFlags == null) targetFlags = 0; - targetFlags |= forgeBit; + targetFlags |= targetForgeBit; target.setFlags(targetFlags); target.setForgingEnabler(creator.getAddress()); @@ -169,18 +169,15 @@ public class EnableForgingTransaction extends Transaction { int creatorFlags = creator.getFlags(); - int forgeBit = 0; - - if ((creatorFlags & Account.TIER1_FORGING_MASK) != 0) - forgeBit = Account.TIER2_FORGING_MASK; - else - forgeBit = Account.TIER3_FORGING_MASK; + int forgeBit = creatorFlags & Forging.getForgingMask(); + // Target's forging bit is next level from creator's + int targetForgeBit = forgeBit << 1; Account target = getTarget(); int targetFlags = target.getFlags(); - targetFlags &= ~forgeBit; + targetFlags &= ~targetForgeBit; target.setFlags(targetFlags); target.setForgingEnabler(null); diff --git a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java b/src/main/java/org/qora/transaction/ProxyForgingTransaction.java index 65ba2fd7..00d37fad 100644 --- a/src/main/java/org/qora/transaction/ProxyForgingTransaction.java +++ b/src/main/java/org/qora/transaction/ProxyForgingTransaction.java @@ -6,6 +6,7 @@ import java.util.Collections; import java.util.List; import org.qora.account.Account; +import org.qora.account.Forging; import org.qora.account.PublicKeyAccount; import org.qora.asset.Asset; import org.qora.crypto.Crypto; @@ -84,12 +85,8 @@ public class ProxyForgingTransaction extends Transaction { PublicKeyAccount creator = getCreator(); - // Creator needs to have at least one forging-enabled account flag set - Integer creatorFlags = creator.getFlags(); - if (creatorFlags == null) - return ValidationResult.INVALID_ADDRESS; - - if ((creatorFlags & Account.FORGING_MASK) == 0) + // Creator themselves needs to be allowed to forge + if (!Forging.canForge(creator)) return ValidationResult.NO_FORGING_PERMISSION; // Check proxy public key is correct length diff --git a/src/test/java/org/qora/test/common/AccountUtils.java b/src/test/java/org/qora/test/common/AccountUtils.java index 08f602cd..6f533379 100644 --- a/src/test/java/org/qora/test/common/AccountUtils.java +++ b/src/test/java/org/qora/test/common/AccountUtils.java @@ -5,6 +5,8 @@ import java.util.HashMap; import java.util.Map; import org.qora.account.PrivateKeyAccount; +import org.qora.crypto.Crypto; +import org.qora.data.transaction.EnableForgingTransactionData; import org.qora.data.transaction.PaymentTransactionData; import org.qora.data.transaction.ProxyForgingTransactionData; import org.qora.data.transaction.TransactionData; @@ -46,6 +48,30 @@ public class AccountUtils { return proxyPrivateKey; } + public static TransactionData createEnableForging(Repository repository, String forger, byte[] recipientPublicKey) throws DataException { + PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger); + + byte[] reference = forgingAccount.getLastReference(); + long timestamp = repository.getTransactionRepository().fromSignature(reference).getTimestamp() + 1000; + + return new EnableForgingTransactionData(timestamp, txGroupId, reference, forgingAccount.getPublicKey(), Crypto.toAddress(recipientPublicKey), fee); + } + + public static TransactionData createEnableForging(Repository repository, String forger, String recipient) throws DataException { + PrivateKeyAccount recipientAccount = Common.getTestAccount(repository, recipient); + + return createEnableForging(repository, forger, recipientAccount.getPublicKey()); + } + + public static TransactionData enableForging(Repository repository, String forger, String recipient) throws DataException { + TransactionData transactionData = createEnableForging(repository, forger, recipient); + + PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, forger); + TransactionUtils.signAndForge(repository, transactionData, forgingAccount); + + return transactionData; + } + public static Map> getBalances(Repository repository, long... assetIds) throws DataException { Map> balances = new HashMap<>(); diff --git a/src/test/java/org/qora/test/forging/GrantForgingTests.java b/src/test/java/org/qora/test/forging/GrantForgingTests.java new file mode 100644 index 00000000..03a58b22 --- /dev/null +++ b/src/test/java/org/qora/test/forging/GrantForgingTests.java @@ -0,0 +1,163 @@ +package org.qora.test.forging; + +import static org.junit.Assert.*; + +import java.util.Random; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.qora.account.PrivateKeyAccount; +import org.qora.block.BlockChain; +import org.qora.block.BlockGenerator; +import org.qora.data.transaction.TransactionData; +import org.qora.repository.DataException; +import org.qora.repository.Repository; +import org.qora.repository.RepositoryManager; +import org.qora.test.common.AccountUtils; +import org.qora.test.common.Common; +import org.qora.test.common.TransactionUtils; +import org.qora.transaction.Transaction; +import org.qora.transaction.Transaction.ValidationResult; + +public class GrantForgingTests extends Common { + + + @Before + public void beforeTest() throws DataException { + Common.useDefaultSettings(); + } + + @After + public void afterTest() throws DataException { + Common.orphanCheck(); + } + + @Test + public void testSimpleGrant() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, "alice"); + + TransactionData transactionData = AccountUtils.createEnableForging(repository, "alice", "bob"); + Transaction transaction = Transaction.fromData(repository, transactionData); + transaction.sign(forgingAccount); + + ValidationResult result = transaction.isValidUnconfirmed(); + // Alice can't grant without forging minimum number of blocks + assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result); + + // Forge a load of blocks + int blocksNeeded = BlockChain.getInstance().getForgingTiers().get(0).minBlocks; + for (int i = 0; i < blocksNeeded; ++i) + BlockGenerator.generateTestingBlock(repository, forgingAccount); + + // Alice should be able to grant now + result = transaction.isValidUnconfirmed(); + assertEquals(ValidationResult.OK, result); + + TransactionUtils.signAndForge(repository, transactionData, forgingAccount); + } + } + + @Test + public void testMaxGrant() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, "alice"); + + TransactionData transactionData = AccountUtils.createEnableForging(repository, "alice", "bob"); + Transaction transaction = Transaction.fromData(repository, transactionData); + transaction.sign(forgingAccount); + + ValidationResult result = transaction.isValidUnconfirmed(); + // Alice can't grant without forging minimum number of blocks + assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result); + + // Forge a load of blocks + int blocksNeeded = BlockChain.getInstance().getForgingTiers().get(0).minBlocks; + for (int i = 0; i < blocksNeeded; ++i) + BlockGenerator.generateTestingBlock(repository, forgingAccount); + + // Alice should be able to grant up to 5 now + + // Gift to random accounts + Random random = new Random(); + for (int i = 0; i < 5; ++i) { + byte[] publicKey = new byte[32]; + random.nextBytes(publicKey); + + transactionData = AccountUtils.createEnableForging(repository, "alice", publicKey); + transaction = Transaction.fromData(repository, transactionData); + transaction.sign(forgingAccount); + + result = transaction.isValidUnconfirmed(); + assertEquals("Couldn't enable account #" + i, ValidationResult.OK, result); + + TransactionUtils.signAndForge(repository, transactionData, forgingAccount); + } + + // Alice's allocation used up + byte[] publicKey = new byte[32]; + random.nextBytes(publicKey); + + transactionData = AccountUtils.createEnableForging(repository, "alice", publicKey); + transaction = Transaction.fromData(repository, transactionData); + transaction.sign(forgingAccount); + + result = transaction.isValidUnconfirmed(); + assertEquals(ValidationResult.FORGING_ENABLE_LIMIT, result); + } + } + + @Test + public void testFinalTier() throws DataException { + try (final Repository repository = RepositoryManager.getRepository()) { + PrivateKeyAccount aliceAccount = Common.getTestAccount(repository, "alice"); + + TransactionData transactionData = AccountUtils.createEnableForging(repository, "alice", "bob"); + Transaction transaction = Transaction.fromData(repository, transactionData); + transaction.sign(aliceAccount); + + ValidationResult result = transaction.isValidUnconfirmed(); + // Alice can't grant without forging minimum number of blocks + assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result); + + // Forge a load of blocks + int blocksNeeded = BlockChain.getInstance().getForgingTiers().get(0).minBlocks; + for (int i = 0; i < blocksNeeded; ++i) + BlockGenerator.generateTestingBlock(repository, aliceAccount); + + // Alice should be able to grant now + AccountUtils.enableForging(repository, "alice", "bob"); + + // Bob can't grant without forging minimum number of blocks + transactionData = AccountUtils.createEnableForging(repository, "bob", "chloe"); + transaction = Transaction.fromData(repository, transactionData); + transaction.sign(aliceAccount); + + result = transaction.isValidUnconfirmed(); + assertEquals(ValidationResult.FORGE_MORE_BLOCKS, result); + + // Bob needs to forge a load of blocks + PrivateKeyAccount bobAccount = Common.getTestAccount(repository, "bob"); + blocksNeeded = BlockChain.getInstance().getForgingTiers().get(1).minBlocks; + for (int i = 0; i < blocksNeeded; ++i) + BlockGenerator.generateTestingBlock(repository, bobAccount); + + // Bob should be able to grant now + AccountUtils.enableForging(repository, "bob", "chloe"); + + // Chloe is final tier so shouldn't be able to grant + Random random = new Random(); + byte[] publicKey = new byte[32]; + random.nextBytes(publicKey); + + transactionData = AccountUtils.createEnableForging(repository, "chloe", publicKey); + transaction = Transaction.fromData(repository, transactionData); + transaction.sign(aliceAccount); + + result = transaction.isValidUnconfirmed(); + assertEquals(ValidationResult.FORGING_ENABLE_LIMIT, result); + } + } + +} diff --git a/src/test/java/org/qora/test/forging/RewardTests.java b/src/test/java/org/qora/test/forging/RewardTests.java index aabe710c..0142e492 100644 --- a/src/test/java/org/qora/test/forging/RewardTests.java +++ b/src/test/java/org/qora/test/forging/RewardTests.java @@ -11,7 +11,7 @@ import org.junit.Test; import org.qora.account.PrivateKeyAccount; import org.qora.asset.Asset; import org.qora.block.BlockChain; -import org.qora.block.BlockChain.RewardsByHeight; +import org.qora.block.BlockChain.RewardByHeight; import org.qora.block.BlockGenerator; import org.qora.repository.DataException; import org.qora.repository.Repository; @@ -54,11 +54,11 @@ public class RewardTests extends Common { PrivateKeyAccount forgingAccount = Common.getTestAccount(repository, "alice"); - List rewards = BlockChain.getInstance().getBlockRewardsByHeight(); + List rewards = BlockChain.getInstance().getBlockRewardsByHeight(); int rewardIndex = rewards.size() - 1; - RewardsByHeight rewardInfo = rewards.get(rewardIndex); + RewardByHeight rewardInfo = rewards.get(rewardIndex); BigDecimal expectedBalance = initialBalances.get("alice").get(Asset.QORA); for (int height = rewardInfo.height; height > 1; --height) { diff --git a/src/test/resources/test-chain-v2.json b/src/test/resources/test-chain-v2.json index 838b757c..18597782 100644 --- a/src/test/resources/test-chain-v2.json +++ b/src/test/resources/test-chain-v2.json @@ -30,6 +30,11 @@ { "height": 11, "reward": 10 }, { "height": 21, "reward": 1 } ], + "forgingTiers": [ + { "minBlocks": 5, "maxSubAccounts": 5 }, + { "minBlocks": 4, "maxSubAccounts": 3 }, + { "minBlocks": 0, "maxSubAccounts": 0 } + ], "featureTriggers": { "messageHeight": 0, "atHeight": 0,