diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 3be89512..9a9c6ba0 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -9,9 +9,10 @@ import java.math.RoundingMode; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.function.Function; +import java.util.Set; import java.util.stream.Collectors; import org.apache.logging.log4j.LogManager; @@ -1267,42 +1268,31 @@ public class Block { } protected void increaseAccountLevels() throws DataException { - // We need to do this for both minters and recipients - this.increaseAccountLevels(false, expandedAccount -> expandedAccount.mintingAccountData); - this.increaseAccountLevels(true, expandedAccount -> expandedAccount.recipientAccountData); - } - - private void increaseAccountLevels(boolean isProcessingRecipients, Function getAccountData) throws DataException { + // We are only interested in accounts that are NOT already lowest level final List cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel(); + final int maximumLevel = cumulativeBlocksByLevel.size() - 1; + final List expandedAccounts = this.getExpandedAccounts(); - // Increase blocks-minted count for all accounts - for (int a = 0; a < expandedAccounts.size(); ++a) { - ExpandedAccount expandedAccount = expandedAccounts.get(a); + Set allUniqueExpandedAccounts = new HashSet<>(); + for (ExpandedAccount expandedAccount : expandedAccounts) { + allUniqueExpandedAccounts.add(expandedAccount.mintingAccountData); - // Don't increase twice if recipient is also minter. - if (isProcessingRecipients && expandedAccount.isRecipientAlsoMinter) - continue; - - AccountData accountData = getAccountData.apply(expandedAccount); - - accountData.setBlocksMinted(accountData.getBlocksMinted() + 1); - // repository.getAccountRepository().setMintedBlockCount(accountData); int rowCount = 1; // Until HSQLDB rev 6100 is fixed - int rowCount = repository.getAccountRepository().modifyMintedBlockCount(accountData.getAddress(), +1); - LOGGER.trace(() -> String.format("Block minter %s up to %d minted block%s (rowCount: %d)", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""), rowCount)); + if (!expandedAccount.isRecipientAlsoMinter) + allUniqueExpandedAccounts.add(expandedAccount.recipientAccountData); } - // We are only interested in accounts that are NOT already highest level - final int maximumLevel = cumulativeBlocksByLevel.size() - 1; - List candidateAccounts = expandedAccounts.stream().filter(expandedAccount -> getAccountData.apply(expandedAccount).getLevel() < maximumLevel).collect(Collectors.toList()); + // Decrease blocks minted count for all accounts + for (AccountData accountData : allUniqueExpandedAccounts) { + // Adjust count locally (in Java) + accountData.setBlocksMinted(accountData.getBlocksMinted() + 1); - for (int c = 0; c < candidateAccounts.size(); ++c) { - ExpandedAccount expandedAccount = candidateAccounts.get(c); - final AccountData accountData = getAccountData.apply(expandedAccount); + int rowCount = repository.getAccountRepository().modifyMintedBlockCount(accountData.getAddress(), +1); + LOGGER.trace(() -> String.format("Block minter %s up to %d minted block%s (rowCount: %d)", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""), rowCount)); final int effectiveBlocksMinted = accountData.getBlocksMinted() + accountData.getBlocksMintedAdjustment(); - for (int newLevel = maximumLevel; newLevel > 0; --newLevel) + for (int newLevel = maximumLevel; newLevel >= 0; --newLevel) if (effectiveBlocksMinted >= cumulativeBlocksByLevel.get(newLevel)) { if (newLevel > accountData.getLevel()) { // Account has increased in level! @@ -1592,38 +1582,27 @@ public class Block { } protected void decreaseAccountLevels() throws DataException { - // We need to do this for both minters and recipients - this.decreaseAccountLevels(false, expandedAccount -> expandedAccount.mintingAccountData); - this.decreaseAccountLevels(true, expandedAccount -> expandedAccount.recipientAccountData); - } - - private void decreaseAccountLevels(boolean isProcessingRecipients, Function getAccountData) throws DataException { + // We are only interested in accounts that are NOT already lowest level final List cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel(); + final int maximumLevel = cumulativeBlocksByLevel.size() - 1; + final List expandedAccounts = this.getExpandedAccounts(); - // Decrease blocks minted count for all accounts - for (int a = 0; a < expandedAccounts.size(); ++a) { - ExpandedAccount expandedAccount = expandedAccounts.get(a); + Set allUniqueExpandedAccounts = new HashSet<>(); + for (ExpandedAccount expandedAccount : expandedAccounts) { + allUniqueExpandedAccounts.add(expandedAccount.mintingAccountData); - // Don't decrease twice if recipient is also minter. - if (isProcessingRecipients && expandedAccount.isRecipientAlsoMinter) - continue; - - AccountData accountData = getAccountData.apply(expandedAccount); - - accountData.setBlocksMinted(accountData.getBlocksMinted() - 1); - // repository.getAccountRepository().setMintedBlockCount(accountData); int rowCount = 1; // Until HSQLDB rev 6100 is fixed - int rowCount = repository.getAccountRepository().modifyMintedBlockCount(accountData.getAddress(), -1); - LOGGER.trace(() -> String.format("Block minter %s down to %d minted block%s (rowCount: %d)", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""), rowCount)); + if (!expandedAccount.isRecipientAlsoMinter) + allUniqueExpandedAccounts.add(expandedAccount.recipientAccountData); } - // We are only interested in accounts that are NOT already lowest level - final int maximumLevel = cumulativeBlocksByLevel.size() - 1; - List candidateAccounts = expandedAccounts.stream().filter(expandedAccount -> getAccountData.apply(expandedAccount).getLevel() > 0).collect(Collectors.toList()); + // Decrease blocks minted count for all accounts + for (AccountData accountData : allUniqueExpandedAccounts) { + // Adjust count locally (in Java) + accountData.setBlocksMinted(accountData.getBlocksMinted() - 1); - for (int c = 0; c < candidateAccounts.size(); ++c) { - ExpandedAccount expandedAccount = candidateAccounts.get(c); - final AccountData accountData = getAccountData.apply(expandedAccount); + int rowCount = repository.getAccountRepository().modifyMintedBlockCount(accountData.getAddress(), -1); + LOGGER.trace(() -> String.format("Block minter %s down to %d minted block%s (rowCount: %d)", accountData.getAddress(), accountData.getBlocksMinted(), (accountData.getBlocksMinted() != 1 ? "s" : ""), rowCount)); final int effectiveBlocksMinted = accountData.getBlocksMinted() + accountData.getBlocksMintedAdjustment(); diff --git a/src/test/java/org/qortal/test/minting/BlocksMintedCountTests.java b/src/test/java/org/qortal/test/minting/BlocksMintedCountTests.java index 3b5de70c..d1062a79 100644 --- a/src/test/java/org/qortal/test/minting/BlocksMintedCountTests.java +++ b/src/test/java/org/qortal/test/minting/BlocksMintedCountTests.java @@ -8,6 +8,7 @@ import org.junit.Before; import org.junit.Test; import org.qortal.account.PrivateKeyAccount; import org.qortal.block.BlockMinter; +import org.qortal.controller.Controller; import org.qortal.data.account.RewardShareData; import org.qortal.repository.DataException; import org.qortal.repository.Repository; @@ -59,13 +60,45 @@ public class BlocksMintedCountTests extends Common { } } - private void testRewardShare(Repository repository, PrivateKeyAccount mintingAccount, int aliceDelta, int bobDelta) throws DataException { + @Test + public void testMixedShares() throws DataException { + final int sharePercent = 12_80; + + try (final Repository repository = RepositoryManager.getRepository()) { + // Fetch usual minting account + PrivateKeyAccount mintingAccount = Common.getTestAccount(repository, "alice-reward-share"); + + // Create reward-share + byte[] testRewardSharePrivateKey = AccountUtils.rewardShare(repository, "alice", "bob", sharePercent); + PrivateKeyAccount testRewardShareAccount = new PrivateKeyAccount(repository, testRewardSharePrivateKey); + + // Confirm reward-share info set correctly + RewardShareData testRewardShareData = repository.getAccountRepository().getRewardShare(testRewardShareAccount.getPublicKey()); + assertNotNull(testRewardShareData); + + // Create signed timestamps + Controller.getInstance().ensureTestingAccountsOnline(mintingAccount, testRewardShareAccount); + + // Even though Alice features in two online reward-shares, she should only gain +1 blocksMinted + // Bob only features in one online reward-share, so should also only gain +1 blocksMinted + testRewardShareRetainingTimestamps(repository, testRewardShareAccount, +1, +1); + } + } + + private void testRewardShare(Repository repository, PrivateKeyAccount testRewardShareAccount, int aliceDelta, int bobDelta) throws DataException { + // Create signed timestamps + Controller.getInstance().ensureTestingAccountsOnline(testRewardShareAccount); + + testRewardShareRetainingTimestamps(repository, testRewardShareAccount, aliceDelta, bobDelta); + } + + private void testRewardShareRetainingTimestamps(Repository repository, PrivateKeyAccount mintingAccount, int aliceDelta, int bobDelta) throws DataException { // Fetch pre-mint blocks minted counts int alicePreMintCount = getBlocksMinted(repository, "alice"); int bobPreMintCount = getBlocksMinted(repository, "bob"); // Mint another block - BlockMinter.mintTestingBlock(repository, mintingAccount); + BlockMinter.mintTestingBlockRetainingTimestamps(repository, mintingAccount); // Fetch post-mint blocks minted counts int alicePostMintCount = getBlocksMinted(repository, "alice");