Coverage for silkaj/wot/idty_tools.py: 100%

77 statements  

« prev     ^ index     » next       coverage.py v7.6.10, created at 2025-01-20 12:29 +0000

1# Copyright 2016-2025 Maël Azimi <m.a@moul.re> 

2# 

3# Silkaj is free software: you can redistribute it and/or modify 

4# it under the terms of the GNU Affero General Public License as published by 

5# the Free Software Foundation, either version 3 of the License, or 

6# (at your option) any later version. 

7# 

8# Silkaj is distributed in the hope that it will be useful, 

9# but WITHOUT ANY WARRANTY; without even the implied warranty of 

10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

11# GNU Affero General Public License for more details. 

12# 

13# You should have received a copy of the GNU Affero General Public License 

14# along with Silkaj. If not, see <https://www.gnu.org/licenses/>. 

15 

16import shutil 

17import sys 

18import urllib 

19from typing import Union 

20 

21import arrow 

22import rich_click as click 

23from duniterpy.api import bma 

24from duniterpy.documents import BlockID, Identity, Revocation 

25from texttable import Texttable 

26 

27from silkaj.constants import ALL 

28from silkaj.network import client_instance 

29from silkaj.public_key import gen_pubkey_checksum 

30from silkaj.wot.tools import wot_lookup 

31 

32 

33def display_identity(idty: Identity) -> Texttable: 

34 """ 

35 Creates a table containing the identity infos 

36 """ 

37 client = client_instance() 

38 id_table = [] 

39 id_table.append(["Public key", gen_pubkey_checksum(idty.pubkey)]) 

40 id_table.append(["User ID", idty.uid]) 

41 id_table.append(["Blockstamp", str(idty.block_id)]) 

42 creation_block = client(bma.blockchain.block, idty.block_id.number) 

43 creation_date = arrow.get(creation_block["time"]).to("local").format(ALL) 

44 id_table.append(["Created on", creation_date]) 

45 # display infos 

46 table = Texttable(max_width=shutil.get_terminal_size().columns) 

47 table.add_rows(id_table, header=False) 

48 return table 

49 

50 

51def check_many_identities(document: Union[Identity, Revocation]) -> bool: 

52 """ 

53 Checks if many identities match the one looked after. 

54 Returns True if the same identity is found, False if not. 

55 """ 

56 doc_type = document.__class__.__name__ 

57 error_no_identical_id = f"{doc_type} document does not match any valid identity." 

58 idty = document if doc_type == "Identity" else document.identity 

59 

60 try: 

61 results_pubkey = wot_lookup(idty.pubkey) 

62 results_uid = wot_lookup(idty.uid) 

63 except urllib.error.HTTPError: 

64 sys.exit( 

65 f"{error_no_identical_id}\nuid: {idty.uid}\npubkey: \ 

66{gen_pubkey_checksum(idty.pubkey)}", 

67 ) 

68 

69 # get all matching identities 

70 lookup_ids = merge_ids_lists(results_pubkey, results_uid, idty.currency) 

71 match = False 

72 for n, lookup in enumerate(lookup_ids): 

73 if idty == lookup: 

74 lookup_ids.pop(n) 

75 match = True 

76 break 

77 alternate_ids = display_alternate_ids(lookup_ids).draw() 

78 if match: 

79 if len(lookup_ids) >= 1: 

80 click.echo(f"One matching identity!\nSimilar identities:\n{alternate_ids}") 

81 return True 

82 click.echo(f"{error_no_identical_id}\nSimilar identities:\n{alternate_ids}") 

83 return False 

84 

85 

86def display_alternate_ids(ids_list: list) -> Texttable: 

87 labels = ["uid", "public key", "timestamp"] 

88 table = Texttable(max_width=shutil.get_terminal_size().columns) 

89 table.header(labels) 

90 for _id in ids_list: 

91 table.add_row( 

92 [_id.uid, gen_pubkey_checksum(_id.pubkey), str(_id.block_id)[:12]], 

93 ) 

94 return table 

95 

96 

97def merge_ids_lists(lookups_pubkey: list, lookups_uid: list, currency: str) -> list: 

98 """ 

99 merge two lists of identities and remove duplicate identities. 

100 """ 

101 ids = ids_list_from_lookups(lookups_pubkey, currency) 

102 ids_uid = ids_list_from_lookups(lookups_uid, currency) 

103 for _id in ids_uid: 

104 # __equal__ does not work. This is condition "id in ids". 

105 for listed_id in ids: 

106 if _id.signed_raw() == listed_id.signed_raw(): 

107 id_in_ids = True 

108 break 

109 id_in_ids = False 

110 if not id_in_ids: 

111 ids.append(_id) 

112 return ids 

113 

114 

115def ids_list_from_lookups(lookups: list, currency: str) -> list: 

116 ids = [] 

117 for lookup in lookups: 

118 pubkey = lookup["pubkey"] 

119 lookup_ids = lookup["uids"] 

120 for _id in lookup_ids: 

121 appended_id = Identity( 

122 currency=currency, 

123 pubkey=pubkey, 

124 uid=_id["uid"], 

125 block_id=BlockID.from_str(_id["meta"]["timestamp"]), 

126 ) 

127 appended_id.signature = _id["self"] 

128 ids.append(appended_id) 

129 return ids