Compare commits

..

1 Commits

Author SHA1 Message Date
crowetic
8366c3db1e Revert "Fix for most unit tests fails" 2024-11-08 09:14:37 -08:00
15 changed files with 145 additions and 249 deletions

12
pom.xml
View File

@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.qortal</groupId>
<artifactId>qortal</artifactId>
<version>4.6.5</version>
<version>4.6.0</version>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
@@ -16,7 +16,7 @@
<ciyam-at.version>1.4.2</ciyam-at.version>
<commons-net.version>3.8.0</commons-net.version>
<commons-text.version>1.12.0</commons-text.version>
<commons-io.version>2.18.0</commons-io.version>
<commons-io.version>2.17.0</commons-io.version>
<commons-compress.version>1.27.1</commons-compress.version>
<commons-lang3.version>3.17.0</commons-lang3.version>
<dagger.version>1.2.2</dagger.version>
@@ -26,9 +26,9 @@
<guava.version>33.3.1-jre</guava.version>
<hamcrest-library.version>2.2</hamcrest-library.version>
<homoglyph.version>1.2.1</homoglyph.version>
<hsqldb.version>2.7.4</hsqldb.version>
<hsqldb.version>2.5.1</hsqldb.version>
<icu4j.version>76.1</icu4j.version>
<java-diff-utils.version>4.15</java-diff-utils.version>
<java-diff-utils.version>4.12</java-diff-utils.version>
<javax.servlet-api.version>4.0.1</javax.servlet-api.version>
<jaxb-runtime.version>2.3.9</jaxb-runtime.version>
<jersey.version>2.42</jersey.version>
@@ -45,7 +45,7 @@
<maven-dependency-plugin.version>3.6.1</maven-dependency-plugin.version>
<maven-jar-plugin.version>3.4.2</maven-jar-plugin.version>
<maven-package-info-plugin.version>1.1.0</maven-package-info-plugin.version>
<maven-plugin.version>2.18.0</maven-plugin.version>
<maven-plugin.version>2.17.1</maven-plugin.version>
<maven-reproducible-build-plugin.version>0.17</maven-reproducible-build-plugin.version>
<maven-resources-plugin.version>3.3.1</maven-resources-plugin.version>
<maven-shade-plugin.version>3.6.0</maven-shade-plugin.version>
@@ -55,7 +55,7 @@
<simplemagic.version>1.17</simplemagic.version>
<slf4j.version>1.7.36</slf4j.version>
<swagger-api.version>2.0.10</swagger-api.version>
<swagger-ui.version>5.18.2</swagger-ui.version>
<swagger-ui.version>5.17.14</swagger-ui.version>
<upnp.version>1.2</upnp.version>
<xz.version>1.10</xz.version>
</properties>

View File

