Merge branch 'master' into sync-multiple-blocks

# Conflicts:
#	src/main/java/org/qortal/settings/Settings.java
This commit is contained in:
CalDescent 2021-06-20 08:16:29 +01:00
commit a48a9592d0
14 changed files with 144 additions and 25 deletions

2
.gitignore vendored
View File

@ -21,7 +21,7 @@
/qortal.iml
.DS_Store
/src/main/resources/resources
/src/main/resources/log*.properties
/*.jar
/run.pid
/run.log
/WindowsInstaller/Install Files/qortal.jar

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<DOCUMENT Type="Advanced Installer" CreateVersion="14.9" version="18.2" Modules="enterprise" RootPath="." Language="en_GB" Id="{713E21E0-28FC-422F-8A95-823D01A5F80B}">
<COMPONENT cid="caphyon.advinst.msicomp.MsiPropsComponent">
<ROW Property="AI_BITMAP_DISPLAY_MODE" Value="0"/>
@ -17,10 +17,10 @@
<ROW Property="Manufacturer" Value="Qortal"/>
<ROW Property="MsiLogging" MultiBuildValue="DefaultBuild:vp"/>
<ROW Property="NTP_GOOD" Value="false"/>
<ROW Property="ProductCode" Value="1033:{259B155C-9CE9-4EF7-AA89-CFF2F118196B} 1049:{61A8713C-B9C5-489D-92FF-0A578697E1C2} 2052:{C59AF8F7-326D-4C43-A02D-7F4BAB56861A} 2057:{58DEB696-96AD-49DC-AD18-02B7B04FDAE3} " Type="16"/>
<ROW Property="ProductCode" Value="1033:{EB5562C3-664E-4A8B-8907-6D2033B98836} 1049:{36D0E774-B970-4A13-BCC4-1BA6AB3B2633} 2052:{AF6B6B44-9404-403A-B00F-B7110C28E453} 2057:{68BB9EB8-5991-42E5-841C-E76ACE51166D} " Type="16"/>
<ROW Property="ProductLanguage" Value="2057"/>
<ROW Property="ProductName" Value="Qortal"/>
<ROW Property="ProductVersion" Value="1.5.3" Type="32"/>
<ROW Property="ProductVersion" Value="1.5.4" Type="32"/>
<ROW Property="RECONFIG_NTP" Value="true"/>
<ROW Property="REMOVE_BLOCKCHAIN" Value="YES" Type="4"/>
<ROW Property="REPAIR_BLOCKCHAIN" Value="YES" Type="4"/>
@ -212,7 +212,7 @@
<ROW Component="ADDITIONAL_LICENSE_INFO_71" ComponentId="{12A3ADBE-BB7A-496C-8869-410681E6232F}" Directory_="jdk.zipfs_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_71" Type="0"/>
<ROW Component="ADDITIONAL_LICENSE_INFO_8" ComponentId="{D53AD95E-CF96-4999-80FC-5812277A7456}" Directory_="java.naming_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_8" Type="0"/>
<ROW Component="ADDITIONAL_LICENSE_INFO_9" ComponentId="{6B7EA9B0-5D17-47A8-B78C-FACE86D15E01}" Directory_="java.net.http_Dir" Attributes="0" KeyPath="ADDITIONAL_LICENSE_INFO_9" Type="0"/>
<ROW Component="AI_CustomARPName" ComponentId="{5B3B8F5D-88BD-495F-9DD4-F749533C1F89}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
<ROW Component="AI_CustomARPName" ComponentId="{83DFE721-3F68-4ABE-8697-8EC3A91EEB8A}" Directory_="APPDIR" Attributes="260" KeyPath="DisplayName" Options="1"/>
<ROW Component="AI_ExePath" ComponentId="{3644948D-AE0B-41BB-9FAF-A79E70490A08}" Directory_="APPDIR" Attributes="260" KeyPath="AI_ExePath"/>
<ROW Component="APPDIR" ComponentId="{680DFDDE-3FB4-47A5-8FF5-934F576C6F91}" Directory_="APPDIR" Attributes="0"/>
<ROW Component="AccessBridgeCallbacks.h" ComponentId="{288055D1-1062-47A3-AA44-5601B4E38AED}" Directory_="bridge_Dir" Attributes="0" KeyPath="AccessBridgeCallbacks.h" Type="0"/>

View File

@ -12,7 +12,7 @@ configured paths, or create a dummy `D:` drive with the expected layout.
Typical build procedure:
* Overwrite the `qortal.jar` file in `Install-Files\`
* Place the `qortal.jar` file in `Install-Files\`
* Open AdvancedInstaller with qortal.aip file
* If releasing a new version, change version number in:
+ "Product Information" side menu

View File

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.qortal</groupId>
<artifactId>qortal</artifactId>
<version>1.5.3</version>
<version>1.5.4</version>
<packaging>jar</packaging>
<properties>
<skipTests>true</skipTests>

View File

@ -738,6 +738,7 @@ public class Controller extends Thread {
hasStatusChanged = true;
}
}
peer.setSyncInProgress(true);
if (hasStatusChanged)
updateSysTray();
@ -817,6 +818,7 @@ public class Controller extends Thread {
return syncResult;
} finally {
isSynchronizing = false;
peer.setSyncInProgress(false);
}
}

View File

@ -68,6 +68,9 @@ public class Synchronizer {
// Keep track of the size of the last re-org, so it can be logged
private int lastReorgSize;
private static Synchronizer instance;
public enum SynchronizationResult {
@ -515,9 +518,22 @@ public class Synchronizer {
byte[] peersLastBlockSignature = peerChainTipData.getLastBlockSignature();
byte[] ourLastBlockSignature = ourLatestBlockData.getSignature();
LOGGER.debug(String.format("Synchronizing with peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
String syncString = String.format("Synchronizing with peer %s at height %d, sig %.8s, ts %d; our height %d, sig %.8s, ts %d", peer,
peerHeight, Base58.encode(peersLastBlockSignature), peer.getChainTipData().getLastBlockTimestamp(),
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp()));
ourInitialHeight, Base58.encode(ourLastBlockSignature), ourLatestBlockData.getTimestamp());
// If our latest block is very old, we should log that we're attempting to sync with a peer
// Otherwise, it can appear as though nothing is happening for a while after launch
final Long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
if (minLatestBlockTimestamp != null && ourLatestBlockData.getTimestamp() < minLatestBlockTimestamp) {
LOGGER.info(syncString);
}
else {
LOGGER.debug(syncString);
}
// Reset last re-org size as we are starting a new sync round
this.lastReorgSize = 0;
List<BlockSummaryData> peerBlockSummaries = new ArrayList<>();
SynchronizationResult findCommonBlockResult = fetchSummariesFromCommonBlock(repository, peer, ourInitialHeight, force, peerBlockSummaries, true);
@ -576,10 +592,19 @@ public class Synchronizer {
// Commit
repository.saveChanges();
// Create string for logging
final BlockData newLatestBlockData = repository.getBlockRepository().getLastBlock();
LOGGER.info(String.format("Synchronized with peer %s to height %d, sig %.8s, ts: %d", peer,
String syncLog = String.format("Synchronized with peer %s to height %d, sig %.8s, ts: %d", peer,
newLatestBlockData.getHeight(), Base58.encode(newLatestBlockData.getSignature()),
newLatestBlockData.getTimestamp()));
newLatestBlockData.getTimestamp());
// Append re-org info
if (this.lastReorgSize > 0) {
syncLog = syncLog.concat(String.format(", size: %d", this.lastReorgSize));
}
// Log sync info
LOGGER.info(syncLog);
return SynchronizationResult.OK;
} finally {
@ -933,6 +958,7 @@ public class Synchronizer {
// Unwind to common block (unless common block is our latest block)
int ourHeight = ourInitialHeight;
LOGGER.debug(String.format("Orphaning blocks back to common block height %d, sig %.8s. Our height: %d", commonBlockHeight, commonBlockSig58, ourHeight));
int reorgSize = ourHeight - commonBlockHeight;
BlockData orphanBlockData = repository.getBlockRepository().fromHeight(ourInitialHeight);
while (ourHeight > commonBlockHeight) {
@ -981,6 +1007,7 @@ public class Synchronizer {
Controller.getInstance().onNewBlock(newBlock.getBlockData());
}
this.lastReorgSize = reorgSize;
return SynchronizationResult.OK;
}

View File

@ -67,7 +67,11 @@ public class Bitcoin extends Bitcoiny {
new Server("192.166.219.200", Server.ConnectionType.SSL, 50002),
new Server("2ex.digitaleveryware.com", Server.ConnectionType.SSL, 50002),
new Server("dxm.no-ip.biz", Server.ConnectionType.SSL, 50002),
new Server("caleb.vegas", Server.ConnectionType.SSL, 50002));
new Server("caleb.vegas", Server.ConnectionType.SSL, 50002),
new Server("ecdsa.net", Server.ConnectionType.SSL, 110),
new Server("electrum.hsmiths.com", Server.ConnectionType.SSL, 995),
new Server("elec.luggs.co", Server.ConnectionType.SSL, 443),
new Server("btc.smsys.me", Server.ConnectionType.SSL, 995));
}
@Override

View File

@ -51,7 +51,10 @@ public class Litecoin extends Bitcoiny {
new Server("ltc.rentonisk.com", Server.ConnectionType.TCP, 50001),
new Server("ltc.rentonisk.com", Server.ConnectionType.SSL, 50002),
new Server("electrum-ltc.petrkr.net", Server.ConnectionType.SSL, 60002),
new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022));
new Server("ltc.litepay.ch", Server.ConnectionType.SSL, 50022),
new Server("electrum-ltc-bysh.me", Server.ConnectionType.TCP, 50002),
new Server("electrum.jochen-hoenicke.de", Server.ConnectionType.TCP, 50005),
new Server("node.ispol.sk", Server.ConnectionType.TCP, 50004));
}
@Override

View File

@ -72,7 +72,8 @@ public class Network {
private static final String[] INITIAL_PEERS = new String[]{
"node1.qortal.org", "node2.qortal.org", "node3.qortal.org", "node4.qortal.org", "node5.qortal.org",
"node6.qortal.org", "node7.qortal.org", "node8.qortal.org", "node9.qortal.org", "node10.qortal.org",
"node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk"
"node.qortal.ru", "node2.qortal.ru", "node3.qortal.ru", "node.qortal.uk", "node22.qortal.org",
"cinfu1.crowetic.com", "node.cwd.systems"
};
private static final long NETWORK_EPC_KEEPALIVE = 10L; // seconds
@ -80,6 +81,8 @@ public class Network {
public static final int MAX_SIGNATURES_PER_REPLY = 500;
public static final int MAX_BLOCK_SUMMARIES_PER_REPLY = 500;
private static final long DISCONNECTION_CHECK_INTERVAL = 10 * 1000L; // milliseconds
// Generate our node keys / ID
private final Ed25519PrivateKeyParameters edPrivateKeyParams = new Ed25519PrivateKeyParameters(new SecureRandom());
private final Ed25519PublicKeyParameters edPublicKeyParams = edPrivateKeyParams.generatePublicKey();
@ -89,6 +92,8 @@ public class Network {
private final int minOutboundPeers;
private final int maxPeers;
private long nextDisconnectionCheck = 0L;
private final List<PeerData> allKnownPeers = new ArrayList<>();
private final List<Peer> connectedPeers = new ArrayList<>();
private final List<PeerAddress> selfPeers = new ArrayList<>();
@ -611,6 +616,8 @@ public class Network {
// Don't consider already connected peers (resolved address match)
// XXX This might be too slow if we end up waiting a long time for hostnames to resolve via DNS
peers.removeIf(isResolvedAsConnectedPeer);
this.checkLongestConnection(now);
}
// Any left?
@ -668,6 +675,29 @@ public class Network {
return null;
}
private void checkLongestConnection(Long now) {
if (now == null || now < nextDisconnectionCheck) {
return;
}
// Find peers that have reached their maximum connection age, and disconnect them
List<Peer> peersToDisconnect = this.connectedPeers.stream()
.filter(peer -> !peer.isSyncInProgress())
.filter(peer -> peer.hasReachedMaxConnectionAge())
.collect(Collectors.toList());
if (peersToDisconnect != null && peersToDisconnect.size() > 0) {
for (Peer peer : peersToDisconnect) {
LOGGER.info("Forcing disconnection of peer {} because connection age ({} ms) " +
"has reached the maximum ({} ms)", peer, peer.getConnectionAge(), peer.getMaxConnectionAge());
peer.disconnect("Connection age too old");
}
}
// Check again after a minimum fixed interval
nextDisconnectionCheck = now + DISCONNECTION_CHECK_INTERVAL;
}
// Peer callbacks
protected void wakeupChannelSelector() {

View File

@ -84,6 +84,7 @@ public class Peer {
private Handshake handshakeStatus = Handshake.STARTED;
private volatile boolean handshakeMessagePending = false;
private long handshakeComplete = -1L;
private long maxConnectionAge = 0L;
/**
* Timestamp of when socket was accepted, or connected.
@ -101,6 +102,8 @@ public class Peer {
byte[] ourChallenge;
private boolean syncInProgress = false;
// Versioning
public static final Pattern VERSION_PATTERN = Pattern.compile(Controller.VERSION_PREFIX
+ "(\\d{1,3})\\.(\\d{1,5})\\.(\\d{1,5})");
@ -197,10 +200,24 @@ public class Peer {
this.handshakeStatus = handshakeStatus;
if (handshakeStatus.equals(Handshake.COMPLETED)) {
this.handshakeComplete = System.currentTimeMillis();
this.generateRandomMaxConnectionAge();
}
}
}
private void generateRandomMaxConnectionAge() {
// Retrieve the min and max connection time from the settings, and calculate the range
final int minPeerConnectionTime = Settings.getInstance().getMinPeerConnectionTime();
final int maxPeerConnectionTime = Settings.getInstance().getMaxPeerConnectionTime();
final int peerConnectionTimeRange = maxPeerConnectionTime - minPeerConnectionTime;
// Generate a random number between the min and the max
Random random = new Random();
this.maxConnectionAge = (random.nextInt(peerConnectionTimeRange) + minPeerConnectionTime) * 1000L;
LOGGER.debug(String.format("Generated max connection age for peer %s. Min: %ds, max: %ds, range: %ds, random max: %dms", this, minPeerConnectionTime, maxPeerConnectionTime, peerConnectionTimeRange, this.maxConnectionAge));
}
protected void resetHandshakeMessagePending() {
this.handshakeMessagePending = false;
}
@ -330,6 +347,14 @@ public class Peer {
}
}
public boolean isSyncInProgress() {
return this.syncInProgress;
}
public void setSyncInProgress(boolean syncInProgress) {
this.syncInProgress = syncInProgress;
}
@Override
public String toString() {
// Easier, and nicer output, than peer.getRemoteSocketAddress()
@ -810,4 +835,12 @@ public class Peer {
}
return handshakeComplete;
}
public long getMaxConnectionAge() {
return maxConnectionAge;
}
public boolean hasReachedMaxConnectionAge() {
return this.getConnectionAge() > this.getMaxConnectionAge();
}
}

View File

@ -133,6 +133,11 @@ public class Settings {
* If false, sync will be blocked both ways, and they will not appear in the peers list */
private boolean allowConnectionsWithOlderPeerVersions = true;
/** Minimum time (in seconds) that we should attempt to remain connected to a peer for */
private int minPeerConnectionTime = 2 * 60; // seconds
/** Maximum time (in seconds) that we should attempt to remain connected to a peer for */
private int maxPeerConnectionTime = 20 * 60; // seconds
/** Whether to sync multiple blocks at once in normal operation */
private boolean fastSyncEnabled = true;
/** Whether to sync multiple blocks at once when the peer has a different chain */
@ -436,6 +441,10 @@ public class Settings {
public boolean getAllowConnectionsWithOlderPeerVersions() { return this.allowConnectionsWithOlderPeerVersions; }
public int getMinPeerConnectionTime() { return this.minPeerConnectionTime; }
public int getMaxPeerConnectionTime() { return this.maxPeerConnectionTime; }
public String getBlockchainConfig() {
return this.blockchainConfig;
}

View File

@ -1,7 +1,8 @@
#!/usr/bin/env bash
# Change this to where AdvancedInstaller outputs built EXE installers
WINDOWS_INSTALLER_DIR=/home/transfer/Qortal/Qortal-SetupFiles
SCRIPT_DIR=$(dirname $(realpath "$0"))
WINDOWS_INSTALLER_DIR="${SCRIPT_DIR}/../WindowsInstaller/Qortal-SetupFiles"
set -e
shopt -s expand_aliases
@ -16,6 +17,16 @@ saved_pwd=$PWD
alias SHA256='(sha256 -q || sha256sum | cut -d" " -f1) 2>/dev/null'
function 3hash {
local zip_src=$1
local md5hash=$(md5 ${zip_src} | awk '{ print $NF }')
local sha1hash=$(shasum ${zip_src} | awk '{ print $1 }')
local sha256hash=$(sha256sum ${zip_src} | awk '{ print $1 }')
echo "\`MD5: ${md5hash}\`"
echo "\`SHA1: ${sha1hash}\`"
echo "\`SHA256: ${sha256hash}\`"
}
# Check we are within a git repo
git_dir=$( git rev-parse --show-toplevel )
if [ -z "${git_dir}" ]; then
@ -27,7 +38,7 @@ fi
cd ${git_dir}
# Check we are in 'master' branch
# branch_name=$( git symbolic-ref -q HEAD )
# branch_name=$( git symbolic-ref -q HEAD ) || echo "Cannot determine branch name" && exit 1
# branch_name=${branch_name##refs/heads/}
# if [ "${branch_name}" != "master" ]; then
# echo "Unexpected current branch '${branch_name}' - expecting 'master'"
@ -56,7 +67,7 @@ git_url=https://github.com/${git_url##*:}
git_url=${git_url%%.git}
# Check for EXE
exe=${project^}-${git_tag#v}.exe
exe=${project}-${git_tag#v}.exe
exe_src="${WINDOWS_INSTALLER_DIR}/${exe}"
if [ ! -r "${exe_src}" ]; then
echo "Cannot find EXE installer at ${exe_src}"

View File

@ -21,13 +21,13 @@ fi
cd ${git_dir}
# Check we are in 'master' branch
branch_name=$( git symbolic-ref -q HEAD )
branch_name=${branch_name##refs/heads/}
echo "Current git branch: ${branch_name}"
if [ "${branch_name}" != "master" ]; then
echo "Unexpected current branch '${branch_name}' - expecting 'master'"
exit 1
fi
# branch_name=$( git symbolic-ref -q HEAD ) || echo "Cannot determine branch name" && exit 1
# branch_name=${branch_name##refs/heads/}
# echo "Current git branch: ${branch_name}"
# if [ "${branch_name}" != "master" ]; then
# echo "Unexpected current branch '${branch_name}' - expecting 'master'"
# exit 1
# fi
# Determine project name
project=$( perl -n -e 'if (m/<artifactId>(\w+)<.artifactId>/) { print $1; exit }' pom.xml $)
@ -60,7 +60,7 @@ git show HEAD:stop.sh > ${build_dir}/stop.sh
printf "{\n}\n" > ${build_dir}/settings.json
touch -d ${commit_ts%%+??:??} ${build_dir} ${build_dir}/*
gtouch -d ${commit_ts%%+??:??} ${build_dir} ${build_dir}/*
rm -f ${saved_pwd}/${project}.zip
(cd ${build_dir}/..; 7z a -r -tzip ${saved_pwd}/${project}-${git_tag#v}.zip ${project}/)