Fix for incorrect blocksMinted count. Added test to cover

This commit is contained in:
catbref 2020-05-08 08:51:56 +01:00
parent 6d8f41ab05
commit 3fa7da5115
2 changed files with 66 additions and 54 deletions

View File

@ -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<ExpandedAccount, AccountData> getAccountData) throws DataException {
// We are only interested in accounts that are NOT already lowest level
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
final List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
// Increase blocks-minted count for all accounts
for (int a = 0; a < expandedAccounts.size(); ++a) {
ExpandedAccount expandedAccount = expandedAccounts.get(a);
Set<AccountData> 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<ExpandedAccount> 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<ExpandedAccount, AccountData> getAccountData) throws DataException {
// We are only interested in accounts that are NOT already lowest level
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
final List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
// Decrease blocks minted count for all accounts
for (int a = 0; a < expandedAccounts.size(); ++a) {
ExpandedAccount expandedAccount = expandedAccounts.get(a);
Set<AccountData> 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<ExpandedAccount> 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();

View File

@ -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");