diff --git a/requirements.txt b/requirements.txt index c4379dc46caef502b33db85fe8b273c1058bd20a..529ab201db4ce5871a0c936ec00cb2eb00164342 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -ucoinpy>=0.13 +ucoinpy>=0.20 git+https://github.com/harvimt/quamash.git@gh45 asynctest git+https://github.com/networkx/networkx.git@v1.11 \ No newline at end of file diff --git a/src/sakia/core/account.py b/src/sakia/core/account.py index de06266dfb80d2682560b8890373fa0f4f47589e..5078209702f2f61d5e7d518b44042c342cb52893 100644 --- a/src/sakia/core/account.py +++ b/src/sakia/core/account.py @@ -4,8 +4,7 @@ Created on 1 févr. 2014 @author: inso """ -from ucoinpy.documents.certification import SelfCertification, Certification, Revocation -from ucoinpy.documents.membership import Membership +from ucoinpy.documents import Membership, SelfCertification, Certification, Revokation, BlockUID from ucoinpy.key import SigningKey import logging @@ -345,13 +344,13 @@ class Account(QObject): return self.name == data['uid'], self.name, data['uid'] def _parse_uid_lookup(data): - timestamp = 0 + timestamp = BlockUID.empty() found_uid = "" for result in data['results']: if result["pubkey"] == self.pubkey: uids = result['uids'] for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: + if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp: timestamp = uid_data["meta"]["timestamp"] found_uid = uid_data["uid"] return self.name == found_uid, self.name, found_uid @@ -360,13 +359,13 @@ class Account(QObject): return self.pubkey == data['pubkey'], self.pubkey, data['pubkey'] def _parse_pubkey_lookup(data): - timestamp = 0 + timestamp = BlockUID.empty() found_uid = "" found_result = ["", ""] for result in data['results']: uids = result['uids'] for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: + if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp: timestamp = uid_data["meta"]["timestamp"] found_uid = uid_data["uid"] if found_uid == self.name: @@ -464,12 +463,12 @@ class Account(QObject): """ logging.debug("Send membership") - blockid = await community.blockid() + blockUID = await community.blockUID() self_identity = await self._identities_registry.future_find(self.pubkey, community) selfcert = await self_identity.selfcert(community) membership = Membership(PROTOCOL_VERSION, community.currency, - selfcert.pubkey, blockid, mstype, selfcert.uid, + selfcert.pubkey, blockUID, mstype, selfcert.uid, selfcert.timestamp, None) key = SigningKey(self.salt, password) membership.sign([key]) @@ -495,12 +494,12 @@ class Account(QObject): :param str pubkey: The certified identity pubkey """ logging.debug("Certdata") - blockid = await community.blockid() + blockUID = await community.blockUID() identity = await self._identities_registry.future_find(pubkey, community) selfcert = await identity.selfcert(community) if selfcert: certification = Certification(PROTOCOL_VERSION, community.currency, - self.pubkey, pubkey, blockid, None) + self.pubkey, pubkey, blockUID, None) key = SigningKey(self.salt, password) certification.sign(selfcert, [key]) @@ -531,23 +530,23 @@ class Account(QObject): Revoke self-identity on server, not in blockchain :param str password: The account SigningKey password - :param sakia.core.community.Community community: The community target of the revocation + :param sakia.core.community.Community community: The community target of the revokation """ revoked = await self._identities_registry.future_find(self.pubkey, community) - revocation = Revocation(PROTOCOL_VERSION, community.currency, None) + revokation = Revokation(PROTOCOL_VERSION, community.currency, None) selfcert = await revoked.selfcert(community) key = SigningKey(self.salt, password) - revocation.sign(selfcert, [key]) + revokation.sign(selfcert, [key]) - logging.debug("Self-Revocation Document : \n{0}".format(revocation.raw(selfcert))) - logging.debug("Signature : \n{0}".format(revocation.signatures[0])) + logging.debug("Self-Revokation Document : \n{0}".format(revokation.raw(selfcert))) + logging.debug("Signature : \n{0}".format(revokation.signatures[0])) data = { 'pubkey': revoked.pubkey, 'self_': selfcert.signed_raw(), - 'sig': revocation.signatures[0] + 'sig': revokation.signatures[0] } logging.debug("Posted data : {0}".format(data)) responses = await community.bma_access.broadcast(bma.wot.Revoke, {}, data) diff --git a/src/sakia/core/community.py b/src/sakia/core/community.py index 04ddbe1af6c19e3f119639bea62093eb33e9bbae..bfdfbd4c4c9702c4b3cb4c3b490661ae5d332153 100644 --- a/src/sakia/core/community.py +++ b/src/sakia/core/community.py @@ -16,7 +16,7 @@ from PyQt5.QtCore import QObject, pyqtSignal from ..tools.exceptions import NoPeerAvailable from .net.network import Network from ucoinpy.api import bma -from ucoinpy.documents import Block, BlockId +from ucoinpy.documents import Block, BlockUID from .net.api.bma.access import BmaAccess @@ -187,7 +187,7 @@ class Community(QObject): :return: The monetary mass value """ # Get cached block by block number - block_number = self.network.current_blockid.number + block_number = self.network.current_blockUID.number if block_number: block = await self.bma_access.future_request(bma.blockchain.Block, req_args={'number': block_number}) @@ -203,7 +203,7 @@ class Community(QObject): """ try: # Get cached block by block number - block_number = self.network.current_blockid.number + block_number = self.network.current_blockUID.number block = await self.bma_access.future_request(bma.blockchain.Block, req_args={'number': block_number}) return block['membersCount'] @@ -214,20 +214,22 @@ class Community(QObject): logging.debug(str(e)) return 0 - async def time(self): + async def time(self, block_number=None): """ Get the blockchain time + :param block_number: The block number, None if current block :return: The community blockchain time :rtype: int """ try: # Get cached block by block number - block_number = self.network.current_blockid.number + if block_number is None: + block_number = self.network.current_blockUID.number block = await self.bma_access.future_request(bma.blockchain.Block, req_args={'number': block_number}) return block['medianTime'] except ValueError as e: - if '404' in e: + if '404' in str(e): return 0 except NoPeerAvailable as e: logging.debug(str(e)) @@ -290,7 +292,7 @@ class Community(QObject): :param int number: The block number. If none, returns current block. """ if number is None: - block_number = self.network.current_blockid.number + block_number = self.network.current_blockUID.number data = await self.bma_access.future_request(bma.blockchain.Block, req_args={'number': block_number}) else: @@ -299,22 +301,22 @@ class Community(QObject): req_args={'number': number}) return data - async def blockid(self): + async def blockUID(self): """ Get the block id. :return: The current block ID as [NUMBER-HASH] format. """ try: - block_number = self.network.current_blockid.number + block_number = self.network.current_blockUID.number block = await self.bma_access.future_request(bma.blockchain.Block, req_args={'number': block_number}) signed_raw = "{0}{1}\n".format(block['raw'], block['signature']) except ValueError as e: if '404' in str(e): - return BlockId.empty() + return BlockUID.empty() - return Block.from_signed_raw(signed_raw).blockid + return Block.from_signed_raw(signed_raw).blockUID async def members_pubkeys(self): """ diff --git a/src/sakia/core/net/api/bma/access.py b/src/sakia/core/net/api/bma/access.py index 5e23008ad29f81ca80fdba4f44ce41bce3d54e3f..fea810af9ae45acea1b8c940284d00bc38bd4562 100644 --- a/src/sakia/core/net/api/bma/access.py +++ b/src/sakia/core/net/api/bma/access.py @@ -125,7 +125,7 @@ class BmaAccess(QObject): if request is bma.blockchain.Parameters and self._rollback_to == 0: need_reload = True elif str(request) in BmaAccess.__saved_requests \ - or cached_data['metadata']['block_hash'] == self._network.current_blockid.sha_hash: + or cached_data['metadata']['block_hash'] == self._network.current_blockUID.sha_hash: need_reload = False ret_data = cached_data['value'] else: @@ -168,8 +168,8 @@ class BmaAccess(QObject): self._data[cache_key] = {'metadata': {}, 'value': {}} - self._data[cache_key]['metadata']['block_number'] = self._network.current_blockid.number - self._data[cache_key]['metadata']['block_hash'] = self._network.current_blockid.sha_hash + self._data[cache_key]['metadata']['block_number'] = self._network.current_blockUID.number + self._data[cache_key]['metadata']['block_hash'] = self._network.current_blockUID.sha_hash self._data[cache_key]['metadata']['sakia_version'] = __version__ if not self._compare_json(self._data[cache_key]['value'], data): self._data[cache_key]['value'] = data @@ -236,7 +236,7 @@ class BmaAccess(QObject): conn_handler = node.endpoint.conn_handler() req = request(conn_handler, **req_args) try: - json_data = await req.get(**get_args) + json_data = await req.get(**get_args, session=self._network.session) self._update_cache(request, req_args, get_args, json_data) return json_data except ValueError as e: @@ -245,9 +245,9 @@ class BmaAccess(QObject): tries += 1 except (ClientError, ServerDisconnectedError, gaierror, asyncio.TimeoutError) as e: tries += 1 - except jsonschema.ValidationError as e: - logging.debug(str(e)) - tries += 1 + #except jsonschema.ValidationError as e: + # logging.debug(str(e)) + # tries += 1 if len(nodes) == 0 or json_data is None: raise NoPeerAvailable("", len(nodes)) return json_data @@ -269,7 +269,7 @@ class BmaAccess(QObject): json_data = None while tries < 3: try: - json_data = await req.get(**get_args) + json_data = await req.get(**get_args, session=self._network.session) return json_data except ValueError as e: if '404' in str(e) or '400' in str(e): @@ -277,9 +277,9 @@ class BmaAccess(QObject): tries += 1 except (ClientError, ServerDisconnectedError, gaierror, asyncio.TimeoutError) as e: tries += 1 - except jsonschema.ValidationError as e: - logging.debug(str(e)) - tries += 1 + #except jsonschema.ValidationError as e: + # logging.debug(str(e)) + # tries += 1 if len(nodes) == 0 or not json_data: raise NoPeerAvailable("", len(nodes)) return json_data @@ -307,7 +307,7 @@ class BmaAccess(QObject): logging.debug("Trying to connect to : " + node.pubkey) conn_handler = node.endpoint.conn_handler() req = request(conn_handler, **req_args) - reply = asyncio.ensure_future(req.post(**post_args)) + reply = asyncio.ensure_future(req.post(**post_args, session=self._network.session)) replies.append(reply) self._invalidate_cache(request) else: diff --git a/src/sakia/core/net/network.py b/src/sakia/core/net/network.py index 06880f02e12820427834d5b5218783860c9902ae..0681bfcaed04c7a75135d23a6f93b14721ceb0f3 100644 --- a/src/sakia/core/net/network.py +++ b/src/sakia/core/net/network.py @@ -6,11 +6,11 @@ Created on 24 févr. 2015 from .node import Node from ...tools.exceptions import InvalidNodeCurrency import logging -import statistics +import aiohttp import time import asyncio from ucoinpy.documents.peer import Peer -from ucoinpy.documents.block import Block, BlockId +from ucoinpy.documents.block import Block, BlockUID from PyQt5.QtCore import pyqtSignal, pyqtSlot, QObject, QTimer from collections import Counter @@ -27,7 +27,7 @@ class Network(QObject): new_block_mined = pyqtSignal(int) blockchain_rollback = pyqtSignal(int) - def __init__(self, currency, nodes): + def __init__(self, currency, nodes, session): """ Constructor of a network @@ -41,8 +41,9 @@ class Network(QObject): self.add_node(n) self.currency = currency self._must_crawl = False - self._block_found = self.current_blockid + self._block_found = self.current_blockUID self._timer = QTimer() + self._client_session = session @classmethod def create(cls, node): @@ -54,7 +55,7 @@ class Network(QObject): :param node: The first knew node of the network """ nodes = [node] - network = cls(node.currency, nodes) + network = cls(node.currency, nodes, node.session) return network def merge_with_json(self, json_data, file_version): @@ -96,11 +97,12 @@ class Network(QObject): :param dict json_data: A json_data view of a network :param NormalizedVersion file_version: the version of the json file """ + session = aiohttp.ClientSession() nodes = [] for data in json_data: - node = Node.from_json(currency, data, file_version) + node = Node.from_json(currency, data, file_version, session) nodes.append(node) - network = cls(currency, nodes) + network = cls(currency, nodes, session) return network def jsonify(self): @@ -140,8 +142,12 @@ class Network(QObject): for node in self.nodes: close_tasks.append(asyncio.ensure_future(node.close_ws())) await asyncio.wait(close_tasks, timeout=15) + await self._client_session.close() logging.debug("Closed") + @property + def session(self): + return self._client_session def continue_crawling(self): return self._must_crawl @@ -175,16 +181,16 @@ class Network(QObject): return self._root_nodes @property - def current_blockid(self): + def current_blockUID(self): """ Get the latest block considered valid It is the most frequent last block of every known nodes """ blocks = [n.block for n in self.synced_nodes if n.block] if len(blocks) > 0: - return BlockId(blocks[0]['number'], blocks[0]['hash']) + return BlockUID(blocks[0]['number'], blocks[0]['hash']) else: - return BlockId.empty() + return BlockUID.empty() def _check_nodes_sync(self): """ @@ -267,9 +273,9 @@ class Network(QObject): :rtype: int """ if block_number: - if block_number > self.current_blockid.number: + if block_number > self.current_blockUID.number: raise ValueError("Could not compute confirmations : data block number is after current block") - return self.current_blockid.number - block_number + 1 + return self.current_blockUID.number - block_number + 1 else: return 0 @@ -346,7 +352,7 @@ class Network(QObject): logging.debug(str(e)) else: node = [n for n in self.nodes if n.pubkey == pubkey][0] - if node.peer.blockid.number < peer.blockid.number: + if node.peer.blockUID.number < peer.blockUID.number: node.peer = peer @pyqtSlot() @@ -376,19 +382,19 @@ class Network(QObject): self.nodes_changed.emit() if node.state == Node.ONLINE: - logging.debug("{0} -> {1}".format(self._block_found.sha_hash[:10], self.current_blockid.sha_hash[:10])) - if self._block_found.sha_hash != self.current_blockid.sha_hash: - logging.debug("Latest block changed : {0}".format(self.current_blockid.number)) + logging.debug("{0} -> {1}".format(self._block_found.sha_hash[:10], self.current_blockUID.sha_hash[:10])) + if self._block_found.sha_hash != self.current_blockUID.sha_hash: + logging.debug("Latest block changed : {0}".format(self.current_blockUID.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.current_blockid.number <= self._block_found.number \ + self.current_blockUID.number <= self._block_found.number \ or node.main_chain_previous_block and \ node.main_chain_previous_block['hash'] != self._block_found.sha_hash: - self._block_found = self.current_blockid - self.blockchain_rollback.emit(self.current_blockid.number) + self._block_found = self.current_blockUID + self.blockchain_rollback.emit(self.current_blockUID.number) else: - self._block_found = self.current_blockid - self.new_block_mined.emit(self.current_blockid.number) + self._block_found = self.current_blockUID + self.new_block_mined.emit(self.current_blockUID.number) diff --git a/src/sakia/core/net/node.py b/src/sakia/core/net/node.py index fbc5fd41b415da3243577e808de19fc77682ac4e..ddce9db9c14acdbbe1b2f7bc965dceec31a42426 100644 --- a/src/sakia/core/net/node.py +++ b/src/sakia/core/net/node.py @@ -5,7 +5,7 @@ Created on 21 févr. 2015 """ from ucoinpy.documents.peer import Peer, Endpoint, BMAEndpoint -from ucoinpy.documents import Block, BlockId, MalformedDocumentError +from ucoinpy.documents import Block, BlockUID, MalformedDocumentError from ...tools.exceptions import InvalidNodeCurrency from ...tools.decorators import asyncify from ucoinpy.api import bma as bma @@ -46,7 +46,9 @@ class Node(QObject): neighbour_found = pyqtSignal(Peer, str) def __init__(self, peer, uid, pubkey, block, - state, last_change, last_merkle, software, version, fork_window): + state, last_change, last_merkle, + software, version, fork_window, + session): """ Constructor """ @@ -68,6 +70,7 @@ class Node(QObject): 'peer': None} self._connected = {'block': False, 'peer': False} + self._session = session def __del__(self): for ws in self._ws_tasks.values(): @@ -75,7 +78,7 @@ class Node(QObject): ws.cancel() @classmethod - async def from_address(cls, currency, address, port): + async def from_address(cls, currency, address, port, session): """ Factory method to get a node from a given address @@ -86,7 +89,7 @@ class Node(QObject): :return: A new node :rtype: sakia.core.net.Node """ - peer_data = await bma.network.Peering(ConnectionHandler(address, port)).get() + peer_data = await bma.network.Peering(ConnectionHandler(address, port)).get(session) peer = Peer.from_signed_raw("{0}{1}\n".format(peer_data['raw'], peer_data['signature'])) @@ -97,12 +100,12 @@ class Node(QObject): node = cls(peer, "", peer.pubkey, None, Node.ONLINE, time.time(), - {'root': "", 'leaves': []}, "", "", 0) + {'root': "", 'leaves': []}, "", "", 0, session) logging.debug("Node from address : {:}".format(str(node))) return node @classmethod - def from_peer(cls, currency, peer, pubkey): + def from_peer(cls, currency, peer, pubkey, session): """ Factory method to get a node from a peer document. @@ -119,12 +122,12 @@ class Node(QObject): node = cls(peer, "", pubkey, None, Node.OFFLINE, time.time(), {'root': "", 'leaves': []}, - "", "", 0) + "", "", 0, session) logging.debug("Node from peer : {:}".format(str(node))) return node @classmethod - def from_json(cls, currency, data, file_version): + def from_json(cls, currency, data, file_version, session): """ Loads a node from json data @@ -174,14 +177,14 @@ class Node(QObject): if currency in data: currency = data['currency'] - peer = Peer("1", currency, pubkey, BlockId(0, Block.Empty_Hash), endpoints, "SOMEFAKESIGNATURE") + peer = Peer(2, currency, pubkey, BlockUID(0, Block.Empty_Hash), endpoints, "SOMEFAKESIGNATURE") else: peer = Peer.from_signed_raw(data['peer']) node = cls(peer, uid, pubkey, block, state, last_change, {'root': "", 'leaves': []}, - software, version, fork_window) + software, version, fork_window, session) logging.debug("Node from json : {:}".format(str(node))) return node @@ -223,6 +226,10 @@ class Node(QObject): await asyncio.sleep(0) await asyncio.sleep(0) + @property + def session(self): + return self._session + @property def pubkey(self): return self._pubkey @@ -344,7 +351,7 @@ class Node(QObject): try: conn_handler = self.endpoint.conn_handler() block_websocket = bma.ws.Block(conn_handler) - ws_connection = block_websocket.connect() + ws_connection = block_websocket.connect(self._session) async with ws_connection as ws: self._connected['block'] = True logging.debug("Connected successfully to block ws : {0}".format(self.pubkey[:5])) @@ -381,7 +388,7 @@ class Node(QObject): """ try: conn_handler = self.endpoint.conn_handler() - block_data = await bma.blockchain.Current(conn_handler).get() + block_data = await bma.blockchain.Current(conn_handler).get(self._session) await self.refresh_block(block_data) except ValueError as e: if '404' in str(e): @@ -415,7 +422,7 @@ class Node(QObject): try: if self.block: self.main_chain_previous_block = await bma.blockchain.Block(conn_handler, - self.block['number']).get() + self.block['number']).get(self._session) except ValueError as e: if '404' in str(e): self.main_chain_previous_block = None @@ -445,14 +452,14 @@ class Node(QObject): conn_handler = self.endpoint.conn_handler() try: - peering_data = await bma.network.Peering(conn_handler).get() + peering_data = await bma.network.Peering(conn_handler).get(self._session) node_pubkey = peering_data["pubkey"] node_currency = peering_data["currency"] self.state = Node.ONLINE if peering_data['raw'] != self.peer.raw(): peer = Peer.from_signed_raw("{0}{1}\n".format(peering_data['raw'], peering_data['signature'])) - if peer.blockid.number > peer.blockid.number: + if peer.blockUID.number > peer.blockUID.number: self.peer = Peer.from_signed_raw("{0}{1}\n".format(peering_data['raw'], peering_data['signature'])) if node_pubkey != self.pubkey: @@ -484,7 +491,7 @@ class Node(QObject): conn_handler = self.endpoint.conn_handler() try: - summary_data = await bma.node.Summary(conn_handler).get() + summary_data = await bma.node.Summary(conn_handler).get(self._session) self.software = summary_data["ucoin"]["software"] self.version = summary_data["ucoin"]["version"] self.state = Node.ONLINE @@ -511,7 +518,7 @@ class Node(QObject): """ conn_handler = self.endpoint.conn_handler() try: - data = await bma.wot.Lookup(conn_handler, self.pubkey).get() + data = await bma.wot.Lookup(conn_handler, self.pubkey).get(self._session) self.state = Node.ONLINE timestamp = 0 uid = "" @@ -549,7 +556,7 @@ class Node(QObject): try: conn_handler = self.endpoint.conn_handler() peer_websocket = bma.ws.Peer(conn_handler) - ws_connection = peer_websocket.connect() + ws_connection = peer_websocket.connect(self._session) async with ws_connection as ws: self._connected['peer'] = True logging.debug("Connected successfully to peer ws : {0}".format(self.pubkey[:5])) @@ -586,14 +593,15 @@ class Node(QObject): conn_handler = self.endpoint.conn_handler() try: - peers_data = await bma.network.peering.Peers(conn_handler).get(leaves='true') + peers_data = await bma.network.peering.Peers(conn_handler).get(leaves='true', session=self._session) self.state = Node.ONLINE if peers_data['root'] != self._last_merkle['root']: leaves = [leaf for leaf in peers_data['leaves'] if leaf not in self._last_merkle['leaves']] for leaf_hash in leaves: try: - leaf_data = await bma.network.peering.Peers(conn_handler).get(leaf=leaf_hash) + leaf_data = await bma.network.peering.Peers(conn_handler).get(leaf=leaf_hash, + session=self._session) self.refresh_peer_data(leaf_data['leaf']['value']) except (AttributeError, ValueError) as e: logging.debug("{pubkey} : Incorrect peer data in {leaf}".format(pubkey=self.pubkey[:5], diff --git a/src/sakia/core/registry/identities.py b/src/sakia/core/registry/identities.py index 8ef736f2ead3abbbc9caff83b9c21c6f1f494651..ff235e818587a791a3e13f542cb1abd9abdd8cdb 100644 --- a/src/sakia/core/registry/identities.py +++ b/src/sakia/core/registry/identities.py @@ -1,9 +1,8 @@ from ucoinpy.api import bma +from ucoinpy.documents import BlockUID from .identity import Identity, LocalState, BlockchainState -import json import asyncio -import logging from aiohttp.errors import ClientError from ...tools.exceptions import NoPeerAvailable @@ -68,13 +67,13 @@ class IdentitiesRegistry: try: data = await community.bma_access.simple_request(bma.wot.Lookup, req_args={'search': pubkey}) - timestamp = 0 + timestamp = BlockUID.empty() for result in data['results']: if result["pubkey"] == identity.pubkey: uids = result['uids'] for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: - identity.sigdate = uid_data["meta"]["timestamp"] + if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp: + identity.sigdate = BlockUID.from_str(uid_data["meta"]["timestamp"]) identity.uid = uid_data["uid"] identity.blockchain_state = BlockchainState.BUFFERED identity.local_state = LocalState.PARTIAL @@ -109,7 +108,7 @@ class IdentitiesRegistry: data = await community.bma_access.simple_request(bma.blockchain.Membership, req_args={'search': pubkey}) identity.uid = data['uid'] - identity.sigdate = data['sigDate'] + identity.sigdate = BlockUID.from_str(data['sigDate']) identity.local_state = LocalState.PARTIAL identity.blockchain_state = BlockchainState.VALIDATED except ValueError as e: @@ -134,7 +133,7 @@ class IdentitiesRegistry: :param str uid: The person uid, also known as its uid on the network :param str pubkey: The person pubkey - :param int sig_date: The date of signature of the self certification + :param BlockUID sig_date: The date of signature of the self certification :param LocalState local_state: The local status of the identity :param sakia.core.Community community: The community from which we found data :rtype: sakia.core.registry.Identity diff --git a/src/sakia/core/registry/identity.py b/src/sakia/core/registry/identity.py index ce0e1d7757c284c59c612c4f4e7b2b7964911826..e8335cb0a53ac15ee4dcc78c9de37fea02547f8a 100644 --- a/src/sakia/core/registry/identity.py +++ b/src/sakia/core/registry/identity.py @@ -6,10 +6,9 @@ Created on 11 févr. 2014 import logging import time -import asyncio from enum import Enum -from ucoinpy.documents.certification import SelfCertification +from ucoinpy.documents import BlockUID, SelfCertification, MalformedDocumentError from ucoinpy.api import bma as bma from ucoinpy.api.bma import PROTOCOL_VERSION @@ -55,14 +54,16 @@ class Identity(QObject): :param str uid: The identity uid, also known as its uid on the network :param str pubkey: The identity pubkey - :parma int sig_date: The date of signature of the self certification + :parma BlockUID sig_date: The date of signature of the self certification :param LocalState local_state: The local status of the identity :param BlockchainState blockchain_state: The blockchain status of the identity """ + if sigdate: + assert type(sigdate) is BlockUID super().__init__() self.uid = uid self.pubkey = pubkey - self.sigdate = sigdate + self._sigdate = sigdate self.local_state = local_state self.blockchain_state = blockchain_state @@ -84,12 +85,21 @@ class Identity(QObject): """ pubkey = json_data['pubkey'] uid = json_data['uid'] - sigdate = json_data['sigdate'] + sigdate = BlockUID.from_str(json_data['sigdate']) local_state = LocalState[json_data['local_state']] blockchain_state = BlockchainState[json_data['blockchain_state']] return cls(uid, pubkey, sigdate, local_state, blockchain_state) + @property + def sigdate(self): + return self._sigdate + + @sigdate.setter + def sigdate(self, sigdate): + assert type(sigdate) is BlockUID + self._sigdate = sigdate + async def selfcert(self, community): """ Get the identity self certification. @@ -100,7 +110,7 @@ class Identity(QObject): :rtype: ucoinpy.documents.certification.SelfCertification """ try: - timestamp = 0 + timestamp = BlockUID.empty() lookup_data = await community.bma_access.future_request(bma.wot.Lookup, req_args={'search': self.pubkey}) @@ -108,14 +118,14 @@ class Identity(QObject): if result["pubkey"] == self.pubkey: uids = result['uids'] for uid_data in uids: - # If we sigDate was written in the blockchain - if self.sigdate and uid_data["meta"]["timestamp"] == self.sigdate: - timestamp = uid_data["meta"]["timestamp"] - uid = uid_data["uid"] - signature = uid_data["self"] + # If the sigDate was written in the blockchain + if self._sigdate and BlockUID.from_str(uid_data["meta"]["timestamp"]) == self._sigdate: + timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"]) + uid = uid_data["uid"] + signature = uid_data["self"] # Else we choose the latest one found - elif uid_data["meta"]["timestamp"] > timestamp: - timestamp = uid_data["meta"]["timestamp"] + elif BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp: + timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"]) uid = uid_data["uid"] signature = uid_data["self"] @@ -125,12 +135,14 @@ class Identity(QObject): return SelfCertification(PROTOCOL_VERSION, community.currency, self.pubkey, - timestamp, uid, + timestamp, signature) except ValueError as e: if '404' in str(e): raise LookupFailureError(self.pubkey, community) + except MalformedDocumentError: + raise LookupFailureError(self.pubkey, community) except NoPeerAvailable: logging.debug("No peer available") @@ -192,9 +204,7 @@ class Identity(QObject): {'search': self.pubkey}) block_number = -1 membership_data = None - #TODO: Should not be here, should be set when we look for the identity - #We do it because we do not have this info in certifiers-of yet... - self.sigdate = search['sigDate'] + for ms in search['memberships']: if ms['blockNumber'] > block_number: block_number = ms['blockNumber'] @@ -226,7 +236,7 @@ class Identity(QObject): uids = result['uids'] person_uid = "" for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: + if uid_data["meta"]["timestamp"] >= timestamp: timestamp = uid_data["meta"]["timestamp"] person_uid = uid_data["uid"] if person_uid == self.uid: @@ -456,11 +466,11 @@ class Identity(QObject): Refresh UID from uids list, got from a successful lookup request :param list uids: UIDs got from a lookup request """ - timestamp = 0 + timestamp = BlockUID.empty() if self.local_state == LocalState.NOT_FOUND: for uid_data in uids: - if uid_data["meta"]["timestamp"] > timestamp: - timestamp = uid_data["meta"]["timestamp"] + if BlockUID.from_str(uid_data["meta"]["timestamp"]) >= timestamp: + timestamp = BlockUID.from_str(uid_data["meta"]["timestamp"]) identity_uid = uid_data["uid"] self.uid = identity_uid self.blockchain_state = BlockchainState.BUFFERED @@ -473,7 +483,7 @@ class Identity(QObject): """ data = {'uid': self.uid, 'pubkey': self.pubkey, - 'sigdate': self.sigdate, + 'sigdate': self._sigdate, 'local_state': self.local_state.name, 'blockchain_state': self.blockchain_state.name} return data @@ -481,6 +491,6 @@ class Identity(QObject): def __str__(self): return "{uid} - {pubkey} - {sigdate} - {local} - {blockchain}".format(uid=self.uid, pubkey=self.pubkey, - sigdate=self.sigdate, + sigdate=self._sigdate, local=self.local_state, blockchain=self.blockchain_state) diff --git a/src/sakia/core/transfer.py b/src/sakia/core/transfer.py index f1d269fdda00659adfe45240bc6242704092eb01..7f51ee5308b62106d5e478ae30a8ba3d0d7583dd 100644 --- a/src/sakia/core/transfer.py +++ b/src/sakia/core/transfer.py @@ -6,7 +6,7 @@ Created on 31 janv. 2015 import logging import time from ucoinpy.api import bma -from ucoinpy.documents import Block, BlockId +from ucoinpy.documents import Block, BlockUID from PyQt5.QtCore import pyqtSignal, QObject from enum import Enum @@ -36,7 +36,7 @@ class Transfer(QObject): transfer_broadcasted = pyqtSignal(str) broadcast_error = pyqtSignal(int, str) - def __init__(self, sha_hash, state, blockid, metadata, locally_created): + def __init__(self, sha_hash, state, blockUID, metadata, locally_created): """ The constructor of a transfer. Check for metadata keys which must be present : @@ -49,7 +49,7 @@ class Transfer(QObject): :param str sha_hash: The hash of the transaction :param TransferState state: The state of the Transfer - :param ucoinpy.documents.BlockId blockid: The blockid of the transaction in the blockchain + :param ucoinpy.documents.BlockUID blockUID: The blockUID of the transaction in the blockchain :param dict metadata: The transfer metadata """ assert('receiver' in metadata) @@ -64,7 +64,7 @@ class Transfer(QObject): self.sha_hash = sha_hash self.state = state - self.blockid = blockid + self.blockUID = blockUID self._locally_created = locally_created self._metadata = metadata @@ -111,16 +111,16 @@ class Transfer(QObject): return cls(None, TransferState.TO_SEND, None, metadata, True) @classmethod - def create_from_blockchain(cls, hash, blockid, metadata): + def create_from_blockchain(cls, hash, blockUID, metadata): """ Create a new transfer sent from another sakia instance :param str hash: The transaction hash - :param ucoinpy.documents.BlockId blockid: The block id were we found the tx + :param ucoinpy.documents.BlockUID blockUID: The block id were we found the tx :param dict metadata: The computed metadata of the transaction :return: A new transfer :rtype: Transfer """ - return cls(hash, TransferState.VALIDATING, blockid, metadata, False) + return cls(hash, TransferState.VALIDATING, blockUID, metadata, False) @classmethod def load(cls, data): @@ -132,7 +132,7 @@ class Transfer(QObject): """ return cls(data['hash'], TransferState[data['state']], - BlockId.from_str(data['blockid']) if data['blockid'] else None, + BlockUID.from_str(data['blockUID']) if data['blockUID'] else None, data['metadata'], data['local']) def jsonify(self): @@ -141,7 +141,7 @@ class Transfer(QObject): """ return {'hash': self.sha_hash, 'state': self.state.name, - 'blockid': str(self.blockid) if self.blockid else None, + 'blockUID': str(self.blockUID) if self.blockUID else None, 'metadata': self._metadata, 'local': self._locally_created} @@ -212,7 +212,7 @@ class Transfer(QObject): :return: True if the transfer reached enough confrmations :rtype: bool """ - return not rollback and self.blockid.number + fork_window <= current_block.number + return not rollback and self.blockUID.number + fork_window <= current_block.number def _rollback_and_removed(self, rollback, block): """ @@ -222,7 +222,7 @@ class Transfer(QObject): :return: True if the transfer is not found in the block """ if rollback: - if not block or block.blockid != self.blockid: + if not block or block.blockUID != self.blockUID: return True else: return self.sha_hash not in [t.sha_hash for t in block.transactions] @@ -236,7 +236,7 @@ class Transfer(QObject): :return: True if the transfer is found in the block """ if rollback: - return self.blockid.number + fork_window > current_block.number + return self.blockUID.number + fork_window > current_block.number return False def _rollback_and_local(self, rollback, block): @@ -246,7 +246,7 @@ class Transfer(QObject): :param ucoinpy.documents.Block block: The block to check for the transaction :return: True if the transfer is found in the block """ - if rollback and self._locally_created and block.blockid == self.blockid: + if rollback and self._locally_created and block.blockUID == self.blockUID: return self.sha_hash not in [t.sha_hash for t in block.transactions] return False @@ -262,7 +262,7 @@ class Transfer(QObject): Set the transfer as AWAITING confrmation. :param ucoinpy.documents.Block current_block: Current block of the main blockchain """ - self.blockid = current_block.blockid + self.blockUID = current_block.blockUID self._metadata['time'] = int(time.time()) def _be_validating(self, block): @@ -272,7 +272,7 @@ class Transfer(QObject): :param bool rollback: True if we are in a rollback procedure :param ucoinpy.documents.Block block: The block checked """ - self.blockid = block.blockid + self.blockUID = block.blockUID self._metadata['time'] = block.mediantime def _drop(self): @@ -280,7 +280,7 @@ class Transfer(QObject): Cancel the transfer locally. The transfer state becomes TransferState.DROPPED. """ - self.blockid = None + self.blockUID = None def _try_transition(self, transition_key, inputs): """ @@ -342,9 +342,9 @@ class Transfer(QObject): self.sha_hash = txdoc.sha_hash responses = await community.bma_access.broadcast(bma.tx.Process, post_args={'transaction': txdoc.signed_raw()}) - blockid = await community.blockid() + blockUID = await community.blockUID() block = await community.bma_access.future_request(bma.blockchain.Block, - req_args={'number': blockid.number}) + req_args={'number': blockUID.number}) signed_raw = "{0}{1}\n".format(block['raw'], block['signature']) block_doc = Block.from_signed_raw(signed_raw) result = (False, "") @@ -363,7 +363,7 @@ class Transfer(QObject): """ Get the raw documents of this transfer """ - block = await community.get_block(self.blockid.number) + block = await community.get_block(self.blockUID.number) if block: block_doc = Block.from_signed_raw("{0}{1}\n".format(block['raw'], block['signature'])) for tx in block_doc.transactions: diff --git a/src/sakia/core/txhistory.py b/src/sakia/core/txhistory.py index 3e0fe056238bd4e404d41ebd75935d20c61af470..3fadf80aac0c349ededaddcceabc9d0ab7fe41f0 100644 --- a/src/sakia/core/txhistory.py +++ b/src/sakia/core/txhistory.py @@ -103,13 +103,13 @@ class TxHistory(): tries += 1 return block_doc - async def _parse_transaction(self, community, tx, blockid, + async def _parse_transaction(self, community, tx, blockUID, mediantime, received_list, txid): """ Parse a transaction :param sakia.core.Community community: The community :param ucoinpy.documents.Transaction tx: The tx json data - :param ucoinpy.documents.BlockId blockid: The block id where we found the tx + :param ucoinpy.documents.BlockUID blockUID: The block id where we found the tx :param int mediantime: Median time on the network :param list received_list: The list of received transactions :param int txid: The latest txid @@ -160,7 +160,7 @@ class TxHistory(): amount += o.amount metadata['amount'] = amount transfer = Transfer.create_from_blockchain(tx_hash, - blockid, + blockUID, metadata.copy()) return transfer # If we are not in the issuers, @@ -174,7 +174,7 @@ class TxHistory(): metadata['amount'] = amount transfer = Transfer.create_from_blockchain(tx_hash, - blockid, + blockUID, metadata.copy()) received_list.append(transfer) return transfer @@ -200,7 +200,7 @@ class TxHistory(): ] for (txid, tx) in enumerate(new_tx): - transfer = await self._parse_transaction(community, tx, block_doc.blockid, + transfer = await self._parse_transaction(community, tx, block_doc.blockUID, block_doc.mediantime, received_list, txid+txmax) if transfer != None: #logging.debug("Transfer amount : {0}".format(transfer.metadata['amount'])) @@ -308,8 +308,8 @@ class TxHistory(): # We check if transactions are still present for transfer in [t for t in self._transfers if t.state in (TransferState.VALIDATING, TransferState.VALIDATED) and - t.blockid.number == block_number]: - if transfer.blockid.sha_hash == block_doc.blockid.sha_hash: + t.blockUID.number == block_number]: + if transfer.blockUID.sha_hash == block_doc.blockUID.sha_hash: return True transfer.run_state_transitions((True, block_doc)) return False @@ -325,16 +325,16 @@ class TxHistory(): logging.debug("Rollback from : {0}".format(self.latest_block)) # We look for the block goal to check for rollback, # depending on validating and validated transfers... - tx_blocks = [tx.blockid.number for tx in self._transfers + tx_blocks = [tx.blockUID.number for tx in self._transfers if tx.state in (TransferState.VALIDATED, TransferState.VALIDATING) and - tx.blockid is not None] + tx.blockUID is not None] tx_blocks.reverse() for i, block_number in enumerate(tx_blocks): self.wallet.refresh_progressed.emit(i, len(tx_blocks), self.wallet.pubkey) if (await self._check_block(community, block_number)): break - current_block = await self._get_block_doc(community, community.network.current_blockid.number) + current_block = await self._get_block_doc(community, community.network.current_blockUID.number) members_pubkeys = await community.members_pubkeys() for transfer in [t for t in self._transfers if t.state == TransferState.VALIDATED]: @@ -345,15 +345,15 @@ class TxHistory(): async def refresh(self, community, received_list): # We update the block goal try: - current_block_number = community.network.current_blockid.number + current_block_number = community.network.current_blockUID.number if current_block_number: current_block = await community.bma_access.future_request(bma.blockchain.Block, req_args={'number': current_block_number}) members_pubkeys = await community.members_pubkeys() # We look for the first block to parse, depending on awaiting and validating transfers and ud... - tx_blocks = [tx.blockid.number for tx in self._transfers + tx_blocks = [tx.blockUID.number for tx in self._transfers if tx.state in (TransferState.AWAITING, TransferState.VALIDATING) \ - and tx.blockid is not None] + and tx.blockUID is not None] ud_blocks = [ud['block_number'] for ud in self._dividends if ud['state'] in (TransferState.AWAITING, TransferState.VALIDATING)] blocks = tx_blocks + ud_blocks + \ diff --git a/src/sakia/core/wallet.py b/src/sakia/core/wallet.py index 70a0b7e0b37aedc7165d6e03fe79a990e1cf24e1..222716d8602b31a01945c17589380877c63c1fb2 100644 --- a/src/sakia/core/wallet.py +++ b/src/sakia/core/wallet.py @@ -4,7 +4,8 @@ Created on 1 févr. 2014 @author: inso """ -from ucoinpy.documents.transaction import InputSource, OutputSource, Transaction +from ucoinpy.documents.transaction import InputSource, OutputSource, Unlock, SIGParameter, Transaction, reduce_base +from ucoinpy.grammars import output from ucoinpy.key import SigningKey from ucoinpy.api import bma @@ -12,9 +13,8 @@ from ucoinpy.api.bma import PROTOCOL_VERSION from ..tools.exceptions import NotEnoughMoneyError, NoPeerAvailable, LookupFailureError from .transfer import Transfer from .txhistory import TxHistory -from .registry import IdentitiesRegistry, Identity -from PyQt5.QtCore import QObject, pyqtSignal, QCoreApplication +from PyQt5.QtCore import QObject, pyqtSignal import logging import asyncio @@ -159,10 +159,10 @@ class Wallet(QObject): value = 0 sources = await self.sources(community) for s in sources: - value += s.amount + value += s['amount'] * pow(10, s['base']) return value - def tx_inputs(self, amount, community): + def tx_sources(self, amount, community): """ Get inputs to generate a transaction with a given amount of money @@ -172,20 +172,45 @@ class Wallet(QObject): :return: The list of inputs to use in the transaction document """ value = 0 - inputs = [] - cache = self.caches[community.currency] + sources = [] - buf_inputs = list(cache.available_sources) + cache = self.caches[community.currency] + buf_sources = list(cache.available_sources) for s in cache.available_sources: - value += s.amount - s.index = 0 - inputs.append(s) - buf_inputs.remove(s) + value += s['amount'] * pow(10, s['base']) + sources.append(s) + buf_sources.remove(s) if value >= amount: - return (inputs, buf_inputs) + return (sources, buf_sources) raise NotEnoughMoneyError(value, community.currency, - len(inputs), amount) + len(sources), amount) + + def tx_inputs(self, sources): + """ + Get inputs to generate a transaction with a given amount of money + + :param list sources: The sources used to send the given amount of money + + :return: The list of inputs to use in the transaction document + """ + inputs = [] + for s in sources: + inputs.append(InputSource(s['type'], s['identifier'], s['noffset'])) + return inputs + + def tx_unlocks(self, sources): + """ + Get unlocks to generate a transaction with a given amount of money + + :param list sources: The sources used to send the given amount of money + + :return: The list of unlocks to use in the transaction document + """ + unlocks = [] + for i, s in enumerate(sources): + unlocks.append(Unlock(i, [SIGParameter(0)])) + return unlocks def tx_outputs(self, pubkey, amount, inputs): """ @@ -201,14 +226,41 @@ class Wallet(QObject): inputs_value = 0 for i in inputs: logging.debug(i) - inputs_value += i.amount + inputs_value += i['amount'] * pow(10, i['base']) overhead = inputs_value - int(amount) - outputs.append(OutputSource(pubkey, int(amount))) + + amount, amount_base = reduce_base(amount, 0) + overhead, overhead_base = reduce_base(overhead, 0) + + outputs.append(OutputSource(amount, amount_base, output.Condition.token(output.SIG.token(pubkey)))) if overhead != 0: - outputs.append(OutputSource(self.pubkey, overhead)) + outputs.append(OutputSource(overhead, overhead_base, output.Condition.token(output.SIG.token(self.pubkey)))) return outputs + def prepare_tx(self, pubkey, amount, message, community): + """ + Prepare a simple Transaction document + :param str pubkey: the target of the transaction + :param int amount: the amount sent to the receiver + :param Community community: the target community + :return: the transaction document + :rtype: ucoinpy.documents.Transaction + """ + result = self.tx_sources(int(amount), community) + sources = result[0] + self.caches[community.currency].available_sources = result[1][1:] + logging.debug("Inputs : {0}".format(sources)) + + inputs = self.tx_inputs(sources) + unlocks = self.tx_unlocks(sources) + outputs = self.tx_outputs(pubkey, amount, sources) + logging.debug("Outputs : {0}".format(outputs)) + tx = Transaction(PROTOCOL_VERSION, community.currency, 0, + [self.pubkey], inputs, unlocks, + outputs, message, None) + return tx + async def send_money(self, salt, password, community, recipient, amount, message): """ @@ -222,9 +274,9 @@ class Wallet(QObject): :param str message: The message to send with the transfer """ try: - blockid = await community.blockid() + blockUID = await community.blockUID() block = await community.bma_access.future_request(bma.blockchain.Block, - req_args={'number': blockid.number}) + req_args={'number': blockUID.number}) except ValueError as e: if '404' in str(e): return False, "Could not send transfer with null blockchain" @@ -260,44 +312,30 @@ class Wallet(QObject): 'txid': txid } transfer = Transfer.initiate(metadata) - self.caches[community.currency]._transfers.append(transfer) - try: - result = self.tx_inputs(int(amount), community) - inputs = result[0] - self.caches[community.currency].available_sources = result[1][1:] - except NotEnoughMoneyError as e: - return False, str(e) - logging.debug("Inputs : {0}".format(inputs)) - - outputs = self.tx_outputs(recipient, amount, inputs) - logging.debug("Outputs : {0}".format(outputs)) - tx = Transaction(PROTOCOL_VERSION, community.currency, - [self.pubkey], inputs, - outputs, message, None) + tx = self.prepare_tx(recipient, amount, message, community) logging.debug("TX : {0}".format(tx.raw())) tx.sign([key]) logging.debug("Transaction : {0}".format(tx.signed_raw())) - return (await transfer.send(tx, community)) + return await transfer.send(tx, community) async def sources(self, community): """ Get available sources in a given community :param sakia.core.community.Community community: The community where we want available sources - :return: List of InputSource ucoinpy objects + :return: List of bma sources """ - tx = [] + sources = [] try: data = await community.bma_access.future_request(bma.tx.Sources, req_args={'pubkey': self.pubkey}) - for s in data['sources']: - tx.append(InputSource.from_bma(s)) + return data['sources'].copy() except NoPeerAvailable as e: logging.debug(str(e)) - return tx + return sources def transfers(self, community): """ diff --git a/src/sakia/gui/community_tile.py b/src/sakia/gui/community_tile.py index d8b20cc8668241f25bac6a2fcc8d461d6cfda6f8..d7a8bcaedd00d5ae992b8371a38fd3aed2eb38e5 100644 --- a/src/sakia/gui/community_tile.py +++ b/src/sakia/gui/community_tile.py @@ -62,7 +62,7 @@ background-color: palette(base); def handle_nodes_change(self): if len(self.community.network.online_nodes) > 0: - if self.community.network.current_blockid.sha_hash == Block.Empty_Hash: + if self.community.network.current_blockUID.sha_hash == Block.Empty_Hash: state = CommunityState.NOT_INIT else: state = CommunityState.READY diff --git a/src/sakia/gui/community_view.py b/src/sakia/gui/community_view.py index cbc95c66b0aedbf800bde9654db0ef68c2fadb95..744a7d594197c0e8804982df31c31cb044140aa8 100644 --- a/src/sakia/gui/community_view.py +++ b/src/sakia/gui/community_view.py @@ -231,7 +231,7 @@ class CommunityWidget(QWidget, Ui_CommunityWidget): if self.community: text = "" - current_block_number = self.community.network.current_blockid.number + current_block_number = self.community.network.current_blockUID.number if current_block_number: text += self.tr("Block {0}").format(current_block_number) try: diff --git a/src/sakia/gui/identities_tab.py b/src/sakia/gui/identities_tab.py index 02badc49cf9e7b5c52531f7ca038002c00097377..a3be7ad03025d83c27b10e48e753ba0b93208830 100644 --- a/src/sakia/gui/identities_tab.py +++ b/src/sakia/gui/identities_tab.py @@ -11,6 +11,7 @@ from PyQt5.QtGui import QCursor from PyQt5.QtWidgets import QWidget, QAction, QMenu, QDialog, \ QAbstractItemView from ucoinpy.api import bma +from ucoinpy.documents import BlockUID from ..models.identities import IdentitiesFilterProxyModel, IdentitiesTableModel from ..gen_resources.identities_tab_uic import Ui_IdentitiesTab @@ -125,7 +126,7 @@ class IdentitiesTabWidget(QObject): for uid_data in identity_data['uids']: identity = Identity.from_handled_data(uid_data['uid'], identity_data['pubkey'], - uid_data['meta']['timestamp'], + BlockUID.from_str(uid_data['meta']['timestamp']), BlockchainState.BUFFERED) identities.append(identity) diff --git a/src/sakia/gui/member.py b/src/sakia/gui/member.py index fc88680bf24f580def7eaf11a608d9b0b50704fc..16904fe11887d0bb58e9d0aa5449d702670085a7 100644 --- a/src/sakia/gui/member.py +++ b/src/sakia/gui/member.py @@ -65,11 +65,11 @@ class MemberDialog(QObject): else: join_date = datetime.datetime.fromtimestamp(join_date).strftime("%d/%m/%Y %I:%M") - identity_selfcert = await self.identity.selfcert(self.community) + time = await self.community.time(identity_selfcert.timestamp.number) uid_publish_date = QLocale.toString( QLocale(), - QDateTime.fromTime_t(identity_selfcert.timestamp), + QDateTime.fromTime_t(time), QLocale.dateTimeFormat(QLocale(), QLocale.ShortFormat) ) diff --git a/src/sakia/gui/process_cfg_community.py b/src/sakia/gui/process_cfg_community.py index 0622c10d03d81237d331f902b1fab1bf022f80fc..8061efacd7162a59f3b496d6915241fcf1efb465 100644 --- a/src/sakia/gui/process_cfg_community.py +++ b/src/sakia/gui/process_cfg_community.py @@ -64,7 +64,7 @@ class StepPageInit(Step): port = self.config_dialog.spinbox_port.value() logging.debug("Is valid ? ") try: - self.node = await Node.from_address(None, server, port) + self.node = await Node.from_address(None, server, port, session=aiohttp.ClientSession()) community = Community.create(self.node) self.config_dialog.button_connect.setEnabled(False) self.config_dialog.button_register.setEnabled(False) @@ -74,8 +74,8 @@ class StepPageInit(Step): self.config_dialog.label_error.setText(str(e)) except aiohttp.errors.ClientError as e: self.config_dialog.label_error.setText(str(e)) - except (MalformedDocumentError, ValueError) as e: - self.config_dialog.label_error.setText(str(e)) + #except (MalformedDocumentError, ValueError) as e: + # self.config_dialog.label_error.setText(str(e)) @asyncify async def check_connect(self, checked=False): @@ -83,7 +83,7 @@ class StepPageInit(Step): port = self.config_dialog.spinbox_port.value() logging.debug("Is valid ? ") try: - self.node = await Node.from_address(None, server, port) + self.node = await Node.from_address(None, server, port, session=aiohttp.ClientSession()) community = Community.create(self.node) self.config_dialog.button_connect.setEnabled(False) self.config_dialog.button_register.setEnabled(False) @@ -102,8 +102,8 @@ Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) self.config_dialog.label_error.setText(str(e)) except aiohttp.errors.ClientError as e: self.config_dialog.label_error.setText(str(e)) - except (MalformedDocumentError, ValueError) as e: - self.config_dialog.label_error.setText(str(e)) + #except (MalformedDocumentError, ValueError) as e: + # self.config_dialog.label_error.setText(str(e)) @asyncify async def check_register(self, checked=False): @@ -111,7 +111,7 @@ Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) port = self.config_dialog.spinbox_port.value() logging.debug("Is valid ? ") try: - self.node = await Node.from_address(None, server, port) + self.node = await Node.from_address(None, server, port, session=aiohttp.ClientSession()) community = Community.create(self.node) self.config_dialog.button_connect.setEnabled(False) self.config_dialog.button_register.setEnabled(False) @@ -145,8 +145,8 @@ Yours : {0}, the network : {1}""".format(registered[1], registered[2]))) self.config_dialog.label_error.setText(str(e)) except aiohttp.errors.ClientError as e: self.config_dialog.label_error.setText(str(e)) - except (MalformedDocumentError, ValueError) as e: - self.config_dialog.label_error.setText(str(e)) + #except (MalformedDocumentError, ValueError) as e: + # self.config_dialog.label_error.setText(str(e)) def is_valid(self): return self.node is not None diff --git a/src/sakia/gui/transfer.py b/src/sakia/gui/transfer.py index f94788e581525d611e745f553bbffead11f128a7..5f73e4f79948ac9d5327cea416af3071c7b3b1f2 100644 --- a/src/sakia/gui/transfer.py +++ b/src/sakia/gui/transfer.py @@ -139,7 +139,6 @@ class TransferMoneyDialog(QObject): return QApplication.setOverrideCursor(Qt.WaitCursor) - QApplication.processEvents() result = await self.wallet.send_money(self.account.salt, password, self.community, recipient, amount, comment) if result[0]: diff --git a/src/sakia/gui/widgets/context_menu.py b/src/sakia/gui/widgets/context_menu.py index 34b6025ec5748ee2687ebdc288b4d5a0be8272c3..3907084365bc4b6b4a60ff9d59836c5931ccba83 100644 --- a/src/sakia/gui/widgets/context_menu.py +++ b/src/sakia/gui/widgets/context_menu.py @@ -96,9 +96,9 @@ class ContextMenu(QObject): copy_doc.triggered.connect(lambda checked, tx=transfer: menu.copy_transaction_to_clipboard(tx)) menu.qmenu.addAction(copy_doc) - if transfer.blockid: + if transfer.blockUID: copy_doc = QAction(menu.qmenu.tr("Copy transaction block to clipboard"), menu.qmenu.parent()) - copy_doc.triggered.connect(lambda checked, number=transfer.blockid.number: + copy_doc.triggered.connect(lambda checked, number=transfer.blockUID.number: menu.copy_block_to_clipboard(number)) menu.qmenu.addAction(copy_doc) diff --git a/src/sakia/models/identities.py b/src/sakia/models/identities.py index 77573b21c2c3c4418e08e30e7eb5737a6f9ee9af..8e016652cfb31d6393807697010c907da5c2e449 100644 --- a/src/sakia/models/identities.py +++ b/src/sakia/models/identities.py @@ -145,7 +145,12 @@ class IdentitiesTableModel(QAbstractTableModel): join_date = None expiration_date = None - return identity.uid, identity.pubkey, join_date, expiration_date, identity.sigdate + if identity.sigdate: + sigdate_ts = await self.community.time(identity.sigdate.number) + else: + sigdate_ts = None + + return identity.uid, identity.pubkey, join_date, expiration_date, sigdate_ts async def refresh_identities(self, identities): """ diff --git a/src/sakia/models/txhistory.py b/src/sakia/models/txhistory.py index dea8b8d8fadd7a75c56efa224c5dba723f6ff4e9..7ee61c2cdc6d6f75a27efcaf1f138e2f2d6efaa0 100644 --- a/src/sakia/models/txhistory.py +++ b/src/sakia/models/txhistory.py @@ -154,9 +154,9 @@ class TxFilterProxyModel(QSortFilterProxyModel): current_confirmations = 0 if state_data == TransferState.VALIDATING: - current_blockid_number = self.community.network.current_blockid.number - if current_blockid_number: - current_confirmations = current_blockid_number - block_data + current_blockUID_number = self.community.network.current_blockUID.number + if current_blockUID_number: + current_confirmations = current_blockUID_number - block_data elif state_data == TransferState.AWAITING: current_confirmations = 0 @@ -230,8 +230,8 @@ class HistoryTableModel(QAbstractTableModel): async def data_received(self, transfer): amount = transfer.metadata['amount'] - if transfer.blockid: - block_number = transfer.blockid.number + if transfer.blockUID: + block_number = transfer.blockUID.number else: block_number = None try: @@ -256,8 +256,8 @@ class HistoryTableModel(QAbstractTableModel): transfer.metadata['issuer'], block_number, amount) async def data_sent(self, transfer): - if transfer.blockid: - block_number = transfer.blockid.number + if transfer.blockUID: + block_number = transfer.blockUID.number else: block_number = None diff --git a/src/sakia/tests/functional/certification/test_certification.py b/src/sakia/tests/functional/certification/test_certification.py index 2b4c120322076d3d7778cedd07a9c667b749a917..5c6e74d701bcfb84c967d5f37c5467e7e12c3548 100644 --- a/src/sakia/tests/functional/certification/test_certification.py +++ b/src/sakia/tests/functional/certification/test_certification.py @@ -3,6 +3,7 @@ import unittest import asyncio import time import logging +import aiohttp from ucoinpy.documents.peer import BMAEndpoint from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QMessageBox, QApplication from PyQt5.QtCore import QLocale, Qt @@ -33,7 +34,7 @@ class TestCertificationDialog(unittest.TestCase, QuamashTest): self.node = Node(self.mock_new_community.peer(), "", "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", None, Node.ONLINE, - time.time(), {}, "ucoin", "0.14.0", 0) + time.time(), {}, "ucoin", "0.14.0", 0, session=aiohttp.ClientSession()) self.network = Network.create(self.node) self.bma_access = BmaAccess.create(self.network) self.community = Community("test_currency", self.network, self.bma_access) @@ -50,6 +51,9 @@ class TestCertificationDialog(unittest.TestCase, QuamashTest): self.password_asker.password = "testsakia" self.password_asker.remember = True + def tearDown(self): + self.tearDownQuamash() + def test_certification_init_community(self): time.sleep(2) certification_dialog = CertificationDialog(self.application, @@ -61,8 +65,7 @@ class TestCertificationDialog(unittest.TestCase, QuamashTest): async def open_dialog(certification_dialog): srv, port, url = await self.mock_new_community.create_server() self.addCleanup(srv.close) - result = await certification_dialog.async_exec() - self.assertEqual(result, QDialog.Accepted) + await certification_dialog.async_exec() def close_dialog(): if certification_dialog.widget.isVisible(): @@ -79,12 +82,6 @@ class TestCertificationDialog(unittest.TestCase, QuamashTest): if type(w) is QMessageBox: QTest.keyClick(w, Qt.Key_Enter) - self.lp.call_later(15, close_dialog) + self.lp.call_later(10, close_dialog) asyncio.ensure_future(exec_test()) self.lp.run_until_complete(open_dialog(certification_dialog)) - - -if __name__ == '__main__': - logging.basicConfig(stream=sys.stderr) - logging.getLogger().setLevel(logging.DEBUG) - unittest.main() diff --git a/src/sakia/tests/functional/identities_tab/test_identities_table.py b/src/sakia/tests/functional/identities_tab/test_identities_table.py index bb7aeb65882868e74b4ed9d1e124cd440ef4e1d4..14fa7ad730c8fcdc95186489b607b8455194e738 100644 --- a/src/sakia/tests/functional/identities_tab/test_identities_table.py +++ b/src/sakia/tests/functional/identities_tab/test_identities_table.py @@ -1,14 +1,11 @@ import sys import unittest import asyncio -import quamash +import aiohttp import logging import time -from PyQt5.QtWidgets import QDialog -from PyQt5.QtCore import QLocale, Qt, QPoint +from PyQt5.QtCore import QLocale, Qt from PyQt5.QtTest import QTest -from ucoinpy.api import bma -from ucoinpy.api.bma import API from sakia.tests.mocks.bma import nice_blockchain from sakia.core.registry.identities import IdentitiesRegistry @@ -17,7 +14,6 @@ from sakia.gui.password_asker import PasswordAskerDialog from sakia.core.app import Application from sakia.core import Account, Community, Wallet from sakia.core.net import Network, Node -from ucoinpy.documents.peer import BMAEndpoint from sakia.core.net.api.bma.access import BmaAccess from sakia.tests import QuamashTest @@ -35,7 +31,7 @@ class TestIdentitiesTable(unittest.TestCase, QuamashTest): self.node = Node(self.mock_nice_blockchain.peer(), "", "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", None, Node.ONLINE, - time.time(), {}, "ucoin", "0.14.0", 0) + time.time(), {}, "ucoin", "0.14.0", 0, session=aiohttp.ClientSession()) self.network = Network.create(self.node) self.bma_access = BmaAccess.create(self.network) self.community = Community("test_currency", self.network, self.bma_access) @@ -105,8 +101,3 @@ class TestIdentitiesTable(unittest.TestCase, QuamashTest): asyncio.ensure_future(exec_test()) self.lp.call_later(15, close_dialog) self.lp.run_until_complete(open_widget()) - -if __name__ == '__main__': - logging.basicConfig( stream=sys.stderr ) - logging.getLogger().setLevel( logging.DEBUG ) - unittest.main() diff --git a/src/sakia/tests/functional/main_window/test_main_window_dialogs.py b/src/sakia/tests/functional/main_window/test_main_window_dialogs.py index ab393d1e69aac85c7c2bfea194f929e3ff2d29b6..422f9ded275efc8110b237bd657ecc77170ede68 100644 --- a/src/sakia/tests/functional/main_window/test_main_window_dialogs.py +++ b/src/sakia/tests/functional/main_window/test_main_window_dialogs.py @@ -6,8 +6,6 @@ from sakia.core.app import Application from sakia.tests import QuamashTest from sakia.core.registry.identities import IdentitiesRegistry -# Qapplication cause a core dumped when re-run in setup -# set it as global var class MainWindowDialogsTest(unittest.TestCase, QuamashTest): def setUp(self): diff --git a/src/sakia/tests/functional/transfer/test_transfer.py b/src/sakia/tests/functional/transfer/test_transfer.py index 6ba1ca1ef1f8f7e78ef3c89efb800c57353d1714..cc4fdab7a0c3ee2e255169a4a2b992f8100666a9 100644 --- a/src/sakia/tests/functional/transfer/test_transfer.py +++ b/src/sakia/tests/functional/transfer/test_transfer.py @@ -1,13 +1,12 @@ import sys import unittest import asyncio -import quamash +import aiohttp import time import logging from PyQt5.QtWidgets import QDialog, QDialogButtonBox, QMessageBox, QApplication from PyQt5.QtCore import QLocale, Qt from PyQt5.QtTest import QTest -from ucoinpy.api.bma import API from sakia.tests.mocks.bma import nice_blockchain from sakia.core.registry.identities import IdentitiesRegistry @@ -16,10 +15,8 @@ from sakia.gui.password_asker import PasswordAskerDialog from sakia.core.app import Application from sakia.core import Account, Community, Wallet from sakia.core.net import Network, Node -from ucoinpy.documents.peer import BMAEndpoint from sakia.core.net.api.bma.access import BmaAccess from sakia.tests import QuamashTest -from ucoinpy.api import bma class TestTransferDialog(unittest.TestCase, QuamashTest): @@ -35,7 +32,7 @@ class TestTransferDialog(unittest.TestCase, QuamashTest): self.node = Node(self.mock_nice_blockchain.peer(), "", "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", None, Node.ONLINE, - time.time(), {}, "ucoin", "0.14.0", 0) + time.time(), {}, "ucoin", "0.14.0", 0, session=aiohttp.ClientSession()) self.network = Network.create(self.node) self.bma_access = BmaAccess.create(self.network) self.community = Community("test_currency", self.network, self.bma_access) @@ -66,16 +63,15 @@ class TestTransferDialog(unittest.TestCase, QuamashTest): async def open_dialog(transfer_dialog): srv, port, url = await self.mock_nice_blockchain.create_server() self.addCleanup(srv.close) - + await asyncio.sleep(1) result = await transfer_dialog.async_exec() self.assertEqual(result, QDialog.Accepted) def close_dialog(): - if transfer_dialog.isVisible(): - transfer_dialog.close() + if transfer_dialog.widget.isVisible(): + transfer_dialog.widget.close() async def exec_test(): - await asyncio.sleep(1) self.account.wallets[0].caches[self.community.currency].available_sources = await self.wallet.sources(self.community) QTest.mouseClick(transfer_dialog.ui.radio_pubkey, Qt.LeftButton) QTest.keyClicks(transfer_dialog.ui.edit_pubkey, "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn") @@ -86,13 +82,8 @@ class TestTransferDialog(unittest.TestCase, QuamashTest): for w in topWidgets: if type(w) is QMessageBox: QTest.keyClick(w, Qt.Key_Enter) + await asyncio.sleep(1) self.lp.call_later(15, close_dialog) asyncio.ensure_future(exec_test()) self.lp.run_until_complete(open_dialog(transfer_dialog)) - - -if __name__ == '__main__': - logging.basicConfig(stream=sys.stderr) - logging.getLogger().setLevel(logging.DEBUG) - unittest.main() diff --git a/src/sakia/tests/functional/wot_tab/test_wot_tab.py b/src/sakia/tests/functional/wot_tab/test_wot_tab.py index fc3def5b612e047e706303fb2b5515bc3f74f56d..b1f6a568563775c191a56a94a56116f754fffa68 100644 --- a/src/sakia/tests/functional/wot_tab/test_wot_tab.py +++ b/src/sakia/tests/functional/wot_tab/test_wot_tab.py @@ -2,10 +2,10 @@ import asyncio import logging import sys import time +import aiohttp import unittest from PyQt5.QtCore import QLocale -from ucoinpy.documents.peer import BMAEndpoint from sakia.core import Account, Community, Wallet from sakia.core.app import Application @@ -31,7 +31,7 @@ class TestWotTab(unittest.TestCase, QuamashTest): self.node = Node(self.mock_nice_blockchain.peer(), "", "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", None, Node.ONLINE, - time.time(), {}, "ucoin", "0.14.0", 0) + time.time(), {}, "ucoin", "0.14.0", 0, session=aiohttp.ClientSession()) self.network = Network.create(self.node) self.bma_access = BmaAccess.create(self.network) self.community = Community("test_currency", self.network, self.bma_access) diff --git a/src/sakia/tests/mocks/bma/init_new_community.py b/src/sakia/tests/mocks/bma/init_new_community.py index 8166fd95ec7e238b845900c67f501cc5de895e1c..28deb6b8c676095df7ce09c5428c86a0335c15e1 100644 --- a/src/sakia/tests/mocks/bma/init_new_community.py +++ b/src/sakia/tests/mocks/bma/init_new_community.py @@ -1,65 +1,69 @@ - from ..server import MockServer - bma_lookup_test_john = { - "partial": False, - "results": [ - { - "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", - "uids": [ + "partial": False, + "results": [ { - "uid": "john", - "meta": { - "timestamp": 1441130831 - }, - "self": "ZrHK0cCqrxWReROK0ciiSb45+dRphJa68qFaSjdve8bBdnGAu7+DIu0d+u/fXrNRXuObihOKMBIawaIVPNHqDw==", - "others": [] + "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "uids": [ + { + "uid": "john", + "meta": { + "timestamp": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" + }, + "self": "ZrHK0cCqrxWReROK0ciiSb45+dRphJa68qFaSjdve8bBdnGAu7+DIu0d+u/fXrNRXuObihOKMBIawaIVPNHqDw==", + "others": [], + "revocation_sig": "CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==", + "revoked": False, + } + ], + "signed": [] } - ], - "signed": [] - } - ] + ] } bma_lookup_test_doe = { - "partial": False, - "results": [ - { - "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", - "uids": [ + "partial": False, + "results": [ { - "uid": "doe", - "meta": { - "timestamp": 1441130831 - }, - "self": "cIkHPQQ5+xTb4cKWv85rcYcZT+E3GDtX8B2nCK9Vs12p2Yz4bVaZiMvBBwisAAy2WBOaqHS3ydpXGtADchOICw==", - "others": [] + "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "uids": [ + { + "uid": "doe", + "meta": { + "timestamp": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" + }, + "self": "cIkHPQQ5+xTb4cKWv85rcYcZT+E3GDtX8B2nCK9Vs12p2Yz4bVaZiMvBBwisAAy2WBOaqHS3ydpXGtADchOICw==", + "others": [], + "revocation_sig": "CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==", + "revoked": False, + } + ], + "signed": [] } - ], - "signed": [] - } - ] + ] } bma_lookup_test_patrick = { - "partial": False, - "results": [ - { - "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", - "uids": [ + "partial": False, + "results": [ { - "uid": "patrick", - "meta": { - "timestamp": 1441130831 - }, - "self": "QNX2HDAxcHawc47TnMqb5/ou2lwa+zYOyeNk0a52dQDJX/NWmeTzGfTjdCtjpXmSCuPSg0F1mOnLQVd60xAzDA==", - "others": [] + "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "uids": [ + { + "uid": "patrick", + "meta": { + "timestamp": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" + }, + "self": "QNX2HDAxcHawc47TnMqb5/ou2lwa+zYOyeNk0a52dQDJX/NWmeTzGfTjdCtjpXmSCuPSg0F1mOnLQVd60xAzDA==", + "others": [], + "revocation_sig": "CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==", + "revoked": False, + } + ], + "signed": [] } - ], - "signed": [] - } - ] + ] } diff --git a/src/sakia/tests/mocks/bma/new_blockchain.py b/src/sakia/tests/mocks/bma/new_blockchain.py index fd73693ebf41b6ed2700f2e1d215c878a7667367..b2883b45e94a4cc4ead61d9ab5ae4440b9c40693 100644 --- a/src/sakia/tests/mocks/bma/new_blockchain.py +++ b/src/sakia/tests/mocks/bma/new_blockchain.py @@ -7,11 +7,13 @@ bma_wot_add = { { "uid": "test", "meta": { - "timestamp": 1409990782 + "timestamp": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" }, "self": "J3G9oM5AKYZNLAB5Wx499w61NuUoS57JVccTShUbGpCMjCqj9yXXqNq7dyZpDWA6BxipsiaMZhujMeBfCznzyci", "others": [ - ] + ], + "revocation_sig": "CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==", + "revoked": False, } ] } diff --git a/src/sakia/tests/mocks/bma/nice_blockchain.py b/src/sakia/tests/mocks/bma/nice_blockchain.py index 91ef3a050f73bdfc03648144b3b73c8be5eab51a..31190d48639149b435463bc7ec6f2bb0e8dc5c8b 100644 --- a/src/sakia/tests/mocks/bma/nice_blockchain.py +++ b/src/sakia/tests/mocks/bma/nice_blockchain.py @@ -9,22 +9,24 @@ bma_lookup_john = { { "uid": "john", "meta": { - "timestamp": 1441130831 + "timestamp": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" }, + "revocation_sig": "CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==", + "revoked": False, "self": "ZrHK0cCqrxWReROK0ciiSb45+dRphJa68qFaSjdve8bBdnGAu7+DIu0d+u/fXrNRXuObihOKMBIawaIVPNHqDw==", "others": [ { - "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", - "meta": { - "block_number": 38580 - }, - "uids": [ - "doe" - ], - "isMember": True, - "wasMember": True, - "signature": "4ulycI2MtBu/8bZipy+OsXDCNm9EyUIdZ1HA7hbJ66phKRNvv70Oo2YOF/+VDRJb97z9TqWKgfIQ0NbXU15xDg==" - }, + "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "meta": { + "block_number": 38580 + }, + "uids": [ + "doe" + ], + "isMember": True, + "wasMember": True, + "signature": "4ulycI2MtBu/8bZipy+OsXDCNm9EyUIdZ1HA7hbJ66phKRNvv70Oo2YOF/+VDRJb97z9TqWKgfIQ0NbXU15xDg==" + }, ] } ], @@ -35,16 +37,16 @@ bma_lookup_john = { bma_membership_john = { "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", - "uid": "inso", - "sigDate": 1441130831, + "uid": "john", + "sigDate": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", "memberships": [ { - "version": 1, + "version": 2, "currency": "test_currency", "membership": "IN", "blockNumber": 0, - "blockHash": "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709", + "blockHash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", "written": 10000 } ] @@ -59,8 +61,10 @@ bma_lookup_doe = { { "uid": "doe", "meta": { - "timestamp": 1441130831 + "timestamp": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" }, + "revocation_sig": "CTmlh3tO4B8f8IbL8iDy5ZEr3jZDcxkPmDmRPQY74C39MRLXi0CKUP+oFzTZPYmyUC7fZrUXrb3LwRKWw1jEBQ==", + "revoked": False, "self": "cIkHPQQ5+xTb4cKWv85rcYcZT+E3GDtX8B2nCK9Vs12p2Yz4bVaZiMvBBwisAAy2WBOaqHS3ydpXGtADchOICw==", "others": [] } @@ -76,19 +80,20 @@ bma_certifiers_of_john = { "isMember": True, "certifications": [ { - "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", - "uid": "doe", - "isMember": True, - "wasMember": True, - "cert_time": { - "block": 15, - "medianTime": 1447693329 - }, - "written": { - "number": 15, - "hash": "0000EC88BBBAA29D530D2B815DEE264DDC9F07F4" - }, - "signature": "oliiPDhniZAGHrIFL66oHR+cqD4aTgXX+20VFLMfNHwdYPeik76hy334zxhoDC4cPODMb9df2nF/EDfCefrNBg==" + "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "uid": "doe", + "isMember": True, + "wasMember": True, + "cert_time": { + "block": 15, + "medianTime": 1447693329 + }, + "sigDate": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", + "written": { + "number": 15, + "hash": "0000EC88BBBAA29D530D2B815DEE264DDC9F07F4" + }, + "signature": "oliiPDhniZAGHrIFL66oHR+cqD4aTgXX+20VFLMfNHwdYPeik76hy334zxhoDC4cPODMb9df2nF/EDfCefrNBg==" }, ] } @@ -97,6 +102,7 @@ bma_certified_by_john = { "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", "uid": "john", "isMember": True, + "wasMember": False, "certifications": [ ] } @@ -110,6 +116,8 @@ bma_parameters = { "sigValidity": 2629800, "sigQty": 3, "sigWoT": 3, + "sigStock": 10, + "sigWindow": 1000, "msValidity": 2629800, "stepMax": 3, "medianTimeBlocks": 11, @@ -120,7 +128,7 @@ bma_parameters = { } bma_blockchain_0 = { - "version": 1, + "version": 2, "nonce": 10144, "number": 0, "powMin": 3, @@ -132,25 +140,27 @@ bma_blockchain_0 = { "issuer": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", "signature": "+78w7251vvRdhoIJ6IWHEiEOLxNrmfQf45Y5sYvPdnAdXkVpO1unMV5YA/G5Vhphyz1dICrbeKCPM5qbFsoWAQ==", "hash": "00063EB6E83F8717CEF1D25B3E2EE308374A14B1", + "inner_hash": "00063EB6E83F8717CEF1D25B3E2EE308374A14B1", "parameters": "0.1:86400:100:604800:2629800:3:3:2629800:3:11:600:20:144:0.67", "previousHash": None, "previousIssuer": None, "dividend": None, "membersChanges": [], "identities": [ - "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:Ot3zIp/nsHT3zgJy+2YcXPL6vaM5WFsD+F8w3qnJoBRuBG6lv761zoaExp2iyUnm8fDAyKPpMxRK2kf437QSCw==:1421787800:inso", - "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:GZKLgaxJKL+GqxVLePMt8OVLJ6qTLrib5Mr/j2gjiNRY2k485YLB2OlzhBzZVnD3xLs0xi69JUfmLnM54j3aCA==:1421786393:cgeek", - "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:th576H89dfymkG7/sH+DAIzjlmIqNEW6zY3ONrGeAml+k3f1ver399kYnEgG5YCaKXnnVM7P0oJHah80BV3mDw==:1421790376:moul", - "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:XRmbTYFkPeGVEU2mJzzN4h1oVNDsZ4yyNZlDAfBm9CWhBsZ82QqX9GPHye2hBxxiu4Nz1BHgQiME6B4JcAC8BA==:1421787461:galuel" + "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:Ot3zIp/nsHT3zgJy+2YcXPL6vaM5WFsD+F8w3qnJoBRuBG6lv761zoaExp2iyUnm8fDAyKPpMxRK2kf437QSCw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso", + "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:GZKLgaxJKL+GqxVLePMt8OVLJ6qTLrib5Mr/j2gjiNRY2k485YLB2OlzhBzZVnD3xLs0xi69JUfmLnM54j3aCA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek", + "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:th576H89dfymkG7/sH+DAIzjlmIqNEW6zY3ONrGeAml+k3f1ver399kYnEgG5YCaKXnnVM7P0oJHah80BV3mDw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul", + "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:XRmbTYFkPeGVEU2mJzzN4h1oVNDsZ4yyNZlDAfBm9CWhBsZ82QqX9GPHye2hBxxiu4Nz1BHgQiME6B4JcAC8BA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel" ], "joiners": [ - "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:ccJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421787800:inso", - "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:1lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421786393:cgeek", - "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:ctyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421790376:moul", - "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:uoiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421787461:galuel" + "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:ccJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso", + "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:1lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek", + "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:ctyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul", + "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:uoiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel" ], "actives": [], "leavers": [], + "revoked": [], "excluded": [], "certifications": [ "37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:0:3wmCVW8AbVxRFm2PuLXD9UTCIg93MhUblZJvlYrDldSV4xuA7mZCd8TV4vb/6Bkc0FMQgBdHtpXrQ7dpo20uBA==", @@ -167,11 +177,51 @@ bma_blockchain_0 = { "BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:0:q4PCneYkcPH8AHEqEvqTtYQWslhlYO2B87aReuOl1uPczn5Q3VkZFAsU48ZTYryeyWp2nxdQojdFYhlAUNchAw==" ], "transactions": [], - "raw": "Version: 1\nType: Block\nCurrency: test_currency\nNonce: 10144\nNumber: 0\nPoWMin: 3\nTime: 1421838980\nMedianTime: 1421838980\nIssuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\nParameters: 0.1:86400:100:604800:2629800:3:3:2629800:3:11:600:20:144:0.67\nMembersCount: 4\nIdentities:\n8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:Ot3zIp/nsHT3zgJy+2YcXPL6vaM5WFsD+F8w3qnJoBRuBG6lv761zoaExp2iyUnm8fDAyKPpMxRK2kf437QSCw==:1421787800:inso\nHnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:GZKLgaxJKL+GqxVLePMt8OVLJ6qTLrib5Mr/j2gjiNRY2k485YLB2OlzhBzZVnD3xLs0xi69JUfmLnM54j3aCA==:1421786393:cgeek\nBMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:th576H89dfymkG7/sH+DAIzjlmIqNEW6zY3ONrGeAml+k3f1ver399kYnEgG5YCaKXnnVM7P0oJHah80BV3mDw==:1421790376:moul\n37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:XRmbTYFkPeGVEU2mJzzN4h1oVNDsZ4yyNZlDAfBm9CWhBsZ82QqX9GPHye2hBxxiu4Nz1BHgQiME6B4JcAC8BA==:1421787461:galuel\nJoiners:\n8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:ccJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421787800:inso\nHnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:1lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421786393:cgeek\nBMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:ctyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421790376:moul\n37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:uoiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0:DA39A3EE5E6B4B0D3255BFEF95601890AFD80709:1421787461:galuel\nActives:\nLeavers:\nExcluded:\nCertifications:\n37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:0:3wmCVW8AbVxRFm2PuLXD9UTCIg93MhUblZJvlYrDldSV4xuA7mZCd8TV4vb/6Bkc0FMQgBdHtpXrQ7dpo20uBA==\nHnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:0:7UMQsUjLvuiZKIzOH5rrZDdDi5rXUo69EuQulY1Zm42xpRx/Gt5CkoTcJ/Mu83oElQbcZZTz/lVJ6IS0jzMiCQ==\nBMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:0:twWSY9etI82FLEHzhdqIoHsC9ehWCA7DCPiGxDLCWGPO4TG77hwtn3RcC68qoKHCib577JCp+fcKyp2vyI6FDA==\n8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:0:7K5MHkO8ibf5SchmPkRrmsg9owEZZ23uEMJJSQYG7L3PUmAKmmV/0VSjivxXH8gJGQBGsXQoK79x1jsYnj2nAg==\nBMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:0:Jua4FcEJFptSE5OoG1/Mgzx4e9jgGnYu7t8g1sqqPujI9hRhLFNXbQXedPS1q1OD5vWivA045gKOq/gnj8opDg==\n37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:0:R/DV4/wYjvBG09QSOGtnxd3bfPFhVjEE5Uy3BsBMVUvjLsgxjf8NgLhYVozcHTRWS43ArxlXKfS5m3+KIPhhAQ==\n8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:0:4hP+ahJK021akL4UxB6c5QLaGJXa9eapd3nfdFQe+Xy87f/XLhj8BCa22XbbOlyGdaZRT3AYzbCL2UD5tI8mCw==\nHnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:0:sZTQJr0d/xQnxrIIdSePUJpSTOa8v6IYGXMF2fVDZxQU8vwfzPm2dUKTaF0nU6E9wOYszzkBHaXL85nir+WtCQ==\n37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:0:hDuBkoFhWhR/FgOU1+9SbQGBMIr47xqUzw1ZMERaPQo4aWm0WFbZurG4lvuJZzTyG6RF/gSw4VPvYZFPxWmADg==\n8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:0:79ZVrBehElVZh82fJdR18IJx06GkEVZTbwdHH4zb0S6VaGwdtLh1rvomm4ukBvUc8r/suTweG/SScsJairXNAg==\nHnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:0:e/ai9E4G5CFB9Qi329e0ffYpZMgxj8mM4rviqIr2+UESA0UG86OuAAyHO11hYeyolZRiU8I7WdtNE98B1uZuBg==\nBMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:0:q4PCneYkcPH8AHEqEvqTtYQWslhlYO2B87aReuOl1uPczn5Q3VkZFAsU48ZTYryeyWp2nxdQojdFYhlAUNchAw==\nTransactions:\n" + "raw": """Version: 2 +Type: Block +Currency: test_currency +Number: 0 +PoWMin: 3 +Time: 1421838980 +MedianTime: 1421838980 +Issuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk +Parameters: 0.1:86400:100:604800:15:604800:2629800:3:3:2629800:3:11:600:20:144:0.67 +MembersCount: 4 +Identities: +8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:Ot3zIp/nsHT3zgJy+2YcXPL6vaM5WFsD+F8w3qnJoBRuBG6lv761zoaExp2iyUnm8fDAyKPpMxRK2kf437QSCw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso +HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:GZKLgaxJKL+GqxVLePMt8OVLJ6qTLrib5Mr/j2gjiNRY2k485YLB2OlzhBzZVnD3xLs0xi69JUfmLnM54j3aCA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek +BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:th576H89dfymkG7/sH+DAIzjlmIqNEW6zY3ONrGeAml+k3f1ver399kYnEgG5YCaKXnnVM7P0oJHah80BV3mDw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul +37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:XRmbTYFkPeGVEU2mJzzN4h1oVNDsZ4yyNZlDAfBm9CWhBsZ82QqX9GPHye2hBxxiu4Nz1BHgQiME6B4JcAC8BA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel +Joiners: +8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:ccJm3F44eLMhQtnQY/7+14SWCDqVTL3Miw65hBVpV+YiUSUknIGhBNN0C0Cf+Pf0/pa1tjucW8Us3z5IklFSDg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:inso +HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:1lFIiaR0QX0jibr5zQpXVGzBvMGqcsTRlmHiwGz5HOAZT8PTdVUb5q6YGZ6qAUZjdMjPmhLaiMIpYc47wUnzBA==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:cgeek +BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:ctyAhpTRrAAOhFJukWI8RBr//nqYYdQibVzjOfaCdcWLb3TNFKrNBBothNsq/YrYHr7gKrpoftucf/oxLF8zAg==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:moul +37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:uoiGaC5b7kWqtqdPxwatPk9QajZHCNT9rf8/8ud9Rli24z/igcOf0Zr4A6RTAIKWUq9foW39VqJe+Y9R3rhACw==:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855:galuel +Actives: +Leavers: +Revoked: +Excluded: +Certifications: +37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:0:3wmCVW8AbVxRFm2PuLXD9UTCIg93MhUblZJvlYrDldSV4xuA7mZCd8TV4vb/6Bkc0FMQgBdHtpXrQ7dpo20uBA== +HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:0:7UMQsUjLvuiZKIzOH5rrZDdDi5rXUo69EuQulY1Zm42xpRx/Gt5CkoTcJ/Mu83oElQbcZZTz/lVJ6IS0jzMiCQ== +BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:0:twWSY9etI82FLEHzhdqIoHsC9ehWCA7DCPiGxDLCWGPO4TG77hwtn3RcC68qoKHCib577JCp+fcKyp2vyI6FDA== +8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:0:7K5MHkO8ibf5SchmPkRrmsg9owEZZ23uEMJJSQYG7L3PUmAKmmV/0VSjivxXH8gJGQBGsXQoK79x1jsYnj2nAg== +BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:0:Jua4FcEJFptSE5OoG1/Mgzx4e9jgGnYu7t8g1sqqPujI9hRhLFNXbQXedPS1q1OD5vWivA045gKOq/gnj8opDg== +37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:0:R/DV4/wYjvBG09QSOGtnxd3bfPFhVjEE5Uy3BsBMVUvjLsgxjf8NgLhYVozcHTRWS43ArxlXKfS5m3+KIPhhAQ== +8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:0:4hP+ahJK021akL4UxB6c5QLaGJXa9eapd3nfdFQe+Xy87f/XLhj8BCa22XbbOlyGdaZRT3AYzbCL2UD5tI8mCw== +HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:0:sZTQJr0d/xQnxrIIdSePUJpSTOa8v6IYGXMF2fVDZxQU8vwfzPm2dUKTaF0nU6E9wOYszzkBHaXL85nir+WtCQ== +37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:0:hDuBkoFhWhR/FgOU1+9SbQGBMIr47xqUzw1ZMERaPQo4aWm0WFbZurG4lvuJZzTyG6RF/gSw4VPvYZFPxWmADg== +8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:0:79ZVrBehElVZh82fJdR18IJx06GkEVZTbwdHH4zb0S6VaGwdtLh1rvomm4ukBvUc8r/suTweG/SScsJairXNAg== +HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:0:e/ai9E4G5CFB9Qi329e0ffYpZMgxj8mM4rviqIr2+UESA0UG86OuAAyHO11hYeyolZRiU8I7WdtNE98B1uZuBg== +BMAVuMDcGhYAV4wA27DL1VXX2ZARZGJYaMwpf7DJFMYH:37qBxM4hLV2jfyYo2bNzAjkeLngLr2r7G2HpdpKieVxw:0:q4PCneYkcPH8AHEqEvqTtYQWslhlYO2B87aReuOl1uPczn5Q3VkZFAsU48ZTYryeyWp2nxdQojdFYhlAUNchAw== +Transactions: +InnerHash: 09500111588846873CA0110602DDC17FB34AA9F4548B7CE322C845902FFC1429 +Nonce: 10144 +""" } bma_blockchain_current = { - "version": 1, + "version": 2, "nonce": 6909, "number": 15, "powMin": 4, @@ -195,7 +245,28 @@ bma_blockchain_current = { "excluded": [], "certifications": [], "transactions": [], - "raw": "Version: 1\nType: Block\nCurrency: meta_brouzouf\nNonce: 6909\nNumber: 30898\nPoWMin: 4\nTime: 1441618206\nMedianTime: 1441614759\nIssuer: EPs9qX7HmCDy6ptUoMLpTzbh9toHu4au488pBTU9DN6y\nPreviousHash: 00003BDA844D77EEE7CF32A6C3C87F2ACBFCFCBB\nPreviousIssuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\nMembersCount: 20\nIdentities:\nJoiners:\nActives:\nLeavers:\nExcluded:\nCertifications:\nTransactions:\n" + "raw": """Version: 2 +Type: Block +Currency: meta_brouzouf +Number: 30898 +PoWMin: 4 +Time: 1441618206 +MedianTime: 1441614759 +Issuer: EPs9qX7HmCDy6ptUoMLpTzbh9toHu4au488pBTU9DN6y +PreviousHash: 00003BDA844D77EEE7CF32A6C3C87F2ACBFCFCBB +PreviousIssuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk +MembersCount: 20 +Identities: +Joiners: +Actives: +Leavers: +Revoked: +Excluded: +Certifications: +Transactions: +InnerHash: 6BB2E0BE18BEA428379336FB5F09DCE0EB594D09CDD705085CA91AA966C27CFA +Nonce: 6909 +""" } # Sent 6, received 20 + 30 @@ -207,19 +278,23 @@ bma_txhistory_john = { "sent": [ { - "version": 1, + "version": 2, "issuers": [ "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ" ], "inputs": [ - "0:D:1:000A8362AE0C1B8045569CE07735DE4C18E81586:8" + "D:7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ:8" + ], + "unlocks": + [ + "SIG(0)" ], "outputs": [ - "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ:2", - "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn:6" + "2:1:SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)", + "6:1:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)" ], "comment": "", "signatures": @@ -234,19 +309,23 @@ bma_txhistory_john = { "received": [ { - "version": 1, + "version": 2, "issuers": [ "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn" ], "inputs": [ - "0:D:1:000A8362AE0C1B8045569CE07735DE4C18E81586:8" + "D:FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn:8" + ], + "unlocks": + [ + "SIG(0)" ], "outputs": [ - "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn:2", - "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ:20" + "2:1:SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)", + "6:1:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)" ], "comment": "", "signatures": @@ -258,19 +337,23 @@ bma_txhistory_john = { "time": 1421932545 }, { - "version": 1, + "version": 2, "issuers": [ "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn" ], "inputs": [ - "0:D:1:000A8362AE0C1B8045569CE07735DE4C18E81586:8" + "D:FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn:8" + ], + "unlocks": + [ + "SIG(0)" ], "outputs": [ - "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn:5", - "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ:40" + "5:1:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)", + "40:1:SIG(7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ)" ], "comment": "", "signatures": @@ -278,9 +361,9 @@ bma_txhistory_john = { "1Mn8q3K7N+R4GZEpAUm+XSyty1Uu+BuOy5t7BIRqgZcKqiaxfhAUfDBOcuk2i4TJy1oA5Rntby8hDN+cUCpvDg==" ], "hash": "5FB3CB80A982E2BDFBB3EA94673A74763F58CB2A", - "block_number": 12, - "time": 1421932454 - } + "block_number": 2, + "time": 1421932545 + }, ], "sending": [], "receiving": [] @@ -298,15 +381,15 @@ bma_udhistory_john = { "block_number": 2, "consumed": False, "time": 1435749971, - "amount": 5 + "amount": 5, + "base": 1 }, { - "block_number": 10, "consumed": False, "time": 1435836032, - "amount": 10 - + "amount": 10, + "base": 1 } ] }} @@ -319,16 +402,18 @@ bma_txsources_john = { { "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", "type": "D", - "number": 2, - "fingerprint": "4A317E3D676E9800E1E92AA2A7255BCEEFF31185", - "amount": 7 + "noffset": 2, + "identifier": "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365", + "amount": 7, + "base": 1 }, { "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", "type": "D", - "number": 4, - "fingerprint": "4A317E3D676E9800E1E92AA2A7255BCEEFF31185", - "amount": 9 + "noffset": 4, + "identifier": "A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5", + "amount": 9, + "base": 1 } ]} @@ -353,13 +438,15 @@ def get_mock(loop): mock.add_route('GET', '/blockchain/block/15', bma_blockchain_current, 200) - mock.add_route('GET', '/tx/history/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ/blocks/0/99', bma_txhistory_john, 200) + mock.add_route('GET', '/tx/history/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ/blocks/0/99', bma_txhistory_john, + 200) mock.add_route('GET', '/tx/sources/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ', bma_txsources_john, 200) mock.add_route('GET', '/ud/history/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ', bma_udhistory_john, 200) - mock.add_route('GET', '/wot/certifiers-of/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ', bma_certifiers_of_john, 200) + mock.add_route('GET', '/wot/certifiers-of/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ', bma_certifiers_of_john, + 200) mock.add_route('GET', '/wot/certified-by/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ', bma_certified_by_john, 200) @@ -369,16 +456,17 @@ def get_mock(loop): mock.add_route('GET', '/wot/lookup/doe', bma_lookup_doe, 200) - mock.add_route('GET', '/wot/lookup/FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn',bma_lookup_doe,200) + mock.add_route('GET', '/wot/lookup/FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn', bma_lookup_doe, 200) - mock.add_route('GET', '/blockchain/memberships/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ',bma_membership_john,200) + mock.add_route('GET', '/blockchain/memberships/7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ', bma_membership_john, + 200) mock.add_route('GET', '/wot/certifiers-of/FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn', - {'error':"No member matching this pubkey or uid"},404) + {'error': "No member matching this pubkey or uid"}, 404) mock.add_route('GET', '/blockchain/memberships/FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn', - {'error':"No member matching this pubkey or uid"}, 404) + {'error': "No member matching this pubkey or uid"}, 404) - mock.add_route('POST', '/tx/process', {},200,) + mock.add_route('POST', '/tx/process', {}, 200, ) return mock diff --git a/src/sakia/tests/mocks/server.py b/src/sakia/tests/mocks/server.py index d614a72ec6a04fa06cbb87a18ac3adac0180389e..e30930619d419229bc6358ac4387dd935f597315 100644 --- a/src/sakia/tests/mocks/server.py +++ b/src/sakia/tests/mocks/server.py @@ -6,25 +6,25 @@ from ucoinpy.documents import Peer def bma_peering_generator(port): return { - "version": 1, + "version": 2, "currency": "test_currency", "endpoints": [ "BASIC_MERKLED_API 127.0.0.1 {port}".format(port=port) ], "status": "UP", - "block": "30152-00003E7F9234E7542FCF669B69B0F84FF79CCCD3", + "block": "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", "signature": "cXuqZuDfyHvxYAEUkPH1TQ1M+8YNDpj8kiHGYi3LIaMqEdVqwVc4yQYGivjxFMYyngRfxXkyvqBKZA6rKOulCA==", - "raw": "Version: 1\nType: Peer\nCurrency: meta_brouzouf\nPublicKey: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\nBlock: 30152-00003E7F9234E7542FCF669B69B0F84FF79CCCD3\nEndpoints:\nBASIC_MERKLED_API 127.0.0.1 {port}\n".format(port=port), + "raw": "Version: 2\nType: Peer\nCurrency: meta_brouzouf\nPublicKey: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\nBlock: 30152-00003E7F9234E7542FCF669B69B0F84FF79CCCD3\nEndpoints:\nBASIC_MERKLED_API 127.0.0.1 {port}\n".format(port=port), "pubkey": "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk" } def peer_document_generator(port): - return Peer.from_signed_raw("""Version: 1 + return Peer.from_signed_raw("""Version: 2 Type: Peer Currency: meta_brouzouf PublicKey: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk -Block: 30152-00003E7F9234E7542FCF669B69B0F84FF79CCCD3 +Block: 0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855 Endpoints: BASIC_MERKLED_API 127.0.0.1 {port} cXuqZuDfyHvxYAEUkPH1TQ1M+8YNDpj8kiHGYi3LIaMqEdVqwVc4yQYGivjxFMYyngRfxXkyvqBKZA6rKOulCA== diff --git a/src/sakia/tests/unit/core/test_account.py b/src/sakia/tests/unit/core/test_account.py index c5134aa731c8a8059a01debd6f5f71d5a1468ab8..bdc8daffcd2c04aa920a7eedcbe020bc65a35d08 100644 --- a/src/sakia/tests/unit/core/test_account.py +++ b/src/sakia/tests/unit/core/test_account.py @@ -4,7 +4,7 @@ from PyQt5.QtCore import QLocale from sakia.core.registry.identities import IdentitiesRegistry, Identity from sakia.core import Account from sakia.tests import QuamashTest -from ucoinpy.documents import BlockId, SelfCertification +from ucoinpy.documents import BlockUID, SelfCertification class TestAccount(unittest.TestCase, QuamashTest): @@ -79,17 +79,17 @@ class TestAccount(unittest.TestCase, QuamashTest): "test_account", [], [], [], self.identities_registry) account_identity = MagicMock(autospec='sakia.core.registry.Identity') - account_identity.selfcert = CoroutineMock(return_value=SelfCertification(1, "meta_brouzouf", + account_identity.selfcert = CoroutineMock(return_value=SelfCertification(2, "meta_brouzouf", "H8uYXvyF6GWeCr8cwFJ6V5B8tNprwRdjepFNJBqivrzr", "test_account", 1000000000, "")) community = MagicMock(autospec='sakia.core.Community') - community.blockid = CoroutineMock(return_value=BlockId(3102, "0000C5336F0B64BFB87FF4BC858AE25726B88175")) + community.blockUID = CoroutineMock(return_value=BlockUID(3102, "0000C5336F0B64BFB87FF4BC858AE25726B88175")) self.identities_registry.future_find = CoroutineMock(return_value=account_identity) community.bma_access = MagicMock(autospec='sakia.core.net.api.bma.access.BmaAccess') response = Mock() response.json = CoroutineMock(return_value={ "signature": "H41/8OGV2W4CLKbE35kk5t1HJQsb3jEM0/QGLUf80CwJvGZf3HvVCcNtHPUFoUBKEDQO9mPK3KJkqOoxHpqHCw==", "membership": { - "version": "2", + "version": 2, "currency": "beta_brouzouf", "issuer": "HsLShAtzXTVxeUtQd7yi5Z5Zh4zNvbu8sTEZ53nfKcqY", "membership": "IN", @@ -105,7 +105,6 @@ class TestAccount(unittest.TestCase, QuamashTest): self.lp.run_until_complete(exec_test()) - def test_send_certification(self): cert_signal_sent = False def check_certification_accepted(): @@ -117,18 +116,22 @@ class TestAccount(unittest.TestCase, QuamashTest): self.identities_registry) account.certification_accepted.connect(check_certification_accepted) account_identity = MagicMock(autospec='sakia.core.registry.Identity') - account_identity.selfcert = CoroutineMock(return_value=SelfCertification(1, "meta_brouzouf", - "H8uYXvyF6GWeCr8cwFJ6V5B8tNprwRdjepFNJBqivrzr", "test_account", 1000000000, "")) + account_identity.selfcert = CoroutineMock(return_value=SelfCertification(2, "meta_brouzouf", + "H8uYXvyF6GWeCr8cwFJ6V5B8tNprwRdjepFNJBqivrzr", "test_account", + BlockUID(1000, "49E2A1D1131F1496FAD6EDAE794A9ADBFA8844029675E3732D3B027ABB780243"), + "82o1sNCh1bLpUXU6nacbK48HBcA9Eu2sPkL1/3c2GtDPxBUZd2U2sb7DxwJ54n6ce9G0Oy7nd1hCxN3fS0oADw==")) certified = MagicMock(autospec='sakia.core.registry.Identity') certified.uid = "john" certified.pubkey = "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ" certified.sigdate = 1441130831 - certified.selfcert = CoroutineMock(return_value=SelfCertification(1, "meta_brouzouf", - "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", "john", 1441130831, "")) + certified.selfcert = CoroutineMock(return_value=SelfCertification(2, "meta_brouzouf", + "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", "john", + BlockUID(1200, "49E2A1D1131F1496FAD6EDAE794A9ADBFA8844029675E3732D3B027ABB780243"), + "82o1sNCh1bLpUXU6nacbK48HBcA9Eu2sPkL1/3c2GtDPxBUZd2U2sb7DxwJ54n6ce9G0Oy7nd1hCxN3fS0oADw==")) community = MagicMock(autospec='sakia.core.Community') - community.blockid = CoroutineMock(return_value=BlockId(3102, "0000C5336F0B64BFB87FF4BC858AE25726B88175")) + community.blockUID = CoroutineMock(return_value=BlockUID(3102, "49E2A1D1131F1496FAD6EDAE794A9ADBFA8844029675E3732D3B027ABB780243")) self.identities_registry.future_find = CoroutineMock(side_effect=lambda pubkey, community :account_identity \ if pubkey == "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ" else certified) community.bma_access = MagicMock(autospec='sakia.core.net.api.bma.access.BmaAccess') diff --git a/src/sakia/tests/unit/core/test_bma_access.py b/src/sakia/tests/unit/core/test_bma_access.py index d382a5bac015fa36ddb10f474d8defd2fecebc65..1b61da8e617a1b0bd2f3424fcd22eccbe8dc1fe4 100644 --- a/src/sakia/tests/unit/core/test_bma_access.py +++ b/src/sakia/tests/unit/core/test_bma_access.py @@ -1,4 +1,5 @@ import unittest +from unittest.mock import Mock import time from PyQt5.QtCore import QLocale from sakia.core.registry.identities import Identity, IdentitiesRegistry, LocalState, BlockchainState @@ -20,7 +21,7 @@ class TestBmaAccess(unittest.TestCase, QuamashTest): self.application = Application(self.qapplication, self.lp, self.identities_registry) self.application.preferences['notifications'] = False - self.peer = Peer.from_signed_raw("""Version: 1 + self.peer = Peer.from_signed_raw("""Version: 2 Type: Peer Currency: meta_brouzouf PublicKey: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU @@ -32,7 +33,7 @@ BASIC_MERKLED_API ucoin.inso.ovh 80 self.node = Node(self.peer, "", "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", None, Node.ONLINE, - time.time(), {}, "ucoin", "0.12.0", 0) + time.time(), {}, "ucoin", "0.12.0", 0, Mock("aiohttp.ClientSession")) self.network = Network.create(self.node) self.bma_access = BmaAccess.create(self.network) self.community = Community("test_currency", self.network, self.bma_access) diff --git a/src/sakia/tests/unit/core/test_community.py b/src/sakia/tests/unit/core/test_community.py index 760764e330c3b007631edef042b841ef634a5021..16d2fd66a49a39b449979190a7d9797eed863f74 100644 --- a/src/sakia/tests/unit/core/test_community.py +++ b/src/sakia/tests/unit/core/test_community.py @@ -1,5 +1,5 @@ -import sys import unittest +from unittest.mock import Mock from pkg_resources import parse_version from PyQt5.QtCore import QLocale from sakia.core.net.api.bma.access import BmaAccess @@ -17,7 +17,7 @@ class TestCommunity(unittest.TestCase, QuamashTest): self.tearDownQuamash() def test_load_save_community(self): - network = Network("test_currency", []) + network = Network("test_currency", [], Mock("aiohttp.ClientSession")) bma_access = BmaAccess([], network) community = Community("test_currency", network, bma_access) @@ -25,3 +25,5 @@ class TestCommunity(unittest.TestCase, QuamashTest): community_from_json = Community.load(json_data, parse_version('0.12.0')) self.assertEqual(community.name, community_from_json.name) self.assertEqual(len(community.network._nodes), len(community_from_json.network._nodes)) + community_from_json.network.session.close() + diff --git a/src/sakia/tests/unit/core/test_identity.py b/src/sakia/tests/unit/core/test_identity.py index 0702b5902e8c2198ccd5d765cb8a7605ad48f6ce..4aa90b3e8f242eef7eab4b28d8203e368c63a17e 100644 --- a/src/sakia/tests/unit/core/test_identity.py +++ b/src/sakia/tests/unit/core/test_identity.py @@ -6,6 +6,7 @@ from sakia.core.registry.identities import Identity, LocalState, BlockchainState from sakia.tests.mocks.bma import nice_blockchain, corrupted from sakia.tests import QuamashTest from ucoinpy.api import bma +from ucoinpy.documents import BlockUID from sakia.tools.exceptions import MembershipNotFoundError @@ -30,9 +31,11 @@ class TestIdentity(unittest.TestCase, QuamashTest): if request is bma.blockchain.Block: return nice_blockchain.bma_blockchain_current - identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", 1441130831, + identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + BlockUID(20, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67"), LocalState.COMPLETED, BlockchainState.VALIDATED) - id_doe = Identity("doe", "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", 1441230831, + id_doe = Identity("doe", "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + BlockUID(101, "BAD49448A1AD73C978CEDCB8F137D20A5715EBAA739DAEF76B1E28EE67B2C00C"), LocalState.COMPLETED, BlockchainState.VALIDATED) self.community.bma_access.future_request = CoroutineMock(side_effect=bma_access) @@ -51,7 +54,8 @@ class TestIdentity(unittest.TestCase, QuamashTest): if request is bma.blockchain.Membership: return nice_blockchain.bma_membership_john - identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", 1441130831, + identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + BlockUID(20, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67"), LocalState.COMPLETED, BlockchainState.VALIDATED) self.community.bma_access.future_request = CoroutineMock(side_effect=bma_access) @@ -59,7 +63,7 @@ class TestIdentity(unittest.TestCase, QuamashTest): async def exec_test(): ms = await identity.membership(self.community) self.assertEqual(ms["blockNumber"], 0) - self.assertEqual(ms["blockHash"], "DA39A3EE5E6B4B0D3255BFEF95601890AFD80709") + self.assertEqual(ms["blockHash"], "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855") self.assertEqual(ms["membership"], "IN") self.assertEqual(ms["currency"], "test_currency") self.assertEqual(ms["written"], 10000) @@ -71,7 +75,8 @@ class TestIdentity(unittest.TestCase, QuamashTest): if request is bma.blockchain.Membership: return corrupted.bma_memberships_empty_array - identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", 1441130831, + identity = Identity("john", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + BlockUID(20, "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67"), LocalState.COMPLETED, BlockchainState.VALIDATED) self.community.bma_access.future_request = CoroutineMock(side_effect=bma_access) async def exec_test(): diff --git a/src/sakia/tests/unit/core/test_network.py b/src/sakia/tests/unit/core/test_network.py index 60b3a7071e85529f34601c98396bcddd5a502bec..575e880168f58bbfb77140da9c6a4f55c86b189c 100644 --- a/src/sakia/tests/unit/core/test_network.py +++ b/src/sakia/tests/unit/core/test_network.py @@ -1,8 +1,8 @@ -import sys +import aiohttp import unittest from unittest.mock import PropertyMock from asynctest import Mock, patch -from ucoinpy.documents.block import BlockId +from ucoinpy.documents.block import BlockUID from PyQt5.QtCore import QLocale from sakia.core.net import Network from sakia.tests import QuamashTest @@ -17,8 +17,8 @@ class TestCommunity(unittest.TestCase, QuamashTest): self.tearDownQuamash() def test_confirmations(self): - network = Network("test_currency", []) - Network.current_blockid = PropertyMock(return_value=BlockId(1000, "fbf9271d0df23ee03044795aebca8be06dd7f998".upper())) + network = Network("test_currency", [], Mock("aiohttp.ClientSession")) + Network.current_blockUID = PropertyMock(return_value=BlockUID(1000, "fbf9271d0df23ee03044795aebca8be06dd7f998".upper())) self.assertEqual(network.confirmations(996), 5) self.assertEqual(network.confirmations(900), 101) diff --git a/src/sakia/tests/unit/core/test_node.py b/src/sakia/tests/unit/core/test_node.py index 1191b28b1c252a66ec531532acc344db29755130..b1ea994b13d542151f00fc20b499205ee626d0fc 100644 --- a/src/sakia/tests/unit/core/test_node.py +++ b/src/sakia/tests/unit/core/test_node.py @@ -1,6 +1,7 @@ import unittest +from unittest.mock import Mock from asynctest import CoroutineMock, patch -from ucoinpy.documents import Peer, BlockId +from ucoinpy.documents import Peer, BlockUID from PyQt5.QtCore import QLocale from sakia.core.net import Node from sakia.tests import QuamashTest @@ -17,7 +18,7 @@ class TestNode(unittest.TestCase, QuamashTest): self.tearDownQuamash() def test_from_peer(self): - peer = Peer.from_signed_raw("""Version: 1 + peer = Peer.from_signed_raw("""Version: 2 Type: Peer Currency: meta_brouzouf PublicKey: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU @@ -26,7 +27,7 @@ Endpoints: BASIC_MERKLED_API ucoin.inso.ovh 80 82o1sNCh1bLpUXU6nacbK48HBcA9Eu2sPkL1/3c2GtDPxBUZd2U2sb7DxwJ54n6ce9G0Oy7nd1hCxN3fS0oADw== """) - node = Node.from_peer('meta_brouzouf', peer, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") + node = Node.from_peer('meta_brouzouf', peer, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU", Mock("aiohttp.ClientSession")) self.assertEqual(node.pubkey, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") self.assertEqual(node.endpoint.inline(), "BASIC_MERKLED_API ucoin.inso.ovh 80") self.assertEqual(node.currency, "meta_brouzouf") @@ -34,48 +35,29 @@ BASIC_MERKLED_API ucoin.inso.ovh 80 @patch('ucoinpy.api.bma.network.Peering') def test_from_address(self, peering): peering.return_value.get = CoroutineMock(return_value={ - "version": 1, + "version": 2, "currency": "meta_brouzouf", "endpoints": [ "BASIC_MERKLED_API ucoin.inso.ovh 80" ], "block": "48698-000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8", "signature": "82o1sNCh1bLpUXU6nacbK48HBcA9Eu2sPkL1/3c2GtDPxBUZd2U2sb7DxwJ54n6ce9G0Oy7nd1hCxN3fS0oADw==", - "raw": "Version: 1\nType: Peer\nCurrency: meta_brouzouf\nPublicKey: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU\nBlock: 48698-000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8\nEndpoints:\nBASIC_MERKLED_API ucoin.inso.ovh 80\n", + "raw": "Version: 2\nType: Peer\nCurrency: meta_brouzouf\nPublicKey: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU\nBlock: 48698-000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8\nEndpoints:\nBASIC_MERKLED_API ucoin.inso.ovh 80\n", "pubkey": "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU" }) async def exec_test(): - node = await Node.from_address("meta_brouzouf", "127.0.0.1", 9000) + node = await Node.from_address("meta_brouzouf", "127.0.0.1", 9000, Mock("aiohttp.ClientSession")) self.assertEqual(node.pubkey, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") self.assertEqual(node.endpoint.inline(), "BASIC_MERKLED_API ucoin.inso.ovh 80") self.assertEqual(node.currency, "meta_brouzouf") self.lp.run_until_complete(exec_test()) - def test_from_json_011(self): - json_data = {"version": "0.12.0", "state": 1, "fork_window": 0, "uid": "cgeek", - "block": nice_blockchain.bma_blockchain_current, - "endpoints": ["BASIC_MERKLED_API metab.ucoin.io 88.174.120.187 9201"], - "pubkey": "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", - "last_change": 1448199706.6561477, "currency": "meta_brouzouf", "sofware": "ucoin"} - node = Node.from_json("meta_brouzouf", json_data, parse_version('0.11.5')) - self.assertEqual(node.version, "0.12.0") - self.assertEqual(node.state, 1) - self.assertEqual(node.fork_window, 0) - self.assertEqual(node.uid, "cgeek") - self.assertEqual(node.block, nice_blockchain.bma_blockchain_current) - self.assertEqual(node.endpoint.inline(), "BASIC_MERKLED_API metab.ucoin.io 88.174.120.187 9201") - self.assertEqual(node.pubkey, "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk") - self.assertEqual(node.last_change, 1448199706.6561477) - self.assertEqual(node.currency, "meta_brouzouf") - self.assertEqual(node.peer.pubkey, "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk") - self.assertEqual(node.peer.blockid.number, 0) - def test_from_json_to_json(self): json_data = {"version": "0.12.0", "state": 1, "fork_window": 0, "uid": "inso", "block": nice_blockchain.bma_blockchain_current, - "peer": """Version: 1 + "peer": """Version: 2 Type: Peer Currency: meta_brouzouf PublicKey: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU @@ -86,7 +68,7 @@ BASIC_MERKLED_API ucoin.inso.ovh 80 """, "pubkey": "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU", "last_change": 1448199706.6561477, "software": "ucoin"} - node = Node.from_json("meta_brouzouf", json_data, parse_version('0.12.0')) + node = Node.from_json("meta_brouzouf", json_data, parse_version('0.12.0'), Mock("aiohttp.ClientSession")) self.assertEqual(node.version, "0.12.0") self.assertEqual(node.state, 1) self.assertEqual(node.fork_window, 0) @@ -97,15 +79,15 @@ BASIC_MERKLED_API ucoin.inso.ovh 80 self.assertEqual(node.last_change, 1448199706.6561477) self.assertEqual(node.currency, "meta_brouzouf") self.assertEqual(node.peer.pubkey, "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") - self.assertEqual(node.peer.blockid.number, 48698) - self.assertEqual(node.peer.blockid.sha_hash, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8") + self.assertEqual(node.peer.blockUID.number, 48698) + self.assertEqual(node.peer.blockUID.sha_hash, "000005E0F228038E4DDD4F6CA4ACB01EC88FBAF8") result = node.jsonify() for key in result: self.assertEqual(result[key], json_data[key], "Error with key {0}".format(key)) def test_jsonify_root_node(self): - peer = Peer.from_signed_raw("""Version: 1 + peer = Peer.from_signed_raw("""Version: 2 Type: Peer Currency: meta_brouzouf PublicKey: 8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU @@ -115,7 +97,7 @@ BASIC_MERKLED_API ucoin.inso.ovh 80 82o1sNCh1bLpUXU6nacbK48HBcA9Eu2sPkL1/3c2GtDPxBUZd2U2sb7DxwJ54n6ce9G0Oy7nd1hCxN3fS0oADw== """) node = Node(peer, "inso", "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU", nice_blockchain.bma_blockchain_current, - Node.ONLINE, 1111111111, {}, "ucoin", "0.12", 0) + Node.ONLINE, 1111111111, {}, "ucoin", "0.12", 0, Mock("aiohttp.ClientSession")) result = node.jsonify_root_node() self.assertEqual(result['pubkey'], "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU") self.assertEqual(result['uid'], "inso") diff --git a/src/sakia/tests/unit/core/test_wallet.py b/src/sakia/tests/unit/core/test_wallet.py index 04253105d291e1a8ee4264cd646690ff2bc76be3..787efe255d05b65151587da0155f11636578f036 100644 --- a/src/sakia/tests/unit/core/test_wallet.py +++ b/src/sakia/tests/unit/core/test_wallet.py @@ -1,8 +1,8 @@ -import sys import unittest -import asyncio -import quamash -import logging +import pypeg2 +from unittest.mock import MagicMock, PropertyMock +from asynctest import CoroutineMock +from ucoinpy.grammars import output from PyQt5.QtCore import QLocale from sakia.core.registry.identities import IdentitiesRegistry from sakia.core import Wallet @@ -28,3 +28,71 @@ class TestWallet(unittest.TestCase, QuamashTest): self.assertEqual(wallet.pubkey, wallet_from_json.pubkey) self.assertEqual(wallet.name, wallet_from_json.name) self.assertEqual(wallet._identities_registry, wallet_from_json._identities_registry) + + def test_prepare_tx(self): + community = MagicMock("sakia.core.Community") + community.currency = "test_currency" + cache = MagicMock("sakia.core.txhistory.TxHistory") + cache.available_sources = [{ + "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "type": "D", + "noffset": 2, + "identifier": "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365", + "amount": 15, + "base": 0 + }, + { + "pubkey": "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "type": "D", + "noffset": 4, + "identifier": "A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5", + "amount": 85, + "base": 0 + }, + { + "pubkey": "FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + "type": "T", + "noffset": 4, + "identifier": "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", + "amount": 11, + "base": 1 + }] + wallet = Wallet(0, "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", + "Wallet 1", self.identities_registry) + wallet.caches["test_currency"] = cache + tx = wallet.prepare_tx("FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn", + 100, "", community) + self.assertEqual(len(tx.issuers), 1) + self.assertEqual(tx.issuers[0], "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ") + self.assertEqual(len(tx.inputs), 2) + self.assertEqual(tx.inputs[0].origin_id, "FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365") + self.assertEqual(tx.inputs[0].source, "D") + self.assertEqual(tx.inputs[0].index, 2) + self.assertEqual(tx.inputs[1].origin_id, "A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5") + self.assertEqual(tx.inputs[1].source, "D") + self.assertEqual(tx.inputs[1].index, 4) + self.assertEqual(len(tx.outputs), 1) + self.assertEqual(tx.outputs[0].amount, 1) + self.assertEqual(tx.outputs[0].base, 2) + self.assertEqual(pypeg2.compose(tx.outputs[0].conditions, output.Condition), + "SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn)") + self.assertEqual(len(tx.unlocks), 2) + self.assertEqual(tx.unlocks[0].index, 0) + self.assertEqual(tx.unlocks[0].parameters[0].index, 0) + self.assertEqual(tx.unlocks[1].index, 1) + self.assertEqual(tx.unlocks[0].parameters[0].index, 0) + self.assertEqual(tx.raw(), """Version: 2 +Type: Transaction +Currency: test_currency +Locktime: 0 +Issuers: +7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ +Inputs: +D:FCAD5A388AC8A811B45A9334A375585E77071AA9F6E5B6896582961A6C66F365:2 +D:A0AC57E2E4B24D66F2D25E66D8501D8E881D9E6453D1789ED753D7D426537ED5:4 +Unlocks: +0:SIG(0) +1:SIG(0) +Outputs: +1:2:SIG(FADxcH5LmXGmGFgdixSes6nWnC4Vb4pRUBYT81zQRhjn) +Comment:""" + " \n") diff --git a/src/sakia/tests/unit/gui/test_context_menu.py b/src/sakia/tests/unit/gui/test_context_menu.py index d12a2aa223d1467acbb77dfdb872359d44563401..fde37a211e81b158b1fd7ba6886a20a9a3e59104 100644 --- a/src/sakia/tests/unit/gui/test_context_menu.py +++ b/src/sakia/tests/unit/gui/test_context_menu.py @@ -5,7 +5,7 @@ from PyQt5.QtCore import QLocale from sakia.tests import QuamashTest from sakia.tests.mocks.bma import nice_blockchain from sakia.gui.widgets.context_menu import ContextMenu -from ucoinpy.documents import Membership, BlockId +from ucoinpy.documents import Membership, BlockUID from sakia.tools.exceptions import MembershipNotFoundError @@ -73,20 +73,20 @@ class TestContextMenu(unittest.TestCase, QuamashTest): @patch('PyQt5.QtWidgets.QMenu', create=True) def test_copy_membership_to_clipboard(self, qmenu): ms_data = { - "version": 1, + "version": 2, "currency": "meta_brouzouf", "membership": "IN", "blockNumber": 49116, - "blockHash": "000004CA4F77E36CE52C23A9F2A8F2A259773CE9", + "blockHash": "7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", "written": 49119 } ms_document = Membership(ms_data["version"], ms_data["currency"], self.identity.pubkey, - BlockId(ms_data["blockNumber"], ms_data["blockHash"]), - ms_data["membership"], self.identity.uid, 1421787800, + BlockUID(ms_data["blockNumber"], ms_data["blockHash"]), + ms_data["membership"], self.identity.uid, "49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", "znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==") self.identity.membership = CoroutineMock(return_value=ms_data) self.community.get_block = CoroutineMock(return_value={ - "version": 1, + "version": 2, "nonce": 127424, "number": 49119, "powMin": 5, @@ -97,21 +97,45 @@ class TestContextMenu(unittest.TestCase, QuamashTest): "currency": "meta_brouzouf", "issuer": "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk", "signature": "ZmjhoRubftJ/T2WYQ3gaDeTGGUJ3beUshtlWn1k/r5opk0vt48KG3w+9JU0T9YFR5uezllaek9efoNwAHRBLDw==", - "hash": "0000075129361571E74380B686DE6E1E29FF8400", + "hash": "49E2A1D1131F1496FAD6EDAE794A9ADBFA8844029675E3732D3B027ABB780243", + "innerhash": "273DE1845F8A63677D69DD427E00DAD73D9AEDBA80356A2E0D2152939D9DAF0C", "parameters": "", "previousHash": "000005C27A1636FE07AB01766FBA060565142D79", "previousIssuer": "HBSSmqZjT4UQKsCntTSmZbu7iRP14HYtifLE6mW1PsBD", "dividend": None, "identities": [], "joiners": [ - "8Fi1VSTbjkXguwThF4v2ZxC5whK7pwG2vcGTkPUPjPGU:znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==:49116:000004CA4F77E36CE52C23A9F2A8F2A259773CE9:1421787800:inso" + "HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:A" ], "actives": [], "leavers": [], "excluded": [], + "revoked": [], "certifications": [], "transactions": [], - "raw": "Version: 1\nType: Block\nCurrency: meta_brouzouf\nNonce: 127424\nNumber: 49119\nPoWMin: 5\nTime: 1453921638\nMedianTime: 1453912797\nIssuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk\nPreviousHash: 000005C27A1636FE07AB01766FBA060565142D79\nPreviousIssuer: HBSSmqZjT4UQKsCntTSmZbu7iRP14HYtifLE6mW1PsBD\nMembersCount: 18\nIdentities:\nJoiners:\nHnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==:49116:000004CA4F77E36CE52C23A9F2A8F2A259773CE9:1421787800:A\nActives:\nLeavers:\nExcluded:\nCertifications:\nTransactions:\n" + "raw": """Version: 2 +Type: Block +Currency: meta_brouzouf +Number: 49119 +PoWMin: 5 +Time: 1453921638 +MedianTime: 1453912797 +Issuer: HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk +PreviousHash: 000005C27A1636FE07AB01766FBA060565142D79 +PreviousIssuer: HBSSmqZjT4UQKsCntTSmZbu7iRP14HYtifLE6mW1PsBD +MembersCount: 18 +Identities: +Joiners: +HnFcSms8jzwngtVomTTnzudZx7SHUQY8sVE1y8yBmULk:znWiWP7Sy9gg9pZq4YWKNpel8MM16VBM1lgBg2gWjSonnc+KVRCtQng5JB4JD0PgJJ0F8jdITuggFrRwqRfzAA==:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:49116-7518C700E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67:A +Actives: +Leavers: +Revoked: +Excluded: +Certifications: +Transactions: +InnerHash: 273DE1845F8A63677D69DD427E00DAD73D9AEDBA80356A2E0D2152939D9DAF0C +Nonce: 127424 +""" }) self.qapplication.clipboard().clear()