Skip to content

Commit 5c1d8ed

Browse files
authored
Merge pull request #89 from kinode-dao/dr/valid-note-valid-fact
valid_entry
2 parents 4eccb97 + 8fc0c8f commit 5c1d8ed

File tree

1 file changed

+99
-16
lines changed

1 file changed

+99
-16
lines changed

src/kimap.rs

Lines changed: 99 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,15 @@ pub struct Note {
236236
pub data: Bytes,
237237
}
238238

239+
/// A fact log from the kimap, converted to a 'resolved' format using
240+
/// namespace data saved in the kns_indexer
241+
#[derive(Clone, Debug, Deserialize, Serialize)]
242+
pub struct Fact {
243+
pub fact: String,
244+
pub parent_path: String,
245+
pub data: Bytes,
246+
}
247+
239248
/// Errors that can occur when decoding a log from the kimap using
240249
/// [`decode_mint_log`] or [`decode_note_log`].
241250
#[derive(Clone, Debug, Deserialize, Serialize)]
@@ -257,24 +266,47 @@ pub enum DecodeLogError {
257266
///
258267
/// This checks a **single name**, not the full path-name. A full path-name
259268
/// is comprised of valid names separated by `.`
260-
pub fn valid_name(name: &str, note: bool) -> bool {
269+
pub fn valid_entry(entry: &str, note: bool, fact: bool) -> bool {
270+
if note && fact {
271+
return false;
272+
}
261273
if note {
262-
name.is_ascii()
263-
&& name.len() >= 2
264-
&& name.chars().next() == Some('~')
265-
&& name
266-
.chars()
267-
.skip(1)
268-
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
274+
valid_note(entry)
275+
} else if fact {
276+
valid_fact(entry)
269277
} else {
270-
name.is_ascii()
271-
&& name.len() >= 1
272-
&& name
273-
.chars()
274-
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
278+
valid_name(entry)
275279
}
276280
}
277281

282+
pub fn valid_name(name: &str) -> bool {
283+
name.is_ascii()
284+
&& name.len() >= 1
285+
&& name
286+
.chars()
287+
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
288+
}
289+
290+
pub fn valid_note(note: &str) -> bool {
291+
note.is_ascii()
292+
&& note.len() >= 2
293+
&& note.chars().next() == Some('~')
294+
&& note
295+
.chars()
296+
.skip(1)
297+
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
298+
}
299+
300+
pub fn valid_fact(fact: &str) -> bool {
301+
fact.is_ascii()
302+
&& fact.len() >= 2
303+
&& fact.chars().next() == Some('!')
304+
&& fact
305+
.chars()
306+
.skip(1)
307+
.all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-')
308+
}
309+
278310
/// Produce a namehash from a kimap name.
279311
pub fn namehash(name: &str) -> String {
280312
let mut node = B256::default();
@@ -299,7 +331,7 @@ pub fn decode_mint_log(log: &crate::eth::Log) -> Result<Mint, DecodeLogError> {
299331
let decoded = contract::Mint::decode_log_data(log.data(), true)
300332
.map_err(|e| DecodeLogError::DecodeError(e.to_string()))?;
301333
let name = String::from_utf8_lossy(&decoded.label).to_string();
302-
if !valid_name(&name, false) {
334+
if !valid_name(&name) {
303335
return Err(DecodeLogError::InvalidName(name));
304336
}
305337
match resolve_parent(log, None) {
@@ -318,7 +350,7 @@ pub fn decode_note_log(log: &crate::eth::Log) -> Result<Note, DecodeLogError> {
318350
let decoded = contract::Note::decode_log_data(log.data(), true)
319351
.map_err(|e| DecodeLogError::DecodeError(e.to_string()))?;
320352
let note = String::from_utf8_lossy(&decoded.label).to_string();
321-
if !valid_name(&note, true) {
353+
if !valid_note(&note) {
322354
return Err(DecodeLogError::InvalidName(note));
323355
}
324356
match resolve_parent(log, None) {
@@ -331,6 +363,26 @@ pub fn decode_note_log(log: &crate::eth::Log) -> Result<Note, DecodeLogError> {
331363
}
332364
}
333365

366+
pub fn decode_fact_log(log: &crate::eth::Log) -> Result<Fact, DecodeLogError> {
367+
let contract::Fact::SIGNATURE_HASH = log.topics()[0] else {
368+
return Err(DecodeLogError::UnexpectedTopic(log.topics()[0]));
369+
};
370+
let decoded = contract::Fact::decode_log_data(log.data(), true)
371+
.map_err(|e| DecodeLogError::DecodeError(e.to_string()))?;
372+
let fact = String::from_utf8_lossy(&decoded.label).to_string();
373+
if !valid_fact(&fact) {
374+
return Err(DecodeLogError::InvalidName(fact));
375+
}
376+
match resolve_parent(log, None) {
377+
Some(parent_path) => Ok(Fact {
378+
fact,
379+
parent_path,
380+
data: decoded.data,
381+
}),
382+
None => Err(DecodeLogError::UnresolvedParent(fact)),
383+
}
384+
}
385+
334386
/// Given a [`crate::eth::Log`] (which must be a log from kimap), resolve the parent name
335387
/// of the new entry or note.
336388
pub fn resolve_parent(log: &crate::eth::Log, timeout: Option<u64>) -> Option<String> {
@@ -354,10 +406,18 @@ pub fn resolve_full_name(log: &crate::eth::Log, timeout: Option<u64>) -> Option<
354406
let decoded = contract::Note::decode_log_data(log.data(), true).unwrap();
355407
decoded.label
356408
}
409+
contract::Fact::SIGNATURE_HASH => {
410+
let decoded = contract::Fact::decode_log_data(log.data(), true).unwrap();
411+
decoded.label
412+
}
357413
_ => return None,
358414
};
359415
let name = String::from_utf8_lossy(&log_name);
360-
if !valid_name(&name, log.topics()[0] == contract::Note::SIGNATURE_HASH) {
416+
if !valid_entry(
417+
&name,
418+
log.topics()[0] == contract::Note::SIGNATURE_HASH,
419+
log.topics()[0] == contract::Fact::SIGNATURE_HASH,
420+
) {
361421
return None;
362422
}
363423
Some(format!("{name}.{parent_name}"))
@@ -468,6 +528,13 @@ impl Kimap {
468528
.event(contract::Note::SIGNATURE)
469529
}
470530

531+
/// Create a filter for all fact events.
532+
pub fn fact_filter(&self) -> crate::eth::Filter {
533+
crate::eth::Filter::new()
534+
.address(self.address)
535+
.event(contract::Fact::SIGNATURE)
536+
}
537+
471538
/// Create a filter for a given set of specific notes. This function will
472539
/// hash the note labels and use them as the topic3 filter.
473540
///
@@ -483,4 +550,20 @@ impl Kimap {
483550
.collect::<Vec<_>>(),
484551
)
485552
}
553+
554+
/// Create a filter for a given set of specific facts. This function will
555+
/// hash the fact labels and use them as the topic3 filter.
556+
///
557+
/// Example:
558+
/// ```rust
559+
/// let filter = kimap.facts_filter(&["!fact1", "!fact2"]);
560+
/// ```
561+
pub fn facts_filter(&self, facts: &[&str]) -> crate::eth::Filter {
562+
self.fact_filter().topic3(
563+
facts
564+
.into_iter()
565+
.map(|fact| keccak256(fact))
566+
.collect::<Vec<_>>(),
567+
)
568+
}
486569
}

0 commit comments

Comments
 (0)