mirror of
https://github.com/Qortal/qortal.git
synced 2025-02-11 17:55:50 +00:00
Merge pull request #238 from AlphaX-Qortal/master
Added real address to API results - Currently the address shown in the API results when querying blocks, shows an address formed by the 'reward share public key', this address is not useful for viewing, as it is not the address utilized for QORT. This change makes it so the 'real' Qortal address is displayed instead of this useless address. Thanks @AlphaX-Qortal Added group member check to validations - validation fixes. Network changes - Moved unnecessary 'we already have connection' messages from info logging to debug. Updated minPeerVersion default to current release version. (4.6.5). Updated default peer list. Updated syntax. Updated formatting. Updated dependencies Thanks @AlphaX-Qortal
This commit is contained in:
commit
a8d73926b3
4
pom.xml
4
pom.xml
@ -22,7 +22,7 @@
|
|||||||
<dagger.version>1.2.2</dagger.version>
|
<dagger.version>1.2.2</dagger.version>
|
||||||
<extendedset.version>0.12.3</extendedset.version>
|
<extendedset.version>0.12.3</extendedset.version>
|
||||||
<git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
|
<git-commit-id-plugin.version>4.9.10</git-commit-id-plugin.version>
|
||||||
<grpc.version>1.68.1</grpc.version>
|
<grpc.version>1.68.2</grpc.version>
|
||||||
<guava.version>33.3.1-jre</guava.version>
|
<guava.version>33.3.1-jre</guava.version>
|
||||||
<hamcrest-library.version>2.2</hamcrest-library.version>
|
<hamcrest-library.version>2.2</hamcrest-library.version>
|
||||||
<homoglyph.version>1.2.1</homoglyph.version>
|
<homoglyph.version>1.2.1</homoglyph.version>
|
||||||
@ -35,7 +35,7 @@
|
|||||||
<jetty.version>9.4.56.v20240826</jetty.version>
|
<jetty.version>9.4.56.v20240826</jetty.version>
|
||||||
<json-simple.version>1.1.1</json-simple.version>
|
<json-simple.version>1.1.1</json-simple.version>
|
||||||
<json.version>20240303</json.version>
|
<json.version>20240303</json.version>
|
||||||
<jsoup.version>1.18.1</jsoup.version>
|
<jsoup.version>1.18.3</jsoup.version>
|
||||||
<junit-jupiter-engine.version>5.11.0-M2</junit-jupiter-engine.version>
|
<junit-jupiter-engine.version>5.11.0-M2</junit-jupiter-engine.version>
|
||||||
<lifecycle-mapping.version>1.0.0</lifecycle-mapping.version>
|
<lifecycle-mapping.version>1.0.0</lifecycle-mapping.version>
|
||||||
<log4j.version>2.23.1</log4j.version>
|
<log4j.version>2.23.1</log4j.version>
|
||||||
|
@ -349,10 +349,28 @@ public class Account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 repository
|
||||||
* @param rewardSharePublicKey
|
* @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+
|
* @return 0+
|
||||||
* @throws DataException
|
* @throws DataException
|
||||||
*/
|
*/
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
package org.qortal.api.model;
|
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.XmlAccessType;
|
||||||
import javax.xml.bind.annotation.XmlAccessorType;
|
import javax.xml.bind.annotation.XmlAccessorType;
|
||||||
|
import javax.xml.bind.annotation.XmlElement;
|
||||||
|
|
||||||
// All properties to be converted to JSON via JAXB
|
// All properties to be converted to JSON via JAXB
|
||||||
@XmlAccessorType(XmlAccessType.FIELD)
|
@XmlAccessorType(XmlAccessType.FIELD)
|
||||||
@ -47,4 +53,31 @@ public class ApiOnlineAccount {
|
|||||||
return this.recipientAddress;
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import java.math.BigInteger;
|
|||||||
public class BlockMintingInfo {
|
public class BlockMintingInfo {
|
||||||
|
|
||||||
public byte[] minterPublicKey;
|
public byte[] minterPublicKey;
|
||||||
|
public String minterAddress;
|
||||||
public int minterLevel;
|
public int minterLevel;
|
||||||
public int onlineAccountsCount;
|
public int onlineAccountsCount;
|
||||||
public BigDecimal maxDistance;
|
public BigDecimal maxDistance;
|
||||||
@ -19,5 +20,4 @@ public class BlockMintingInfo {
|
|||||||
|
|
||||||
public BlockMintingInfo() {
|
public BlockMintingInfo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -542,6 +542,7 @@ public class BlocksResource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String minterAddress = Account.getRewardShareMintingAddress(repository, blockData.getMinterPublicKey());
|
||||||
int minterLevel = Account.getRewardShareEffectiveMintingLevel(repository, blockData.getMinterPublicKey());
|
int minterLevel = Account.getRewardShareEffectiveMintingLevel(repository, blockData.getMinterPublicKey());
|
||||||
if (minterLevel == 0)
|
if (minterLevel == 0)
|
||||||
// This may be unavailable when requesting a trimmed block
|
// This may be unavailable when requesting a trimmed block
|
||||||
@ -554,6 +555,7 @@ public class BlocksResource {
|
|||||||
|
|
||||||
BlockMintingInfo blockMintingInfo = new BlockMintingInfo();
|
BlockMintingInfo blockMintingInfo = new BlockMintingInfo();
|
||||||
blockMintingInfo.minterPublicKey = blockData.getMinterPublicKey();
|
blockMintingInfo.minterPublicKey = blockData.getMinterPublicKey();
|
||||||
|
blockMintingInfo.minterAddress = minterAddress;
|
||||||
blockMintingInfo.minterLevel = minterLevel;
|
blockMintingInfo.minterLevel = minterLevel;
|
||||||
blockMintingInfo.onlineAccountsCount = blockData.getOnlineAccountsCount();
|
blockMintingInfo.onlineAccountsCount = blockData.getOnlineAccountsCount();
|
||||||
blockMintingInfo.maxDistance = new BigDecimal(block.MAX_DISTANCE);
|
blockMintingInfo.maxDistance = new BigDecimal(block.MAX_DISTANCE);
|
||||||
@ -887,5 +889,4 @@ public class BlocksResource {
|
|||||||
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
throw ApiExceptionFactory.INSTANCE.createException(request, ApiError.REPOSITORY_ISSUE, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -145,7 +145,7 @@ public class Block {
|
|||||||
|
|
||||||
private final Account recipientAccount;
|
private final Account recipientAccount;
|
||||||
private final AccountData recipientAccountData;
|
private final AccountData recipientAccountData;
|
||||||
|
|
||||||
final BlockChain blockChain = BlockChain.getInstance();
|
final BlockChain blockChain = BlockChain.getInstance();
|
||||||
|
|
||||||
ExpandedAccount(Repository repository, RewardShareData rewardShareData) throws DataException {
|
ExpandedAccount(Repository repository, RewardShareData rewardShareData) throws DataException {
|
||||||
@ -414,6 +414,21 @@ public class Block {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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()) {
|
if (onlineAccounts.isEmpty()) {
|
||||||
LOGGER.debug("No online accounts - not even our own?");
|
LOGGER.debug("No online accounts - not even our own?");
|
||||||
return null;
|
return null;
|
||||||
@ -721,19 +736,19 @@ public class Block {
|
|||||||
List<ExpandedAccount> expandedAccounts = new ArrayList<>();
|
List<ExpandedAccount> expandedAccounts = new ArrayList<>();
|
||||||
|
|
||||||
for (RewardShareData rewardShare : this.cachedOnlineRewardShares) {
|
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() && isMinterGroupMember)
|
||||||
expandedAccounts.add(new ExpandedAccount(repository, rewardShare));
|
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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.cachedExpandedAccounts = expandedAccounts;
|
this.cachedExpandedAccounts = expandedAccounts;
|
||||||
|
LOGGER.trace(() -> String.format("Online reward-shares after expanded accounts %s", this.cachedOnlineRewardShares));
|
||||||
|
|
||||||
return this.cachedExpandedAccounts;
|
return this.cachedExpandedAccounts;
|
||||||
}
|
}
|
||||||
@ -1143,8 +1158,17 @@ public class Block {
|
|||||||
if (this.getBlockData().getHeight() >= BlockChain.getInstance().getOnlineAccountMinterLevelValidationHeight()) {
|
if (this.getBlockData().getHeight() >= BlockChain.getInstance().getOnlineAccountMinterLevelValidationHeight()) {
|
||||||
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
List<ExpandedAccount> expandedAccounts = this.getExpandedAccounts();
|
||||||
for (ExpandedAccount account : expandedAccounts) {
|
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)
|
if (account.getMintingAccount().getEffectiveMintingLevel() == 0)
|
||||||
return ValidationResult.ONLINE_ACCOUNTS_INVALID;
|
return ValidationResult.ONLINE_ACCOUNTS_INVALID;
|
||||||
|
|
||||||
|
if (this.getBlockData().getHeight() >= BlockChain.getInstance().getFixBatchRewardHeight()) {
|
||||||
|
if (!isMinterGroupMember)
|
||||||
|
return ValidationResult.ONLINE_ACCOUNTS_INVALID;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1273,6 +1297,7 @@ public class Block {
|
|||||||
|
|
||||||
// Online Accounts
|
// Online Accounts
|
||||||
ValidationResult onlineAccountsResult = this.areOnlineAccountsValid();
|
ValidationResult onlineAccountsResult = this.areOnlineAccountsValid();
|
||||||
|
LOGGER.trace("Accounts valid = {}", onlineAccountsResult);
|
||||||
if (onlineAccountsResult != ValidationResult.OK)
|
if (onlineAccountsResult != ValidationResult.OK)
|
||||||
return onlineAccountsResult;
|
return onlineAccountsResult;
|
||||||
|
|
||||||
@ -1361,7 +1386,7 @@ public class Block {
|
|||||||
// Check transaction can even be processed
|
// Check transaction can even be processed
|
||||||
validationResult = transaction.isProcessable();
|
validationResult = transaction.isProcessable();
|
||||||
if (validationResult != Transaction.ValidationResult.OK) {
|
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;
|
return ValidationResult.TRANSACTION_INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1562,6 +1587,7 @@ public class Block {
|
|||||||
this.blockData.setHeight(blockchainHeight + 1);
|
this.blockData.setHeight(blockchainHeight + 1);
|
||||||
|
|
||||||
LOGGER.trace(() -> String.format("Processing block %d", this.blockData.getHeight()));
|
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) {
|
if (this.blockData.getHeight() > 1) {
|
||||||
|
|
||||||
@ -2280,7 +2306,6 @@ public class Block {
|
|||||||
// Select the correct set of share bins based on block height
|
// Select the correct set of share bins based on block height
|
||||||
List<AccountLevelShareBin> accountLevelShareBinsForBlock = (this.blockData.getHeight() >= BlockChain.getInstance().getSharesByLevelV2Height()) ?
|
List<AccountLevelShareBin> accountLevelShareBinsForBlock = (this.blockData.getHeight() >= BlockChain.getInstance().getSharesByLevelV2Height()) ?
|
||||||
BlockChain.getInstance().getAccountLevelShareBinsV2() : BlockChain.getInstance().getAccountLevelShareBinsV1();
|
BlockChain.getInstance().getAccountLevelShareBinsV2() : BlockChain.getInstance().getAccountLevelShareBinsV1();
|
||||||
|
|
||||||
// Determine reward candidates based on account level
|
// Determine reward candidates based on account level
|
||||||
// This needs a deep copy, so the shares can be modified when tiers aren't activated yet
|
// This needs a deep copy, so the shares can be modified when tiers aren't activated yet
|
||||||
List<AccountLevelShareBin> accountLevelShareBins = new ArrayList<>();
|
List<AccountLevelShareBin> accountLevelShareBins = new ArrayList<>();
|
||||||
@ -2570,9 +2595,11 @@ public class Block {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int minterLevel = Account.getRewardShareEffectiveMintingLevel(this.repository, this.getMinter().getPublicKey());
|
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("======= BLOCK %d (%.8s) =======", this.getBlockData().getHeight(), Base58.encode(this.getSignature())));
|
||||||
LOGGER.debug(String.format("Timestamp: %d", this.getBlockData().getTimestamp()));
|
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("Minter level: %d", minterLevel));
|
||||||
LOGGER.debug(String.format("Online accounts: %d", this.getBlockData().getOnlineAccountsCount()));
|
LOGGER.debug(String.format("Online accounts: %d", this.getBlockData().getOnlineAccountsCount()));
|
||||||
LOGGER.debug(String.format("AT count: %d", this.getBlockData().getATCount()));
|
LOGGER.debug(String.format("AT count: %d", this.getBlockData().getATCount()));
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
package org.qortal.data.block;
|
package org.qortal.data.block;
|
||||||
|
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
import org.qortal.account.Account;
|
||||||
import org.qortal.block.BlockChain;
|
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.settings.Settings;
|
||||||
import org.qortal.utils.NTP;
|
import org.qortal.utils.NTP;
|
||||||
|
|
||||||
@ -224,7 +227,7 @@ public class BlockData implements Serializable {
|
|||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTrimmed() {
|
public boolean isTrimmed() {
|
||||||
long onlineAccountSignaturesTrimmedTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime();
|
long onlineAccountSignaturesTrimmedTimestamp = NTP.getTime() - BlockChain.getInstance().getOnlineAccountSignaturesMaxLifetime();
|
||||||
long currentTrimmableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime();
|
long currentTrimmableTimestamp = NTP.getTime() - Settings.getInstance().getAtStatesMaxLifetime();
|
||||||
@ -232,11 +235,31 @@ public class BlockData implements Serializable {
|
|||||||
return blockTimestamp < onlineAccountSignaturesTrimmedTimestamp && blockTimestamp < currentTrimmableTimestamp;
|
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
|
// JAXB special
|
||||||
|
|
||||||
@XmlElement(name = "minterAddress")
|
@XmlElement(name = "minterAddress")
|
||||||
protected String getMinterAddress() {
|
protected String getMinterAddress() {
|
||||||
return Crypto.toAddress(this.minterPublicKey);
|
return getMinterAddressFromPublicKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@XmlElement(name = "minterLevel")
|
||||||
|
protected int getMinterLevel() {
|
||||||
|
return getMinterLevelFromPublicKey();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ public enum Handshake {
|
|||||||
|
|
||||||
String versionString = helloMessage.getVersionString();
|
String versionString = helloMessage.getVersionString();
|
||||||
|
|
||||||
Matcher matcher = peer.VERSION_PATTERN.matcher(versionString);
|
Matcher matcher = Peer.VERSION_PATTERN.matcher(versionString);
|
||||||
if (!matcher.lookingAt()) {
|
if (!matcher.lookingAt()) {
|
||||||
LOGGER.debug(() -> String.format("Peer %s sent invalid HELLO version string '%s'", peer, versionString));
|
LOGGER.debug(() -> String.format("Peer %s sent invalid HELLO version string '%s'", peer, versionString));
|
||||||
return null;
|
return null;
|
||||||
@ -71,7 +71,7 @@ public enum Handshake {
|
|||||||
|
|
||||||
// Ensure the peer is running at least the version specified in MIN_PEER_VERSION
|
// Ensure the peer is running at least the version specified in MIN_PEER_VERSION
|
||||||
if (!peer.isAtLeastVersion(MIN_PEER_VERSION)) {
|
if (!peer.isAtLeastVersion(MIN_PEER_VERSION)) {
|
||||||
LOGGER.debug(String.format("Ignoring peer %s because it is on an old version (%s)", peer, versionString));
|
LOGGER.debug("Ignoring peer {} because it is on an old version ({})", peer, versionString);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +79,7 @@ public enum Handshake {
|
|||||||
// Ensure the peer is running at least the minimum version allowed for connections
|
// Ensure the peer is running at least the minimum version allowed for connections
|
||||||
final String minPeerVersion = Settings.getInstance().getMinPeerVersion();
|
final String minPeerVersion = Settings.getInstance().getMinPeerVersion();
|
||||||
if (!peer.isAtLeastVersion(minPeerVersion)) {
|
if (!peer.isAtLeastVersion(minPeerVersion)) {
|
||||||
LOGGER.debug(String.format("Ignoring peer %s because it is on an old version (%s)", peer, versionString));
|
LOGGER.debug("Ignoring peer {} because it is on an old version ({})", peer, versionString);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -106,7 +106,7 @@ public enum Handshake {
|
|||||||
byte[] peersPublicKey = challengeMessage.getPublicKey();
|
byte[] peersPublicKey = challengeMessage.getPublicKey();
|
||||||
byte[] peersChallenge = challengeMessage.getChallenge();
|
byte[] peersChallenge = challengeMessage.getChallenge();
|
||||||
|
|
||||||
// If public key matches our public key then we've connected to self
|
// If public key matches our public key, then we've connected to self
|
||||||
byte[] ourPublicKey = Network.getInstance().getOurPublicKey();
|
byte[] ourPublicKey = Network.getInstance().getOurPublicKey();
|
||||||
if (Arrays.equals(ourPublicKey, peersPublicKey)) {
|
if (Arrays.equals(ourPublicKey, peersPublicKey)) {
|
||||||
// If outgoing connection then record destination as self so we don't try again
|
// If outgoing connection then record destination as self so we don't try again
|
||||||
@ -121,11 +121,11 @@ public enum Handshake {
|
|||||||
peer.disconnect("failed to send CHALLENGE to self");
|
peer.disconnect("failed to send CHALLENGE to self");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We return CHALLENGE here to prevent us from closing connection. Closing
|
* We return the CHALLENGE here to prevent us from closing the connection.
|
||||||
* connection currently preempts remote end from reading any pending messages,
|
* Closing the connection currently preempts the remote end from reading any pending messages,
|
||||||
* specifically the CHALLENGE message we just sent above. When our 'remote'
|
* specifically the CHALLENGE message we just sent above. When our 'remote'
|
||||||
* outbound counterpart reads our message, they will close both connections.
|
* outbound counterpart reads our message, they will close both connections.
|
||||||
* Failing that, our connection will timeout or a future handshake error will
|
* Failing that, our connection will time out or a future handshake error will
|
||||||
* occur.
|
* occur.
|
||||||
*/
|
*/
|
||||||
return CHALLENGE;
|
return CHALLENGE;
|
||||||
@ -135,7 +135,7 @@ public enum Handshake {
|
|||||||
// Are we already connected to this peer?
|
// Are we already connected to this peer?
|
||||||
Peer existingPeer = Network.getInstance().getHandshakedPeerWithPublicKey(peersPublicKey);
|
Peer existingPeer = Network.getInstance().getHandshakedPeerWithPublicKey(peersPublicKey);
|
||||||
if (existingPeer != null) {
|
if (existingPeer != null) {
|
||||||
LOGGER.info(() -> String.format("We already have a connection with peer %s - discarding", peer));
|
LOGGER.debug(() -> String.format("We already have a connection with peer %s - discarding", peer));
|
||||||
// Handshake failure - caller will deal with disconnect
|
// Handshake failure - caller will deal with disconnect
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -148,7 +148,7 @@ public enum Handshake {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(Peer peer) {
|
public void action(Peer peer) {
|
||||||
// Send challenge
|
// Send a challenge
|
||||||
byte[] publicKey = Network.getInstance().getOurPublicKey();
|
byte[] publicKey = Network.getInstance().getOurPublicKey();
|
||||||
byte[] challenge = peer.getOurChallenge();
|
byte[] challenge = peer.getOurChallenge();
|
||||||
|
|
||||||
@ -254,16 +254,17 @@ public enum Handshake {
|
|||||||
|
|
||||||
private static final Logger LOGGER = LogManager.getLogger(Handshake.class);
|
private static final Logger LOGGER = LogManager.getLogger(Handshake.class);
|
||||||
|
|
||||||
/** Maximum allowed difference between peer's reported timestamp and when they connected, in milliseconds. */
|
/** The Maximum allowed difference between peer's reported timestamp and when they connected, in milliseconds. */
|
||||||
private static final long MAX_TIMESTAMP_DELTA = 30 * 1000L; // ms
|
private static final long MAX_TIMESTAMP_DELTA = 30 * 1000L; // ms
|
||||||
|
|
||||||
private static final long PEER_VERSION_131 = 0x0100030001L;
|
private static final long PEER_VERSION_131 = 0x0100030001L;
|
||||||
|
|
||||||
/** Minimum peer version that we are allowed to communicate with */
|
/** Minimum peer version that we are allowed to communicate with */
|
||||||
private static final String MIN_PEER_VERSION = "4.1.1";
|
private static final String MIN_PEER_VERSION = "4.6.5";
|
||||||
|
|
||||||
private static final int POW_BUFFER_SIZE_PRE_131 = 8 * 1024 * 1024; // bytes
|
private static final int POW_BUFFER_SIZE_PRE_131 = 8 * 1024 * 1024; // bytes
|
||||||
private static final int POW_DIFFICULTY_PRE_131 = 8; // leading zero bits
|
private static final int POW_DIFFICULTY_PRE_131 = 8; // leading zero bits
|
||||||
|
|
||||||
// Can always be made harder in the future...
|
// Can always be made harder in the future...
|
||||||
private static final int POW_BUFFER_SIZE_POST_131 = 2 * 1024 * 1024; // bytes
|
private static final int POW_BUFFER_SIZE_POST_131 = 2 * 1024 * 1024; // bytes
|
||||||
private static final int POW_DIFFICULTY_POST_131 = 2; // leading zero bits
|
private static final int POW_DIFFICULTY_POST_131 = 2; // leading zero bits
|
||||||
@ -275,12 +276,11 @@ public enum Handshake {
|
|||||||
|
|
||||||
public final MessageType expectedMessageType;
|
public final MessageType expectedMessageType;
|
||||||
|
|
||||||
private Handshake(MessageType expectedMessageType) {
|
Handshake(MessageType expectedMessageType) {
|
||||||
this.expectedMessageType = expectedMessageType;
|
this.expectedMessageType = expectedMessageType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract Handshake onMessage(Peer peer, Message message);
|
public abstract Handshake onMessage(Peer peer, Message message);
|
||||||
|
|
||||||
public abstract void action(Peer peer);
|
public abstract void action(Peer peer);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -80,7 +80,7 @@ public class Network {
|
|||||||
"node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk", "node22.qortal.org",
|
"node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk", "node22.qortal.org",
|
||||||
"cinfu1.crowetic.com", "node.cwd.systems", "bootstrap.cwd.systems", "node1.qortalnodes.live",
|
"cinfu1.crowetic.com", "node.cwd.systems", "bootstrap.cwd.systems", "node1.qortalnodes.live",
|
||||||
"node2.qortalnodes.live", "node3.qortalnodes.live", "node4.qortalnodes.live", "node5.qortalnodes.live",
|
"node2.qortalnodes.live", "node3.qortalnodes.live", "node4.qortalnodes.live", "node5.qortalnodes.live",
|
||||||
"node6.qortalnodes.live", "node7.qortalnodes.live", "node8.qortalnodes.live"
|
"node.qortalnodes.live", "qortex.live",
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final long NETWORK_EPC_KEEPALIVE = 5L; // seconds
|
private static final long NETWORK_EPC_KEEPALIVE = 5L; // seconds
|
||||||
@ -149,7 +149,7 @@ public class Network {
|
|||||||
|
|
||||||
private final Lock mergePeersLock = new ReentrantLock();
|
private final Lock mergePeersLock = new ReentrantLock();
|
||||||
|
|
||||||
private List<String> ourExternalIpAddressHistory = new ArrayList<>();
|
private final List<String> ourExternalIpAddressHistory = new ArrayList<>();
|
||||||
private String ourExternalIpAddress = null;
|
private String ourExternalIpAddress = null;
|
||||||
private int ourExternalPort = Settings.getInstance().getListenPort();
|
private int ourExternalPort = Settings.getInstance().getListenPort();
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ public class Network {
|
|||||||
ExecutorService networkExecutor = new ThreadPoolExecutor(2,
|
ExecutorService networkExecutor = new ThreadPoolExecutor(2,
|
||||||
Settings.getInstance().getMaxNetworkThreadPoolSize(),
|
Settings.getInstance().getMaxNetworkThreadPoolSize(),
|
||||||
NETWORK_EPC_KEEPALIVE, TimeUnit.SECONDS,
|
NETWORK_EPC_KEEPALIVE, TimeUnit.SECONDS,
|
||||||
new SynchronousQueue<Runnable>(),
|
new SynchronousQueue<>(),
|
||||||
new NamedThreadFactory("Network-EPC", Settings.getInstance().getNetworkThreadPriority()));
|
new NamedThreadFactory("Network-EPC", Settings.getInstance().getNetworkThreadPriority()));
|
||||||
networkEPC = new NetworkProcessor(networkExecutor);
|
networkEPC = new NetworkProcessor(networkExecutor);
|
||||||
}
|
}
|
||||||
@ -314,7 +314,7 @@ public class Network {
|
|||||||
|
|
||||||
public List<Peer> getImmutableConnectedDataPeers() {
|
public List<Peer> getImmutableConnectedDataPeers() {
|
||||||
return this.getImmutableConnectedPeers().stream()
|
return this.getImmutableConnectedPeers().stream()
|
||||||
.filter(p -> p.isDataPeer())
|
.filter(Peer::isDataPeer)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -346,7 +346,7 @@ public class Network {
|
|||||||
public boolean requestDataFromPeer(String peerAddressString, byte[] signature) {
|
public boolean requestDataFromPeer(String peerAddressString, byte[] signature) {
|
||||||
if (peerAddressString != null) {
|
if (peerAddressString != null) {
|
||||||
PeerAddress peerAddress = PeerAddress.fromString(peerAddressString);
|
PeerAddress peerAddress = PeerAddress.fromString(peerAddressString);
|
||||||
PeerData peerData = null;
|
PeerData peerData;
|
||||||
|
|
||||||
// Reuse an existing PeerData instance if it's already in the known peers list
|
// Reuse an existing PeerData instance if it's already in the known peers list
|
||||||
synchronized (this.allKnownPeers) {
|
synchronized (this.allKnownPeers) {
|
||||||
@ -370,9 +370,9 @@ public class Network {
|
|||||||
|
|
||||||
// Check if we're already connected to and handshaked with this peer
|
// Check if we're already connected to and handshaked with this peer
|
||||||
Peer connectedPeer = this.getImmutableConnectedPeers().stream()
|
Peer connectedPeer = this.getImmutableConnectedPeers().stream()
|
||||||
.filter(p -> p.getPeerData().getAddress().equals(peerAddress))
|
.filter(p -> p.getPeerData().getAddress().equals(peerAddress))
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
|
|
||||||
boolean isConnected = (connectedPeer != null);
|
boolean isConnected = (connectedPeer != null);
|
||||||
|
|
||||||
@ -710,7 +710,7 @@ public class Network {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Peer getConnectablePeer(final Long now) throws InterruptedException {
|
private Peer getConnectablePeer(final Long now) {
|
||||||
// We can't block here so use tryRepository(). We don't NEED to connect a new peer.
|
// We can't block here so use tryRepository(). We don't NEED to connect a new peer.
|
||||||
try (Repository repository = RepositoryManager.tryRepository()) {
|
try (Repository repository = RepositoryManager.tryRepository()) {
|
||||||
if (repository == null) {
|
if (repository == null) {
|
||||||
@ -807,7 +807,7 @@ public class Network {
|
|||||||
// Find peers that have reached their maximum connection age, and disconnect them
|
// Find peers that have reached their maximum connection age, and disconnect them
|
||||||
List<Peer> peersToDisconnect = this.getImmutableConnectedPeers().stream()
|
List<Peer> peersToDisconnect = this.getImmutableConnectedPeers().stream()
|
||||||
.filter(peer -> !peer.isSyncInProgress())
|
.filter(peer -> !peer.isSyncInProgress())
|
||||||
.filter(peer -> peer.hasReachedMaxConnectionAge())
|
.filter(Peer::hasReachedMaxConnectionAge)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
if (peersToDisconnect != null && !peersToDisconnect.isEmpty()) {
|
if (peersToDisconnect != null && !peersToDisconnect.isEmpty()) {
|
||||||
@ -996,9 +996,9 @@ public class Network {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add to per-message thread count (first initializing to 0 if not already present)
|
// Add to per-message thread count (first initializing to 0 if not already present)
|
||||||
threadsPerMessageType.computeIfAbsent(message.getType(), key -> 0);
|
threadsPerMessageType.putIfAbsent(message.getType(), 0);
|
||||||
threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value + 1);
|
threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value + 1);
|
||||||
|
|
||||||
// Add to total thread count
|
// Add to total thread count
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
totalThreadCount++;
|
totalThreadCount++;
|
||||||
@ -1037,7 +1037,7 @@ public class Network {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove from per-message thread count (first initializing to 0 if not already present)
|
// Remove from per-message thread count (first initializing to 0 if not already present)
|
||||||
threadsPerMessageType.computeIfAbsent(message.getType(), key -> 0);
|
threadsPerMessageType.putIfAbsent(message.getType(), 0);
|
||||||
threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value - 1);
|
threadsPerMessageType.computeIfPresent(message.getType(), (key, value) -> value - 1);
|
||||||
|
|
||||||
// Remove from total thread count
|
// Remove from total thread count
|
||||||
@ -1135,7 +1135,7 @@ public class Network {
|
|||||||
Peer existingPeer = getHandshakedPeerWithPublicKey(peer.getPeersPublicKey());
|
Peer existingPeer = getHandshakedPeerWithPublicKey(peer.getPeersPublicKey());
|
||||||
// NOTE: actual object reference compare, not Peer.equals()
|
// NOTE: actual object reference compare, not Peer.equals()
|
||||||
if (existingPeer != peer) {
|
if (existingPeer != peer) {
|
||||||
LOGGER.info("[{}] We already have a connection with peer {} - discarding",
|
LOGGER.debug("[{}] We already have a connection with peer {} - discarding",
|
||||||
peer.getPeerConnectionId(), peer);
|
peer.getPeerConnectionId(), peer);
|
||||||
peer.disconnect("existing connection");
|
peer.disconnect("existing connection");
|
||||||
return;
|
return;
|
||||||
@ -1216,29 +1216,7 @@ public class Network {
|
|||||||
* Returns PEERS message made from peers we've connected to recently, and this node's details
|
* Returns PEERS message made from peers we've connected to recently, and this node's details
|
||||||
*/
|
*/
|
||||||
public Message buildPeersMessage(Peer peer) {
|
public Message buildPeersMessage(Peer peer) {
|
||||||
List<PeerData> knownPeers = this.getAllKnownPeers();
|
final var knownPeers = getPeerData();
|
||||||
|
|
||||||
// Filter out peers that we've not connected to ever or within X milliseconds
|
|
||||||
final long connectionThreshold = NTP.getTime() - RECENT_CONNECTION_THRESHOLD;
|
|
||||||
Predicate<PeerData> notRecentlyConnected = peerData -> {
|
|
||||||
final Long lastAttempted = peerData.getLastAttempted();
|
|
||||||
final Long lastConnected = peerData.getLastConnected();
|
|
||||||
|
|
||||||
if (lastAttempted == null || lastConnected == null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastConnected < lastAttempted) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lastConnected < connectionThreshold) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
knownPeers.removeIf(notRecentlyConnected);
|
|
||||||
|
|
||||||
List<PeerAddress> peerAddresses = new ArrayList<>();
|
List<PeerAddress> peerAddresses = new ArrayList<>();
|
||||||
|
|
||||||
@ -1262,6 +1240,29 @@ public class Network {
|
|||||||
return new PeersV2Message(peerAddresses);
|
return new PeersV2Message(peerAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<PeerData> getPeerData() {
|
||||||
|
List<PeerData> knownPeers = this.getAllKnownPeers();
|
||||||
|
|
||||||
|
// Filter out peers that we've not connected to ever or within X milliseconds
|
||||||
|
final long connectionThreshold = NTP.getTime() - RECENT_CONNECTION_THRESHOLD;
|
||||||
|
Predicate<PeerData> notRecentlyConnected = peerData -> {
|
||||||
|
final Long lastAttempted = peerData.getLastAttempted();
|
||||||
|
final Long lastConnected = peerData.getLastConnected();
|
||||||
|
|
||||||
|
if (lastAttempted == null || lastConnected == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastConnected < lastAttempted) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastConnected < connectionThreshold;
|
||||||
|
};
|
||||||
|
knownPeers.removeIf(notRecentlyConnected);
|
||||||
|
return knownPeers;
|
||||||
|
}
|
||||||
|
|
||||||
/** Builds either (legacy) HeightV2Message or (newer) BlockSummariesV2Message, depending on peer version.
|
/** Builds either (legacy) HeightV2Message or (newer) BlockSummariesV2Message, depending on peer version.
|
||||||
*
|
*
|
||||||
* @return Message, or null if DataException was thrown.
|
* @return Message, or null if DataException was thrown.
|
||||||
@ -1328,7 +1329,7 @@ public class Network {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
String host = parts[0];
|
String host = parts[0];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
InetAddress addr = InetAddress.getByName(host);
|
InetAddress addr = InetAddress.getByName(host);
|
||||||
if (addr.isAnyLocalAddress() || addr.isSiteLocalAddress()) {
|
if (addr.isAnyLocalAddress() || addr.isSiteLocalAddress()) {
|
||||||
@ -1369,12 +1370,12 @@ public class Network {
|
|||||||
for (int i = size-1; i >= 0; i--) {
|
for (int i = size-1; i >= 0; i--) {
|
||||||
String reading = ipAddressHistory.get(i);
|
String reading = ipAddressHistory.get(i);
|
||||||
if (lastReading != null) {
|
if (lastReading != null) {
|
||||||
if (Objects.equals(reading, lastReading)) {
|
if (Objects.equals(reading, lastReading)) {
|
||||||
consecutiveReadings++;
|
consecutiveReadings++;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
consecutiveReadings = 0;
|
consecutiveReadings = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastReading = reading;
|
lastReading = reading;
|
||||||
}
|
}
|
||||||
@ -1515,12 +1516,8 @@ public class Network {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (peerData.getLastConnected() == null
|
return peerData.getLastConnected() == null
|
||||||
|| peerData.getLastConnected() > now - OLD_PEER_CONNECTION_PERIOD) {
|
|| peerData.getLastConnected() > now - OLD_PEER_CONNECTION_PERIOD;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Disregard peers that are NOT 'old'
|
// Disregard peers that are NOT 'old'
|
||||||
@ -1655,7 +1652,7 @@ public class Network {
|
|||||||
|
|
||||||
// Stop processing threads
|
// Stop processing threads
|
||||||
try {
|
try {
|
||||||
if (!this.networkEPC.shutdown(5000)) {
|
if (!this.networkEPC.shutdown(10000)) {
|
||||||
LOGGER.warn("Network threads failed to terminate");
|
LOGGER.warn("Network threads failed to terminate");
|
||||||
}
|
}
|
||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
@ -1667,5 +1664,4 @@ public class Network {
|
|||||||
peer.shutdown();
|
peer.shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user