diff --git a/src/main/java/org/qora/block/Block.java b/src/main/java/org/qora/block/Block.java
index e5788f0e..db2aaf60 100644
--- a/src/main/java/org/qora/block/Block.java
+++ b/src/main/java/org/qora/block/Block.java
@@ -730,6 +730,20 @@ public class Block {
}
}
+ /**
+ * Returns timestamp based on previous block and this block's generator.
+ *
+ * For qora-core, we'll using the minimum from BlockChain config.
+ */
+ public static long calcMinimumTimestamp(BlockData parentBlockData, byte[] generatorPublicKey) {
+ long minBlockTime = BlockChain.getInstance().getMinBlockTime(); // seconds
+ return parentBlockData.getTimestamp() + (minBlockTime * 1000L);
+ }
+
+ public long calcMinimumTimestamp(BlockData parentBlockData) {
+ return calcMinimumTimestamp(parentBlockData, this.generator.getPublicKey());
+ }
+
/**
* Recalculate block's generator and transactions signatures, thus giving block full signature.
*
diff --git a/src/main/java/org/qora/block/BlockGenerator.java b/src/main/java/org/qora/block/BlockGenerator.java
index a1a998a2..3534aa6c 100644
--- a/src/main/java/org/qora/block/BlockGenerator.java
+++ b/src/main/java/org/qora/block/BlockGenerator.java
@@ -18,6 +18,7 @@ import org.qora.data.account.ProxyForgerData;
import org.qora.data.block.BlockData;
import org.qora.data.transaction.TransactionData;
import org.qora.network.Network;
+import org.qora.network.Peer;
import org.qora.repository.BlockRepository;
import org.qora.repository.DataException;
import org.qora.repository.Repository;
@@ -78,16 +79,30 @@ public class BlockGenerator extends Thread {
return;
}
+ List peers = Network.getInstance().getUniqueHandshakedPeers();
+ BlockData lastBlockData = blockRepository.getLastBlock();
+
+ // Disregard peers that have "misbehaved" recently
+ peers.removeIf(Controller.hasPeerMisbehaved);
+
// Don't generate if we don't have enough connected peers as where would the transactions/consensus come from?
- if (Network.getInstance().getUniqueHandshakedPeers().size() < Settings.getInstance().getMinBlockchainPeers())
+ if (peers.size() < Settings.getInstance().getMinBlockchainPeers())
continue;
- // Don't generate if it looks like we're behind
- if (!Controller.getInstance().isUpToDate())
+ final long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
+
+ // Disregard peers that don't have a recent block
+ peers.removeIf(peer -> peer.getPeerData().getLastBlockTimestamp() == null || peer.getPeerData().getLastBlockTimestamp() < minLatestBlockTimestamp);
+
+ // If we have any peers with a recent block, but our latest block isn't recent
+ // then we need to synchronize instead of generating.
+ if (!peers.isEmpty() && lastBlockData.getTimestamp() < minLatestBlockTimestamp)
continue;
+ // There are no peers with a recent block and/or our latest block is recent
+ // so go ahead and generate a block if possible.
+
// Check blockchain hasn't changed
- BlockData lastBlockData = blockRepository.getLastBlock();
if (previousBlock == null || !Arrays.equals(previousBlock.getSignature(), lastBlockData.getSignature())) {
previousBlock = new Block(repository, lastBlockData);
newBlocks.clear();
@@ -95,6 +110,10 @@ public class BlockGenerator extends Thread {
// Do we need to build any potential new blocks?
List forgingAccountsData = repository.getAccountRepository().getForgingAccounts();
+ // No forging accounts?
+ if (forgingAccountsData.isEmpty())
+ continue;
+
List forgingAccounts = forgingAccountsData.stream().map(accountData -> new PrivateKeyAccount(repository, accountData.getSeed())).collect(Collectors.toList());
// Discard accounts we have blocks for
@@ -112,6 +131,10 @@ public class BlockGenerator extends Thread {
}
}
+ // No potential block candidates?
+ if (newBlocks.isEmpty())
+ continue;
+
// Make sure we're the only thread modifying the blockchain
ReentrantLock blockchainLock = Controller.getInstance().getBlockchainLock();
if (!blockchainLock.tryLock())
diff --git a/src/main/java/org/qora/controller/Controller.java b/src/main/java/org/qora/controller/Controller.java
index 636ccad6..780072f2 100644
--- a/src/main/java/org/qora/controller/Controller.java
+++ b/src/main/java/org/qora/controller/Controller.java
@@ -1008,39 +1008,30 @@ public class Controller extends Thread {
/** Returns whether we think our node has up-to-date blockchain based on our info about other peers. */
public boolean isUpToDate() {
- // Is our blockchain too old?
final long minLatestBlockTimestamp = getMinimumLatestBlockTimestamp();
BlockData latestBlockData = getChainTip();
+
+ // Is our blockchain too old?
if (latestBlockData.getTimestamp() < minLatestBlockTimestamp)
return false;
List peers = Network.getInstance().getUniqueHandshakedPeers();
+ // Disregard peers that have "misbehaved" recently
+ peers.removeIf(hasPeerMisbehaved);
+
// Check we have enough peers to potentially synchronize/generator
if (peers.size() < Settings.getInstance().getMinBlockchainPeers())
return false;
- // Disregard peers that have "misbehaved" recently
- peers.removeIf(hasPeerMisbehaved);
-
- // Disregard peers with unknown height, lower height or same height and same block signature (unless we don't have their block signature)
- // peers.removeIf(hasShorterBlockchain());
-
- // Disregard peers that within 1 block of our height (actually ourHeight + 1)
- // final int maxHeight = getChainHeight() + 1;
- // peers.removeIf(peer -> peer.getPeerData().getLastHeight() <= maxHeight );
-
// Disregard peers that don't have a recent block
peers.removeIf(peer -> peer.getPeerData().getLastBlockTimestamp() == null || peer.getPeerData().getLastBlockTimestamp() < minLatestBlockTimestamp);
- // If we have any peers left, then they would be candidates for synchronization therefore we're not up to date.
- // return peers.isEmpty();
-
// If we don't have any peers left then can't synchronize, therefore consider ourself not up to date
return !peers.isEmpty();
}
- public long getMinimumLatestBlockTimestamp() {
+ public static long getMinimumLatestBlockTimestamp() {
return NTP.getTime() - BlockChain.getInstance().getMaxBlockTime() * 1000L * MAX_BLOCKCHAIN_TIP_AGE;
}
diff --git a/src/main/java/org/qora/controller/Synchronizer.java b/src/main/java/org/qora/controller/Synchronizer.java
index 0d602e46..e220a6eb 100644
--- a/src/main/java/org/qora/controller/Synchronizer.java
+++ b/src/main/java/org/qora/controller/Synchronizer.java
@@ -9,7 +9,6 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.qora.block.Block;
import org.qora.block.Block.ValidationResult;
-import org.qora.block.BlockChain;
import org.qora.data.block.BlockData;
import org.qora.data.network.BlockSummaryData;
import org.qora.data.transaction.TransactionData;
@@ -28,7 +27,6 @@ import org.qora.repository.Repository;
import org.qora.repository.RepositoryManager;
import org.qora.transaction.Transaction;
import org.qora.transaction.Transaction.ApprovalStatus;
-import org.qora.utils.NTP;
public class Synchronizer {
@@ -38,8 +36,6 @@ public class Synchronizer {
private static final int MAXIMUM_BLOCK_STEP = 500;
private static final int MAXIMUM_HEIGHT_DELTA = 300; // XXX move to blockchain config?
private static final int MAXIMUM_COMMON_DELTA = 60; // XXX move to blockchain config?
- /** Maximum age for our latest block before we consider ditching our fork. */
- private static final long MAXIMUM_TIP_AGE = BlockChain.getInstance().getMaxBlockTime() * 1000L * 10; // XXX move to blockchain config?
private static final int SYNC_BATCH_SIZE = 200;
private static Synchronizer instance;
@@ -103,9 +99,9 @@ public class Synchronizer {
byte[] peersLastBlockSignature = peer.getPeerData().getLastBlockSignature();
byte[] ourLastBlockSignature = ourLatestBlockData.getSignature();
if (peerHeight == ourHeight && (peersLastBlockSignature == null || !Arrays.equals(peersLastBlockSignature, ourLastBlockSignature)))
- LOGGER.info(String.format("Synchronizing with peer %s at height %d, our height %d, signatures differ", peer, peerHeight, ourHeight));
+ LOGGER.debug(String.format("Synchronizing with peer %s at height %d, our height %d, signatures differ", peer, peerHeight, ourHeight));
else
- LOGGER.info(String.format("Synchronizing with peer %s at height %d, our height %d", peer, peerHeight, ourHeight));
+ LOGGER.debug(String.format("Synchronizing with peer %s at height %d, our height %d", peer, peerHeight, ourHeight));
List signatures = findSignaturesFromCommonBlock(peer, ourHeight);
if (signatures == null) {
@@ -134,9 +130,9 @@ public class Synchronizer {
// If common block is peer's latest block then we simply have the same, or longer, chain to peer, so exit now
if (commonBlockHeight == peerHeight) {
if (peerHeight == ourHeight)
- LOGGER.info(String.format("We have the same blockchain as peer %s", peer));
+ LOGGER.debug(String.format("We have the same blockchain as peer %s", peer));
else
- LOGGER.info(String.format("We have the same blockchain as peer %s, but longer", peer));
+ LOGGER.debug(String.format("We have the same blockchain as peer %s, but longer", peer));
return SynchronizationResult.NOTHING_TO_DO;
}
@@ -151,9 +147,9 @@ public class Synchronizer {
// If we have blocks after common block then decide whether we want to sync (lowest block signature wins)
int highestMutualHeight = Math.min(peerHeight, ourHeight);
- // XXX This might be obsolete now
// If our latest block is very old, we're very behind and should ditch our fork.
- if (ourInitialHeight > commonBlockHeight && ourLatestBlockData.getTimestamp() < NTP.getTime() - MAXIMUM_TIP_AGE) {
+ final long minLatestBlockTimestamp = Controller.getMinimumLatestBlockTimestamp();
+ if (ourInitialHeight > commonBlockHeight && ourLatestBlockData.getTimestamp() < minLatestBlockTimestamp) {
LOGGER.info(String.format("Ditching our chain after height %d as our latest block is very old", commonBlockHeight));
highestMutualHeight = commonBlockHeight;
}