From 2777208da3c95085222fc44ce1eab1746588a7f9 Mon Sep 17 00:00:00 2001 From: Inso <insomniak.fr@gmail.com> Date: Wed, 30 Sep 2015 19:38:44 +0200 Subject: [PATCH] Detect blockchain rollback --- src/cutecoin/core/net/network.py | 32 +++++++++++++++++++++++--------- src/cutecoin/core/net/node.py | 27 ++++++++++++++++++++++----- 2 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/cutecoin/core/net/network.py b/src/cutecoin/core/net/network.py index f9d7dc6e..4146da8d 100644 --- a/src/cutecoin/core/net/network.py +++ b/src/cutecoin/core/net/network.py @@ -12,7 +12,7 @@ import asyncio from ucoinpy.documents.peer import Peer from ucoinpy.documents.block import Block -from ucoinpy.api import bma +from ...tools.decorators import asyncify from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer from collections import Counter @@ -25,6 +25,7 @@ class Network(QObject): """ nodes_changed = pyqtSignal() new_block_mined = pyqtSignal(int) + blockchain_rollback = pyqtSignal(int) def __init__(self, currency, nodes): """ @@ -40,7 +41,7 @@ class Network(QObject): self.add_node(n) self.currency = currency self._must_crawl = False - self._block_found = self.latest_block_hash + self._refresh_block_found() self._timer = QTimer() @classmethod @@ -189,6 +190,10 @@ class Network(QObject): else: return Block.Empty_Hash + def _refresh_block_found(self): + self._block_found = {'hash': self.latest_block_hash, + 'number': self.latest_block_number} + def check_nodes_sync(self): """ Check nodes sync with the following rules : @@ -334,10 +339,19 @@ class Network(QObject): self.nodes.remove(node) self.nodes_changed.emit() - - logging.debug("{0} -> {1}".format(self._block_found[:10], self.latest_block_hash[:10])) - if self._block_found != self.latest_block_hash and node.state == Node.ONLINE: - logging.debug("Latest block changed : {0}".format(self.latest_block_number)) - self._block_found = self.latest_block_hash - # Do not emit block change for empty block - self.new_block_mined.emit(self.latest_block_number) + if node.state == Node.ONLINE: + logging.debug("{0} -> {1}".format(self._block_found['hash'][:10], self.latest_block_hash[:10])) + if self._block_found['hash'] != self.latest_block_hash: + logging.debug("Latest block changed : {0}".format(self.latest_block_number)) + # If new latest block is lower than the previously found one + # or if the previously found block is different locally + # than in the main chain, we declare a rollback + if self._block_found['number'] and \ + self.latest_block_number <= self._block_found['number'] \ + or node.main_chain_previous_block and \ + node.main_chain_previous_block['hash'] != self._block_found['hash']: + self._refresh_block_found() + self.blockchain_rollback.emit(self.latest_block_number) + else: + self._refresh_block_found() + self.new_block_mined.emit(self.latest_block_number) diff --git a/src/cutecoin/core/net/node.py b/src/cutecoin/core/net/node.py index c1b001ed..a64b6fa8 100644 --- a/src/cutecoin/core/net/node.py +++ b/src/cutecoin/core/net/node.py @@ -5,7 +5,6 @@ Created on 21 févr. 2015 """ from ucoinpy.documents.peer import Peer, Endpoint, BMAEndpoint -from ucoinpy.documents.block import Block from ...tools.exceptions import InvalidNodeCurrency from ...tools.decorators import asyncify from ucoinpy.api import bma as bma @@ -49,6 +48,7 @@ class Node(QObject): self._uid = uid self._pubkey = pubkey self._block = block + self.main_chain_previous_block = None self._state = state self._neighbours = [] self._currency = currency @@ -295,12 +295,29 @@ class Node(QObject): self.state = Node.ONLINE if not self.block or block_hash != self.block['hash']: - self.set_block(block_data) - logging.debug("Changed block {0} -> {1}".format(self.block['number'], - block_data['number'])) - self.changed.emit() + try: + #TODO: Check previous block + self.main_chain_previous_block = yield from bma.blockchain.Block(conn_handler, + self.block['number']).get() + except ValueError as e: + if '404' in str(e): + self.main_chain_previous_block = None + logging.debug("Error in block reply") + self.changed.emit() + except ClientError: + logging.debug("Client error : {0}".format(self.pubkey)) + self.state = Node.OFFLINE + except asyncio.TimeoutError: + logging.debug("Timeout error : {0}".format(self.pubkey)) + self.state = Node.OFFLINE + finally: + self.set_block(block_data) + logging.debug("Changed block {0} -> {1}".format(self.block['number'], + block_data['number'])) + self.changed.emit() except ValueError as e: if '404' in str(e): + self.main_chain_previous_block = None self.set_block(None) logging.debug("Error in block reply") self.changed.emit() -- GitLab