/ bin / dnet / src / model.py
model.py
  1  # This file is part of DarkFi (https://dark.fi)
  2  #
  3  # Copyright (C) 2020-2025 Dyne.org foundation
  4  #
  5  # This program is free software: you can redistribute it and/or modify
  6  # it under the terms of the GNU Affero General Public License as
  7  # published by the Free Software Foundation, either version 3 of the
  8  # License, or (at your option) any later version.
  9  #
 10  # This program is distributed in the hope that it will be useful,
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  # GNU Affero General Public License for more details.
 14  #
 15  # You should have received a copy of the GNU Affero General Public License
 16  # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17  
 18  import logging, time 
 19  import datetime as dt
 20  from collections import defaultdict as dd
 21  
 22  
 23  class Model:
 24  
 25      def __init__(self):
 26          self.nodes = {}
 27          self.liliths = {}
 28  
 29      def add_node(self, node):
 30          channel_lookup = {}
 31          name = list(node.keys())[0]
 32          values = list(node.values())[0]
 33          info = values['result']
 34          channels = info['channels']
 35          
 36          self.nodes[name] = {}
 37          self.nodes[name]['outbound'] = {}
 38          self.nodes[name]['inbound'] = {}
 39          self.nodes[name]['manual'] = {}
 40          self.nodes[name]['direct'] = {}
 41          self.nodes[name]['direct_peer_discovery'] = None
 42          self.nodes[name]['event'] = {}
 43          self.nodes[name]['seed'] = {}
 44          self.nodes[name]['msgs'] = dd(list)
 45  
 46          for channel in channels:
 47              id = channel['id']
 48              channel_lookup[id] = channel
 49  
 50          for channel in channels:
 51              if channel['session'] != 'inbound':
 52                  continue
 53              id = channel['id']
 54              url = channel_lookup[id]['url']
 55              self.nodes[name]['inbound'][f'{id}'] = url
 56  
 57          for i, id in enumerate(info['outbound_slots']):
 58              if id == 0:
 59                  outbounds = self.nodes[name]['outbound'][f'{i}'] = ['none', 0]
 60                  continue
 61              assert id in channel_lookup
 62              url = channel_lookup[id]['url']
 63              outbounds = self.nodes[name]['outbound'][f'{i}'] = [url, id]
 64  
 65          for channel in channels:
 66              if channel['session'] != 'seed':
 67                  continue
 68              id = channel['id']
 69              url = channel['url']
 70              self.nodes[name]['seed'][f'{id}'] = url
 71  
 72          for channel in channels:
 73              if channel['session'] != 'manual':
 74                  continue
 75              id = channel['id']
 76              url = channel['url']
 77              self.nodes[name]['manual'][f'{id}'] = url
 78  
 79          for channel in channels:
 80              if channel['session'] != 'direct':
 81                  continue
 82              id = channel['id']
 83              url = channel['url']
 84              self.nodes[name]['direct'][f'{url}'] = [url, id]
 85      
 86      def add_offline(self, node, is_lilith: bool):
 87          name = list(node.keys())[0]
 88          values = list(node.values())[0]
 89          if is_lilith:
 90              self.liliths[name] = values
 91          else:
 92              self.nodes[name] = values
 93  
 94      def add_event(self, event):
 95          name = list(event.keys())[0]
 96          values = list(event.values())[0]
 97          params = values.get('params')
 98          event = params[0].get('event')
 99          info = params[0].get('info')
