This commit is contained in:
2026-09-22 15:30:12 +07:00
parent 04fb70e657
commit 28f4c1596d
9 changed files with 1431 additions and 80 deletions
+137 -21
View File
@@ -10,7 +10,7 @@ use concord::cord04::roles::{Permissions, citation_ok};
use concord::derive::{
channel_group_key, control_group_key, control_signer_group_key, guestbook_group_key,
};
use concord::state::{CommunityState, list_entry};
use concord::state::{CommunityState, HeldKey, HeldRoot, list_entry};
use concord::{ChannelId, CommunityId, Epoch, GroupKey};
use gpui::AsyncApp;
use nostr_sdk::prelude::*;
@@ -35,18 +35,42 @@ pub struct Plane {
/// The wrap's author: the control signer for Control, the group's own key otherwise.
pub address: PublicKey,
pub group: GroupKey,
/// Epoch seconds the key behind this plane was retired.
pub retired_at: Option<u64>,
}
impl Plane {
/// Whether a wrap sealed under this plane is still inside its key's life.
pub fn accepts(&self, wrap: &Event) -> bool {
self.retired_at
.is_none_or(|retired| wrap.created_at.as_secs() <= retired)
}
}
pub fn planes(state: &CommunityState) -> Result<Vec<Plane>> {
let mut planes = Vec::new();
let roots = state.roots();
for (epoch, address) in &state.control_pks {
let epoch = Epoch(*epoch);
let group = control_group_key(&state.community_root, &state.id, epoch)?;
// An epoch's Control Plane reads under the root that was current then,
// so a rotation that kept a floor for the prior root republishes here.
let root = roots
.iter()
.find(|root| root.epoch == epoch)
.copied()
.unwrap_or(HeldRoot {
epoch,
key: state.community_root,
control_pk: None,
retired_at: None,
});
let group = control_group_key(&root.key, &state.id, epoch)?;
planes.push(Plane {
kind: PlaneKind::Control(epoch),
address: *address,
group,
retired_at: root.retired_at,
});
}
@@ -56,31 +80,38 @@ pub fn planes(state: &CommunityState) -> Result<Vec<Plane>> {
kind: PlaneKind::Control(state.root_epoch),
address: group.pk(),
group,
retired_at: None,
});
}
let group = guestbook_group_key(&state.community_root, &state.id, state.root_epoch)?;
planes.push(Plane {
kind: PlaneKind::Guestbook,
address: group.pk(),
group,
});
for channel in &state.channels {
let secret = match (channel.private, channel.key) {
(true, None) => continue,
(true, Some(key)) => key,
(false, _) => state.community_root,
};
let group = channel_group_key(&secret, &channel.id, channel.epoch)?;
for root in &roots {
let group = guestbook_group_key(&root.key, &state.id, root.epoch)?;
planes.push(Plane {
kind: PlaneKind::Channel(channel.id, channel.epoch),
kind: PlaneKind::Guestbook,
address: group.pk(),
group,
retired_at: root.retired_at,
});
}
for channel in &state.channels {
for held in state.held_keys(&channel.id) {
let group = channel_group_key(&held.key, &channel.id, held.epoch)?;
planes.push(Plane {
kind: PlaneKind::Channel(channel.id, held.epoch),
address: group.pk(),
group,
retired_at: held.retired_at,
});
}
}
// A rotation can re-derive an address the current root already produced.
//
// A duplicate author would only repeat a filter, so keep the set unique.
let mut seen = BTreeSet::new();
planes.retain(|plane| seen.insert(plane.address));
Ok(planes)
}
@@ -389,17 +420,44 @@ fn refresh(mut held: CommunityState, fresh: CommunityState) -> CommunityState {
held.relays = fresh.relays;
for channel in fresh.channels {
let cut = held.channel_cuts.get(&channel.id).copied();
match held.channels.iter_mut().find(|held| held.id == channel.id) {
Some(held) => {
held.name = channel.name;
held.epoch = channel.epoch;
if cut.is_some_and(|cut| channel.epoch.0 <= cut) {
continue;
}
if channel.private {
held.private = true;
held.key = channel.key;
if let Some(key) = channel.key
&& (held.key != Some(key) || held.epoch != channel.epoch)
{
// The key being superseded still reads everything
// written under it, so it is retained, never overwritten.
if let Some((epoch, previous)) = held.current()
&& !held.priors.iter().any(|prior| prior.epoch == epoch)
{
held.priors.push(HeldKey {
epoch,
key: previous,
retired_at: None,
});
}
held.key = Some(key);
held.epoch = channel.epoch;
}
}
}
None => {
if cut.is_none() {
held.channels.push(channel);
}
}
None => held.channels.push(channel),
}
}
@@ -486,6 +544,11 @@ pub async fn fold(client: &Client, state: &CommunityState) -> Result<Option<Snap
continue;
};
// A retired key reads only what was sealed before its rotation published.
if !plane.accepts(wrap) {
continue;
}
match plane.kind {
PlaneKind::Control(_) => {
if let Ok(edition) = cord02::open_edition(wrap, &plane.group, &plane.address, true)
@@ -609,6 +672,7 @@ mod tests {
private: false,
epoch: Epoch(0),
key: None,
priors: Vec::new(),
},
concord::state::ChannelKeyRef {
id: staff,
@@ -616,6 +680,7 @@ mod tests {
private: true,
epoch: Epoch(0),
key: Some([0x04; 32]),
priors: Vec::new(),
},
concord::state::ChannelKeyRef {
id: ChannelId::from_bytes([0x9e; 32]),
@@ -623,12 +688,17 @@ mod tests {
private: true,
epoch: Epoch(0),
key: None,
priors: Vec::new(),
},
],
relays: vec![RelayUrl::parse("wss://relay.example").expect("a url")],
heads: Vec::new(),
banned: BTreeSet::new(),
cursors: BTreeMap::new(),
held_roots: Vec::new(),
channel_cuts: BTreeMap::new(),
removed_at: None,
stranded: false,
dissolved: false,
added_at_ms: 0,
};
@@ -719,11 +789,16 @@ mod tests {
private: false,
epoch: Epoch(0),
key: None,
priors: Vec::new(),
}],
relays: Vec::new(),
heads: Vec::new(),
banned: BTreeSet::new(),
cursors: BTreeMap::new(),
held_roots: Vec::new(),
channel_cuts: BTreeMap::new(),
removed_at: None,
stranded: false,
dissolved: false,
added_at_ms: 1_700_000_000_000,
}
@@ -1021,4 +1096,45 @@ mod tests {
assert!(subscription_id(&id).as_str().len() <= 64);
}
fn event_at(at_ms: u64) -> Event {
EventBuilder::new(Kind::TextNote, "wrap")
.custom_created_at(Timestamp::from_secs(at_ms / 1000))
.finalize(&Keys::generate())
.expect("signs")
}
/// A key a rotation stepped off still reads its own history, but nothing
/// sealed after the rotation published it.
#[test]
fn a_superseded_key_reads_only_up_to_the_rotation_that_retired_it() {
let channel = ChannelId::from_bytes([0x9c; 32]);
let mut state = held(
CommunityId::from_bytes([0x42; 32]),
Keys::generate().public_key(),
);
state.channels = vec![concord::state::ChannelKeyRef {
id: channel,
name: "staff".to_owned(),
private: true,
epoch: Epoch(1),
key: Some([0x08; 32]),
priors: vec![HeldKey {
epoch: Epoch(0),
key: [0x07; 32],
retired_at: Some(1_000),
}],
}];
let planes = planes(&state).expect("planes");
let retired = planes
.iter()
.find(|plane| plane.kind == PlaneKind::Channel(channel, Epoch(0)))
.expect("the retired epoch keeps a plane");
assert_eq!(retired.retired_at, Some(1_000));
assert!(retired.accepts(&event_at(1_000_000)));
assert!(!retired.accepts(&event_at(1_001_000)));
}
}