dasman 2 лет назад
Родитель
Сommit
214458322a
5 измененных файлов с 317 добавлено и 27 удалено
  1. 1 0
      Cargo.toml
  2. 257 0
      bin/deg/deg2
  3. 4 4
      src/event_graph/event.rs
  4. 41 23
      src/event_graph/mod.rs
  5. 14 0
      src/rpc/from_impl.rs

+ 1 - 0
Cargo.toml

@@ -202,6 +202,7 @@ event-graph = [
     "sled-overlay",
     "smol",
     "tinyjson",
+    "bs58",
 
     "darkfi-serial",
     "darkfi-serial/collections",

+ 257 - 0
bin/deg/deg2

@@ -0,0 +1,257 @@
+#!/usr/bin/python3
+
+# This file is part of DarkFi (https://dark.fi)
+#
+# Copyright (C) 2020-2024 Dyne.org foundation
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+import asyncio, random, re, sys, base58, json
+import base58
+import urwid as u
+import networkx as nx
+# import matplotlib.pyplot as plt
+import src.util
+
+from os.path import join
+
+# # Create a directed graph
+# dag = nx.DiGraph()
+
+# # Add edges to the graph (this also adds nodes)
+# dag.add_edges_from([
+#     ("root", "a"),
+#     ("a", "b"),
+#     ("a", "e"),
+#     ("b", "c"),
+#     ("b", "d"),
+#     ("d", "e")
+# ])
+
+class JsonRpc:
+
+    async def start(self, server, port):
+        reader, writer = await asyncio.open_connection(server, port, limit=1024 * 128)
+        self.reader = reader
+        self.writer = writer
+
+    async def stop(self):
+        self.writer.close()
+        await self.writer.wait_closed()
+
+    async def _make_request(self, method, params):
+        ident = random.randint(0, 2**16)
+        #print(ident)
+        request = {
+            "jsonrpc": "2.0",
+            "method": method,
+            "params": params,
+            "id": ident,
+        }
+
+        message = json.dumps(request) + "\n"
+        self.writer.write(message.encode())
+        await self.writer.drain()
+
+        data = await self.reader.readline()
+        message = data.decode().strip()
+        response = json.loads(message)
+        #print(response)
+        return response
+
+    async def _subscribe(self, method, params):
+        ident = random.randint(0, 2**16)
+        request = {
+            "jsonrpc": "2.0",
+            "method": method,
+            "params": params,
+            "id": ident,
+        }
+
+        message = json.dumps(request) + "\n"
+        self.writer.write(message.encode())
+        await self.writer.drain()
+        #print("Subscribed")
+
+    async def ping(self):
+        return await self._make_request("ping", [])
+
+    async def dnet_switch(self, state):
+        return await self._make_request("dnet.switch", [state])
+
+    async def dnet_subscribe_events(self):
+        return await self._subscribe("dnet.subscribe_events", [])
+    
+    async def deg_switch(self, state):
+        return await self._make_request("deg.switch", [state])
+
+
+class ListItem(u.WidgetWrap):
+    
+    def __init__ (self, event):
+        self.content = event
+        layer_num = int(event["layer"])
+        layer = "layer " + str(layer_num) if layer_num != 0 else "genesis"
+        t = u.AttrWrap(u.Text(layer), "event", "event_selected")
+        u.WidgetWrap.__init__(self, t)
+
+    def selectable (self):
+        return True
+    
+    def keypress(self, size, key):
+        return key
+
+class ListView(u.WidgetWrap):
+
+    def __init__(self):
+        u.register_signal(self.__class__, ['show_details'])
+        self.walker = u.SimpleFocusListWalker([])
+        lb = u.ListBox(self.walker)
+        u.WidgetWrap.__init__(self, lb)
+
+    def modified(self):
+        focus_w, _ = self.walker.get_focus()
+        u.emit_signal(self, 'show_details', focus_w.content)
+
+    def set_data(self, events):
+        events_widgets = [ListItem(e) for e in events]
+        u.disconnect_signal(self.walker, 'modified', self.modified)
+
+        while len(self.walker) > 0:
+            self.walker.pop()
+        
+        self.walker.extend(events_widgets)
+        u.connect_signal(self.walker, "modified", self.modified)
+        self.walker.set_focus(0)
+
+class DetailView(u.WidgetWrap):
+    
+    def __init__ (self):
+        t = u.Text("")
+        u.WidgetWrap.__init__(self, t)
+        
+    def set_event(self, c):
+        s = f'Hash: {c["hash"]}\nChildren: {c["children"]}\nContent: {c["content"]}\nLayer: {c["layer"]}'
+        self._w.set_text(s)
+
+class App(object):
+    
+    def unhandled_input(self, key):
+        if key in ('q',):
+            raise u.ExitMainLoop()
+
+    def show_details(self, event):
+        self.detail_view.set_event(event)
+        
+    def __init__(self):
+        self.palette = {
+            ("bg",               "white",       "black"),
+            ("event",            "white",       "black"),
+            ("event_selected",   "white",       "yellow"),
+            ("footer",           "white, bold", "dark red")
+        }
+
+        self.list_view = ListView()
+        self.detail_view = DetailView()
+        u.connect_signal(self.list_view, 'show_details', self.show_details)
+        footer = u.AttrWrap(u.Text(" Q to exit"), "footer") 
+        col_rows = u.raw_display.Screen().get_cols_rows()
+        h = col_rows[0] - 2
+        f1 = u.Filler(self.list_view, valign='top', height=h)
+        f2 = u.Filler(self.detail_view, valign='top')
+        c_list = u.LineBox(f1, title="Layers")
+        c_details = u.LineBox(f2, title="Details")
+        columns = u.Columns([('weight', 15, c_list), ('weight', 85, c_details)])            
+        frame = u.AttrMap(u.Frame(body=columns, footer=footer), 'bg')
+        self.loop = u.MainLoop(frame, self.palette, unhandled_input=self.unhandled_input)
+
+    async def update_data(self, config):
+        host = config['host']
+        port = config['port']
+        rpc = JsonRpc()
+        while True:
+            try:
+                await rpc.start(host, port)
+                break
+            except OSError:
+                print("Error: Couldn't connent to rpc")
+                exit(-1)
+
+        await rpc.deg_switch(True)
+        await rpc.deg_switch(False)
+
+        json_result = await rpc._make_request("eventgraph.get_info", [])
+        
+        if json_result['result']['eventgraph_info']:
+            dag_dict = json_result['result']['eventgraph_info']['dag']
+            dag_list = list(dag_dict.items())
+            # sorted_dag = sorted(dag_list, key=lambda x:x[1]['layer'])
+
+            # genesis_hash = sorted_dag[0][0]
+
+            parent_child_pairs = []
+            for item in dag_list:
+                parents = item[1]['parents']
+                child = item[0]
+                for parent in parents:
+                    if parent == '0' * 64:
+                        continue
+                    parent_child_pairs.append((parent, child))
+                    
+            # Create a directed graph
+            dag = nx.DiGraph()
+
+            # Add edges to the graph (this also adds nodes)
+            dag.add_edges_from(parent_child_pairs)
+            l = []
+            topological_order = list(nx.topological_sort(dag))
+            for node in topological_order:
+                event_details = dag_dict.get(node) # details
+                layer = int(event_details['layer'])
+                content = event_details['content'] # event content
+                # print(content)
+                pattern = r'\\x[0-9A-Fa-f]{2}'
+                decoded_str = str(base58.b58decode(content))
+                matches = re.split(pattern, decoded_str)
+                children = dag.successors(node)
+                l.append({"layer":f"{layer}", "hash":f"{node}", "children":f"{list(children)}", "content":f"{matches[1:]}"})
+            
+            self.list_view.set_data(l)
+
+    async def start(self, config):
+        await self.update_data(config)
+        self.loop.run()
+
+async def main(argv):
+
+    os = src.util.get_os()
+    config_path = src.util.user_config_dir('darkfi', os)
+
+    suffix = '.toml'
+    filename = 'deg_config'
+    path = join(config_path, filename + suffix)
+    config = src.util.spawn_config(path)
+    config = config['nodes'][0]
+
+    if len(argv) > 1:
+        if argv[1] in ['darkirc', 'irc']:
+            config['port'] = 26660
+        elif argv[1] in ['taud', 'tau']:
+            config['port'] = 23330
+
+    app = App()
+    await app.start(config)
+    
+
+asyncio.run(main(sys.argv))

+ 4 - 4
src/event_graph/event.rs

@@ -32,13 +32,13 @@ use super::{
 #[derive(Debug, Clone, SerialEncodable, SerialDecodable)]
 pub struct Event {
     /// Timestamp of the event
-    pub(super) timestamp: u64,
+    pub(crate) timestamp: u64,
     /// Content of the event
-    pub(super) content: Vec<u8>,
+    pub(crate) content: Vec<u8>,
     /// Parent nodes in the event DAG
-    pub(super) parents: [blake3::Hash; N_EVENT_PARENTS],
+    pub(crate) parents: [blake3::Hash; N_EVENT_PARENTS],
     /// DAG layer index of the event
-    pub(super) layer: u64,
+    pub(crate) layer: u64,
 }
 
 impl Event {

+ 41 - 23
src/event_graph/mod.rs

@@ -838,31 +838,49 @@ impl EventGraph {
     }
 
     pub async fn eventgraph_info(&self, id: u16, _params: JsonValue) -> JsonResult {
-        let u_tips = self.unreferenced_tips.read().await.clone();
-        let u_tips_vals = u_tips
-            .into_values()
-            .map(|v| v.into_iter().map(|x| JsonValue::String(x.to_string())).collect::<Vec<_>>())
-            .collect::<Vec<_>>()
-            .concat();
+        let mut graph = HashMap::new();
+        for iter_elem in self.dag.iter() {
+            let (id, val) = iter_elem.unwrap();
+            let id = blake3::Hash::from_bytes((&id as &[u8]).try_into().unwrap());
+            let val: Event = deserialize_async(&val).await.unwrap();
+            graph.insert(id, val);
+        }
 
-        let b_ids = self
-            .broadcasted_ids
-            .read()
-            .await
-            .clone()
+        let json_graph = graph
             .into_iter()
-            .map(|id| JsonValue::String(id.to_string()))
-            .collect::<Vec<_>>();
-
-        let values = json_map([
-            ("unreferenced_tips", JsonValue::Array(u_tips_vals)),
-            ("broadcasted_ids", JsonValue::Array(b_ids)),
-            ("synced", JsonValue::Boolean(*self.synced.read().await)),
-            (
-                "current_genesis",
-                JsonValue::String(self.current_genesis.read().await.clone().id().to_string()),
-            ),
-        ]);
+            .map(|(k, v)| {
+                let key = k.to_string();
+                let value = JsonValue::from(v);
+                (key, value)
+            })
+            .collect();
+        let values = json_map([("dag", JsonValue::Object(json_graph))]);
+
+        // let u_tips = self.unreferenced_tips.read().await.clone();
+        // let u_tips_vals = u_tips
+        //     .into_values()
+        //     .map(|v| v.into_iter().map(|x| JsonValue::String(x.to_string())).collect::<Vec<_>>())
+        //     .collect::<Vec<_>>()
+        //     .concat();
+
+        // let b_ids = self
+        //     .broadcasted_ids
+        //     .read()
+        //     .await
+        //     .clone()
+        //     .into_iter()
+        //     .map(|id| JsonValue::String(id.to_string()))
+        //     .collect::<Vec<_>>();
+
+        // let values = json_map([
+        //     ("unreferenced_tips", JsonValue::Array(u_tips_vals)),
+        //     ("broadcasted_ids", JsonValue::Array(b_ids)),
+        //     ("synced", JsonValue::Boolean(*self.synced.read().await)),
+        //     (
+        //         "current_genesis",
+        //         JsonValue::String(self.current_genesis.read().await.clone().id().to_string()),
+        //     ),
+        // ]);
 
         let result = JsonValue::Object(HashMap::from([("eventgraph_info".to_string(), values)]));
 

+ 14 - 0
src/rpc/from_impl.rs

@@ -130,6 +130,20 @@ impl From<net::dnet::DnetEvent> for JsonValue {
     }
 }
 
+#[cfg(feature = "event-graph")]
+impl From<event_graph::Event> for JsonValue {
+    fn from(event: event_graph::Event) -> JsonValue {
+        let parents =
+            event.parents.into_iter().map(|id| JsonStr(id.to_string())).collect::<Vec<_>>();
+        json_map([
+            ("timestamp", JsonNum(event.timestamp as f64)),
+            ("content", JsonStr(bs58::encode(event.content()).into_string())),
+            ("parents", JsonArray(parents)),
+            ("layer", JsonNum(event.layer as f64)),
+        ])
+    }
+}
+
 #[cfg(feature = "event-graph")]
 impl From<event_graph::deg::MessageInfo> for JsonValue {
     fn from(info: event_graph::deg::MessageInfo) -> JsonValue {