Skip to content
Snippets Groups Projects
Commit b76d5753 authored by inso's avatar inso
Browse files

Fix bug in blocks handling

 - Last ud was not correctly refreshed
 - Certifications were not refreshed
parent 682aeb7f
No related branches found
No related tags found
No related merge requests found
......@@ -61,7 +61,7 @@ class Application(QObject):
sources_services = attr.ib(default=attr.Factory(dict))
transactions_services = attr.ib(default=attr.Factory(dict))
documents_service = attr.ib(default=None)
current_ref = attr.ib(default=Relative)
current_ref = attr.ib(default=Quantitative)
_logger = attr.ib(default=attr.Factory(lambda:logging.getLogger('sakia')))
available_version = attr.ib(init=False)
_translator = attr.ib(init=False)
......
......@@ -282,7 +282,7 @@ class BlockchainProcessor:
try:
self._repo.insert(blockchain)
except sqlite3.IntegrityError:
pass
self._repo.update(blockchain)
def handle_new_blocks(self, currency, blocks):
"""
......
......@@ -59,17 +59,26 @@ class CertificationsProcessor:
return parameters.sig_period - (blockchain_time - certified.timestamp)
return 0
def create_certification(self, currency, cert, blockstamp):
def create_or_update_certification(self, currency, cert, timestamp, blockstamp):
"""
Creates a certification and insert it in the db
:param duniterpy.documents.Certification cert:
:param int timestamp: the timestamp of the transaction
:param duniterpy.documents.BlockUID blockstamp:
:return: the instanciated certification
:rtype: sakia.data.entities.Certification
"""
cert = Certification(currency, cert.pubkey_from, cert.pubkey_to, cert.timestamp.number,
0, cert.signatures[0], blockstamp)
cert = Certification(currency=currency,
certifier=cert.pubkey_from,
certified=cert.pubkey_to,
block=cert.timestamp.number,
timestamp=timestamp,
signature=cert.signatures[0],
written_on=blockstamp.number)
try:
self._certifications_repo.insert(cert)
except sqlite3.IntegrityError:
self._certifications_repo.update(cert)
return cert
def insert_or_update_certification(self, cert):
......
......@@ -35,7 +35,10 @@ class ConnectionsProcessor:
def connections(self):
return self._connections_repo.get_all()
def connections_with_uids(self):
def connections_with_uids(self, currency=""):
if currency:
return [r for r in self._connections_repo.get_all(currency=currency) if r.uid]
else:
return [r for r in self._connections_repo.get_all() if r.uid]
def connections_to(self, currency):
......
......@@ -102,7 +102,7 @@ class TxFilterProxyModel(QSortFilterProxyModel):
)
if source_index.column() == model.columns_types.index('amount'):
amount = self.app.current_ref.instance(source_data, model.connection.currency,
self.app, block_data).diff_localized(False, True)
self.app, block_data).diff_localized(False, False)
return amount
if role == Qt.FontRole:
......
......@@ -67,7 +67,7 @@ class Quantitative(BaseReferential):
unicodes[str(n)] = ord('\u2070') + n
if base > 1:
return "x10" + "".join([chr(unicodes[e]) for e in str(base)])
return ".10" + "".join([chr(unicodes[e]) for e in str(base)])
else:
return ""
......@@ -92,12 +92,8 @@ class Quantitative(BaseReferential):
def localized(self, units=False, show_base=False):
value = self.value()
dividend, base = self._blockchain_processor.last_ud(self.currency)
if show_base:
localized_value = Quantitative.to_si(value, base)
prefix = Quantitative.base_str(base)
else:
localized_value = QLocale().toString(float(value), 'f', 2)
prefix = ""
if units or show_base:
return QCoreApplication.translate("Quantitative",
......@@ -111,12 +107,8 @@ class Quantitative(BaseReferential):
def diff_localized(self, units=False, show_base=False):
value = self.differential()
dividend, base = self._blockchain_processor.last_ud(self.currency)
if show_base:
localized_value = Quantitative.to_si(value, base)
prefix = Quantitative.base_str(base)
else:
localized_value = QLocale().toString(float(value), 'f', 2)
prefix = ""
if units or show_base:
return QCoreApplication.translate("Quantitative",
......
......@@ -33,6 +33,9 @@ class BlockchainService(QObject):
self._sources_service = sources_service
self._logger = logging.getLogger('sakia')
def handle_new_blocks(self, blocks):
self._blockchain_processor.handle_new_blocks(self.currency, blocks)
async def handle_blockchain_progress(self, network_blockstamp):
"""
Handle a new current block uid
......@@ -48,7 +51,7 @@ class BlockchainService(QObject):
if len(blocks) > 0:
identities = await self._identities_service.handle_new_blocks(blocks)
changed_tx, new_tx, new_dividends = await self._transactions_service.handle_new_blocks(blocks)
self._blockchain_processor.handle_new_blocks(self.currency, blocks)
self.handle_new_blocks(blocks)
self.app.db.commit()
for tx in changed_tx:
self.app.transaction_state_changed.emit(tx)
......
......@@ -4,15 +4,15 @@ import logging
from duniterpy.key import SigningKey
from duniterpy.documents import Certification, Membership, Revocation, InputSource, \
OutputSource, SIGParameter, Unlock, block_uid
OutputSource, SIGParameter, Unlock, block_uid, BlockUID
from duniterpy.documents import Identity as IdentityDoc
from duniterpy.documents import Transaction as TransactionDoc
from duniterpy.documents.transaction import reduce_base
from duniterpy.grammars import output
from duniterpy.api import bma, errors
from duniterpy.api import bma
from sakia.data.entities import Identity, Transaction
from sakia.data.processors import BlockchainProcessor, IdentitiesProcessor, NodesProcessor, \
TransactionsProcessor, SourcesProcessor
TransactionsProcessor, SourcesProcessor, CertificationsProcessor
from sakia.data.connectors import BmaConnector, parse_bma_responses
from sakia.errors import NotEnoughChangeError
......@@ -32,6 +32,7 @@ class DocumentsService:
_bma_connector = attr.ib()
_blockchain_processor = attr.ib()
_identities_processor = attr.ib()
_certifications_processor = attr.ib()
_transactions_processor = attr.ib()
_sources_processor = attr.ib()
_logger = attr.ib(default=attr.Factory(lambda: logging.getLogger('sakia')))
......@@ -45,6 +46,7 @@ class DocumentsService:
return cls(BmaConnector(NodesProcessor(app.db.nodes_repo), app.parameters),
BlockchainProcessor.instanciate(app),
IdentitiesProcessor.instanciate(app),
CertificationsProcessor.instanciate(app),
TransactionsProcessor.instanciate(app),
SourcesProcessor.instanciate(app))
......@@ -157,9 +159,12 @@ class DocumentsService:
certification.sign(identity.document(), [key])
signed_cert = certification.signed_raw(identity.document())
self._logger.debug("Certification : {0}".format(signed_cert))
timestamp = self._blockchain_processor.time(connection.currency)
responses = await self._bma_connector.broadcast(connection.currency, bma.wot.certify, req_args={'cert': signed_cert})
result = await parse_bma_responses(responses)
if result[0]:
self._certifications_processor.create_or_update_certification(connection.currency, certification,
timestamp, BlockUID.empty())
return result
......
......@@ -21,6 +21,7 @@ class IdentitiesService(QObject):
:param sakia.data.processors.IdentitiesProcessor identities_processor: the identities processor for given currency
:param sakia.data.processors.CertificationsProcessor certs_processor: the certifications processor for given currency
: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
"""
super().__init__()
......@@ -66,7 +67,7 @@ class IdentitiesService(QObject):
return 0
def _get_connections_identities(self):
connections = self._connections_processor.connections_to(self.currency)
connections = self._connections_processor.connections_with_uids(self.currency)
identities = []
for c in connections:
identities.append(self._identities_processor.get_identity(self.currency, c.pubkey))
......@@ -216,7 +217,7 @@ class IdentitiesService(QObject):
return need_refresh
def _parse_certifications(self, block):
async def _parse_certifications(self, block):
"""
Parse certified pubkeys found in a block and refresh local data
This method only creates certifications if one of both identities is
......@@ -232,7 +233,8 @@ class IdentitiesService(QObject):
# if we have are a target or a source of the certification
for identity in connections_identities:
if cert.pubkey_from == identity.pubkey or cert.pubkey_to in identity.pubkey:
self._certs_processor.create_certification(self.currency, cert, block.blockUID)
timestamp = await self._blockchain_processor.timestamp(self.currency, cert.timestamp)
self._certs_processor.create_or_update_certification(self.currency, cert, timestamp, block.blockUID)
need_refresh.append(identity)
return need_refresh
......@@ -261,7 +263,7 @@ class IdentitiesService(QObject):
self._logger.debug(str(e))
return identity
def parse_block(self, block):
async def parse_block(self, block):
"""
Parse a block to refresh local data
:param block:
......@@ -270,7 +272,7 @@ class IdentitiesService(QObject):
self._parse_revocations(block)
need_refresh = []
need_refresh += self._parse_memberships(block)
need_refresh += self._parse_certifications(block)
need_refresh += await self._parse_certifications(block)
return set(need_refresh)
async def handle_new_blocks(self, blocks):
......@@ -280,7 +282,7 @@ class IdentitiesService(QObject):
"""
need_refresh = []
for block in blocks:
need_refresh += self.parse_block(block)
need_refresh += await self.parse_block(block)
refresh_futures = []
# for every identity for which we need a refresh, we gather
# requirements requests
......
from sakia.data.entities import Identity
from duniterpy.documents.certification import Certification
import pytest
@pytest.mark.asyncio
async def test_new_block_with_ud(application_with_one_connection, fake_server):
previous_ud = application_with_one_connection.blockchain_services[fake_server.forge.currency].previous_ud()
fake_server.forge.forge_block()
fake_server.forge.generate_dividend()
fake_server.forge.forge_block()
fake_server.forge.generate_dividend()
fake_server.forge.forge_block()
new_blocks = fake_server.forge.blocks[-3:]
application_with_one_connection.blockchain_services[fake_server.forge.currency].handle_new_blocks(
new_blocks)
previous_ud_after_parse = application_with_one_connection.blockchain_services[fake_server.forge.currency].previous_ud()
assert previous_ud_after_parse > previous_ud
await fake_server.close()
\ No newline at end of file
from sakia.data.entities import Identity
from duniterpy.documents.certification import Certification
import pytest
@pytest.mark.asyncio
async def test_new_block_with_unknown_identities(application_with_one_connection, fake_server, bob, alice):
pass
\ No newline at end of file
async def test_new_block_with_certs(application_with_one_connection, fake_server, bob, alice):
certs_before_send = application_with_one_connection.identities_services[fake_server.forge.currency].certifications_sent(
bob.key.pubkey)
alice_user_identity = fake_server.forge.user_identities[bob.key.pubkey]
alice_identity = Identity(currency=fake_server.forge.currency,
pubkey=alice.key.pubkey,
uid=alice.uid,
blockstamp=alice_user_identity.blockstamp,
signature=alice_user_identity.signature)
bob_connection = application_with_one_connection.db.connections_repo.get_one(pubkey=bob.key.pubkey)
await application_with_one_connection.documents_service.certify(bob_connection,
bob.password, alice_identity)
certs_after_send = application_with_one_connection.identities_services[fake_server.forge.currency].certifications_sent(
bob.key.pubkey)
assert len(certs_after_send) == len(certs_before_send) + 1
assert certs_after_send[0].written_on == 0
assert isinstance(fake_server.forge.pool[0], Certification)
fake_server.forge.forge_block()
fake_server.forge.forge_block()
fake_server.forge.forge_block()
new_blocks = fake_server.forge.blocks[-3:]
await application_with_one_connection.identities_services[fake_server.forge.currency].handle_new_blocks(
new_blocks)
certs_after_parse = application_with_one_connection.identities_services[fake_server.forge.currency].certifications_sent(
bob.key.pubkey)
assert len(certs_after_parse) == len(certs_after_send)
assert certs_after_parse[0].written_on == fake_server.forge.blocks[-3].number
await fake_server.close()
\ No newline at end of file
......@@ -36,7 +36,7 @@ def test_localized_with_si(application_with_one_connection, bob):
blockchain.last_ud_base = 3
application_with_one_connection.db.blockchains_repo.update(blockchain)
value = referential.localized(units=True, show_base=True)
assert value == "1,010.10 x10³ TC"
assert value == "1,010.10 .10³ TC"
def test_localized_no_units_no_si(application_with_one_connection, bob):
......@@ -53,7 +53,7 @@ def test_localized_no_units_with_si(application_with_one_connection, bob):
blockchain.last_ud_base = 3
application_with_one_connection.db.blockchains_repo.update(blockchain)
value = referential.localized(units=False, show_base=True)
assert value == "1,010.10 x10³"
assert value == "1,010.10 .10³"
def test_diff_localized_no_si(application_with_one_connection, bob):
......@@ -70,7 +70,7 @@ def test_diff_localized_with_si(application_with_one_connection, bob):
application_with_one_connection.db.blockchains_repo.update(blockchain)
value = referential.diff_localized(units=True, show_base=True)
assert value == "1,010.10 x10³ TC"
assert value == "1,010.10 .10³ TC"
def test_diff_localized_no_units_no_si(application_with_one_connection, bob):
......@@ -87,4 +87,14 @@ def test_diff_localized_no_units_with_si(application_with_one_connection, bob):
blockchain.last_ud_base = 6
application_with_one_connection.db.blockchains_repo.update(blockchain)
value = referential.diff_localized(units=False, show_base=True)
assert value == "101.00 x10⁶"
assert value == "101.00 .10⁶"
def test_diff_localized_no_units_no_base(application_with_one_connection, bob):
application_with_one_connection.parameters.digits_after_comma = 6
referential = Quantitative(10100000000, bob.currency, application_with_one_connection, None)
blockchain = application_with_one_connection.db.blockchains_repo.get_one(currency=bob.currency)
blockchain.last_ud_base = 6
application_with_one_connection.db.blockchains_repo.update(blockchain)
value = referential.diff_localized(units=False, show_base=False)
assert value == "101.00"
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment