From 953c352987aa23df0ded42f041ca227ea0a3edf8 Mon Sep 17 00:00:00 2001
From: Moul <moul@moul.re>
Date: Sat, 13 Apr 2019 22:05:08 +0200
Subject: [PATCH] [enh] #135, #189, #206: wot requests improvements:

- #135: remove get_uid_from_pubkey()
 Replace two uppers function with identity_of() which requests
/wot/identity-of/<pubkey/uid> and only returns members identities with
their pubkey and uid, which improves performances.
- change is_member() as a wrapper of identity_of()
- change usage in `id`, `cert`, `tx`, `net`, `blocks`

- #189: Handle in a good way wot/identity-of requests exceptions

- #206: Set a sleep for async requests: `blocks` and `net` for leaves
---
 silkaj/cert.py          | 12 +++++------
 silkaj/commands.py      | 25 +++++++++++------------
 silkaj/constants.py     |  1 +
 silkaj/network_tools.py |  3 +++
 silkaj/tx.py            | 22 ++++++++++----------
 silkaj/wot.py           | 45 ++++++++++++++++++++++++-----------------
 6 files changed, 60 insertions(+), 48 deletions(-)

diff --git a/silkaj/cert.py b/silkaj/cert.py
index 8bde21ee..f29d7301 100644
--- a/silkaj/cert.py
+++ b/silkaj/cert.py
@@ -26,7 +26,7 @@ from silkaj.tools import convert_time, message_exit, coroutine
 from silkaj.network_tools import ClientInstance, HeadBlock
 from silkaj.blockchain_tools import BlockchainParams
 from silkaj.license import license_approval
-from silkaj.wot import is_member, get_uid_from_pubkey, get_informations_for_identity
+from silkaj.wot import is_member, get_informations_for_identity
 
 
 @command("cert", help="Send certification")
@@ -42,8 +42,8 @@ async def send_certification(id_to_certify):
 
     # Check whether current user is member
     issuer_pubkey = key.pubkey
-    issuer_id = await get_uid_from_pubkey(issuer_pubkey)
-    if not await is_member(issuer_pubkey, issuer_id):
+    issuer = await is_member(issuer_pubkey)
+    if not issuer:
         message_exit("Current identity is not member.")
 
     if issuer_pubkey == id_to_certify["pubkey"]:
