diff --git a/src/main/java/org/qortal/account/Account.java b/src/main/java/org/qortal/account/Account.java index 537f07881..99fa52176 100644 --- a/src/main/java/org/qortal/account/Account.java +++ b/src/main/java/org/qortal/account/Account.java @@ -349,10 +349,28 @@ public int getEffectiveMintingLevel() throws DataException { } /** - * Returns 'effective' minting level, or zero if reward-share does not exist. + * Returns reward-share minting address, or unknown if reward-share does not exist. * * @param repository * @param rewardSharePublicKey + * @return address or unknown + * @throws DataException + */ + public static String getRewardShareMintingAddress(Repository repository, byte[] rewardSharePublicKey) throws DataException { + // Find actual minter address + RewardShareData rewardShareData = repository.getAccountRepository().getRewardShare(rewardSharePublicKey); + + if (rewardShareData == null) + return "Unknown"; + + return rewardShareData.getMinter(); + } + + /** + * Returns 'effective' minting level, or zero if reward-share does not exist. + * + * @param repository + * @param rewardSharePublicKey * @return 0+ * @throws DataException */ diff --git a/src/main/java/org/qortal/api/model/ApiOnlineAccount.java b/src/main/java/org/qortal/api/model/ApiOnlineAccount.java index 08b697aa5..e26eb8164 100644 --- a/src/main/java/org/qortal/api/model/ApiOnlineAccount.java +++ b/src/main/java/org/qortal/api/model/ApiOnlineAccount.java @@ -1,7 +1,13 @@ package org.qortal.api.model; +import org.qortal.account.Account; +import org.qortal.repository.DataException; +import org.qortal.repository.RepositoryManager; +import org.qortal.repository.Repository; + import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; // All properties to be converted to JSON via JAXB @XmlAccessorType(XmlAccessType.FIELD) @@ -47,4 +53,31 @@ public String getRecipientAddress() { return this.recipientAddress; } + public int getMinterLevelFromPublicKey() { + try (final Repository repository = RepositoryManager.getRepository()) { + return Account.getRewardShareEffectiveMintingLevel(repository, this.rewardSharePublicKey); + } catch (DataException e) { + return 0; + } + } + + public boolean getIsMember() { + try (final Repository repository = RepositoryManager.getRepository()) { + return repository.getGroupRepository().memberExists(694, getMinterAddress()); + } catch (DataException e) { + return false; + } + } + + // JAXB special + + @XmlElement(name = "minterLevel") + protected int getMinterLevel() { + return getMinterLevelFromPublicKey(); + } + + @XmlElement(name = "isMinterMember") + protected boolean getMinterMember() { + return getIsMember(); + } } diff --git a/src/main/java/org/qortal/api/model/BlockMintingInfo.java b/src/main/java/org/qortal/api/model/BlockMintingInfo.java index f84e179e1..02765a89b 100644 --- a/src/main/java/org/qortal/api/model/BlockMintingInfo.java +++ b/src/main/java/org/qortal/api/model/BlockMintingInfo.java @@ -9,6 +9,7 @@ public class BlockMintingInfo { public byte[] minterPublicKey; + public String minterAddress; public int minterLevel; public int onlineAccountsCount; public BigDecimal maxDistance; @@ -19,5 +20,4 @@ public class BlockMintingInfo { public BlockMintingInfo() { } - } diff --git a/src/main/java/org/qortal/api/resource/BlocksResource.java b/src/main/java/org/qortal/api/resource/BlocksResource.java index 01d8d2ab2..ff0bb9791 100644 --- a/src/main/java/org/qortal/api/resource/BlocksResource.java +++ b/src/main/java/org/qortal/api/resource/BlocksResource.java @@ -542,6 +542,7 @@ public BlockMintingInfo getBlockMintingInfoByHeight(@PathParam("height") int hei } } + String minterAddress = Account.getRewardShareMintingAddress(repository, blockData.getMinterPublicKey()); int minterLevel = Account.getRewardShareEffectiveMintingLevel(repository, blockData.getMinterPublicKey()); if (minterLevel == 0) // This may be unavailable when requesting a trimmed block @@ -554,6 +555,7 @@ public BlockMintingInfo getBlockMintingInfoByHeight(@PathParam("height") int hei BlockMintingInfo blockMintingInfo = new BlockMintingInfo(); blockMintingInfo.minterPublicKey = blockData.getMinterPublicKey(); + blockMintingInfo.minterAddress = minterAddress; blockMintingInfo.minterLevel = minterLevel; blockMintingInfo.onlineAccountsCount = blockData.getOnlineAccountsCount(); blockMintingInfo.maxDistance = new BigDecimal(block.MAX_DISTANCE); @@ -887,5 +889,4 @@ public List getBlockSummaries( throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e); } } - } diff --git a/src/main/java/org/qortal/block/Block.java b/src/main/java/org/qortal/block/Block.java index 918a20ae9..c9353d700 100644 --- a/src/main/java/org/qortal/block/Block.java +++ b/src/main/java/org/qortal/block/Block.java @@ -145,7 +145,7 @@ public static class ExpandedAccount { private final Account recipientAccount; private final AccountData recipientAccountData; - + final BlockChain blockChain = BlockChain.getInstance(); ExpandedAccount(Repository repository, RewardShareData rewardShareData) throws DataException { @@ -414,6 +414,21 @@ else if (isOnlineAccountsBlock(height)) { }); } + // After feature trigger, remove any online accounts that are not minter group member + if (height >= BlockChain.getInstance().getGroupMemberCheckHeight()) { + onlineAccounts.removeIf(a -> { + try { + int groupId = BlockChain.getInstance().getMintingGroupId(); + String address = Account.getRewardShareMintingAddress(repository, a.getPublicKey()); + boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); + return !isMinterGroupMember; + } catch (DataException e) { + // Something went wrong, so remove the account + return true; + } + }); + } + if (onlineAccounts.isEmpty()) { LOGGER.debug("No online accounts - not even our own?"); return null; @@ -721,19 +736,19 @@ public List getExpandedAccounts() throws DataException { List expandedAccounts = new ArrayList<>(); for (RewardShareData rewardShare : this.cachedOnlineRewardShares) { - if (this.getBlockData().getHeight() < BlockChain.getInstance().getFixBatchRewardHeight()) { + int groupId = BlockChain.getInstance().getMintingGroupId(); + String address = rewardShare.getMinter(); + boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); + + 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)); - } - } - } + if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight() && isMinterGroupMember) + expandedAccounts.add(new ExpandedAccount(repository, rewardShare)); + } this.cachedExpandedAccounts = expandedAccounts; + LOGGER.trace(() -> String.format("Online reward-shares after expanded accounts %s", this.cachedOnlineRewardShares)); return this.cachedExpandedAccounts; } @@ -1143,8 +1158,17 @@ public ValidationResult areOnlineAccountsValid() throws DataException { if (this.getBlockData().getHeight() >= BlockChain.getInstance().getOnlineAccountMinterLevelValidationHeight()) { List expandedAccounts = this.getExpandedAccounts(); for (ExpandedAccount account : expandedAccounts) { + int groupId = BlockChain.getInstance().getMintingGroupId(); + String address = account.getMintingAccount().getAddress(); + boolean isMinterGroupMember = repository.getGroupRepository().memberExists(groupId, address); + if (account.getMintingAccount().getEffectiveMintingLevel() == 0) return ValidationResult.ONLINE_ACCOUNTS_INVALID; + + if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight()) { + if (!isMinterGroupMember) + return ValidationResult.ONLINE_ACCOUNTS_INVALID; + } } } @@ -1273,6 +1297,7 @@ public ValidationResult isValid() throws DataException { // Online Accounts ValidationResult onlineAccountsResult = this.areOnlineAccountsValid(); + LOGGER.trace("Accounts valid = {}", onlineAccountsResult); if (onlineAccountsResult != ValidationResult.OK) return onlineAccountsResult; @@ -1361,7 +1386,7 @@ private ValidationResult areTransactionsValid() throws DataException { // Check transaction can even be processed validationResult = transaction.isProcessable(); if (validationResult != Transaction.ValidationResult.OK) { - LOGGER.info(String.format("Error during transaction validation, tx %s: %s", Base58.encode(transactionData.getSignature()), validationResult.name())); + LOGGER.debug(String.format("Error during transaction validation, tx %s: %s", Base58.encode(transactionData.getSignature()), validationResult.name())); return ValidationResult.TRANSACTION_INVALID; } @@ -1562,6 +1587,7 @@ public void process() throws DataException { this.blockData.setHeight(blockchainHeight + 1); LOGGER.trace(() -> String.format("Processing block %d", this.blockData.getHeight())); + LOGGER.trace(() -> String.format("Online Reward Shares in process %s", this.cachedOnlineRewardShares)); if (this.blockData.getHeight() > 1) { @@ -2280,7 +2306,6 @@ protected List determineBlockRewardCandidates(boolean isPr // Select the correct set of share bins based on block height List accountLevelShareBinsForBlock = (this.blockData.getHeight() >= BlockChain.getInstance().getSharesByLevelV2Height()) ? BlockChain.getInstance().getAccountLevelShareBinsV2() : BlockChain.getInstance().getAccountLevelShareBinsV1(); - // Determine reward candidates based on account level // This needs a deep copy, so the shares can be modified when tiers aren't activated yet List accountLevelShareBins = new ArrayList<>(); @@ -2570,9 +2595,11 @@ private void logDebugInfo() { return; int minterLevel = Account.getRewardShareEffectiveMintingLevel(this.repository, this.getMinter().getPublicKey()); + String minterAddress = Account.getRewardShareMintingAddress(this.repository, this.getMinter().getPublicKey()); LOGGER.debug(String.format("======= BLOCK %d (%.8s) =======", this.getBlockData().getHeight(), Base58.encode(this.getSignature()))); LOGGER.debug(String.format("Timestamp: %d", this.getBlockData().getTimestamp())); + LOGGER.debug(String.format("Minter address: %s", minterAddress)); LOGGER.debug(String.format("Minter level: %d", minterLevel)); LOGGER.debug(String.format("Online accounts: %d", this.getBlockData().getOnlineAccountsCount())); LOGGER.debug(String.format("AT count: %d", this.getBlockData().getATCount())); diff --git a/src/main/java/org/qortal/data/block/BlockData.java b/src/main/java/org/qortal/data/block/BlockData.java index 34df0f9a4..7e2a18725 100644 --- a/src/main/java/org/qortal/data/block/BlockData.java +++ b/src/main/java/org/qortal/data/block/BlockData.java @@ -1,8 +1,11 @@ package org.qortal.data.block; import com.google.common.primitives.Bytes; +import org.qortal.account.Account; import org.qortal.block.BlockChain; -import org.qortal.crypto.Crypto; +import org.qortal.repository.DataException; +import org.qortal.repository.Repository; +import org.qortal.repository.RepositoryManager; import org.qortal.settings.Settings; import org.qortal.utils.NTP; @@ -224,7 +227,7 @@ public int getOnlineAccountsSignaturesCount() { } return 0; } - + public boolean isTrimmed() { long onlineAccountSignaturesTrimmedTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime(); long currentTrimmableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime(); @@ -232,11 +235,31 @@ public boolean isTrimmed() { return blockTimestamp < onlineAccountSignaturesTrimmedTimestamp && blockTimestamp < currentTrimmableTimestamp; } + public String getMinterAddressFromPublicKey() { + try (final Repository repository = RepositoryManager.getRepository()) { + return Account.getRewardShareMintingAddress(repository, this.minterPublicKey); + } catch (DataException e) { + return "Unknown"; + } + } + + public int getMinterLevelFromPublicKey() { + try (final Repository repository = RepositoryManager.getRepository()) { + return Account.getRewardShareEffectiveMintingLevel(repository, this.minterPublicKey); + } catch (DataException e) { + return 0; + } + } + // JAXB special @XmlElement(name = "minterAddress") protected String getMinterAddress() { - return Crypto.toAddress(this.minterPublicKey); + return getMinterAddressFromPublicKey(); } + @XmlElement(name = "minterLevel") + protected int getMinterLevel() { + return getMinterLevelFromPublicKey(); + } } diff --git a/start.sh b/start.sh index cc80dceb6..889370262 100755 --- a/start.sh +++ b/start.sh @@ -33,8 +33,13 @@ fi # Limits Java JVM stack size and maximum heap usage. # Comment out for bigger systems, e.g. non-routers # or when API documentation is enabled -# Uncomment (remove '#' sign) line below if your system has less than 12GB of RAM for optimal RAM defaults -JVM_MEMORY_ARGS="-Xss256m -XX:+UseSerialGC" +# JAVA MEMORY SETTINGS BELOW - These settings are essentially optimized default settings. +# Combined with the latest changes on the Qortal Core in version 4.6.6 and beyond, +# should give a dramatic increase In performance due to optimized Garbage Collection. +# These memory arguments should work on machines with as little as 6GB of RAM. +# If you want to run on a machine with less than 6GB of RAM, it is suggested to increase the '50' below to '75' +# The Qortal Core will utilize only as much RAM as it needs, but up-to the amount set in percentage below. +JVM_MEMORY_ARGS="-XX:MaxRAMPercentage=50 -XX:+UseG1GC -Xss1024k" # Although java.net.preferIPv4Stack is supposed to be false # by default in Java 11, on some platforms (e.g. FreeBSD 12),