@@ -198,76 +198,66 @@ public class Account {
/** Returns whether account can be considered a "minting account".
* <p>
* To be considered a "minting account", the account needs to pass some of these tests:<br>
* To be considered a "minting account", the account needs to pass all of these tests:<br>
* <ul>
* <li>account's level is at least <tt>minAccountLevelToMint</tt> from blockchain config</li>
* <li>account's address has registered a name</li>
* <li>account's address is a member of the minter group</li>
* <li>account's address have registered a name</li>
* <li>account's address is member of minter group</li>
* </ul>
*
* @param isGroupValidated true if this account has already been validated for MINTER Group membership
* @return true if account can be considered "minting account"
* @throws DataException
*/
public boolean canMint(boolean isGroupValidated) throws DataException {
public boolean canMint() throws DataException {
AccountData accountData = this.repository.getAccountRepository().getAccount(this.address);
NameRepository nameRepository = this.repository.getNameRepository();
GroupRepository groupRepository = this.repository.getGroupRepository();
String myAddress = accountData.getAddress();
int blockchainHeight = this.repository.getBlockRepository().getBlockchainHeight();
int nameCheckHeight = BlockChain.getInstance().getOnlyMintWithNameHeight();
int levelToMint = BlockChain.getInstance().getMinAccountLevelToMint();
int level = accountData.getLevel();
int groupIdToMint = BlockChain.getInstance().getMintingGroupId();
int nameCheckHeight = BlockChain.getInstance().getOnlyMintWithNameHeight();
int groupCheckHeight = BlockChain.getInstance().getGroupMemberCheckHeight();
int removeNameCheckHeight = BlockChain.getInstance().getRemoveOnlyMintWithNameHeight();
// Can only mint if:
// Account's level is at least minAccountLevelToMint from blockchain config
if (blockchainHeight < nameCheckHeight) {
if (Account.isFounder(accountData.getFlags())) {
return accountData.getBlocksMintedPenalty() == 0;
} else {
return level >= levelToMint;
}
}
String myAddress = accountData.getAddress();
List<NameData> myName = nameRepository.getNamesByOwner(myAddress);
boolean isMember = groupRepository.memberExists(groupIdToMint, myAddress);
// Can only mint on onlyMintWithNameHeight from blockchain config if:
// Account's level is at least minAccountLevelToMint from blockchain config
// Account's address has registered a name
if (blockchainHeight >= nameCheckHeight && blockchainHeight < groupCheckHeight) {
List<NameData> myName = nameRepository.getNamesByOwner(myAddress);
if (Account.isFounder(accountData.getFlags())) {
return accountData.getBlocksMintedPenalty() == 0 && !myName.isEmpty();
} else {
return level >= levelToMint && !myName.isEmpty();
}
}
if (accountData == null)
return false;
// Can only mint on groupMemberCheckHeight from blockchain config if:
// Account's level is at least minAccountLevelToMint from blockchain config
// Account's address has registered a name
// Account's address is a member of the minter group
if (blockchainHeight >= groupCheckHeight && blockchainHeight < removeNameCheckHeight) {
List<NameData> myName = nameRepository.getNamesByOwner(myAddress);
if (Account.isFounder(accountData.getFlags())) {
return accountData.getBlocksMintedPenalty() == 0 && !myName.isEmpty() && (isGroupValidated || groupRepository.memberExists(groupIdToMint, myAddress));
} else {
return level >= levelToMint && !myName.isEmpty() && (isGroupValidated || groupRepository.memberExists(groupIdToMint, myAddress));
}
}
// Can only mint if level is at least minAccountLevelToMint< from blockchain config
if (blockchainHeight < nameCheckHeight && level >= levelToMint)
return true;
// Can only mint on removeOnlyMintWithNameHeight from blockchain config if:
// Account's level is at least minAccountLevelToMint from blockchain config
// Account's address is a member of the minter group
if (blockchainHeight >= removeNameCheckHeight) {
if (Account.isFounder(accountData.getFlags())) {
return accountData.getBlocksMintedPenalty() == 0 && (isGroupValidated || groupRepository.memberExists(groupIdToMint, myAddress));
} else {
return level >= levelToMint && (isGroupValidated || groupRepository.memberExists(groupIdToMint, myAddress));
}
}
// Can only mint if have registered a name
if (blockchainHeight >= nameCheckHeight && blockchainHeight < groupCheckHeight && level >= levelToMint && !myName.isEmpty())
return true;
// Can only mint if have registered a name and is member of minter group id
if (blockchainHeight >= groupCheckHeight && level >= levelToMint && !myName.isEmpty() && isMember)
return true;
// Founders needs to pass same tests like minters
if (blockchainHeight < nameCheckHeight &&
Account.isFounder(accountData.getFlags()) &&
accountData.getBlocksMintedPenalty() == 0)
return true;
if (blockchainHeight >= nameCheckHeight &&
blockchainHeight < groupCheckHeight &&
Account.isFounder(accountData.getFlags()) &&
accountData.getBlocksMintedPenalty() == 0 &&
!myName.isEmpty())
return true;
if (blockchainHeight >= groupCheckHeight &&
Account.isFounder(accountData.getFlags()) &&
accountData.getBlocksMintedPenalty() == 0 &&
!myName.isEmpty() &&
isMember)
return true;
return false;
}
@@ -282,6 +272,7 @@ public class Account {
return this.repository.getAccountRepository().getBlocksMintedPenaltyCount(this.address);
}
/** Returns whether account can build reward-shares.
* <p>
* To be able to create reward-shares, the account needs to pass at least one of these tests:<br>
@@ -295,7 +286,6 @@ public class Account {
*/
public boolean canRewardShare() throws DataException {
AccountData accountData = this.repository.getAccountRepository().getAccount(this.address);
if (accountData == null)
return false;
@@ -365,7 +355,6 @@ public class Account {
Account rewardShareMinter = new Account(repository, rewardShareData.getMinter());
return rewardShareMinter.getEffectiveMintingLevel();
}
/**
* Returns 'effective' minting level, with a fix for the zero level.
* <p>

View File

@@ -459,7 +459,7 @@ public class AdminResource {
// Qortal: check reward-share's minting account is still allowed to mint
Account rewardShareMintingAccount = new Account(repository, rewardShareData.getMinter());
if (!rewardShareMintingAccount.canMint(false))
if (!rewardShareMintingAccount.canMint())
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.CANNOT_MINT);
MintingAccountData mintingAccountData = new MintingAccountData(mintingAccount.getPrivateKey(), mintingAccount.getPublicKey());

View File

@@ -168,7 +168,7 @@ public class ArbitraryDataRenderer {
byte[] data = Files.readAllBytes(filePath); // TODO: limit file size that can be read into memory
HTMLParser htmlParser = new HTMLParser(resourceId, inPath, prefix, includeResourceIdInPrefix, data, qdnContext, service, identifier, theme, usingCustomRouting);
htmlParser.addAdditionalHeaderTags();
response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; font-src 'self' data:; media-src 'self' data: blob:; img-src 'self' data: blob:; connect-src 'self' wss:;");
response.addHeader("Content-Security-Policy", "default-src 'self' 'unsafe-inline' 'unsafe-eval'; media-src 'self' data: blob:; img-src 'self' data: blob:;");
response.setContentType(context.getMimeType(filename));
response.setContentLength(htmlParser.getData().length);
response.getOutputStream().write(htmlParser.getData());

View File

@@ -25,7 +25,10 @@ import org.qortal.data.block.BlockSummaryData;
import org.qortal.data.block.BlockTransactionData;
import org.qortal.data.network.OnlineAccountData;
import org.qortal.data.transaction.TransactionData;
import org.qortal.repository.*;
import org.qortal.repository.ATRepository;
import org.qortal.repository.DataException;
import org.qortal.repository.Repository;
import org.qortal.repository.TransactionRepository;
import org.qortal.settings.Settings;
import org.qortal.transaction.AtTransaction;
import org.qortal.transaction.Transaction;
@@ -141,12 +144,9 @@ public class Block {
private final Account mintingAccount;
private final AccountData mintingAccountData;
private final boolean isMinterFounder;
private final boolean isMinterMember;
private final Account recipientAccount;
private final AccountData recipientAccountData;
final BlockChain blockChain = BlockChain.getInstance();
ExpandedAccount(Repository repository, RewardShareData rewardShareData) throws DataException {
this.rewardShareData = rewardShareData;
@@ -157,7 +157,6 @@ public class Block {
this.isMinterFounder = Account.isFounder(mintingAccountData.getFlags());
this.isRecipientAlsoMinter = this.rewardShareData.getRecipient().equals(this.mintingAccount.getAddress());
this.isMinterMember = repository.getGroupRepository().memberExists(BlockChain.getInstance().getMintingGroupId(), this.mintingAccount.getAddress());
if (this.isRecipientAlsoMinter) {
// Self-share: minter is also recipient
@@ -182,7 +181,7 @@ public class Block {
* <p>
* This is a method, not a final variable, because account's level can change between construction and call,
* e.g. during Block.process() where account levels are bumped right before Block.distributeBlockReward().
*
*
* @return account-level share "bin" from blockchain config, or null if founder / none found
*/
public AccountLevelShareBin getShareBin(int blockHeight) {
@@ -193,12 +192,8 @@ public class Block {
if (accountLevel <= 0)
return null; // level 0 isn't included in any share bins
if (blockHeight >= blockChain.getFixBatchRewardHeight()) {
if (!this.isMinterMember)
return null; // not member of minter group isn't included in any share bins
}
// Select the correct set of share bins based on block height
final BlockChain blockChain = BlockChain.getInstance();
final AccountLevelShareBin[] shareBinsByLevel = (blockHeight >= blockChain.getSharesByLevelV2Height()) ?
blockChain.getShareBinsByAccountLevelV2() : blockChain.getShareBinsByAccountLevelV1();
@@ -267,7 +262,7 @@ public class Block {
* Constructs new Block without loading transactions and AT states.
* <p>
* Transactions and AT states are loaded on first call to getTransactions() or getATStates() respectively.
*
*
* @param repository
* @param blockData
*/
@@ -338,7 +333,7 @@ public class Block {
/**
* Constructs new Block with empty transaction list, using passed minter account.
*
*
* @param repository
* @param blockData
* @param minter
@@ -356,7 +351,7 @@ public class Block {
* This constructor typically used when minting a new block.
* <p>
* Note that CIYAM ATs will be executed and AT-Transactions prepended to this block, along with AT state data and fees.
*
*
* @param repository
* @param parentBlockData
* @param minter
@@ -382,7 +377,7 @@ public class Block {
byte[] encodedOnlineAccounts = new byte[0];
int onlineAccountsCount = 0;
byte[] onlineAccountsSignatures = null;
if (isBatchRewardDistributionBlock(height)) {
// Batch reward distribution block - copy online accounts from recent block with highest online accounts count
@@ -517,7 +512,7 @@ public class Block {
* Mints new block using this block as template, but with different minting account.
* <p>
* NOTE: uses the same transactions list, AT states, etc.
*
*
* @param minter
* @return
* @throws DataException
@@ -603,7 +598,7 @@ public class Block {
/**
* Return composite block signature (minterSignature + transactionsSignature).
*
*
* @return byte[], or null if either component signature is null.
*/
public byte[] getSignature() {
@@ -618,7 +613,7 @@ public class Block {
* <p>
* We're starting with version 4 as a nod to being newer than successor Qora,
* whose latest block version was 3.
*
*
* @return 1, 2, 3 or 4
*/
public int getNextBlockVersion() {
@@ -632,7 +627,7 @@ public class Block {
* Return block's transactions.
* <p>
* If the block was loaded from repository then it's possible this method will call the repository to fetch the transactions if not done already.
*
*
* @return
* @throws DataException
*/
@@ -666,7 +661,7 @@ public class Block {
* If the block was loaded from repository then it's possible this method will call the repository to fetch the AT states if not done already.
* <p>
* <b>Note:</b> AT states fetched from repository only contain summary info, not actual data like serialized state data or AT creation timestamps!
*
*
* @return
* @throws DataException
*/
@@ -702,7 +697,7 @@ public class Block {
* <p>
* Typically called as part of Block.process() or Block.orphan()
* so ideally after any calls to Block.isValid().
*
*
* @throws DataException
*/
public List<ExpandedAccount> getExpandedAccounts() throws DataException {
@@ -720,18 +715,8 @@ public class Block {
List<ExpandedAccount> expandedAccounts = new ArrayList<>();
for (RewardShareData rewardShare : this.cachedOnlineRewardShares) {
if (this.getBlockData().getHeight() < BlockChain.getInstance().getFixBatchRewardHeight()) {
expandedAccounts.add(new ExpandedAccount(repository, rewardShare));
}
if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight()) {
boolean isMinterGroupMember = repository.getGroupRepository().memberExists(BlockChain.getInstance().getMintingGroupId(), rewardShare.getMinter());
if (isMinterGroupMember) {
expandedAccounts.add(new ExpandedAccount(repository, rewardShare));
}
}
}
for (RewardShareData rewardShare : this.cachedOnlineRewardShares)
expandedAccounts.add(new ExpandedAccount(repository, rewardShare));
this.cachedExpandedAccounts = expandedAccounts;
@@ -742,7 +727,7 @@ public class Block {
/**
* Load parent block's data from repository via this block's reference.
*
*
* @return parent's BlockData, or null if no parent found
* @throws DataException
*/
@@ -756,7 +741,7 @@ public class Block {
/**
* Load child block's data from repository via this block's signature.
*
*
* @return child's BlockData, or null if no parent found
* @throws DataException
*/
@@ -776,7 +761,7 @@ public class Block {
* Used when constructing a new block during minting.
* <p>
* Requires block's {@code minter} being a {@code PrivateKeyAccount} so block's transactions signature can be recalculated.
*
*
* @param transactionData
* @return true if transaction successfully added to block, false otherwise
* @throws IllegalStateException
@@ -829,7 +814,7 @@ public class Block {
* Used when constructing a new block during minting.
* <p>
* Requires block's {@code minter} being a {@code PrivateKeyAccount} so block's transactions signature can be recalculated.
*
*
* @param transactionData
* @throws IllegalStateException
* if block's {@code minter} is not a {@code PrivateKeyAccount}.
@@ -874,7 +859,7 @@ public class Block {
* previous block's minter signature + minter's public key + (encoded) online-accounts data
* <p>
* (Previous block's minter signature is extracted from this block's reference).
*
*
* @throws IllegalStateException
* if block's {@code minter} is not a {@code PrivateKeyAccount}.
* @throws RuntimeException
@@ -891,7 +876,7 @@ public class Block {
* Recalculate block's transactions signature.
* <p>
* Requires block's {@code minter} being a {@code PrivateKeyAccount}.
*
*
* @throws IllegalStateException
* if block's {@code minter} is not a {@code PrivateKeyAccount}.
* @throws RuntimeException
@@ -1013,7 +998,7 @@ public class Block {
* Recalculate block's minter and transactions signatures, thus giving block full signature.
* <p>
* Note: Block instance must have been constructed with a <tt>PrivateKeyAccount</tt> minter or this call will throw an <tt>IllegalStateException</tt>.
*
*
* @throws IllegalStateException
* if block's {@code minter} is not a {@code PrivateKeyAccount}.
*/
@@ -1026,7 +1011,7 @@ public class Block {
/**
* Returns whether this block's signatures are valid.
*
*
* @return true if both minter and transaction signatures are valid, false otherwise
*/
public boolean isSignatureValid() {
@@ -1050,7 +1035,7 @@ public class Block {
* <p>
* Used by BlockMinter to check whether it's time to mint a new block,
* and also used by Block.isValid for checks (if not a testchain).
*
*
* @return ValidationResult.OK if timestamp valid, or some other ValidationResult otherwise.
* @throws DataException
*/
@@ -1230,7 +1215,7 @@ public class Block {
* <p>
* Checks block's transactions by testing their validity then processing them.<br>
* Hence uses a repository savepoint during execution.
*
*
* @return ValidationResult.OK if block is valid, or some other ValidationResult otherwise.
* @throws DataException
*/
@@ -1401,7 +1386,7 @@ public class Block {
* <p>
* NOTE: will execute ATs locally if not already done.<br>
* This is so we have locally-generated AT states for comparison.
*
*
* @return OK, or some AT-related validation result
* @throws DataException
*/
@@ -1477,11 +1462,11 @@ public class Block {
* Note: this method does not store new AT state data into repository - that is handled by <tt>process()</tt>.
* <p>
* This method is not needed if fetching an existing block from the repository as AT state data will be loaded from repository as well.
*
*
* @see #isValid()
*
*
* @throws DataException
*
*
*/
private void executeATs() throws DataException {
// We're expecting a lack of AT state data at this point.
@@ -1533,7 +1518,7 @@ public class Block {
return false;
Account mintingAccount = new PublicKeyAccount(this.repository, rewardShareData.getMinterPublicKey());
return mintingAccount.canMint(false);
return mintingAccount.canMint();
}
/**
@@ -1553,7 +1538,7 @@ public class Block {
/**
* Process block, and its transactions, adding them to the blockchain.
*
*
* @throws DataException
*/
public void process() throws DataException {
@@ -1854,7 +1839,7 @@ public class Block {
/**
* Removes block from blockchain undoing transactions and adding them to unconfirmed pile.
*
*
* @throws DataException
*/
public void orphan() throws DataException {
@@ -1894,7 +1879,7 @@ public class Block {
SelfSponsorshipAlgoV3Block.orphanAccountPenalties(this);
}
}
// Account levels and block rewards are only processed/orphaned on block reward distribution blocks
if (this.isRewardDistributionBlock()) {
// Block rewards, including transaction fees, removed after transactions undone
@@ -2228,7 +2213,6 @@ public class Block {
List<AccountBalanceData> accountBalanceDeltas = balanceChanges.entrySet().stream()
.map(entry -> new AccountBalanceData(entry.getKey(), Asset.QORT, entry.getValue()))
.collect(Collectors.toList());
LOGGER.trace("Account Balance Deltas: {}", accountBalanceDeltas);
this.repository.getAccountRepository().modifyAssetBalances(accountBalanceDeltas);
}
@@ -2241,30 +2225,30 @@ public class Block {
/*
* 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%

View File

@@ -71,7 +71,6 @@ public class BlockChain {
transactionV6Timestamp,
disableReferenceTimestamp,
increaseOnlineAccountsDifficultyTimestamp,
decreaseOnlineAccountsDifficultyTimestamp,
onlineAccountMinterLevelValidationHeight,
selfSponsorshipAlgoV1Height,
selfSponsorshipAlgoV2Height,
@@ -86,9 +85,7 @@ public class BlockChain {
disableRewardshareHeight,
enableRewardshareHeight,
onlyMintWithNameHeight,
removeOnlyMintWithNameHeight,
groupMemberCheckHeight,
fixBatchRewardHeight
groupMemberCheckHeight
}
// Custom transaction fees
@@ -220,10 +217,6 @@ public class BlockChain {
* featureTriggers because unit tests need to set this value via Reflection. */
private long onlineAccountsModulusV2Timestamp;
/** Feature trigger timestamp for ONLINE_ACCOUNTS_MODULUS time interval decrease. Can't use
* featureTriggers because unit tests need to set this value via Reflection. */
private long onlineAccountsModulusV3Timestamp;
/** Snapshot timestamp for self sponsorship algo V1 */
private long selfSponsorshipAlgoV1SnapshotTimestamp;
@@ -410,10 +403,6 @@ public class BlockChain {
return this.onlineAccountsModulusV2Timestamp;
}
public long getOnlineAccountsModulusV3Timestamp() {
return this.onlineAccountsModulusV3Timestamp;
}
/* Block reward batching */
public long getBlockRewardBatchStartHeight() {
return this.blockRewardBatchStartHeight;
@@ -590,10 +579,6 @@ public class BlockChain {
return this.featureTriggers.get(FeatureTrigger.increaseOnlineAccountsDifficultyTimestamp.name()).longValue();
}
public long getDecreaseOnlineAccountsDifficultyTimestamp() {
return this.featureTriggers.get(FeatureTrigger.decreaseOnlineAccountsDifficultyTimestamp.name()).longValue();
}
public int getSelfSponsorshipAlgoV1Height() {
return this.featureTriggers.get(FeatureTrigger.selfSponsorshipAlgoV1Height.name()).intValue();
}
@@ -650,18 +635,10 @@ public class BlockChain {
return this.featureTriggers.get(FeatureTrigger.onlyMintWithNameHeight.name()).intValue();
}
public int getRemoveOnlyMintWithNameHeight() {
return this.featureTriggers.get(FeatureTrigger.removeOnlyMintWithNameHeight.name()).intValue();
}
public int getGroupMemberCheckHeight() {
return this.featureTriggers.get(FeatureTrigger.groupMemberCheckHeight.name()).intValue();
}
public int getFixBatchRewardHeight() {
return this.featureTriggers.get(FeatureTrigger.fixBatchRewardHeight.name()).intValue();
}
// More complex getters for aspects that change by height or timestamp
public long getRewardAtHeight(int ourHeight) {

View File

@@ -148,7 +148,7 @@ public class BlockMinter extends Thread {
}
Account mintingAccount = new Account(repository, rewardShareData.getMinter());
if (!mintingAccount.canMint(true)) {
if (!mintingAccount.canMint()) {
// Minting-account component of reward-share can no longer mint - disregard
madi.remove();
continue;

View File

@@ -13,7 +13,6 @@ import org.qortal.crypto.MemoryPoW;
import org.qortal.crypto.Qortal25519Extras;
import org.qortal.data.account.MintingAccountData;
import org.qortal.data.account.RewardShareData;
import org.qortal.data.group.GroupMemberData;
import org.qortal.data.network.OnlineAccountData;
import org.qortal.network.Network;
import org.qortal.network.Peer;
@@ -45,7 +44,6 @@ public class OnlineAccountsManager {
*/
private static final long ONLINE_TIMESTAMP_MODULUS_V1 = 5 * 60 * 1000L;
private static final long ONLINE_TIMESTAMP_MODULUS_V2 = 30 * 60 * 1000L;
private static final long ONLINE_TIMESTAMP_MODULUS_V3 = 10 * 60 * 1000L;
/**
* How many 'current' timestamp-sets of online accounts we cache.
@@ -69,13 +67,12 @@ public class OnlineAccountsManager {
private static final long ONLINE_ACCOUNTS_COMPUTE_INITIAL_SLEEP_INTERVAL = 30 * 1000L; // ms
// MemoryPoW - mainnet
public static final int POW_BUFFER_SIZE = 1024 * 1024; // bytes
public static final int POW_BUFFER_SIZE = 1 * 1024 * 1024; // bytes
public static final int POW_DIFFICULTY_V1 = 18; // leading zero bits
public static final int POW_DIFFICULTY_V2 = 19; // leading zero bits
public static final int POW_DIFFICULTY_V3 = 6; // leading zero bits
// MemoryPoW - testnet
public static final int POW_BUFFER_SIZE_TESTNET = 1024 * 1024; // bytes
public static final int POW_BUFFER_SIZE_TESTNET = 1 * 1024 * 1024; // bytes
public static final int POW_DIFFICULTY_TESTNET = 5; // leading zero bits
// IMPORTANT: if we ever need to dynamically modify the buffer size using a feature trigger, the
@@ -109,15 +106,11 @@ public class OnlineAccountsManager {
public static long getOnlineTimestampModulus() {
Long now = NTP.getTime();
if (now != null && now >= BlockChain.getInstance().getOnlineAccountsModulusV2Timestamp() && now < BlockChain.getInstance().getOnlineAccountsModulusV3Timestamp()) {
if (now != null && now >= BlockChain.getInstance().getOnlineAccountsModulusV2Timestamp()) {
return ONLINE_TIMESTAMP_MODULUS_V2;
}
if (now != null && now >= BlockChain.getInstance().getOnlineAccountsModulusV3Timestamp()) {
return ONLINE_TIMESTAMP_MODULUS_V3;
}
return ONLINE_TIMESTAMP_MODULUS_V1;
}
public static Long getCurrentOnlineAccountTimestamp() {
Long now = NTP.getTime();
if (now == null)
@@ -142,12 +135,9 @@ public class OnlineAccountsManager {
if (Settings.getInstance().isTestNet())
return POW_DIFFICULTY_TESTNET;
if (timestamp >= BlockChain.getInstance().getIncreaseOnlineAccountsDifficultyTimestamp() && timestamp < BlockChain.getInstance().getDecreaseOnlineAccountsDifficultyTimestamp())
if (timestamp >= BlockChain.getInstance().getIncreaseOnlineAccountsDifficultyTimestamp())
return POW_DIFFICULTY_V2;
if (timestamp >= BlockChain.getInstance().getDecreaseOnlineAccountsDifficultyTimestamp())
return POW_DIFFICULTY_V3;
return POW_DIFFICULTY_V1;
}
@@ -225,12 +215,6 @@ public class OnlineAccountsManager {
Set<OnlineAccountData> onlineAccountsToAdd = new HashSet<>();
Set<OnlineAccountData> onlineAccountsToRemove = new HashSet<>();
try (final Repository repository = RepositoryManager.getRepository()) {
List<String> mintingGroupMemberAddresses
= repository.getGroupRepository()
.getGroupMembers(BlockChain.getInstance().getMintingGroupId()).stream()
.map(GroupMemberData::getMember)
.collect(Collectors.toList());
for (OnlineAccountData onlineAccountData : this.onlineAccountsImportQueue) {
if (isStopping)
return;
@@ -243,7 +227,7 @@ public class OnlineAccountsManager {
continue;
}
boolean isValid = this.isValidCurrentAccount(repository, mintingGroupMemberAddresses, onlineAccountData);
boolean isValid = this.isValidCurrentAccount(repository, onlineAccountData);
if (isValid)
onlineAccountsToAdd.add(onlineAccountData);
@@ -322,7 +306,7 @@ public class OnlineAccountsManager {
return inplaceArray;
}
private static boolean isValidCurrentAccount(Repository repository, List<String> mintingGroupMemberAddresses, OnlineAccountData onlineAccountData) throws DataException {
private static boolean isValidCurrentAccount(Repository repository, OnlineAccountData onlineAccountData) throws DataException {
final Long now = NTP.getTime();
if (now == null)
return false;
@@ -357,14 +341,9 @@ public class OnlineAccountsManager {
LOGGER.trace(() -> String.format("Rejecting unknown online reward-share public key %s", Base58.encode(rewardSharePublicKey)));
return false;
}
// reject account address that are not in the MINTER Group
else if( !mintingGroupMemberAddresses.contains(rewardShareData.getMinter())) {
LOGGER.trace(() -> String.format("Rejecting online reward-share that is not in MINTER Group, account %s", rewardShareData.getMinter()));
return false;
}
Account mintingAccount = new Account(repository, rewardShareData.getMinter());
if (!mintingAccount.canMint(true)) { // group validation is a few lines above
if (!mintingAccount.canMint()) {
// Minting-account component of reward-share can no longer mint - disregard
LOGGER.trace(() -> String.format("Rejecting online reward-share with non-minting account %s", mintingAccount.getAddress()));
return false;
@@ -551,7 +530,7 @@ public class OnlineAccountsManager {
}
Account mintingAccount = new Account(repository, rewardShareData.getMinter());
if (!mintingAccount.canMint(true)) {
if (!mintingAccount.canMint()) {
// Minting-account component of reward-share can no longer mint - disregard
iterator.remove();
continue;

View File

@@ -4,15 +4,10 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qortal.network.Network;
import org.qortal.network.Peer;
import org.qortal.utils.DaemonThreadFactory;
import org.qortal.utils.ExecuteProduceConsume.Task;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class PeerConnectTask implements Task {
private static final Logger LOGGER = LogManager.getLogger(PeerConnectTask.class);
private static final ExecutorService connectionExecutor = Executors.newCachedThreadPool(new DaemonThreadFactory(8));
private final Peer peer;
private final String name;
@@ -29,24 +24,6 @@ public class PeerConnectTask implements Task {
@Override
public void perform() throws InterruptedException {
// Submit connection task to a dedicated thread pool for non-blocking I/O
connectionExecutor.submit(() -> {
try {
connectPeerAsync(peer);
} catch (InterruptedException e) {
LOGGER.error("Connection attempt interrupted for peer {}", peer, e);
Thread.currentThread().interrupt(); // Reset interrupt flag
}
});
}
private void connectPeerAsync(Peer peer) throws InterruptedException {
// Perform peer connection in a separate thread to avoid blocking main task execution
try {
Network.getInstance().connectPeer(peer);
LOGGER.trace("Successfully connected to peer {}", peer);
} catch (Exception e) {
LOGGER.error("Error connecting to peer {}", peer, e);
}
Network.getInstance().connectPeer(peer);
}
}

View File

@@ -454,41 +454,40 @@ public class HSQLDBDatabaseUpdates {
case 12:
// Groups
// NOTE: We need to set Groups to `Groups` here to avoid SQL Standard Keywords in HSQLDB v2.7.4
stmt.execute("CREATE TABLE `Groups` (group_id GroupID, owner QortalAddress NOT NULL, group_name GroupName NOT NULL, "
stmt.execute("CREATE TABLE Groups (group_id GroupID, owner QortalAddress NOT NULL, group_name GroupName NOT NULL, "
+ "created_when EpochMillis NOT NULL, updated_when EpochMillis, is_open BOOLEAN NOT NULL, "
+ "approval_threshold TINYINT NOT NULL, min_block_delay INTEGER NOT NULL, max_block_delay INTEGER NOT NULL, "
+ "reference Signature, creation_group_id GroupID, reduced_group_name GroupName NOT NULL, "
+ "description GenericDescription NOT NULL, PRIMARY KEY (group_id))");
// For finding groups by name
stmt.execute("CREATE INDEX GroupNameIndex on `Groups` (group_name)");
stmt.execute("CREATE INDEX GroupNameIndex on Groups (group_name)");
// For finding groups by reduced name
stmt.execute("CREATE INDEX GroupReducedNameIndex on `Groups` (reduced_group_name)");
stmt.execute("CREATE INDEX GroupReducedNameIndex on Groups (reduced_group_name)");
// For finding groups by owner
stmt.execute("CREATE INDEX GroupOwnerIndex ON `Groups` (owner)");
stmt.execute("CREATE INDEX GroupOwnerIndex ON Groups (owner)");
// We need a corresponding trigger to make sure new group_id values are assigned sequentially starting from 1
stmt.execute("CREATE TRIGGER Group_ID_Trigger BEFORE INSERT ON `Groups` "
stmt.execute("CREATE TRIGGER Group_ID_Trigger BEFORE INSERT ON Groups "
+ "REFERENCING NEW ROW AS new_row FOR EACH ROW WHEN (new_row.group_id IS NULL) "
+ "SET new_row.group_id = (SELECT IFNULL(MAX(group_id) + 1, 1) FROM `Groups`)");
+ "SET new_row.group_id = (SELECT IFNULL(MAX(group_id) + 1, 1) FROM Groups)");
// Admins
stmt.execute("CREATE TABLE GroupAdmins (group_id GroupID, admin QortalAddress, reference Signature NOT NULL, "
+ "PRIMARY KEY (group_id, admin), FOREIGN KEY (group_id) REFERENCES `Groups` (group_id) ON DELETE CASCADE)");
+ "PRIMARY KEY (group_id, admin), FOREIGN KEY (group_id) REFERENCES Groups (group_id) ON DELETE CASCADE)");
// For finding groups by admin address
stmt.execute("CREATE INDEX GroupAdminIndex ON GroupAdmins (admin)");
// Members
stmt.execute("CREATE TABLE GroupMembers (group_id GroupID, address QortalAddress, "
+ "joined_when EpochMillis NOT NULL, reference Signature NOT NULL, "
+ "PRIMARY KEY (group_id, address), FOREIGN KEY (group_id) REFERENCES `Groups` (group_id) ON DELETE CASCADE)");
+ "PRIMARY KEY (group_id, address), FOREIGN KEY (group_id) REFERENCES Groups (group_id) ON DELETE CASCADE)");
// For finding groups by member address
stmt.execute("CREATE INDEX GroupMemberIndex ON GroupMembers (address)");
// Invites
stmt.execute("CREATE TABLE GroupInvites (group_id GroupID, inviter QortalAddress, invitee QortalAddress, "
+ "expires_when EpochMillis, reference Signature, "
+ "PRIMARY KEY (group_id, invitee), FOREIGN KEY (group_id) REFERENCES `Groups` (group_id) ON DELETE CASCADE)");
+ "PRIMARY KEY (group_id, invitee), FOREIGN KEY (group_id) REFERENCES Groups (group_id) ON DELETE CASCADE)");
// For finding invites sent by inviter
stmt.execute("CREATE INDEX GroupInviteInviterIndex ON GroupInvites (inviter)");
// For finding invites by group
@@ -504,7 +503,7 @@ public class HSQLDBDatabaseUpdates {
// NULL expires_when means does not expire!
stmt.execute("CREATE TABLE GroupBans (group_id GroupID, offender QortalAddress, admin QortalAddress NOT NULL, "
+ "banned_when EpochMillis NOT NULL, reason GenericDescription NOT NULL, expires_when EpochMillis, reference Signature NOT NULL, "
+ "PRIMARY KEY (group_id, offender), FOREIGN KEY (group_id) REFERENCES `Groups` (group_id) ON DELETE CASCADE)");
+ "PRIMARY KEY (group_id, offender), FOREIGN KEY (group_id) REFERENCES Groups (group_id) ON DELETE CASCADE)");
// For expiry maintenance
stmt.execute("CREATE INDEX GroupBanExpiryIndex ON GroupBans (expires_when)");
break;

View File

@@ -213,7 +213,7 @@ public class Settings {
public long recoveryModeTimeout = 9999999999999L;
/** Minimum peer version number required in order to sync with them */
private String minPeerVersion = "4.6.3";
private String minPeerVersion = "4.6.0";
/** Whether to allow connections with peers below minPeerVersion
* If true, we won't sync with them but they can still sync with us, and will show in the peers list
* If false, sync will be blocked both ways, and they will not appear in the peers list */

View File

@@ -123,7 +123,7 @@ public class RewardShareTransaction extends Transaction {
final boolean isCancellingSharePercent = this.rewardShareTransactionData.getSharePercent() < 0;
// Creator themselves needs to be allowed to mint (unless cancelling)
if (!isCancellingSharePercent && !creator.canMint(false))
if (!isCancellingSharePercent && !creator.canMint())
return ValidationResult.NOT_MINTING_ACCOUNT;
// Qortal: special rules in play depending whether recipient is also minter

View File

@@ -29,7 +29,6 @@
"onlineAccountSignaturesMinLifetime": 43200000,
"onlineAccountSignaturesMaxLifetime": 86400000,
"onlineAccountsModulusV2Timestamp": 1659801600000,
"onlineAccountsModulusV3Timestamp": 1731961800000,
"selfSponsorshipAlgoV1SnapshotTimestamp": 1670230000000,
"selfSponsorshipAlgoV2SnapshotTimestamp": 1708360200000,
"selfSponsorshipAlgoV3SnapshotTimestamp": 1708432200000,
@@ -96,7 +95,6 @@
"transactionV6Timestamp": 9999999999999,
"disableReferenceTimestamp": 1655222400000,
"increaseOnlineAccountsDifficultyTimestamp": 9999999999999,
"decreaseOnlineAccountsDifficultyTimestamp": 1731958200000,
"onlineAccountMinterLevelValidationHeight": 1092000,
"selfSponsorshipAlgoV1Height": 1092400,
"selfSponsorshipAlgoV2Height": 1611200,
@@ -111,9 +109,7 @@
"disableRewardshareHeight": 1899100,
"enableRewardshareHeight": 1905100,
"onlyMintWithNameHeight": 1900300,
"removeOnlyMintWithNameHeight": 1935500,
"groupMemberCheckHeight": 1902700,
"fixBatchRewardHeight": 1945900
"groupMemberCheckHeight": 1902700
},
"checkpoints": [
{ "height": 1136300, "signature": "3BbwawEF2uN8Ni5ofpJXkukoU8ctAPxYoFB7whq9pKfBnjfZcpfEJT4R95NvBDoTP8WDyWvsUvbfHbcr9qSZuYpSKZjUQTvdFf6eqznHGEwhZApWfvXu6zjGCxYCp65F4jsVYYJjkzbjmkCg5WAwN5voudngA23kMK6PpTNygapCzXt" }

View File

@@ -74,7 +74,7 @@ public class TransferPrivsTests extends Common {
public void testAliceIntoNewAccountTransferPrivs() throws DataException {
try (final Repository repository = RepositoryManager.getRepository()) {
TestAccount alice = Common.getTestAccount(repository, "alice");
assertTrue(alice.canMint(false));
assertTrue(alice.canMint());
PrivateKeyAccount aliceMintingAccount = Common.getTestAccount(repository, "alice-reward-share");
@@ -86,8 +86,8 @@ public class TransferPrivsTests extends Common {
combineAccounts(repository, alice, randomAccount, aliceMintingAccount);
assertFalse(alice.canMint(false));
assertTrue(randomAccount.canMint(false));
assertFalse(alice.canMint());
assertTrue(randomAccount.canMint());
}
}
@@ -97,8 +97,8 @@ public class TransferPrivsTests extends Common {
TestAccount alice = Common.getTestAccount(repository, "alice");
TestAccount dilbert = Common.getTestAccount(repository, "dilbert");
assertTrue(alice.canMint(false));
assertTrue(dilbert.canMint(false));
assertTrue(alice.canMint());
assertTrue(dilbert.canMint());
// Dilbert has level, Alice does not so we need Alice to mint enough blocks to bump Dilbert's level post-combine
final int expectedPostCombineLevel = dilbert.getLevel() + 1;
@@ -118,11 +118,11 @@ public class TransferPrivsTests extends Common {
// Post-combine sender checks
checkSenderPostTransfer(postCombineAliceData);
assertFalse(alice.canMint(false));
assertFalse(alice.canMint());
// Post-combine recipient checks
checkRecipientPostTransfer(preCombineAliceData, preCombineDilbertData, postCombineDilbertData, expectedPostCombineLevel);
assertTrue(dilbert.canMint(false));
assertTrue(dilbert.canMint());
// Orphan previous block
BlockUtils.orphanLastBlock(repository);
@@ -130,12 +130,12 @@ public class TransferPrivsTests extends Common {
// Sender checks
AccountData orphanedAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
checkAccountDataRestored("sender", preCombineAliceData, orphanedAliceData);
assertTrue(alice.canMint(false));
assertTrue(alice.canMint());
// Recipient checks
AccountData orphanedDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
checkAccountDataRestored("recipient", preCombineDilbertData, orphanedDilbertData);
assertTrue(dilbert.canMint(false));
assertTrue(dilbert.canMint());
}
}
@@ -145,8 +145,8 @@ public class TransferPrivsTests extends Common {
TestAccount alice = Common.getTestAccount(repository, "alice");
TestAccount dilbert = Common.getTestAccount(repository, "dilbert");
assertTrue(dilbert.canMint(false));
assertTrue(alice.canMint(false));
assertTrue(dilbert.canMint());
assertTrue(alice.canMint());
// Dilbert has level, Alice does not so we need Alice to mint enough blocks to surpass Dilbert's level post-combine
final int expectedPostCombineLevel = dilbert.getLevel() + 1;
@@ -166,11 +166,11 @@ public class TransferPrivsTests extends Common {
// Post-combine sender checks
checkSenderPostTransfer(postCombineDilbertData);
assertFalse(dilbert.canMint(false));
assertFalse(dilbert.canMint());
// Post-combine recipient checks
checkRecipientPostTransfer(preCombineDilbertData, preCombineAliceData, postCombineAliceData, expectedPostCombineLevel);
assertTrue(alice.canMint(false));
assertTrue(alice.canMint());
// Orphan previous block
BlockUtils.orphanLastBlock(repository);
@@ -178,12 +178,12 @@ public class TransferPrivsTests extends Common {
// Sender checks
AccountData orphanedDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
checkAccountDataRestored("sender", preCombineDilbertData, orphanedDilbertData);
assertTrue(dilbert.canMint(false));
assertTrue(dilbert.canMint());
// Recipient checks
AccountData orphanedAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
checkAccountDataRestored("recipient", preCombineAliceData, orphanedAliceData);
assertTrue(alice.canMint(false));
assertTrue(alice.canMint());
}
}
@@ -202,8 +202,8 @@ public class TransferPrivsTests extends Common {
TestAccount chloe = Common.getTestAccount(repository, "chloe");
TestAccount dilbert = Common.getTestAccount(repository, "dilbert");
assertTrue(dilbert.canMint(false));
assertFalse(chloe.canMint(false));
assertTrue(dilbert.canMint());
assertFalse(chloe.canMint());
// COMBINE DILBERT INTO CHLOE
@@ -225,16 +225,16 @@ public class TransferPrivsTests extends Common {
// Post-combine sender checks
checkSenderPostTransfer(post1stCombineDilbertData);
assertFalse(dilbert.canMint(false));
assertFalse(dilbert.canMint());
// Post-combine recipient checks
checkRecipientPostTransfer(pre1stCombineDilbertData, pre1stCombineChloeData, post1stCombineChloeData, expectedPost1stCombineLevel);
assertTrue(chloe.canMint(false));
assertTrue(chloe.canMint());
// COMBINE ALICE INTO CHLOE
assertTrue(alice.canMint(false));
assertTrue(chloe.canMint(false));
assertTrue(alice.canMint());
assertTrue(chloe.canMint());
// Alice needs to mint enough blocks to surpass Chloe's level post-combine
final int expectedPost2ndCombineLevel = chloe.getLevel() + 1;
@@ -254,11 +254,11 @@ public class TransferPrivsTests extends Common {
// Post-combine sender checks
checkSenderPostTransfer(post2ndCombineAliceData);
assertFalse(alice.canMint(false));
assertFalse(alice.canMint());
// Post-combine recipient checks
checkRecipientPostTransfer(pre2ndCombineAliceData, pre2ndCombineChloeData, post2ndCombineChloeData, expectedPost2ndCombineLevel);
assertTrue(chloe.canMint(false));
assertTrue(chloe.canMint());
// Orphan 2nd combine
BlockUtils.orphanLastBlock(repository);
@@ -266,12 +266,12 @@ public class TransferPrivsTests extends Common {
// Sender checks
AccountData orphanedAliceData = repository.getAccountRepository().getAccount(alice.getAddress());
checkAccountDataRestored("sender", pre2ndCombineAliceData, orphanedAliceData);
assertTrue(alice.canMint(false));
assertTrue(alice.canMint());
// Recipient checks
AccountData orphanedChloeData = repository.getAccountRepository().getAccount(chloe.getAddress());
checkAccountDataRestored("recipient", pre2ndCombineChloeData, orphanedChloeData);
assertTrue(chloe.canMint(false));
assertTrue(chloe.canMint());
// Orphan 1nd combine
BlockUtils.orphanToBlock(repository, pre1stCombineBlockHeight);
@@ -279,7 +279,7 @@ public class TransferPrivsTests extends Common {
// Sender checks
AccountData orphanedDilbertData = repository.getAccountRepository().getAccount(dilbert.getAddress());
checkAccountDataRestored("sender", pre1stCombineDilbertData, orphanedDilbertData);
assertTrue(dilbert.canMint(false));
assertTrue(dilbert.canMint());
// Recipient checks
orphanedChloeData = repository.getAccountRepository().getAccount(chloe.getAddress());
@@ -287,7 +287,7 @@ public class TransferPrivsTests extends Common {
// Chloe canMint() would return true here due to Alice-Chloe reward-share minting at top of method, so undo that minting by orphaning back to block 1
BlockUtils.orphanToBlock(repository, 1);
assertFalse(chloe.canMint(false));
assertFalse(chloe.canMint());
}
}

View File

@@ -96,12 +96,7 @@
"arbitraryOptionalFeeTimestamp": 0,
"unconfirmableRewardSharesHeight": 99999999,
"disableTransferPrivsTimestamp": 9999999999500,
"enableTransferPrivsTimestamp": 9999999999950,
"cancelSellNameValidationTimestamp": 9999999999999,
"disableRewardshareHeight": 9999999999990,
"enableRewardshareHeight": 9999999999999,
"onlyMintWithNameHeight": 9999999999999,
"groupMemberCheckHeight": 9999999999999
"enableTransferPrivsTimestamp": 9999999999950
},
"genesisInfo": {
"version": 4,