1#![deny(missing_docs)]
2use std::collections::BTreeMap;
3use std::collections::BTreeSet;
4use std::str::FromStr;
5
6use serde::Deserialize;
7use serde::Serialize;
8
9use crate::algebra::JoinSemilattice;
10use crate::consts::ENTRY_DATA_MAX_LEN;
11use crate::dht::Did;
12use crate::ecc::HashStr;
13use crate::error::Error;
14use crate::error::Result;
15use crate::message::Encoded;
16use crate::message::Encoder;
17use crate::message::MessagePayload;
18use crate::message::MessageVerificationExt;
19
20mod crdt;
21
22pub use crdt::DataTopicBuffer;
23pub use crdt::EntryCrdt;
24pub use crdt::EntryDot;
25pub use crdt::EntryVersion;
26pub use crdt::RelayMessageSet;
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
30pub enum EntryKind {
31 Data,
33 RelayMessage,
36}
37
38#[derive(Clone, Debug, PartialEq, Eq)]
39enum EntryStampKind {
40 Overwrite,
41 Delta,
42}
43
44#[derive(Serialize)]
49struct OperationDigest<'a> {
50 kind: EntryKind,
51 did: Did,
52 data: &'a [Encoded],
53}
54
55#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
57pub enum EntryOperation {
58 Overwrite(Entry),
60 Extend(Entry),
63 Touch(Entry),
67 Tombstone(Entry),
74 CompactData(Entry),
82}
83
84#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
91pub struct PlacedEntryOperation {
92 pub placement: Did,
94 pub op: EntryOperation,
96}
97
98impl PlacedEntryOperation {
99 pub fn entry_key(&self) -> Result<Did> {
101 self.op.did()
102 }
103
104 pub fn placement_belongs_to_entry(&self, redundancy: u16) -> Result<bool> {
106 let entry_key = self.entry_key()?;
107 placement_belongs_to_entry_key(entry_key, self.placement, redundancy)
108 }
109
110 pub fn validate_placement(&self, redundancy: u16) -> Result<()> {
112 if self.placement_belongs_to_entry(redundancy)? {
113 return Ok(());
114 }
115
116 Err(Error::InvalidMessage(
117 "placed entry operation targets a placement outside the entry's affine replica set"
118 .to_string(),
119 ))
120 }
121}
122
123fn placement_belongs_to_entry_key(entry_key: Did, placement: Did, redundancy: u16) -> Result<bool> {
124 Ok(entry_key.rotate_affine(redundancy)?.contains(&placement))
125}
126
127#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
139pub struct Entry {
140 pub did: Did,
143 pub data: Vec<Encoded>,
145 pub kind: EntryKind,
147 #[serde(default)]
149 pub crdt: EntryCrdt,
150}
151
152#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
157pub struct PlacedEntry {
158 pub key: Did,
160 pub entry: Entry,
162}
163
164impl PlacedEntry {
165 pub fn new(key: Did, entry: Entry) -> Self {
167 Self { key, entry }
168 }
169
170 pub fn placement_belongs_to_entry(&self, redundancy: u16) -> Result<bool> {
172 placement_belongs_to_entry_key(self.entry.did, self.key, redundancy)
173 }
174
175 pub fn validate_placement(&self, redundancy: u16) -> Result<()> {
177 if self.placement_belongs_to_entry(redundancy)? {
178 return Ok(());
179 }
180
181 Err(Error::InvalidMessage(
182 "synced placed entry targets a placement outside the entry's affine replica set"
183 .to_string(),
184 ))
185 }
186}
187
188#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
196pub struct SyncedEntryAck {
197 pub key: Did,
199 pub entry: Entry,
201}
202
203impl SyncedEntryAck {
204 pub fn new(key: Did, entry: Entry) -> Self {
206 Self { key, entry }
207 }
208
209 pub fn confirms_local_value(&self, local: &Entry) -> Result<bool> {
215 Ok(self.entry.clone().try_into_storage_entry()?
216 == local.clone().try_into_storage_entry()?)
217 }
218}
219
220#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
225pub struct EntryLookupKey {
226 pub resource: Did,
228 pub placement: Did,
230}
231
232impl EntryLookupKey {
233 pub fn new(resource: Did, placement: Did) -> Self {
235 Self {
236 resource,
237 placement,
238 }
239 }
240}
241
242#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
244pub struct PlacementMiss {
245 pub key: Did,
247 pub owner: Did,
249}
250
251impl PlacementMiss {
252 pub fn new(key: Did, owner: Did) -> Self {
254 Self { key, owner }
255 }
256}
257
258#[derive(Clone, Debug, PartialEq, Eq)]
260pub struct EntryLookupEvidence {
261 pub entry: Entry,
263 pub misses: Vec<PlacementMiss>,
265}
266
267impl EntryLookupEvidence {
268 pub fn new(entry: Entry, misses: Vec<PlacementMiss>) -> Self {
270 Self { entry, misses }
271 }
272}
273
274impl Entry {
275 pub fn new(did: Did, data: Vec<Encoded>, kind: EntryKind) -> Self {
277 Self {
278 did,
279 data,
280 kind,
281 crdt: EntryCrdt::default(),
282 }
283 }
284
285 pub fn gen_did(topic: &str) -> Result<Did> {
287 let hash: HashStr = topic.into();
288 let did = Did::from_str(&hash.inner());
289 tracing::debug!("gen_did: topic: {}, did: {:?}", topic, did);
290 did
291 }
292}
293
294impl EntryOperation {
295 pub fn stamped(self, actor: Did) -> Result<Self> {
300 Ok(match self {
301 EntryOperation::Overwrite(entry) => EntryOperation::Overwrite(
302 entry.ensure_stamp_after(actor, None, EntryStampKind::Overwrite)?,
303 ),
304 EntryOperation::Extend(entry) => EntryOperation::Extend(entry.ensure_stamp_after(
305 actor,
306 None,
307 EntryStampKind::Delta,
308 )?),
309 EntryOperation::Touch(entry) => EntryOperation::Touch(entry.ensure_stamp_after(
310 actor,
311 None,
312 EntryStampKind::Delta,
313 )?),
314 EntryOperation::Tombstone(entry) => EntryOperation::Tombstone(entry),
315 EntryOperation::CompactData(entry) => {
316 EntryOperation::CompactData(entry.ensure_overwrite_stamp_after(actor, None)?)
317 }
318 })
319 }
320
321 pub fn did(&self) -> Result<Did> {
323 Ok(match self {
324 EntryOperation::Overwrite(entry) => entry.did,
325 EntryOperation::Extend(entry) => entry.did,
326 EntryOperation::Touch(entry) => entry.did,
327 EntryOperation::Tombstone(entry) => entry.did,
328 EntryOperation::CompactData(entry) => entry.did,
329 })
330 }
331
332 pub fn kind(&self) -> EntryKind {
334 match self {
335 EntryOperation::Overwrite(entry) => entry.kind,
336 EntryOperation::Extend(entry) => entry.kind,
337 EntryOperation::Touch(entry) => entry.kind,
338 EntryOperation::Tombstone(entry) => entry.kind,
339 EntryOperation::CompactData(entry) => entry.kind,
340 }
341 }
342
343 pub fn gen_default_entry(self) -> Result<Entry> {
345 Ok(Entry::new(self.did()?, vec![], self.kind()))
346 }
347}
348
349impl TryFrom<MessagePayload> for Entry {
350 type Error = Error;
351 fn try_from(msg: MessagePayload) -> Result<Self> {
352 let did = msg.signer() + Did::from(1u32);
355 let data = msg.encode()?;
356 Ok(Self {
357 did,
358 data: vec![data],
359 kind: EntryKind::RelayMessage,
360 crdt: EntryCrdt::default(),
361 })
362 }
363}
364
365impl TryFrom<(String, Encoded)> for Entry {
366 type Error = Error;
367 fn try_from((topic, e): (String, Encoded)) -> Result<Self> {
368 Ok(Self {
369 did: Self::gen_did(&topic)?,
370 data: vec![e],
371 kind: EntryKind::Data,
372 crdt: EntryCrdt::default(),
373 })
374 }
375}
376
377impl TryFrom<(String, String)> for Entry {
378 type Error = Error;
379 fn try_from((topic, s): (String, String)) -> Result<Self> {
380 let encoded_message = s.encode()?;
381 (topic, encoded_message).try_into()
382 }
383}
384
385impl TryFrom<String> for Entry {
386 type Error = Error;
387 fn try_from(topic: String) -> Result<Self> {
388 (topic.clone(), topic).try_into()
389 }
390}
391
392impl Entry {
393 fn with_element_dots(mut self, version: EntryVersion) -> Result<Self> {
394 self.crdt.dots = self
395 .data
396 .iter()
397 .enumerate()
398 .map(|(index, _)| EntryDot::for_index(version, index))
399 .collect::<Result<Vec<_>>>()?;
400 Ok(self)
401 }
402
403 fn stamp_overwrite(mut self, version: EntryVersion) -> Result<Self> {
404 self.crdt.register = Some(version);
405 self.with_element_dots(version)
406 }
407
408 fn stamp_delta(self, version: EntryVersion) -> Result<Self> {
409 self.with_element_dots(version)
410 }
411
412 fn stamp(self, version: EntryVersion, kind: EntryStampKind) -> Result<Self> {
413 match kind {
414 EntryStampKind::Overwrite => self.stamp_overwrite(version),
415 EntryStampKind::Delta => self.stamp_delta(version),
416 }
417 }
418
419 fn operation_digest(&self) -> Result<Did> {
420 let digest = OperationDigest {
421 kind: self.kind,
422 did: self.did,
423 data: &self.data,
424 };
425 let bytes = rings_codec::serialize(&digest).map_err(Error::CodecSerialize)?;
426 Did::try_from(HashStr::from_bytes(&bytes))
427 }
428
429 fn issue_version_after(&self, actor: Did, floor: Option<EntryVersion>) -> Result<EntryVersion> {
430 Ok(EntryVersion::issued_by(actor, self.operation_digest()?).after(floor))
431 }
432
433 fn ensure_stamp_after(
434 self,
435 actor: Did,
436 floor: Option<EntryVersion>,
437 kind: EntryStampKind,
438 ) -> Result<Self> {
439 match self.crdt.has_write_witness() {
440 true => Ok(self),
441 false => {
442 let version = self.issue_version_after(actor, floor)?;
443 self.stamp(version, kind)
444 }
445 }
446 }
447
448 fn ensure_overwrite_stamp_after(self, actor: Did, floor: Option<EntryVersion>) -> Result<Self> {
449 match self.crdt.register.is_some() {
450 true => Ok(self),
451 false => {
452 let version = self.issue_version_after(actor, floor)?;
453 self.stamp_overwrite(version)
454 }
455 }
456 }
457
458 fn max_observed_version(&self) -> Option<EntryVersion> {
459 self.crdt
460 .dots
461 .iter()
462 .map(|dot| dot.version)
463 .chain(self.crdt.tombstones.iter().map(|dot| dot.version))
464 .chain(self.crdt.register)
465 .max()
466 }
467
468 fn validate_same_carrier(&self, other: &Self) -> Result<()> {
469 if !self.same_kind_as(other) {
470 return Err(Error::EntryKindNotEqual);
471 }
472 if !self.same_key_as(other) {
473 return Err(Error::EntryDidNotEqual);
474 }
475 Ok(())
476 }
477
478 fn dot_for_element(&self, index: usize) -> Result<EntryDot> {
479 if let Some(dot) = self.crdt.dots.get(index).copied() {
480 return Ok(dot);
481 }
482 EntryDot::for_index(self.crdt.legacy_floor(), index)
483 }
484
485 fn topic_buffer(&self) -> Result<DataTopicBuffer> {
486 let mut values = BTreeMap::new();
487 for (index, value) in self.data.iter().cloned().enumerate() {
488 let dot = self.dot_for_element(index)?;
489 values
490 .entry(value)
491 .and_modify(|current: &mut EntryDot| {
492 *current = (*current).max(dot);
493 })
494 .or_insert(dot);
495 }
496 Ok(DataTopicBuffer::new(
497 self.crdt.register,
498 values,
499 self.crdt.tombstones.iter().copied().collect(),
500 ))
501 }
502
503 fn relay_set(&self) -> Result<RelayMessageSet> {
504 Ok(RelayMessageSet::new(
505 self.topic_buffer()?,
506 self.crdt.tombstones.iter().copied().collect(),
507 ))
508 }
509
510 fn materialize_elements(
511 did: Did,
512 kind: EntryKind,
513 register: Option<EntryVersion>,
514 elements: impl IntoIterator<Item = (Encoded, EntryDot)>,
515 tombstones: BTreeSet<EntryDot>,
516 ) -> Self {
517 let mut visible = elements
518 .into_iter()
519 .filter(|(_, dot)| {
520 let visible_after_reset = register.is_none_or(|floor| dot.version >= floor);
521 visible_after_reset && !tombstones.contains(dot)
522 })
523 .collect::<Vec<_>>();
524 visible.sort_by(|(left_value, left_dot), (right_value, right_dot)| {
525 left_dot
526 .cmp(right_dot)
527 .then_with(|| left_value.cmp(right_value))
528 });
529 let skip_count = visible.len().saturating_sub(ENTRY_DATA_MAX_LEN);
530 let visible = visible.into_iter().skip(skip_count).collect::<Vec<_>>();
531 let (data, dots): (Vec<_>, Vec<_>) = visible.into_iter().unzip();
532
533 Self {
534 did,
535 data,
536 kind,
537 crdt: EntryCrdt {
538 register,
539 dots,
540 tombstones: tombstones.into_iter().collect(),
541 },
542 }
543 }
544
545 fn materialize_topic_buffer(&self, buffer: DataTopicBuffer) -> Self {
546 Self::materialize_elements(
547 self.did,
548 self.kind,
549 buffer.register,
550 buffer.values,
551 buffer.removes,
552 )
553 }
554
555 fn materialize_relay_set(&self, set: RelayMessageSet) -> Self {
556 Self::materialize_elements(
557 self.did,
558 self.kind,
559 set.adds.register,
560 set.adds.values,
561 set.removes,
562 )
563 }
564
565 fn compacted_data_dot(floor: EntryVersion, value: &Encoded) -> Result<EntryDot> {
566 let operation = Did::try_from(HashStr::from_bytes(value.value().as_bytes()))?;
567 let version =
568 EntryVersion::new(floor.logical_time_ms, floor.actor, operation).after(Some(floor));
569 EntryDot::for_index(version, 0)
570 }
571
572 fn compact_data_element(
573 floor: EntryVersion,
574 removal_values: &BTreeSet<Encoded>,
575 value: Encoded,
576 dot: EntryDot,
577 ) -> Result<Option<(Encoded, EntryDot)>> {
578 match dot.version < floor {
579 true if removal_values.contains(&value) => Ok(None),
580 true => Self::compacted_data_dot(floor, &value).map(|dot| Some((value, dot))),
581 false => Ok(Some((value, dot))),
582 }
583 }
584
585 fn data_compaction_candidates(
586 payload_order: &[Encoded],
587 live_values: BTreeMap<Encoded, EntryDot>,
588 ) -> Vec<(Encoded, EntryDot)> {
589 let (ordered_values, remaining_values) = payload_order.iter().fold(
590 (Vec::new(), live_values),
591 |(mut ordered, mut remaining), value| {
592 if let Some(dot) = remaining.remove(value) {
593 ordered.push((value.clone(), dot));
594 }
595 (ordered, remaining)
596 },
597 );
598 ordered_values.into_iter().chain(remaining_values).collect()
599 }
600
601 fn compact_data_elements(
602 floor: EntryVersion,
603 removal_values: &BTreeSet<Encoded>,
604 values: impl IntoIterator<Item = (Encoded, EntryDot)>,
605 ) -> Result<Vec<(Encoded, EntryDot)>> {
606 values.into_iter().try_fold(
607 Vec::new(),
608 |mut elements, (value, dot)| -> Result<Vec<(Encoded, EntryDot)>> {
609 match Self::compact_data_element(floor, removal_values, value, dot)? {
610 Some(element) => {
611 elements.push(element);
612 Ok(elements)
613 }
614 None => Ok(elements),
615 }
616 },
617 )
618 }
619
620 fn compact_data_output_floor(
621 current_floor: Option<EntryVersion>,
622 operation_floor: EntryVersion,
623 ) -> EntryVersion {
624 current_floor.map_or(operation_floor, |current| current.max(operation_floor))
625 }
626
627 fn compact_data_tombstones(
628 floor: EntryVersion,
629 tombstones: BTreeSet<EntryDot>,
630 ) -> BTreeSet<EntryDot> {
631 tombstones
632 .into_iter()
633 .filter(|dot| dot.version >= floor)
634 .collect()
635 }
636
637 pub fn join(&self, other: Self) -> Result<Self> {
644 self.validate_same_carrier(&other)?;
645 match self.kind {
646 EntryKind::Data => {
647 Ok(self.materialize_topic_buffer(self.topic_buffer()?.join(other.topic_buffer()?)))
648 }
649 EntryKind::RelayMessage => {
650 Ok(self.materialize_relay_set(self.relay_set()?.join(other.relay_set()?)))
651 }
652 }
653 }
654
655 pub fn affine(&self, scalar: u16) -> Result<Vec<Entry>> {
657 Ok(self
658 .did
659 .rotate_affine(scalar)?
660 .into_iter()
661 .map(|did| self.clone_with_did(did))
662 .collect())
663 }
664
665 pub fn clone_with_did(&self, did: Did) -> Self {
667 let mut entry = self.clone();
668 entry.did = did;
669 entry
670 }
671
672 fn is_data_entry(&self) -> bool {
673 self.kind == EntryKind::Data
674 }
675
676 fn same_kind_as(&self, other: &Self) -> bool {
677 self.kind == other.kind
678 }
679
680 fn same_key_as(&self, other: &Self) -> bool {
681 self.did == other.did
682 }
683
684 pub fn try_into_storage_entry(self) -> Result<Self> {
692 match self.kind {
693 EntryKind::Data => {
694 let buffer = self.topic_buffer()?;
695 Ok(self.materialize_topic_buffer(buffer))
696 }
697 EntryKind::RelayMessage => {
698 let set = self.relay_set()?;
699 Ok(self.materialize_relay_set(set))
700 }
701 }
702 }
703
704 pub fn operate(&self, op: EntryOperation, actor: Did) -> Result<Self> {
707 match op {
708 EntryOperation::Overwrite(entry) => self.overwrite(entry, actor),
709 EntryOperation::Extend(entry) => self.extend(entry, actor),
710 EntryOperation::Touch(entry) => self.touch(entry, actor),
711 EntryOperation::Tombstone(entry) => self.tombstone(entry),
712 EntryOperation::CompactData(entry) => self.compact_data(entry, actor),
713 }
714 }
715
716 pub fn overwrite(&self, other: Self, actor: Did) -> Result<Self> {
725 if !self.is_data_entry() {
726 return Err(Error::EntryNotOverwritable);
727 }
728 self.join(other.ensure_stamp_after(
729 actor,
730 self.max_observed_version(),
731 EntryStampKind::Overwrite,
732 )?)
733 }
734
735 pub fn extend(&self, other: Self, actor: Did) -> Result<Self> {
738 if !self.is_data_entry() {
739 return Err(Error::EntryNotAppendable);
740 }
741 self.join(other.ensure_stamp_after(
742 actor,
743 self.max_observed_version(),
744 EntryStampKind::Delta,
745 )?)
746 }
747
748 pub fn touch(&self, other: Self, actor: Did) -> Result<Self> {
752 if !self.is_data_entry() {
753 return Err(Error::EntryNotAppendable);
754 }
755 self.join(other.ensure_stamp_after(
756 actor,
757 self.max_observed_version(),
758 EntryStampKind::Delta,
759 )?)
760 }
761
762 pub fn tombstone(&self, other: Self) -> Result<Self> {
768 self.validate_same_carrier(&other)?;
769
770 let target_values = other.data.into_iter().collect::<BTreeSet<_>>();
771 let target_dots = other.crdt.dots.into_iter().collect::<BTreeSet<_>>();
772 let has_dot_witness = !target_dots.is_empty();
773
774 match self.kind {
775 EntryKind::Data => {
776 let mut buffer = self.topic_buffer()?;
777 for (value, dot) in &buffer.values {
778 if target_dots.contains(dot)
779 || (!has_dot_witness && target_values.contains(value))
780 {
781 buffer.removes.insert(*dot);
782 }
783 }
784 Ok(self.materialize_topic_buffer(buffer))
785 }
786 EntryKind::RelayMessage => {
787 let mut set = self.relay_set()?;
788 for (value, dot) in &set.adds.values {
789 if target_dots.contains(dot)
790 || (!has_dot_witness && target_values.contains(value))
791 {
792 set.removes.insert(*dot);
793 }
794 }
795 Ok(self.materialize_relay_set(set))
796 }
797 }
798 }
799
800 pub fn compact_data(&self, removals: Self, actor: Did) -> Result<Self> {
807 match self.is_data_entry() {
808 true => self.compact_data_entry(removals, actor),
809 false => Err(Error::EntryNotOverwritable),
810 }
811 }
812
813 fn compact_data_entry(&self, removals: Self, actor: Did) -> Result<Self> {
814 let removals = removals.ensure_overwrite_stamp_after(actor, self.max_observed_version())?;
815 self.validate_same_carrier(&removals)?;
816 let floor = removals.crdt.register.ok_or_else(|| {
817 Error::InvalidMessage("compact data operation has no register floor".to_string())
818 })?;
819 let removal_values = removals.data.into_iter().collect::<BTreeSet<_>>();
820 let buffer = self.topic_buffer()?;
821 let output_floor = Self::compact_data_output_floor(self.crdt.register, floor);
822 let elements = Self::compact_data_elements(
823 floor,
824 &removal_values,
825 Self::data_compaction_candidates(&self.data, buffer.values),
826 )?;
827 let tombstones = Self::compact_data_tombstones(output_floor, buffer.removes);
828 Ok(Self::materialize_elements(
829 self.did,
830 EntryKind::Data,
831 Some(output_floor),
832 elements,
833 tombstones,
834 ))
835 }
836}
837
838#[cfg(test)]
839mod test_entry;