@@ -74,7 +74,7 @@ async def send_certification(id_to_certify):
 
     # Certification confirmation
     await certification_confirmation(
-        issuer_id, issuer_pubkey, id_to_certify, main_id_to_certify
+        issuer, issuer_pubkey, id_to_certify, main_id_to_certify
     )
 
     identity = Identity(
@@ -110,11 +110,11 @@ async def send_certification(id_to_certify):
 
 
 async def certification_confirmation(
-    issuer_id, issuer_pubkey, id_to_certify, main_id_to_certify
+    issuer, issuer_pubkey, id_to_certify, main_id_to_certify
 ):
     cert = list()
     cert.append(["Cert", "From", "–>", "To"])
-    cert.append(["ID", issuer_id, "–>", main_id_to_certify["uid"]])
+    cert.append(["ID", issuer["uid"], "–>", main_id_to_certify["uid"]])
     cert.append(["Pubkey", issuer_pubkey, "–>", id_to_certify["pubkey"]])
     params = await BlockchainParams().params
     cert_begins = convert_time(time(), "date")
diff --git a/silkaj/commands.py b/silkaj/commands.py
index 160a74e5..8370564a 100644
--- a/silkaj/commands.py
+++ b/silkaj/commands.py
@@ -17,11 +17,11 @@ along with Silkaj. If not, see <https://www.gnu.org/licenses/>.
 
 from click import command, option, argument, IntRange, get_terminal_size
 from datetime import datetime
-from time import sleep
 from os import system, popen
 from collections import OrderedDict
 from tabulate import tabulate
 from operator import itemgetter
+from asyncio import sleep
 import aiohttp
 from _socket import gaierror
 import jsonschema
@@ -30,7 +30,7 @@ from duniterpy.api.client import Client, parse_text
 from duniterpy.api.bma import blockchain, node, ws
 
 from silkaj.tools import coroutine
-from silkaj.wot import get_uid_from_pubkey
+from silkaj.wot import identity_of
 from silkaj.network_tools import (
     discover_peers,
     best_endpoint_address,
@@ -39,7 +39,7 @@ from silkaj.network_tools import (
     HeadBlock,
 )
 from silkaj.tools import convert_time, message_exit, CurrencySymbol
-from silkaj.constants import NO_MATCHING_ID
+from silkaj.constants import ASYNC_SLEEP
 
 
 @command("info", help="Display information about currency")
@@ -201,16 +201,14 @@ async def network_info(discover, sort):
         best_ep = best_endpoint_address(info, False)
         print(best_ep if best_ep is None else info[best_ep], end=" ")
         print(info["port"])
+        await sleep(ASYNC_SLEEP)
         try:
-            info["uid"] = await get_uid_from_pubkey(ep, info["pubkey"])
-            if info["uid"] is NO_MATCHING_ID:
-                info["uid"] = None
-            else:
-                info["member"] = "yes"
-                members += 1
+            info["uid"] = await identity_of(info["pubkey"])
+            info["uid"] = info["uid"]["uid"]
+            info["member"] = "yes"
+            members += 1
         except:
-            pass
-        if info.get("member") is None:
+            info["uid"] = None
             info["member"] = "no"
         info["pubkey"] = info["pubkey"][:5] + "…"
         for d in diffi["levels"]:
@@ -286,15 +284,16 @@ async def list_blocks(number, detailed):
         j += 1
     for pubkey in issuers_dict.keys():
         issuer = issuers_dict[pubkey]
-        uid = await get_uid_from_pubkey(issuer["pubkey"])
+        idty = await identity_of(issuer["pubkey"])
         for issuer2 in list_issuers:
             if (
                 issuer2.get("pubkey") is not None
                 and issuer.get("pubkey") is not None
                 and issuer2["pubkey"] == issuer["pubkey"]
             ):
-                issuer2["uid"] = uid
+                issuer2["uid"] = idty["uid"]
                 issuer2.pop("pubkey")
+        await sleep(ASYNC_SLEEP)
     await client.close()
     print(
         "Last {0} blocks from n°{1} to n°{2}".format(
diff --git a/silkaj/constants.py b/silkaj/constants.py
index eecf843d..5831112a 100644
--- a/silkaj/constants.py
+++ b/silkaj/constants.py
@@ -22,4 +22,5 @@ GTEST_SYMBOL = "ÄžTest"
 G1_DEFAULT_ENDPOINT = "g1.duniter.org", "443"
 G1_TEST_DEFAULT_ENDPOINT = "g1-test.duniter.org", "443"
 CONNECTION_TIMEOUT = 10
+ASYNC_SLEEP = 0.1
 SOURCES_PER_TX = 40
diff --git a/silkaj/network_tools.py b/silkaj/network_tools.py
index 21134d58..7ed3865a 100644
--- a/silkaj/network_tools.py
+++ b/silkaj/network_tools.py
@@ -21,6 +21,7 @@ import socket
 import logging
 from sys import exit, stderr
 from click import pass_context
+from asyncio import sleep
 from duniterpy.api.client import Client
 from duniterpy.api.bma import blockchain, network
 
@@ -28,6 +29,7 @@ from silkaj.constants import (
     G1_DEFAULT_ENDPOINT,
     G1_TEST_DEFAULT_ENDPOINT,
     CONNECTION_TIMEOUT,
+    ASYNC_SLEEP,
 )
 
 
@@ -75,6 +77,7 @@ async def get_peers_among_leaves(client):
     leaves = await client(network.peers, leaves=True)
     peers = list()
     for leaf in leaves["leaves"]:
+        await sleep(ASYNC_SLEEP + 0.05)
         leaf_response = await client(network.peers, leaf=leaf)
         peers.append(leaf_response["leaf"]["value"])
     return parse_endpoints(peers)
diff --git a/silkaj/tx.py b/silkaj/tx.py
index fcd030ff..d8fd8cca 100644
--- a/silkaj/tx.py
+++ b/silkaj/tx.py
@@ -25,14 +25,14 @@ from silkaj.network_tools import ClientInstance, HeadBlock
 from silkaj.crypto_tools import check_public_key
 from silkaj.tools import message_exit, CurrencySymbol, coroutine
 from silkaj.auth import auth_method
-from silkaj.wot import get_uid_from_pubkey
+from silkaj.wot import is_member
 from silkaj.money import (
     get_sources,
     get_amount_from_pubkey,
     UDValue,
     amount_in_current_base,
 )
-from silkaj.constants import NO_MATCHING_ID, SOURCES_PER_TX
+from silkaj.constants import SOURCES_PER_TX
 
 from duniterpy.api.bma.tx import process
 from duniterpy.documents import BlockUID, Transaction
@@ -165,19 +165,19 @@ async def transaction_confirmation(
         ]
     )
     tx.append(["from (pubkey)", issuer_pubkey])
-    id_from = await get_uid_from_pubkey(issuer_pubkey)
-    if id_from is not NO_MATCHING_ID:
-        tx.append(["from (id)", id_from])
+    id_from = await is_member(issuer_pubkey)
+    if id_from:
+        tx.append(["from (id)", id_from["uid"]])
     for outputAddress in outputAddresses:
         tx.append(["to (pubkey)", outputAddress])
-        id_to = await get_uid_from_pubkey(outputAddress)
-        if id_to is not NO_MATCHING_ID:
-            tx.append(["to (id)", id_to])
+        id_to = await is_member(outputAddress)
+        if id_to:
+            tx.append(["to (id)", id_to["uid"]])
     if outputBackChange:
         tx.append(["Backchange (pubkey)", outputBackChange])
-        id_backchange = await get_uid_from_pubkey(outputBackChange)
-        if id_backchange is not NO_MATCHING_ID:
-            tx.append(["Backchange (id)", id_backchange])
+        id_backchange = await is_member(outputBackChange)
+        if id_backchange:
+            tx.append(["Backchange (id)", id_backchange["uid"]])
     tx.append(["comment", comment])
     return tx
 
diff --git a/silkaj/wot.py b/silkaj/wot.py
index 582702bc..3ea2cc32 100644
--- a/silkaj/wot.py
+++ b/silkaj/wot.py
@@ -20,6 +20,7 @@ from time import time
 from tabulate import tabulate
 from collections import OrderedDict
 from duniterpy.api.bma import wot, blockchain
+from duniterpy.api.errors import DuniterError
 
 from silkaj.network_tools import ClientInstance
 from silkaj.crypto_tools import check_public_key
@@ -119,7 +120,9 @@ async def membership_status(certifications, certs, pubkey, req):
                 len(certifications["received"]) - params["sigQty"]
             ]
         )
-    member = await is_member(pubkey, certs["uid"])
+    member = await is_member(pubkey)
+    if member:
+        member = True
     print("member:", member)
     if req["revoked"]:
         print(
@@ -159,7 +162,7 @@ async def id_pubkey_correspondence(id_pubkey):
     if check_public_key(id_pubkey, False):
         print(
             "{} public key corresponds to identity: {}".format(
-                id_pubkey, await get_uid_from_pubkey(id_pubkey)
+                id_pubkey, await identity_of(id_pubkey)
             )
         )
     else:
@@ -193,15 +196,30 @@ async def get_informations_for_identity(id):
     message_exit(NO_MATCHING_ID)
 
 
-async def get_uid_from_pubkey(pubkey):
+async def identity_of(pubkey_uid):
+    """
+    Only works for members
+    Not able to get corresponding uid from a non-member identity
+    Able to know if an identity is member or not
+    """
+    client = ClientInstance().client
+    try:
+        return await client(wot.identity_of, pubkey_uid)
+    except DuniterError as e:
+        raise DuniterError(e)
+    except ValueError as e:
+        pass
+
+
+async def is_member(pubkey_uid):
+    """
+    Check identity is member
+    If member, return corresponding identity, else: False
+    """
     try:
-        client = ClientInstance().client
-        lookups = await client(wot.lookup, pubkey)
+        return await identity_of(pubkey_uid)
     except:
-        return NO_MATCHING_ID
-    for lookup in lookups["results"]:
-        if lookup["uids"][0]["uid"] != pubkey:
-            return lookup["uids"][0]["uid"]
+        return False
 
 
 async def get_informations_for_identities(identifier):
@@ -216,12 +234,3 @@ async def get_informations_for_identities(identifier):
     except:
         return NO_MATCHING_ID
     return results["results"]
-
-
-async def is_member(pubkey, uid):
-    client = ClientInstance().client
-    members = await client(wot.members)
-    for member in members["results"]:
-        if pubkey in member["pubkey"] and uid in member["uid"]:
-            return True
-    return False
-- 
GitLab