100  
101          t = time.localtime()
102          current_time = time.strftime('%H:%M:%S', t)
103  
104          match event:                        
105              case 'send':
106                  nano = info.get('time')
107                  cmd = info.get('cmd')
108                  chan = info.get('chan')
109                  addr = chan.get('addr')
110                  t = (dt.datetime
111                          .fromtimestamp(int(nano)/1000000000)
112                          .strftime('%H:%M:%S'))
113                  msgs = self.nodes[name]['msgs']
114                  msgs[addr].append((t, event, cmd))
115              case 'recv':
116                  nano = info.get('time')
117                  cmd = info.get('cmd')
118                  chan = info.get('chan')
119                  addr = chan.get('addr')
120                  t = (dt.datetime
121                          .fromtimestamp(int(nano)/1000000000)
122                          .strftime('%H:%M:%S'))
123                  msgs = self.nodes[name]['msgs']
124                  msgs[addr].append((t, event, cmd))
125              case 'inbound_connected':
126                  addr = info['addr']
127                  id = info.get('channel_id')
128                  self.nodes[name]['inbound'][f'{id}'] = addr
129                  logging.debug(f'{current_time}  inbound (connect):    {addr}')
130              case 'inbound_disconnected':
131                  addr = info['addr']
132                  id = info.get('channel_id')
133                  self.nodes[name]['inbound'][f'{id}'] = {}
134                  logging.debug(f'{current_time}  inbound (disconnect): {addr}')
135              case 'outbound_slot_sleeping':
136                  slot = info['slot']
137                  event = self.nodes[name]['event']
138                  event[(f'{name}', f'{slot}')] = ['sleeping', 0]
139                  logging.debug(f'{current_time}  slot {slot}: sleeping')
140              case 'outbound_slot_connecting':
141                  slot = info['slot']
142                  addr = info['addr']
143                  event = self.nodes[name]['event']
144                  event[(f'{name}', f'{slot}')] = [f'connecting: addr={addr}', 0]
145                  logging.debug(f'{current_time}  slot {slot}: connecting   addr={addr}')
146              case 'outbound_slot_connected':
147                  slot = info['slot']
148                  addr = info['addr']
149                  event = self.nodes[name]['event']
150                  event[(f'{name}', f'{slot}')] = [f'connected: addr={addr}', 0]
151                  id = info['channel_id']
152                  self.nodes[name]['outbound'][f'{slot}'] = [addr, id]
153                  logging.debug(f'{current_time}  slot {slot}: connected    addr={addr}')
154              case 'outbound_slot_disconnected':
155                  slot = info['slot']
156                  err = info['err']
157                  event = self.nodes[name]['event']
158                  event[(f'{name}', f'{slot}')] = [f'disconnected: {err}', 0]
159                  logging.debug(f'{current_time}  slot {slot}: disconnected err={err}')
160              case 'outbound_peer_discovery':
161                  attempt = info['attempt']
162                  state = info['state']
163                  event = self.nodes[name]['event']
164                  key = (f'{name}', 'outbound')
165                  event[key] = f'peer discovery: {state} (attempt {attempt})'
166                  logging.debug(f'{current_time}  peer_discovery: {state} (attempt {attempt})')
167              case 'direct_connecting':
168                  connect_addr = info['connect_addr']
169                  event = self.nodes[name]['event']
170                  event[(f'{name}', f'{connect_addr}')] = [f'connecting: addr={connect_addr}', 0]
171                  self.nodes[name]['direct'][f'{connect_addr}'] = ['', 0]
172                  logging.debug(f'{current_time}  direct (connecting):   addr={connect_addr}')
173              case 'direct_connected':
174                  connect_addr = info['connect_addr']
175                  addr = info['addr']
176                  id = info['channel_id']
177                  event = self.nodes[name]['event']
178                  event[(f'{name}', f'{connect_addr}')] = [addr, id]
179                  self.nodes[name]['direct'][f'{connect_addr}'] = [addr, id]
180                  logging.debug(f'{current_time}  direct (connected):    addr={addr}')
181              case 'direct_disconnected':
182                  connect_addr = info['connect_addr']
183                  err = info['err']
184                  self.nodes[name]['direct'][f'{connect_addr}'] = {}
185                  logging.debug(f'{current_time}  direct (disconnected): addr={connect_addr} err={err}')
186              case 'direct_peer_discovery':
187                  if self.nodes[name]['direct_peer_discovery'] is None:
188                      self.nodes[name]['direct_peer_discovery'] = 0
189                  attempt = info['attempt']
190                  state = info['state']
191                  event = self.nodes[name]['event']
192                  key = (f'{name}', 'direct')
193                  event[key] = f'peer discovery: {state} (attempt {attempt})'
194                  logging.debug(f'{current_time}  peer_discovery: {state} (attempt {attempt})')
195  
196  
197      def add_lilith(self, lilith):
198          key = list(lilith.keys())[0]
199          values = list(lilith.values())[0]
200          info = values['result']
201          spawns = info['spawns']
202  
203          self.liliths[key] = {}
204          self.liliths[key]['spawns'] = {}
205          
206          for (i, spawn) in enumerate(spawns):
207              name = spawn['name']
208              urls = spawn['urls']
209              whitelist = spawn['whitelist']
210              greylist = spawn['greylist']
211              goldlist = spawn['goldlist']
212  
213              spawn = self.liliths[key]['spawns'][name] = {}
214              spawn['urls'] = urls
215              spawn['whitelist'] = whitelist
216              spawn['greylist'] = greylist
217              spawn['goldlist'] = goldlist
218  
219          #logging.debug(f'added lilith {self.liliths}')
220  
221      def __repr__(self):
222          return f'{self.nodes}'
223          return f'{self.liliths}'