From a2568936a066e80b3b55dd5c0d5e3d6385c3d5e5 Mon Sep 17 00:00:00 2001 From: CalDescent Date: Sat, 25 Jun 2022 12:45:19 +0100 Subject: [PATCH] Synchronizer: filter out peers reporting to hold invalid block signatures. We already mark peers as misbehaved if they returned invalid signatures, but this wasn't sufficient when multiple copies of the same invalid block exist on the network (e.g. after a hard fork). In these cases, we need to be more proactive to avoid syncing with these peers, to increase the chances of preserving other candidate blocks. --- .../org/qortal/controller/Controller.java | 37 +++++++++++++++++++ .../org/qortal/controller/Synchronizer.java | 8 ++++ 2 files changed, 45 insertions(+) diff --git a/src/main/java/org/qortal/controller/Controller.java b/src/main/java/org/qortal/controller/Controller.java index cde965c1..7ce94b1e 100644 --- a/src/main/java/org/qortal/controller/Controller.java +++ b/src/main/java/org/qortal/controller/Controller.java @@ -722,6 +722,43 @@ public class Controller extends Thread { return lastMisbehaved != null && lastMisbehaved > NTP.getTime() - MISBEHAVIOUR_COOLOFF; }; + public static final Predicate hasInvalidBlock = peer -> { + final PeerChainTipData peerChainTipData = peer.getChainTipData(); + Map invalidBlockSignatures = Synchronizer.getInstance().getInvalidBlockSignatures(); + List peerSignatures = new ArrayList<>(); + + // Add peer's latest block signature + if (peerChainTipData != null) { + peerSignatures.add(peerChainTipData.getLastBlockSignature()); + } + + // Add peer's blocks since common block + if (peer.getCommonBlockData() == null) { + List peerSummaries = peer.getCommonBlockData().getBlockSummariesAfterCommonBlock(); + if (peerSummaries != null) { + for (BlockSummaryData blockSummaryData : peerSummaries) { + peerSignatures.add(blockSummaryData.getSignature()); + } + } + } + + // Shortcut if no data + if (peerSignatures.isEmpty() || invalidBlockSignatures == null || invalidBlockSignatures.isEmpty()) { + return false; + } + + // Loop through our known invalid blocks and check each one against supplied block summaries + for (String invalidSignature58 : invalidBlockSignatures.keySet()) { + byte[] invalidSignature = Base58.decode(invalidSignature58); + for (byte[] peerSignature : peerSignatures) { + if (Arrays.equals(peerSignature, invalidSignature)) { + 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 597752d2..e1f248dd 100644 --- a/src/main/java/org/qortal/controller/Synchronizer.java +++ b/src/main/java/org/qortal/controller/Synchronizer.java @@ -241,6 +241,10 @@ public class Synchronizer extends Thread { // 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); + // Disregard peers that hold invalid blocks + // Make sure this is after findCommonBlocksWithPeers() so that peers' summaries can be updated + peers.removeIf(Controller.hasInvalidBlock); + // 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); @@ -840,6 +844,10 @@ public class Synchronizer extends Thread { /* Invalid block signature tracking */ + public Map getInvalidBlockSignatures() { + return this.invalidBlockSignatures; + } + private void addInvalidBlockSignature(byte[] signature) { Long now = NTP.getTime(); if (now == null) {