async def load_requirements(self, identity): """ Refresh a given identity information :param sakia.data.entities.Identity identity: :return: """ try: requirements = await self._bma_connector.get(self.currency, bma.wot.requirements, req_args={'search': identity.pubkey}) for identity_data in requirements['identities']: if not identity.uid or identity.uid == identity_data["uid"]: if not identity.blockstamp or identity.blockstamp == block_uid(identity_data["meta"]["timestamp"]): identity.uid = identity_data["uid"] identity.blockstamp = block_uid(identity_data["meta"]["timestamp"]) identity.timestamp = self._blockchain_processor.rounded_timestamp(self.currency, identity.blockstamp.number) identity.outdistanced = identity_data["outdistanced"] identity.written = identity_data["wasMember"] identity.sentry = identity_data["isSentry"] identity.member = identity_data["membershipExpiresIn"] > 0 median_time = self._blockchain_processor.time(self.currency) expiration_time = self._blockchain_processor.parameters(self.currency).ms_validity identity.membership_timestamp = median_time - (expiration_time - identity_data["membershipExpiresIn"]) # We save connections pubkeys if self._identities_processor.get_identity(self.currency, identity.pubkey, identity.uid): self._identities_processor.insert_or_update_identity(identity) except errors.DuniterError as e: if e.ucode == errors.NO_MEMBER_MATCHING_PUB_OR_UID: pass else: self._logger.debug(str(e)) except NoPeerAvailable as e: self._logger.debug(str(e)) return identity
def current_buid(self, currency): c = self._conn.execute("""SELECT COUNT(`uid`) FROM `nodes` WHERE member == 1 AND currency == ? LIMIT 1;""", (currency,)) data = c.fetchone() if data and data[0] > 3: c = self._conn.execute("""SELECT `current_buid`, COUNT(`current_buid`) AS `value_occurrence` FROM `nodes` WHERE member == 1 AND currency == ? GROUP BY `current_buid` ORDER BY `value_occurrence` DESC LIMIT 1;""", (currency,)) data = c.fetchone() if data: return block_uid(data[0]) else: c = self._conn.execute("""SELECT `current_buid`, COUNT(`current_buid`) AS `value_occurrence` FROM `nodes` WHERE currency == ? GROUP BY `current_buid` ORDER BY `value_occurrence` DESC LIMIT 1;""", (currency,)) data = c.fetchone() if data: return block_uid(data[0]) return BlockUID.empty()
def test_add_get_drop_node(meta_repo): nodes_repo = NodesRepo(meta_repo.conn) inserted = Node(currency="testcurrency", pubkey="7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", endpoints="""BASIC_MERKLED_API test-net.duniter.fr 13.222.11.22 9201 BASIC_MERKLED_API testnet.duniter.org 80 UNKNOWNAPI some useless information""", peer_blockstamp=BlockUID.empty(), uid="doe", current_buid="15-76543400E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", current_ts=12376543345, previous_buid="14-AEFFCB00E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", state=0, software="duniter", version="0.30.17") nodes_repo.insert(inserted) node = nodes_repo.get_one(currency="testcurrency", pubkey="7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ") assert node.currency == "testcurrency" assert node.pubkey == "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ" assert node.endpoints[0] == BMAEndpoint("test-net.duniter.fr", "13.222.11.22", None, 9201) assert node.endpoints[1] == BMAEndpoint("testnet.duniter.org", None, None, 80) assert node.endpoints[2] == UnknownEndpoint("UNKNOWNAPI", ["some", "useless", "information"]) assert node.previous_buid == block_uid("14-AEFFCB00E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") assert node.current_buid == block_uid("15-76543400E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") assert node.state == 0 assert node.software == "duniter" assert node.version == "0.30.17" assert node.merkle_peers_root == Node.MERKLE_EMPTY_ROOT assert node.merkle_peers_leaves == tuple() nodes_repo.drop(node) node = nodes_repo.get_one(pubkey="7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ") assert node is None
def current_buid(self, currency): c = self._conn.execute( """SELECT COUNT(`uid`) FROM `nodes` WHERE member == 1 AND currency == ? LIMIT 1;""", (currency, )) data = c.fetchone() if data and data[0] > 3: c = self._conn.execute( """SELECT `current_buid`, COUNT(`current_buid`) AS `value_occurrence` FROM `nodes` WHERE member == 1 AND currency == ? GROUP BY `current_buid` ORDER BY `value_occurrence` DESC LIMIT 1;""", (currency, )) data = c.fetchone() if data: return block_uid(data[0]) else: c = self._conn.execute( """SELECT `current_buid`, COUNT(`current_buid`) AS `value_occurrence` FROM `nodes` WHERE currency == ? GROUP BY `current_buid` ORDER BY `value_occurrence` DESC LIMIT 1;""", (currency, )) data = c.fetchone() if data: return block_uid(data[0]) return BlockUID.empty()
async def find_from_pubkey(self, currency, pubkey): """ Get the most recent identity corresponding to a pubkey from the network and the local db :param currency: :param pubkey: :rtype: sakia.data.entities.Identity """ found_identity = Identity(currency=currency, pubkey=pubkey) identities = self._identities_repo.get_all(currency=currency, pubkey=pubkey) for idty in identities: if idty.blockstamp > found_identity.blockstamp: found_identity = idty if not found_identity.uid: try: data = await self._bma_connector.get( currency, bma.wot.lookup, req_args={'search': pubkey}) for result in data['results']: if result["pubkey"] == pubkey: uids = result['uids'] for uid_data in uids: identity = Identity(currency, pubkey) identity.uid = uid_data['uid'] identity.blockstamp = block_uid( uid_data['meta']['timestamp']) identity.signature = uid_data['self'] if identity.blockstamp >= found_identity.blockstamp: found_identity = identity except (errors.DuniterError, asyncio.TimeoutError, ClientError) as e: self._logger.debug(str(e)) except NoPeerAvailable as e: self._logger.debug(str(e)) return found_identity
async def certification_confirmation(issuer, issuer_pubkey, pubkey_to_certify, idty_to_certify): cert = list() cert.append( ["Cert", "Issuer", "–>", "Recipient: Published: #block-hash date"]) client = ClientInstance().client idty_timestamp = idty_to_certify["meta"]["timestamp"] block_uid_idty = block_uid(idty_timestamp) block = await client(bma.blockchain.block, block_uid_idty.number) block_uid_date = (": #" + idty_timestamp[:15] + "… " + convert_time(block["time"], "all")) cert.append( ["ID", issuer["uid"], "–>", idty_to_certify["uid"] + block_uid_date]) cert.append([ "Pubkey", display_pubkey_and_checksum(issuer_pubkey), "–>", display_pubkey_and_checksum(pubkey_to_certify), ]) params = await BlockchainParams().params cert_begins = convert_time(time(), "date") cert_ends = convert_time(time() + params["sigValidity"], "date") cert.append(["Valid", cert_begins, "—>", cert_ends]) echo(tabulate(cert, tablefmt="fancy_grid")) if not confirm("Do you confirm sending this certification?"): await client.close() sys.exit(SUCCESS_EXIT_STATUS)
async def find_from_pubkey(self, currency, pubkey): """ Get the most recent identity corresponding to a pubkey from the network and the local db :param currency: :param pubkey: :rtype: sakia.data.entities.Identity """ found_identity = Identity(currency=currency, pubkey=pubkey) identities = self._identities_repo.get_all(currency=currency, pubkey=pubkey) for idty in identities: if idty.blockstamp > found_identity.blockstamp: found_identity = idty if not found_identity.uid: try: data = await self._bma_connector.get(currency, bma.wot.lookup, req_args={'search': pubkey}) for result in data['results']: if result["pubkey"] == pubkey: uids = result['uids'] for uid_data in uids: identity = Identity(currency, pubkey) identity.uid = uid_data['uid'] identity.blockstamp = block_uid(uid_data['meta']['timestamp']) identity.signature = uid_data['self'] if identity.blockstamp >= found_identity.blockstamp: found_identity = identity except (errors.DuniterError, asyncio.TimeoutError, ClientError) as e: self._logger.debug(str(e)) except NoPeerAvailable as e: self._logger.debug(str(e)) return found_identity
async def initialize_identity(self, identity, log_stream, progress): """ Initialize memberships and other data for given identity :param sakia.data.entities.Identity identity: :param function log_stream: callback to log text :param function progress: callback for progressbar """ log_stream("Requesting membership data") progress(1 / 3) try: memberships_data = await self._bma_connector.get( identity.currency, bma.blockchain.memberships, req_args={'search': identity.pubkey}) if block_uid(memberships_data['sigDate']) == identity.blockstamp \ and memberships_data['uid'] == identity.uid: identity.written = True for ms in memberships_data['memberships']: if ms['written'] and ms[ 'written'] > identity.membership_written_on: identity.membership_buid = BlockUID( ms['blockNumber'], ms['blockHash']) identity.membership_type = ms['membership'] identity.membership_written_on = ms['written'] progress(1 / 3) if identity.membership_buid: log_stream("Requesting membership timestamp") ms_block_data = await self._bma_connector.get( identity.currency, bma.blockchain.block, req_args={'number': identity.membership_buid.number}) if ms_block_data: identity.membership_timestamp = ms_block_data[ 'medianTime'] log_stream("Requesting identity requirements status") progress(1 / 3) requirements_data = await self._bma_connector.get( identity.currency, bma.wot.requirements, req_args={'search': identity.pubkey}) identity_data = next( (data for data in requirements_data["identities"] if data["pubkey"] == identity.pubkey)) identity.member = identity_data['membershipExpiresIn'] > 0 identity.written = identity_data['wasMember'] identity.sentry = identity_data["isSentry"] identity.outdistanced = identity_data['outdistanced'] self.insert_or_update_identity(identity) except errors.DuniterError as e: if e.ucode == errors.NO_MEMBER_MATCHING_PUB_OR_UID: identity.written = False self.insert_or_update_identity(identity) else: raise
async def load_certs_in_lookup(self, identity, certifiers, certified): """ :param sakia.data.entities.Identity identity: the identity :param list[sakia.data.entities.Certification] certifiers: the list of certifiers got in /wot/certifiers-of :param list[sakia.data.entities.Certification] certified: the list of certified got in /wot/certified-by """ try: lookup_data = await self._bma_connector.get( self.currency, bma.wot.lookup, {'search': identity.pubkey}) for result in lookup_data['results']: if result["pubkey"] == identity.pubkey: for uid_data in result['uids']: if not identity.uid or uid_data["uid"] == identity.uid: if not identity.blockstamp or identity.blockstamp == block_uid( uid_data["meta"]["timestamp"]): for other_data in uid_data["others"]: cert = Certification( currency=self.currency, certified=identity.pubkey, certifier=other_data["pubkey"], block=other_data["meta"] ["block_number"], timestamp=0, signature=other_data['signature']) if cert not in certifiers: cert.timestamp = await self._blockchain_processor.timestamp( self.currency, cert.block) certifiers.append(cert) # We save connections pubkeys if identity.pubkey in self._connections_processor.pubkeys( ): self._certs_processor.insert_or_update_certification( cert) for signed_data in result["signed"]: cert = Certification( currency=self.currency, certified=signed_data["pubkey"], certifier=identity.pubkey, block=signed_data["cert_time"]["block"], timestamp=0, signature=signed_data['signature']) if cert not in certified: certified.append(cert) # We save connections pubkeys if identity.pubkey in self._connections_processor.pubkeys( ): cert.timestamp = await self._blockchain_processor.timestamp( self.currency, cert.block) self._certs_processor.insert_or_update_certification( cert) except errors.DuniterError as e: logging.debug("Certified by error : {0}".format(str(e))) except NoPeerAvailable as e: logging.debug(str(e)) return certifiers, certified
async def send_membership(dry_run): # Authentication key = auth.auth_method() # Get the identity information head_block = await HeadBlock().head_block membership_timestamp = BlockUID(head_block["number"], head_block["hash"]) identity = (await wot.choose_identity(key.pubkey))[0] identity_uid = identity["uid"] identity_timestamp = block_uid(identity["meta"]["timestamp"]) # Display license and ask for confirmation currency = head_block["currency"] if not dry_run: license_approval(currency) # Confirmation client = ClientInstance().client await display_confirmation_table(identity_uid, key.pubkey, identity_timestamp) if not dry_run: if not click.confirm( "Do you confirm sending a membership document for this identity?" ): await client.close() sys.exit(SUCCESS_EXIT_STATUS) membership = generate_membership_document( currency, key.pubkey, membership_timestamp, identity_uid, identity_timestamp, ) # Sign document membership.sign([key]) logging.debug(membership.signed_raw()) if dry_run: await client.close() click.echo(membership.signed_raw()) sys.exit(SUCCESS_EXIT_STATUS) # Send the membership signed raw document to the node response = await client(bma.blockchain.membership, membership.signed_raw()) if response.status == 200: print("Membership successfully sent") else: print("Error while publishing membership: {0}".format(await response.text())) logging.debug(await response.text()) await client.close()
def test_add_update_node(meta_repo): nodes_repo = NodesRepo(meta_repo.conn) node = Node("testcurrency", "7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ", """BASIC_MERKLED_API test-net.duniter.fr 13.222.11.22 9201 BASIC_MERKLED_API testnet.duniter.org 80 UNKNOWNAPI some useless information""", BlockUID.empty(), "doe", "15-76543400E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 12376543345, "14-AEFFCB00E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67", 0, "duniter") nodes_repo.insert(node) node.previous_buid = node.current_buid node.current_buid = "16-77543400E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67" nodes_repo.update(node) node2 = nodes_repo.get_one(pubkey="7Aqw6Efa9EzE7gtsc8SveLLrM7gm6NEGoywSv4FJx6pZ") assert node2.current_buid == block_uid("16-77543400E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67") assert node2.previous_buid == block_uid("15-76543400E78B56CC21FB1DDC6CBAB24E0FACC9A798F5ED8736EA007F38617D67")
async def initialize_identity(self, identity, log_stream, progress): """ Initialize memberships and other data for given identity :param sakia.data.entities.Identity identity: :param function log_stream: callback to log text :param function progress: callback for progressbar """ log_stream("Requesting membership data") progress(1/3) try: memberships_data = await self._bma_connector.get(identity.currency, bma.blockchain.memberships, req_args={'search': identity.pubkey}) if block_uid(memberships_data['sigDate']) == identity.blockstamp \ and memberships_data['uid'] == identity.uid: identity.written = True for ms in memberships_data['memberships']: if ms['written'] and ms['written'] > identity.membership_written_on: identity.membership_buid = BlockUID(ms['blockNumber'], ms['blockHash']) identity.membership_type = ms['membership'] identity.membership_written_on = ms['written'] progress(1 / 3) if identity.membership_buid: log_stream("Requesting membership timestamp") ms_block_data = await self._bma_connector.get(identity.currency, bma.blockchain.block, req_args={'number': identity.membership_buid.number}) if ms_block_data: identity.membership_timestamp = ms_block_data['medianTime'] log_stream("Requesting identity requirements status") progress(1 / 3) requirements_data = await self._bma_connector.get(identity.currency, bma.wot.requirements, req_args={'search': identity.pubkey}) identity_data = next((data for data in requirements_data["identities"] if data["pubkey"] == identity.pubkey)) identity.member = identity_data['membershipExpiresIn'] > 0 identity.written = identity_data['wasMember'] identity.sentry = identity_data["isSentry"] identity.outdistanced = identity_data['outdistanced'] self.insert_or_update_identity(identity) except errors.DuniterError as e: if e.ucode == errors.NO_MEMBER_MATCHING_PUB_OR_UID: identity.written = False self.insert_or_update_identity(identity) else: raise
async def certify(self, connection, secret_key, password, identity): """ Certify an other identity :param sakia.data.entities.Connection connection: the connection published :param str secret_key: the private key salt :param str password: the private key password :param sakia.data.entities.Identity identity: the identity certified """ self._logger.debug("Certdata") blockUID = self._blockchain_processor.current_buid(connection.currency) if not identity.signature: lookup_data = await self._bma_connector.get( connection.currency, bma.wot.lookup, req_args={'search': identity.pubkey}) for uid_data in next(data["uids"] for data in lookup_data["results"] if data["pubkey"] == identity.pubkey): if uid_data["uid"] == identity.uid and block_uid( uid_data["meta"]["timestamp"]) == identity.blockstamp: identity.signature = uid_data["self"] break else: return False, "Could not find certified identity signature" certification = Certification(10, connection.currency, connection.pubkey, identity.pubkey, blockUID, None) key = SigningKey(secret_key, password, connection.scrypt_params) certification.sign(identity.document(), [key]) signed_cert = certification.signed_raw(identity.document()) self._logger.debug("Certification : {0}".format(signed_cert)) timestamp = self._blockchain_processor.time(connection.currency) responses = await self._bma_connector.broadcast( connection.currency, bma.wot.certify, req_args={'cert': signed_cert}) result = await parse_bma_responses(responses) if result[0]: self._identities_processor.insert_or_update_identity(identity) self._certifications_processor.create_or_update_certification( connection.currency, certification, timestamp, BlockUID.empty()) return result
async def certify(self, connection, secret_key, password, identity): """ Certify an other identity :param sakia.data.entities.Connection connection: the connection published :param str secret_key: the private key salt :param str password: the private key password :param sakia.data.entities.Identity identity: the identity certified """ self._logger.debug("Certdata") blockUID = self._blockchain_processor.current_buid(connection.currency) if not identity.signature: lookup_data = await self._bma_connector.get(connection.currency, bma.wot.lookup, req_args={'search': identity.pubkey}) for uid_data in next(data["uids"] for data in lookup_data["results"] if data["pubkey"] == identity.pubkey): if uid_data["uid"] == identity.uid and block_uid(uid_data["meta"]["timestamp"]) == identity.blockstamp: identity.signature = uid_data["self"] break else: return False, "Could not find certified identity signature" certification = Certification(10, connection.currency, connection.pubkey, identity.pubkey, blockUID, None) key = SigningKey(secret_key, password, connection.scrypt_params) certification.sign(identity.document(), [key]) signed_cert = certification.signed_raw(identity.document()) self._logger.debug("Certification : {0}".format(signed_cert)) timestamp = self._blockchain_processor.time(connection.currency) responses = await self._bma_connector.broadcast(connection.currency, bma.wot.certify, req_args={'cert': signed_cert}) result = await parse_bma_responses(responses) if result[0]: self._identities_processor.insert_or_update_identity(identity) self._certifications_processor.create_or_update_certification(connection.currency, certification, timestamp, None) return result
async def send_certification(uid_pubkey_to_certify): client = ClientInstance().client checked_pubkey = is_pubkey_and_check(uid_pubkey_to_certify) if checked_pubkey: uid_pubkey_to_certify = checked_pubkey idty_to_certify, pubkey_to_certify, send_certs = await wot.choose_identity( uid_pubkey_to_certify) # Authentication key = auth_method() # Check whether current user is member issuer_pubkey = key.pubkey issuer = await wot.is_member(issuer_pubkey) if not issuer: message_exit("Current identity is not member.") if issuer_pubkey == pubkey_to_certify: message_exit("You can’t certify yourself!") # Check if the certification can be renewed req = await client(bma.wot.requirements, pubkey_to_certify) req = req["identities"][0] for cert in req["certifications"]: if cert["from"] == issuer_pubkey: params = await BlockchainParams().params # Ğ1: 0<–>2y - 2y + 2m # ĞT: 0<–>4.8m - 4.8m + 12.5d renewable = cert["expiresIn"] - params["sigValidity"] + params[ "sigReplay"] if renewable > 0: renewable_date = convert_time(time() + renewable, "date") message_exit("Certification renewable the " + renewable_date) # Check if the certification is already in the pending certifications for pending_cert in req["pendingCerts"]: if pending_cert["from"] == issuer_pubkey: message_exit("Certification is currently been processed") # Display license and ask for confirmation head = await HeadBlock().head_block currency = head["currency"] license_approval(currency) # Certification confirmation await certification_confirmation(issuer, issuer_pubkey, pubkey_to_certify, idty_to_certify) identity = Identity( version=10, currency=currency, pubkey=pubkey_to_certify, uid=idty_to_certify["uid"], ts=block_uid(idty_to_certify["meta"]["timestamp"]), signature=idty_to_certify["self"], ) certification = Certification( version=10, currency=currency, pubkey_from=issuer_pubkey, identity=identity, timestamp=BlockUID(head["number"], head["hash"]), signature="", ) # Sign document certification.sign([key]) # Send certification document response = await client(bma.wot.certify, certification.signed_raw()) if response.status == 200: print("Certification successfully sent.") else: print("Error while publishing certification: {0}".format( await response.text())) await client.close()
) from silkaj.tui import display_pubkey_and_checksum # AsyncMock available from Python 3.8. asynctest is used for Py < 3.8 if sys.version_info[1] > 7: from unittest.mock import AsyncMock else: from asynctest.mock import CoroutineMock as AsyncMock # To be moved/merged into tests/patched.py or tests/patched/<module_name>.py currency = "g1" pubkey = "EA7Dsw39ShZg4SpURsrgMaMqrweJPUFPYHwZA8e92e3D" identity_timestamp = block_uid( "0-E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855" ) identity_uid = "toto" membership_timestamp = block_uid( "48000-0000010D30B1284D34123E036B7BE0A449AE9F2B928A77D7D20E3BDEAC7EE14C" ) def patched_auth_method(): return SigningKey.from_credentials(identity_uid, identity_uid) async def patched_choose_identity(pubkey): return ( {"uid": identity_uid, "meta": {"timestamp": identity_timestamp}}, pubkey,