Browse Source

Improve code dealing with increase account level due to generated blocks & add orphan equivalent.

pull/67/head
catbref 5 years ago
parent
commit
a3751823eb
  1. 86
      src/main/java/org/qora/block/Block.java
  2. 46
      src/main/java/org/qora/block/BlockChain.java

86
src/main/java/org/qora/block/Block.java

@ -1204,32 +1204,14 @@ public class Block {
}
protected void increaseAccountLevels() throws DataException {
List<Integer> blocksNeededByLevel = BlockChain.getInstance().getBlocksNeededByLevel();
// Pre-calculate cumulative blocks required for each level
int cumulativeBlocks = 0;
int[] cumulativeBlocksByLevel = new int[blocksNeededByLevel.size() + 1];
for (int level = 0; level < cumulativeBlocksByLevel.length; ++level) {
cumulativeBlocksByLevel[level] = cumulativeBlocks;
if (level < blocksNeededByLevel.size())
cumulativeBlocks += blocksNeededByLevel.get(level);
}
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
// We need to do this for both forgers and recipients
this.increaseAccountLevels(expandedAccounts, cumulativeBlocksByLevel,
expandedAccount -> expandedAccount.isForgerFounder,
expandedAccount -> expandedAccount.forgerAccountData);
this.increaseAccountLevels(expandedAccounts, cumulativeBlocksByLevel,
expandedAccount -> expandedAccount.isRecipientFounder,
expandedAccount -> expandedAccount.recipientAccountData);
this.increaseAccountLevels(expandedAccount -> expandedAccount.isForgerFounder, expandedAccount -> expandedAccount.forgerAccountData);
this.increaseAccountLevels(expandedAccount -> expandedAccount.isRecipientFounder, expandedAccount -> expandedAccount.recipientAccountData);
}
private void increaseAccountLevels(List<ExpandedAccount> expandedAccounts, int[] cumulativeBlocksByLevel,
Predicate<ExpandedAccount> isFounder, Function<ExpandedAccount, AccountData> getAccountData) throws DataException {
private void increaseAccountLevels(Predicate<ExpandedAccount> isFounder, Function<ExpandedAccount, AccountData> getAccountData) throws DataException {
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
final List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
final boolean isProcessingRecipients = getAccountData.apply(expandedAccounts.get(0)) == expandedAccounts.get(0).recipientAccountData;
// Increase blocks generated count for all accounts
@ -1244,21 +1226,21 @@ public class Block {
accountData.setBlocksGenerated(accountData.getBlocksGenerated() + 1);
repository.getAccountRepository().setBlocksGenerated(accountData);
LOGGER.trace(() -> String.format("Block generator %s has generated %d block%s", accountData.getAddress(), accountData.getBlocksGenerated(), (accountData.getBlocksGenerated() != 1 ? "s" : "")));
LOGGER.trace(() -> String.format("Block generator %s up to %d generated block%s", accountData.getAddress(), accountData.getBlocksGenerated(), (accountData.getBlocksGenerated() != 1 ? "s" : "")));
}
// We are only interested in accounts that are NOT founders and NOT already highest level
final int maximumLevel = cumulativeBlocksByLevel.length - 1;
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
List<ExpandedAccount> candidateAccounts = expandedAccounts.stream().filter(expandedAccount -> !isFounder.test(expandedAccount) && getAccountData.apply(expandedAccount).getLevel() < maximumLevel).collect(Collectors.toList());
for (int c = 0; c < candidateAccounts.size(); ++c) {
ExpandedAccount expandedAccount = candidateAccounts.get(c);
final AccountData accountData = getAccountData.apply(expandedAccount);
final int effectiveBlocksGenerated = cumulativeBlocksByLevel[accountData.getInitialLevel()] + accountData.getBlocksGenerated();
final int effectiveBlocksGenerated = cumulativeBlocksByLevel.get(accountData.getInitialLevel()) + accountData.getBlocksGenerated();
for (int newLevel = cumulativeBlocksByLevel.length - 1; newLevel > 0; --newLevel)
if (effectiveBlocksGenerated >= cumulativeBlocksByLevel[newLevel]) {
for (int newLevel = maximumLevel; newLevel > 0; --newLevel)
if (effectiveBlocksGenerated >= cumulativeBlocksByLevel.get(newLevel)) {
if (newLevel > accountData.getLevel()) {
// Account has increased in level!
accountData.setLevel(newLevel);
@ -1530,7 +1512,53 @@ public class Block {
}
protected void decreaseAccountLevels() throws DataException {
// TODO !
// We need to do this for both forgers and recipients
this.decreaseAccountLevels(expandedAccount -> expandedAccount.isForgerFounder, expandedAccount -> expandedAccount.forgerAccountData);
this.decreaseAccountLevels(expandedAccount -> expandedAccount.isRecipientFounder, expandedAccount -> expandedAccount.recipientAccountData);
}
private void decreaseAccountLevels(Predicate<ExpandedAccount> isFounder, Function<ExpandedAccount, AccountData> getAccountData) throws DataException {
final List<Integer> cumulativeBlocksByLevel = BlockChain.getInstance().getCumulativeBlocksByLevel();
final List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
final boolean isProcessingRecipients = getAccountData.apply(expandedAccounts.get(0)) == expandedAccounts.get(0).recipientAccountData;
// Decrease blocks generated count for all accounts
for (int a = 0; a < expandedAccounts.size(); ++a) {
ExpandedAccount expandedAccount = expandedAccounts.get(a);
// Don't decrease twice if recipient is also forger.
if (isProcessingRecipients && expandedAccount.isRecipientAlsoForger)
continue;
AccountData accountData = getAccountData.apply(expandedAccount);
accountData.setBlocksGenerated(accountData.getBlocksGenerated() - 1);
repository.getAccountRepository().setBlocksGenerated(accountData);
LOGGER.trace(() -> String.format("Block generator %s down to %d generated block%s", accountData.getAddress(), accountData.getBlocksGenerated(), (accountData.getBlocksGenerated() != 1 ? "s" : "")));
}
// We are only interested in accounts that are NOT founders and NOT already lowest level
final int maximumLevel = cumulativeBlocksByLevel.size() - 1;
List<ExpandedAccount> candidateAccounts = expandedAccounts.stream().filter(expandedAccount -> !isFounder.test(expandedAccount) && getAccountData.apply(expandedAccount).getLevel() > 0).collect(Collectors.toList());
for (int c = 0; c < candidateAccounts.size(); ++c) {
ExpandedAccount expandedAccount = candidateAccounts.get(c);
final AccountData accountData = getAccountData.apply(expandedAccount);
final int effectiveBlocksGenerated = cumulativeBlocksByLevel.get(accountData.getInitialLevel()) + accountData.getBlocksGenerated();
for (int newLevel = maximumLevel; newLevel > 0; --newLevel)
if (effectiveBlocksGenerated >= cumulativeBlocksByLevel.get(newLevel)) {
if (newLevel < accountData.getLevel()) {
// Account has decreased in level!
accountData.setLevel(newLevel);
repository.getAccountRepository().setLevel(accountData);
LOGGER.trace(() -> String.format("Block generator %s reduced to level %d", accountData.getAddress(), accountData.getLevel()));
}
break;
}
}
}
protected void distributeByAccountLevel(BigDecimal totalAmount) throws DataException {

46
src/main/java/org/qora/block/BlockChain.java

@ -6,7 +6,9 @@ import java.io.InputStream;
import java.math.BigDecimal;
import java.math.MathContext;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
@ -111,13 +113,22 @@ public class BlockChain {
BigDecimal qoraHoldersShare;
/**
* Number of generated blocks required to reach next level.
* Number of generated blocks required to reach next level from previous.
* <p>
* Use account's current level as index.<br>
* If account's level isn't valid as an index, then account's level is at maximum.
*/
List<Integer> blocksNeededByLevel;
/**
* Cumulative number of generated blocks required to reach level from scratch.
* <p>
* Generated just after blockchain config is parsed and validated.
* <p>
* Should NOT be present in blockchain config file!
*/
List<Integer> cumulativeBlocksByLevel;
/** Block times by block height */
public static class BlockTimingByHeight {
public int height;
@ -221,9 +232,7 @@ public class BlockChain {
blockchain.validateConfig();
// Minor fix-up
blockchain.maxBytesPerUnitFee = blockchain.maxBytesPerUnitFee.setScale(8);
blockchain.unitFee = blockchain.unitFee.setScale(8);
blockchain.minFeePerByte = blockchain.unitFee.divide(blockchain.maxBytesPerUnitFee, MathContext.DECIMAL32);
blockchain.fixUp();
// Successfully read config now in effect
instance = blockchain;
@ -291,6 +300,10 @@ public class BlockChain {
return this.blocksNeededByLevel;
}
public List<Integer> getCumulativeBlocksByLevel() {
return this.cumulativeBlocksByLevel;
}
public BigDecimal getQoraHoldersShare() {
return this.qoraHoldersShare;
}
@ -402,6 +415,30 @@ public class BlockChain {
Settings.throwValidationError(String.format("Missing feature trigger \"%s\" in blockchain config", featureTrigger.name()));
}
/** Minor normalization, cached value generation, etc. */
private void fixUp() {
this.maxBytesPerUnitFee = this.maxBytesPerUnitFee.setScale(8);
this.unitFee = this.unitFee.setScale(8);
this.minFeePerByte = this.unitFee.divide(this.maxBytesPerUnitFee, MathContext.DECIMAL32);
// Pre-calculate cumulative blocks required for each level
int cumulativeBlocks = 0;
this.cumulativeBlocksByLevel = new ArrayList<>(this.blocksNeededByLevel.size() + 1);
for (int level = 0; level <= this.blocksNeededByLevel.size(); ++level) {
this.cumulativeBlocksByLevel.add(cumulativeBlocks);
if (level < this.blocksNeededByLevel.size())
cumulativeBlocks += this.blocksNeededByLevel.get(level);
}
// Convert collections to unmodifiable form
this.rewardsByHeight = Collections.unmodifiableList(this.rewardsByHeight);
this.sharesByLevel = Collections.unmodifiableList(this.sharesByLevel);
this.blocksNeededByLevel = Collections.unmodifiableList(this.blocksNeededByLevel);
this.cumulativeBlocksByLevel = Collections.unmodifiableList(this.cumulativeBlocksByLevel);
this.blockTimingsByHeight = Collections.unmodifiableList(this.blockTimingsByHeight);
}
/**
* Some sort start-up/initialization/checking method.
*
@ -412,7 +449,6 @@ public class BlockChain {
if (!isGenesisBlockValid())
rebuildBlockchain();
// TODO: walk through blocks
try (final Repository repository = RepositoryManager.getRepository()) {
Block parentBlock = GenesisBlock.getInstance(repository);
BlockData parentBlockData = parentBlock.getBlockData();

Loading…
Cancel
Save