hc_sleuth/
context_log.rs

1use std::{collections::HashSet, io::BufRead};
2
3use holochain_types::prelude::*;
4use once_cell::sync::OnceCell;
5use tracing_subscriber::{prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt};
6
7use super::*;
8
9pub type ContextSubscriber = aitia::logging::AitiaSubscriber<Context>;
10
11// #[derive(Debug, derive_more::From)]
12pub type CtxError = String;
13pub type ContextResult<T> = Result<T, CtxError>;
14
15pub fn init_subscriber() -> ContextSubscriber {
16    let w = SUBSCRIBER.get_or_init(ContextSubscriber::default).clone();
17    let ww = w.clone();
18    tracing_subscriber::registry()
19        .with(holochain_trace::standard_layer(std::io::stderr).unwrap())
20        .with(aitia::logging::tracing_layer(move || ww.clone()))
21        .init();
22    w
23}
24
25pub static SUBSCRIBER: OnceCell<ContextSubscriber> = OnceCell::new();
26
27#[derive(Default, Debug)]
28pub struct Context {
29    /// All facts ever recorded
30    pub facts: HashSet<Event>,
31
32    /// Track which agents are part of which nodes
33    pub map_node_to_agents: HashMap<SleuthId, HashSet<AgentPubKey>>,
34
35    /// Track which node an agent is part of
36    pub map_agent_to_node: HashMap<AgentPubKey, SleuthId>,
37
38    /// Track the sys validation deps for an op hash
39    pub map_op_to_sysval_dep_hashes: HashMap<OpRef, Vec<ActionHash>>,
40
41    /// Track the app validation deps for an op hash
42    pub map_op_to_appval_dep_hash: HashMap<OpRef, HashSet<AnyDhtHash>>,
43
44    /// Track which op a dependency is part of
45    pub map_dep_hash_to_op: HashMap<AnyDhtHash, OpRef>,
46
47    /// Map the (action hash + op type) representation to the actual op hash
48    pub map_action_to_op: HashMap<ChainOpAction, OpRef>,
49
50    /// The full info associated with each op hash
51    pub op_info: HashMap<OpRef, OpInfo>,
52}
53
54impl Context {
55    pub fn from_file(mut r: impl BufRead) -> Self {
56        use aitia::logging::Log;
57        let mut la = Self::default();
58        let mut line = String::new();
59        while let Ok(_bytes) = r.read_line(&mut line) {
60            if let Some(fact) = Self::parse(&line) {
61                la.apply(fact);
62            }
63        }
64        la
65    }
66
67    pub fn check(&self, fact: &Event) -> bool {
68        self.facts.contains(fact)
69    }
70
71    pub fn node_agents(&self, id: &SleuthId) -> ContextResult<&HashSet<AgentPubKey>> {
72        self.map_node_to_agents
73            .get(id)
74            .ok_or(format!("node_agents({id})"))
75    }
76
77    pub fn agent_node(&self, agent: &AgentPubKey) -> ContextResult<&SleuthId> {
78        self.map_agent_to_node
79            .get(agent)
80            .ok_or(format!("agent_node({agent})"))
81    }
82
83    /// Get the sys validation dependency of this op hash if applicable
84    pub fn sysval_op_deps(&self, op: &OpRef) -> ContextResult<Vec<&OpInfo>> {
85        self.map_op_to_sysval_dep_hashes
86            .get(op)
87            .ok_or(format!("map_op_to_sysval_dep_hash({op})"))?
88            .iter()
89            .map(|h| {
90                self.map_dep_hash_to_op
91                    .get(&h.clone().into())
92                    .ok_or(format!("map_dep_hash_to_op({h})"))
93                    .and_then(|d| self.op_info(d))
94            })
95            .collect::<Result<Vec<_>, _>>()
96    }
97
98    /// Get the app validation dependencies of this op hash
99    pub fn appval_op_deps(&self, op: &OpRef) -> ContextResult<HashSet<&OpInfo>> {
100        self.map_op_to_appval_dep_hash
101            .get(op)
102            .ok_or(format!("map_op_to_appval_dep_hash({op})"))?
103            .iter()
104            .map(|h| {
105                self.map_dep_hash_to_op
106                    .get(h)
107                    .ok_or(format!("map_dep_hash_to_op({h})"))
108            })
109            .collect::<Result<Vec<_>, _>>()?
110            .into_iter()
111            .map(|d| self.op_info(d))
112            .collect()
113    }
114
115    pub fn op_info(&self, op: &OpRef) -> ContextResult<&OpInfo> {
116        self.op_info.get(op).ok_or(format!("op_info({op})"))
117    }
118
119    pub fn op_from_action(&self, action: ActionHash, op_type: ChainOpType) -> ContextResult<OpRef> {
120        let oa = ChainOpAction(action, op_type);
121        self.map_action_to_op
122            .get(&oa)
123            .cloned()
124            .ok_or(format!("map_action_to_op({oa:?})"))
125    }
126
127    pub fn as_if(&mut self) {
128        todo!()
129    }
130
131    pub fn all_events_for_topic() {}
132}
133
134impl aitia::logging::Log for Context {
135    type Fact = Event;
136
137    fn apply(&mut self, fact: Event) {
138        match fact.clone() {
139            Event::Integrated { .. } => {}
140            Event::AppValidated { .. } => {}
141            Event::SysValidated { .. } => {}
142            Event::MissingAppValDep { by: _, op, deps } => {
143                self.map_op_to_appval_dep_hash
144                    .entry(op)
145                    .or_default()
146                    .extend(deps);
147            }
148            Event::Fetched { .. } => {}
149            Event::ReceivedHash { .. } => {}
150            Event::SentHash { .. } => {}
151            Event::Authored { by: _, op } => {
152                // TODO: add check that the same op is not authored twice?
153                let op_hash = op.as_hash();
154                let a = match &op.op {
155                    DhtOpLite::Chain(op) => ChainOpAction::from((**op).clone()),
156                    _ => unimplemented!("hc_sleuth can only handle chain ops"),
157                };
158                for h in op.fetch_dependency_hashes() {
159                    self.map_dep_hash_to_op.insert(h, op_hash.clone());
160                }
161                self.map_action_to_op.insert(a, op_hash.clone());
162                self.map_op_to_sysval_dep_hashes
163                    .insert(op_hash.clone(), op.dep.clone());
164                self.op_info.insert(op_hash.clone(), op);
165            }
166            Event::AgentJoined { node, agent } => {
167                self.map_agent_to_node.insert(agent.clone(), node.clone());
168                self.map_node_to_agents
169                    .entry(node)
170                    .or_default()
171                    .insert(agent);
172            }
173            Event::SweetConductorShutdown { node } => {
174                if let Some(agents) = self.map_node_to_agents.remove(&node) {
175                    for a in agents {
176                        self.map_agent_to_node.remove(&a);
177                    }
178                }
179            }
180        }
181        let duplicate = self.facts.insert(fact.clone());
182        if duplicate {
183            tracing::warn!("Duplicate fact {:?}", fact);
184        }
185    }
186}