From 629b5be09feae0da5176abb62e28b92a3cb19d53 Mon Sep 17 00:00:00 2001
From: inso <insomniak.fr@gmaiL.com>
Date: Sat, 6 May 2017 10:22:28 +0200
Subject: [PATCH] Fix issue #687 : Conns changed during refresh

---
 src/sakia/app.py                             |  9 +++++----
 src/sakia/services/blockchain.py             | 10 +++++++---
 src/sakia/services/sources.py                |  8 ++++----
 src/sakia/services/transactions.py           | 13 ++++++-------
 tests/technical/test_documents_service.py    |  6 ++++--
 tests/technical/test_sources_service.py      | 20 +++++++++++++-------
 tests/technical/test_transactions_service.py | 10 +++++++---
 7 files changed, 46 insertions(+), 30 deletions(-)

diff --git a/src/sakia/app.py b/src/sakia/app.py
index ec5c2113..090a5ce9 100644
--- a/src/sakia/app.py
+++ b/src/sakia/app.py
@@ -131,10 +131,11 @@ class Application(QObject):
                                                connections_processor, transactions_processor,
                                                blockchain_processor, bma_connector)
 
-        self.blockchain_service = BlockchainService(self, self.currency, blockchain_processor, bma_connector,
-                                                               self.identities_service,
-                                                               self.transactions_service,
-                                                               self.sources_service)
+        self.blockchain_service = BlockchainService(self, self.currency, blockchain_processor, connections_processor,
+                                                    bma_connector,
+                                                    self.identities_service,
+                                                    self.transactions_service,
+                                                    self.sources_service)
 
         self.network_service = NetworkService.load(self, self.currency, nodes_processor,
                                                     self.blockchain_service,
diff --git a/src/sakia/services/blockchain.py b/src/sakia/services/blockchain.py
index 8f0720a9..a1659d3a 100644
--- a/src/sakia/services/blockchain.py
+++ b/src/sakia/services/blockchain.py
@@ -11,7 +11,7 @@ class BlockchainService(QObject):
     Blockchain service is managing new blocks received
     to update data locally
     """
-    def __init__(self, app, currency, blockchain_processor, bma_connector,
+    def __init__(self, app, currency, blockchain_processor, connections_processor, bma_connector,
                  identities_service, transactions_service, sources_service):
         """
         Constructor the identities service
@@ -19,6 +19,7 @@ class BlockchainService(QObject):
         :param sakia.app.Application app: Sakia application
         :param str currency: The currency name of the community
         :param sakia.data.processors.BlockchainProcessor blockchain_processor: the blockchain processor for given currency
+        :param sakia.data.processors.ConnectionsProcessor connections_processor: the connections processor
         :param sakia.data.connectors.BmaConnector bma_connector: The connector to BMA API
         :param sakia.services.IdentitiesService identities_service: The identities service
         :param sakia.services.TransactionsService transactions_service: The transactions service
@@ -27,6 +28,7 @@ class BlockchainService(QObject):
         super().__init__()
         self.app = app
         self._blockchain_processor = blockchain_processor
+        self._connections_processor = connections_processor
         self._bma_connector = bma_connector
         self.currency = currency
         self._identities_service = identities_service
@@ -64,9 +66,11 @@ class BlockchainService(QObject):
                     self._logger.debug("Parsing from {0}".format(start))
                     blocks = await self._blockchain_processor.next_blocks(start, block_numbers, self.currency)
                     if len(blocks) > 0:
+                        connections = self._connections_processor.connections_to(self.currency)
                         identities = await self._identities_service.handle_new_blocks(blocks)
-                        changed_tx, new_tx, new_dividends = await self._transactions_service.handle_new_blocks(blocks)
-                        destructions = await self._sources_service.refresh_sources(new_tx, new_dividends)
+                        changed_tx, new_tx, new_dividends = await self._transactions_service.handle_new_blocks(connections,
+                                                                                                               blocks)
+                        destructions = await self._sources_service.refresh_sources(connections, new_tx, new_dividends)
                         self.handle_new_blocks(blocks)
                         self.app.db.commit()
                         for tx in changed_tx:
diff --git a/src/sakia/services/sources.py b/src/sakia/services/sources.py
index a1224be5..4235c757 100644
--- a/src/sakia/services/sources.py
+++ b/src/sakia/services/sources.py
@@ -159,14 +159,14 @@ class SourcesServices(QObject):
                 block_number = tx.written_block
         return destructions
 
-    async def refresh_sources(self, transactions, dividends):
+    async def refresh_sources(self, connections, transactions, dividends):
         """
 
-        :param list[sakia.data.entities.Transaction] transactions:
-        :param list[sakia.data.entities.Dividend] dividends:
+        :param list[sakia.data.entities.Connection] connections:
+        :param dict[sakia.data.entities.Transaction] transactions:
+        :param dict[sakia.data.entities.Dividend] dividends:
         :return: the destruction of sources
         """
-        connections = self._connections_processor.connections_to(self.currency)
         destructions = {}
         for conn in connections:
             destructions[conn] = []
diff --git a/src/sakia/services/transactions.py b/src/sakia/services/transactions.py
index eeeb5060..5e20af76 100644
--- a/src/sakia/services/transactions.py
+++ b/src/sakia/services/transactions.py
@@ -51,7 +51,7 @@ class TransactionsService(QObject):
             else:
                 logging.debug("Error during transfer parsing")
 
-    def _parse_block(self, block_doc, txid):
+    def _parse_block(self, connections, block_doc, txid):
         """
         Parse a block
         :param duniterpy.documents.Block block_doc: The block
@@ -64,7 +64,6 @@ class TransactionsService(QObject):
             if self._transactions_processor.run_state_transitions(tx, block_doc):
                 transfers_changed.append(tx)
                 self._logger.debug("New transaction validated : {0}".format(tx.sha_hash))
-        connections = self._connections_processor.connections_to(self.currency)
         for conn in connections:
             new_transactions = [t for t in block_doc.transactions
                                 if not self._transactions_processor.find_by_hash(conn.pubkey, t.sha_hash)
@@ -81,7 +80,7 @@ class TransactionsService(QObject):
 
         return transfers_changed, new_transfers
 
-    async def handle_new_blocks(self, blocks):
+    async def handle_new_blocks(self, connections, blocks):
         """
         Refresh last transactions
 
@@ -92,7 +91,7 @@ class TransactionsService(QObject):
         new_transfers = {}
         txid = 0
         for block in blocks:
-            changes, new_tx = self._parse_block(block, txid)
+            changes, new_tx = self._parse_block(connections, block, txid)
             txid += len(new_tx)
             transfers_changed += changes
             for conn in new_tx:
@@ -100,16 +99,16 @@ class TransactionsService(QObject):
                     new_transfers[conn] += new_tx[conn]
                 except KeyError:
                     new_transfers[conn] = new_tx[conn]
-        new_dividends = await self.parse_dividends_history(blocks, new_transfers)
+        new_dividends = await self.parse_dividends_history(connections, blocks, new_transfers)
         return transfers_changed, new_transfers, new_dividends
 
-    async def parse_dividends_history(self, blocks, transactions):
+    async def parse_dividends_history(self, connections, blocks, transactions):
         """
         Request transactions from the network to initialize data for a given pubkey
+        :param List[sakia.data.entities.Connection] connections: the list of connections found by tx parsing
         :param List[duniterpy.documents.Block] blocks: the list of transactions found by tx parsing
         :param List[sakia.data.entities.Transaction] transactions: the list of transactions found by tx parsing
         """
-        connections = self._connections_processor.connections_to(self.currency)
         min_block_number = blocks[0].number
         max_block_number = blocks[-1].number
         dividends = {}
diff --git a/tests/technical/test_documents_service.py b/tests/technical/test_documents_service.py
index 8fe7d755..2d39b543 100644
--- a/tests/technical/test_documents_service.py
+++ b/tests/technical/test_documents_service.py
@@ -1,5 +1,5 @@
 import pytest
-from sakia.data.entities import Transaction
+from sakia.data.processors import ConnectionsProcessor
 
 
 @pytest.mark.asyncio
@@ -9,7 +9,9 @@ async def test_send_more_than_40_sources(application_with_one_connection, fake_s
         fake_server_with_blockchain.forge.forge_block()
 
     new_blocks = fake_server_with_blockchain.forge.blocks[-60:]
-    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(new_blocks)
+    connections = ConnectionsProcessor.instanciate(application_with_one_connection).connections()
+    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(connections,
+                                                                                                              new_blocks)
 
     for conn in new_tx:
         await application_with_one_connection.sources_service.refresh_sources_of_pubkey(bob.key.pubkey, new_tx[conn], new_ud[conn], None)
diff --git a/tests/technical/test_sources_service.py b/tests/technical/test_sources_service.py
index e9ae6c84..1b1ad862 100644
--- a/tests/technical/test_sources_service.py
+++ b/tests/technical/test_sources_service.py
@@ -1,6 +1,6 @@
 import pytest
 from sakia.data.entities import Transaction
-from sakia.data.processors import TransactionsProcessor
+from sakia.data.processors import TransactionsProcessor, ConnectionsProcessor
 
 
 @pytest.mark.asyncio
@@ -12,8 +12,10 @@ async def test_receive_source(application_with_one_connection, fake_server_with_
     fake_server_with_blockchain.forge.forge_block()
     fake_server_with_blockchain.forge.forge_block()
     new_blocks = fake_server_with_blockchain.forge.blocks[-3:]
-    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(new_blocks)
-    await application_with_one_connection.sources_service.refresh_sources(new_tx, new_ud)
+    connections = ConnectionsProcessor.instanciate(application_with_one_connection).connections()
+    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(connections,
+                                                                                                              new_blocks)
+    await application_with_one_connection.sources_service.refresh_sources(connections, new_tx, new_ud)
     assert amount + 150 == application_with_one_connection.sources_service.amount(bob.key.pubkey)
     await fake_server_with_blockchain.close()
 
@@ -27,8 +29,10 @@ async def test_send_source(application_with_one_connection, fake_server_with_blo
     fake_server_with_blockchain.forge.forge_block()
     fake_server_with_blockchain.forge.forge_block()
     new_blocks = fake_server_with_blockchain.forge.blocks[-3:]
-    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(new_blocks)
-    await application_with_one_connection.sources_service.refresh_sources(new_tx, new_ud)
+    connections = ConnectionsProcessor.instanciate(application_with_one_connection).connections()
+    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(connections,
+                                                                                                              new_blocks)
+    await application_with_one_connection.sources_service.refresh_sources(connections, new_tx, new_ud)
     assert amount - 150 == application_with_one_connection.sources_service.amount(bob.key.pubkey)
     await fake_server_with_blockchain.close()
 
@@ -42,8 +46,10 @@ async def test_destruction(application_with_one_connection, fake_server_with_blo
     fake_server_with_blockchain.forge.forge_block()
     fake_server_with_blockchain.forge.forge_block()
     new_blocks = fake_server_with_blockchain.forge.blocks[-3:]
-    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(new_blocks)
-    await application_with_one_connection.sources_service.refresh_sources(new_tx, new_ud)
+    connections = ConnectionsProcessor.instanciate(application_with_one_connection).connections()
+    changed_tx, new_tx, new_ud = await application_with_one_connection.transactions_service.handle_new_blocks(connections,
+                                                                                                              new_blocks)
+    await application_with_one_connection.sources_service.refresh_sources(connections, new_tx, new_ud)
     assert 0 == application_with_one_connection.sources_service.amount(bob.key.pubkey)
     tx_after_parse = application_with_one_connection.transactions_service.transfers(bob.key.pubkey)
     assert "Too low balance" in [t.comment for t in tx_after_parse]
diff --git a/tests/technical/test_transactions_service.py b/tests/technical/test_transactions_service.py
index 12281a5e..f55a1c91 100644
--- a/tests/technical/test_transactions_service.py
+++ b/tests/technical/test_transactions_service.py
@@ -1,5 +1,6 @@
 import pytest
 from sakia.data.entities import Transaction
+from sakia.data.processors import ConnectionsProcessor
 
 
 @pytest.mark.asyncio
@@ -18,7 +19,8 @@ async def test_send_tx_then_validate(application_with_one_connection, fake_serve
     fake_server_with_blockchain.forge.forge_block()
     fake_server_with_blockchain.forge.forge_block()
     new_blocks = fake_server_with_blockchain.forge.blocks[-3:]
-    await application_with_one_connection.transactions_service.handle_new_blocks(new_blocks)
+    connections = ConnectionsProcessor.instanciate(application_with_one_connection).connections()
+    await application_with_one_connection.transactions_service.handle_new_blocks(connections, new_blocks)
     tx_after_parse = application_with_one_connection.transactions_service.transfers(bob.key.pubkey)
     assert tx_after_parse[-1].state is Transaction.VALIDATED
     assert tx_after_parse[-1].written_block == fake_server_with_blockchain.forge.blocks[-3].number
@@ -34,7 +36,8 @@ async def test_receive_tx(application_with_one_connection, fake_server_with_bloc
     fake_server_with_blockchain.forge.forge_block()
     fake_server_with_blockchain.forge.forge_block()
     new_blocks = fake_server_with_blockchain.forge.blocks[-3:]
-    await application_with_one_connection.transactions_service.handle_new_blocks(new_blocks)
+    connections = ConnectionsProcessor.instanciate(application_with_one_connection).connections()
+    await application_with_one_connection.transactions_service.handle_new_blocks(connections, new_blocks)
     tx_after_parse = application_with_one_connection.transactions_service.transfers(bob.key.pubkey)
     assert tx_after_parse[-1].state is Transaction.VALIDATED
     assert len(tx_before_send) + 1 == len(tx_after_parse)
@@ -52,7 +55,8 @@ async def test_issue_dividend(application_with_one_connection, fake_server_with_
     fake_server_with_blockchain.forge.forge_block()
     fake_server_with_blockchain.forge.forge_block()
     new_blocks = fake_server_with_blockchain.forge.blocks[-5:]
-    await application_with_one_connection.transactions_service.handle_new_blocks(new_blocks)
+    connections = ConnectionsProcessor.instanciate(application_with_one_connection).connections()
+    await application_with_one_connection.transactions_service.handle_new_blocks(connections, new_blocks)
     dividends_after_parse = application_with_one_connection.transactions_service.dividends(bob.key.pubkey)
     assert len(dividends_before_send) + 2 == len(dividends_after_parse)
     await fake_server_with_blockchain.close()
-- 
GitLab