mirror of
https://github.com/darkrenaissance/darkfi.git
synced 2026-01-10 07:08:05 -05:00
add deg2 (dag_browser) code
This commit is contained in:
@@ -202,6 +202,7 @@ event-graph = [
|
||||
"sled-overlay",
|
||||
"smol",
|
||||
"tinyjson",
|
||||
"bs58",
|
||||
|
||||
"darkfi-serial",
|
||||
"darkfi-serial/collections",
|
||||
|
||||
257
bin/deg/deg2
Executable file
257
bin/deg/deg2
Executable file
@@ -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))
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<_>>();
|
||||
.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 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 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)]));
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user