diff --git a/src/main/java/org/qortal/block/BlockChain.java b/src/main/java/org/qortal/block/BlockChain.java index 76815bdf..d896fda6 100644 --- a/src/main/java/org/qortal/block/BlockChain.java +++ b/src/main/java/org/qortal/block/BlockChain.java @@ -69,6 +69,7 @@ public class BlockChain { newBlockSigHeight, shareBinFix, calcChainWeightTimestamp, + newConsensusTimestamp, transactionV5Timestamp, transactionV6Timestamp, disableReferenceTimestamp @@ -403,6 +404,10 @@ public class BlockChain { return this.featureTriggers.get(FeatureTrigger.calcChainWeightTimestamp.name()).longValue(); } + public long getNewConsensusTimestamp() { + return this.featureTriggers.get(FeatureTrigger.newConsensusTimestamp.name()).longValue(); + } + public long getTransactionV5Timestamp() { return this.featureTriggers.get(FeatureTrigger.transactionV5Timestamp.name()).longValue(); } diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index 0a011db5..458c89d0 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -715,6 +715,24 @@ public class Controller extends Thread { return lastMisbehaved != null && lastMisbehaved > NTP.getTime() - MISBEHAVIOUR_COOLOFF; }; + /** True if peer has unknown height or lower height. */ + public static final Predicate hasShorterBlockchain = peer -> { + BlockData ourLatestBlockData = getInstance().getChainTip(); + int ourHeight = ourLatestBlockData.getHeight(); + final PeerChainTipData peerChainTipData = peer.getChainTipData(); + + // Ensure we have chain tip data for this peer + if (peerChainTipData == null) + return true; + + // Remove if peer is at a lower height than us + Integer peerHeight = peerChainTipData.getLastHeight(); + if (peerHeight == null || peerHeight < ourHeight) + return true; + + return false; + }; + public static final Predicate hasNoRecentBlock = peer -> { final Long minLatestBlockTimestamp = getMinimumLatestBlockTimestamp(); final PeerChainTipData peerChainTipData = peer.getChainTipData(); diff --git a/src/main/java/org/qortal/controller/Synchronizer.java b/src/main/java/org/qortal/controller/Synchronizer.java index 4c1985a1..61903bc7 100644 --- a/src/main/java/org/qortal/controller/Synchronizer.java +++ b/src/main/java/org/qortal/controller/Synchronizer.java @@ -199,6 +199,8 @@ public class Synchronizer extends Thread { if (this.isSynchronizing) return true; + boolean isNewConsensusActive = NTP.getTime() >= BlockChain.getInstance().getNewConsensusTimestamp(); + // Needs a mutable copy of the unmodifiableList List peers = new ArrayList<>(Network.getInstance().getImmutableHandshakedPeers()); @@ -227,6 +229,10 @@ public class Synchronizer extends Thread { if (peers.size() < Settings.getInstance().getMinBlockchainPeers()) return true; + if (isNewConsensusActive) + // Disregard peers with a shorter chain + peers.removeIf(Controller.hasShorterBlockchain); + // Disregard peers that have no block signature or the same block signature as us peers.removeIf(Controller.hasNoOrSameBlock); @@ -235,11 +241,13 @@ public class Synchronizer extends Thread { final int peersBeforeComparison = peers.size(); - // Request recent block summaries from the remaining peers, and locate our common block with each - Synchronizer.getInstance().findCommonBlocksWithPeers(peers); + if (!isNewConsensusActive) { + // Request recent block summaries from the remaining peers, and locate our common block with each + Synchronizer.getInstance().findCommonBlocksWithPeers(peers); - // Compare the peers against each other, and against our chain, which will return an updated list excluding those without common blocks - peers = Synchronizer.getInstance().comparePeers(peers); + // Compare the peers against each other, and against our chain, which will return an updated list excluding those without common blocks + peers = Synchronizer.getInstance().comparePeers(peers); + } // We may have added more inferior chain tips when comparing peers, so remove any peers that are currently on those chains peers.removeIf(Controller.hasInferiorChainTip); @@ -985,8 +993,13 @@ public class Synchronizer extends Thread { return SynchronizationResult.NOTHING_TO_DO; } + boolean isNewConsensusActive = NTP.getTime() >= BlockChain.getInstance().getNewConsensusTimestamp(); + // Unless we're doing a forced sync, we might need to compare blocks after common block - if (!force && ourInitialHeight > commonBlockHeight) { + boolean isBlockComparisonNeeded = isNewConsensusActive + ? ourInitialHeight == peerHeight + : ourInitialHeight > commonBlockHeight; + if (!force && isBlockComparisonNeeded) { SynchronizationResult chainCompareResult = compareChains(repository, commonBlockData, ourLatestBlockData, peer, peerHeight, peerBlockSummaries); if (chainCompareResult != SynchronizationResult.OK) return chainCompareResult; @@ -1187,27 +1200,56 @@ public class Synchronizer extends Thread { peerBlockSummaries.addAll(moreBlockSummaries); } - // Fetch our corresponding block summaries - List ourBlockSummaries = repository.getBlockRepository().getBlockSummaries(commonBlockHeight + 1, ourLatestBlockData.getHeight()); + boolean isNewConsensusActive = NTP.getTime() >= BlockChain.getInstance().getNewConsensusTimestamp(); + if (isNewConsensusActive) { + int parentHeight = ourLatestBlockData.getHeight() - 1; - // Populate minter account levels for both lists of block summaries - populateBlockSummariesMinterLevels(repository, ourBlockSummaries); - populateBlockSummariesMinterLevels(repository, peerBlockSummaries); + BlockSummaryData ourLatestBlockSummary = new BlockSummaryData(ourLatestBlockData); + byte[] ourParentBlockSignature = ourLatestBlockData.getReference(); - final int mutualHeight = commonBlockHeight + Math.min(ourBlockSummaries.size(), peerBlockSummaries.size()); + BlockSummaryData peersLatestBlockSummary = peerBlockSummaries.get(peerBlockSummaries.size() - 1); + byte[] peersParentBlockSignature = peerBlockSummaries.size() > 1 + ? peerBlockSummaries.get(peerBlockSummaries.size() - 1 - 1).getSignature() + : commonBlockSig; - // Calculate cumulative chain weights of both blockchain subsets, from common block to highest mutual block. - BigInteger ourChainWeight = Block.calcChainWeight(commonBlockHeight, commonBlockSig, ourBlockSummaries, mutualHeight); - BigInteger peerChainWeight = Block.calcChainWeight(commonBlockHeight, commonBlockSig, peerBlockSummaries, mutualHeight); + // Populate minter account levels for both lists of block summaries + populateBlockSummariesMinterLevels(repository, Collections.singletonList(ourLatestBlockSummary)); + populateBlockSummariesMinterLevels(repository, Collections.singletonList(peersLatestBlockSummary)); - NumberFormat accurateFormatter = new DecimalFormat("0.################E0"); - LOGGER.debug(String.format("commonBlockHeight: %d, commonBlockSig: %.8s, ourBlockSummaries.size(): %d, peerBlockSummaries.size(): %d", commonBlockHeight, Base58.encode(commonBlockSig), ourBlockSummaries.size(), peerBlockSummaries.size())); - LOGGER.debug(String.format("Our chain weight: %s, peer's chain weight: %s (higher is better)", accurateFormatter.format(ourChainWeight), accurateFormatter.format(peerChainWeight))); + BigInteger ourChainWeight = Block.calcBlockWeight(parentHeight, ourParentBlockSignature, ourLatestBlockSummary); + BigInteger peerChainWeight = Block.calcBlockWeight(parentHeight, peersParentBlockSignature, peersLatestBlockSummary); - // If our blockchain has greater weight then don't synchronize with peer - if (ourChainWeight.compareTo(peerChainWeight) >= 0) { - LOGGER.debug(String.format("Not synchronizing with peer %s as we have better blockchain", peer)); - return SynchronizationResult.INFERIOR_CHAIN; + NumberFormat accurateFormatter = new DecimalFormat("0.################E0"); + LOGGER.debug(String.format("Our chain weight: %s, peer's chain weight: %s (higher is better)", accurateFormatter.format(ourChainWeight), accurateFormatter.format(peerChainWeight))); + + // If our blockchain has greater weight then don't synchronize with peer + if (ourChainWeight.compareTo(peerChainWeight) >= 0) { + LOGGER.debug(String.format("Not synchronizing with peer %s as we have better blockchain", peer)); + return SynchronizationResult.INFERIOR_CHAIN; + } + } else { + // Fetch our corresponding block summaries + List ourBlockSummaries = repository.getBlockRepository().getBlockSummaries(commonBlockHeight + 1, ourLatestBlockData.getHeight()); + + // Populate minter account levels for both lists of block summaries + populateBlockSummariesMinterLevels(repository, ourBlockSummaries); + populateBlockSummariesMinterLevels(repository, peerBlockSummaries); + + final int mutualHeight = commonBlockHeight + Math.min(ourBlockSummaries.size(), peerBlockSummaries.size()); + + // Calculate cumulative chain weights of both blockchain subsets, from common block to highest mutual block. + BigInteger ourChainWeight = Block.calcChainWeight(commonBlockHeight, commonBlockSig, ourBlockSummaries, mutualHeight); + BigInteger peerChainWeight = Block.calcChainWeight(commonBlockHeight, commonBlockSig, peerBlockSummaries, mutualHeight); + + NumberFormat accurateFormatter = new DecimalFormat("0.################E0"); + LOGGER.debug(String.format("commonBlockHeight: %d, commonBlockSig: %.8s, ourBlockSummaries.size(): %d, peerBlockSummaries.size(): %d", commonBlockHeight, Base58.encode(commonBlockSig), ourBlockSummaries.size(), peerBlockSummaries.size())); + LOGGER.debug(String.format("Our chain weight: %s, peer's chain weight: %s (higher is better)", accurateFormatter.format(ourChainWeight), accurateFormatter.format(peerChainWeight))); + + // If our blockchain has greater weight then don't synchronize with peer + if (ourChainWeight.compareTo(peerChainWeight) >= 0) { + LOGGER.debug(String.format("Not synchronizing with peer %s as we have better blockchain", peer)); + return SynchronizationResult.INFERIOR_CHAIN; + } } } diff --git a/src/main/resources/blockchain.json b/src/main/resources/blockchain.json index d2104e65..a99ec447 100644 --- a/src/main/resources/blockchain.json +++ b/src/main/resources/blockchain.json @@ -58,6 +58,7 @@ "newBlockSigHeight": 320000, "shareBinFix": 399000, "calcChainWeightTimestamp": 1620579600000, + "newConsensusTimestamp": 9999999999999, "transactionV5Timestamp": 1642176000000, "transactionV6Timestamp": 9999999999999, "disableReferenceTimestamp": 1655222400000