From 44a90b4e12ed2e28944d838e1ead154160bf6a23 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 18 Sep 2021 10:58:05 +0100 Subject: [PATCH] Keep track of invalid block signatures and avoid peers that return them Until now, a high weight invalid block can cause other valid, lower weight alternatives to be discarded. The solution to this problem is to track invalid blocks and quickly avoid them once discovered. This gives other valid alternative blocks the opportunity to become part of a valid chain, where they would otherwise have been discarded. As with the block minter update, this will cause a fork when the highest weight block candidate is invalid. But it is likely that the fork would be short lived, assuming that the majority of nodes pick the valid chain. --- .../org/qortal/controller/Synchronizer.java | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/main/java/org/qortal/controller/Synchronizer.java b/src/main/java/org/qortal/controller/Synchronizer.java index 30f3c6ee..97d70027 100644 --- a/src/main/java/org/qortal/controller/Synchronizer.java +++ b/src/main/java/org/qortal/controller/Synchronizer.java @@ -342,6 +342,12 @@ public class Synchronizer { } } + // Ignore this peer if it holds an invalid block + if (this.containsInvalidBlock(peer.getCommonBlockData().getBlockSummariesAfterCommonBlock())) { + LOGGER.debug("Ignoring peer %s because it holds an invalid block", peer); + peers.remove(peer); + } + // Reduce minChainLength if needed. If we don't have any blocks, this peer will be excluded from chain weight comparisons later in the process, so we shouldn't update minChainLength List peerBlockSummaries = peer.getCommonBlockData().getBlockSummariesAfterCommonBlock(); if (peerBlockSummaries != null && peerBlockSummaries.size() > 0) @@ -485,6 +491,36 @@ public class Synchronizer { } + + /* Invalid block signature tracking */ + + private void addInvalidBlockSignature(byte[] signature) { + for (byte[] invalidSignature : invalidBlockSignatures) { + if (Arrays.equals(invalidSignature, signature)) { + // Already present + return; + } + } + invalidBlockSignatures.add(signature); + } + private boolean containsInvalidBlock(List blockSummaries) { + if (blockSummaries == null || invalidBlockSignatures == null) { + return false; + } + + // Loop through supplied block summaries and check each one against our known invalid blocks + for (BlockSummaryData blockSummary : blockSummaries) { + byte[] signature = blockSummary.getSignature(); + for (byte[] invalidSignature : invalidBlockSignatures) { + if (Arrays.equals(signature, invalidSignature)) { + return true; + } + } + } + return false; + } + + /** * Attempt to synchronize blockchain with peer. *

@@ -990,6 +1026,7 @@ public class Synchronizer { if (blockResult != ValidationResult.OK) { LOGGER.info(String.format("Peer %s sent invalid block for height %d, sig %.8s: %s", peer, newBlock.getBlockData().getHeight(), Base58.encode(newBlock.getSignature()), blockResult.name())); + this.addInvalidBlockSignature(newBlock.getSignature()); this.timeInvalidBlockLastReceived = NTP.getTime(); return SynchronizationResult.INVALID_DATA; } @@ -1082,6 +1119,7 @@ public class Synchronizer { if (blockResult != ValidationResult.OK) { LOGGER.info(String.format("Peer %s sent invalid block for height %d, sig %.8s: %s", peer, ourHeight, Base58.encode(latestPeerSignature), blockResult.name())); + this.addInvalidBlockSignature(newBlock.getSignature()); this.timeInvalidBlockLastReceived = NTP.getTime(); return SynchronizationResult.INVALID_DATA; }