Skip to content

Commit 5d8d800

Browse files
Merge upstream develop
2 parents 46e7741 + 9d2b715 commit 5d8d800

6 files changed

Lines changed: 45 additions & 169 deletions

File tree

Cargo.lock

Lines changed: 8 additions & 153 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"author": "FUTO",
44
"version": "0.0.1",
55
"private": true,
6-
"packageManager": "pnpm@10.34.3",
6+
"packageManager": "pnpm@10.34.4",
77
"scripts": {
88
"docusaurus": "docusaurus",
99
"start": "docusaurus start",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "polycentric",
33
"author": "FUTO",
4-
"packageManager": "pnpm@10.34.3",
4+
"packageManager": "pnpm@10.34.4",
55
"scripts": {
66
"dev": "turbo run dev",
77
"build": "turbo run build",

packages/rs-core/src/client.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,10 @@ impl PolycentricClient {
510510
}
511511
}
512512

513+
// Content is valid once its signature and signer authorization check
514+
// out. The vector clock still has to be structurally sound, but a
515+
// referenced event we haven't synced is a gap to backfill, not grounds
516+
// to reject — so the missing list is allowed to be non-empty here.
513517
crate::vector_clock::verify_vector_clock(
514518
&self.event_store,
515519
vc,
@@ -1149,7 +1153,7 @@ mod tests {
11491153
}
11501154

11511155
#[test]
1152-
fn rejects_event_referencing_unseen_co_signer_event() {
1156+
fn keeps_content_event_referencing_unsynced_event() {
11531157
let mut client = PolycentricClient::new();
11541158
let a = keypair(1);
11551159
let b = keypair(2);
@@ -1162,13 +1166,16 @@ mod tests {
11621166
vec![b.public.clone()],
11631167
);
11641168

1165-
// B claims to have observed A at seq=7 in collection 2 — we have none.
1169+
// B's event references A at seq=7 in collection 2, which we haven't
1170+
// synced. The event is still valid — the gap is ours to fetch.
11661171
let event = sign_event(&b, &identity, 2, 1, 1, vec![7, 1], dummy_post_digest());
1167-
assert_invalid_contains(client.validate_event(&event, &[]), "unseen event");
1172+
client
1173+
.validate_event(&event, &[])
1174+
.expect("content event with an unsynced referenced event should validate");
11681175
}
11691176

11701177
#[test]
1171-
fn validates_event_with_observed_co_signer_event_present() {
1178+
fn validates_event_when_referenced_event_present() {
11721179
let mut client = PolycentricClient::new();
11731180
let a = keypair(1);
11741181
let b = keypair(2);

packages/rs-core/src/identity.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ impl IdentityDirectory {
7979
&& e.content == current.content)
8080
})
8181
.filter(|e| match &e.vc {
82+
// Chain building needs the full causal history: only accept
83+
// a candidate whose vector clock verifies and references no
84+
// unsynced events.
8285
Some(vc) => vector_clock::verify_vector_clock(
8386
store,
8487
vc,
@@ -88,7 +91,8 @@ impl IdentityDirectory {
8891
&e.signer,
8992
e.sequence,
9093
)
91-
.is_ok(),
94+
.map(|missing| missing.is_empty())
95+
.unwrap_or(false),
9296
None => true,
9397
})
9498
.collect();
Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,19 @@
11
use polycentric_common::{
22
error::CoreError,
3-
models::protos_v2::{Identity, PublicKey, VectorClock},
3+
models::protos_v2::{EventKey, Identity, PublicKey, VectorClock},
44
};
55

66
use crate::store::event_store::EventStore;
77

8-
/// Structural check against the identity doc + every non-self non-zero
9-
/// entry must reference an event we've seen from that co-signer.
8+
/// Check a vector clock against the identity doc.
9+
///
10+
/// Returns `Err` if the clock is structurally invalid (wrong length, unknown
11+
/// signer, or a self entry that doesn't match the event's sequence).
12+
///
13+
/// Returns `Ok(missing)` with the referenced events from the identity's other
14+
/// keys that we haven't synced. A missing event does not make the referencing
15+
/// event invalid — it's just an event to fetch. Building the identity chain
16+
/// needs `missing` empty; content readers keep the event either way.
1017
pub fn verify_vector_clock(
1118
store: &EventStore,
1219
vc: &VectorClock,
@@ -15,9 +22,10 @@ pub fn verify_vector_clock(
1522
collection: i32,
1623
signer: &PublicKey,
1724
expected_self_sequence: u64,
18-
) -> Result<(), CoreError> {
25+
) -> Result<Vec<EventKey>, CoreError> {
1926
let self_position = vc.get_signer_position(doc, signer, expected_self_sequence)?;
2027
let dedup = doc.deduplicated_keys();
28+
let mut missing = Vec::new();
2129
for (pos, &observed) in vc.sequence.iter().enumerate() {
2230
if pos == self_position || observed == 0 {
2331
continue;
@@ -27,11 +35,13 @@ pub fn verify_vector_clock(
2735
.by_identity_collection_signer(identity, collection, other.key_type, &other.key, 0)
2836
.any(|(k, _)| k.sequence == observed);
2937
if !seen {
30-
return Err(CoreError::InvalidEvent(format!(
31-
"vector_clock references unseen event from co-signer at sequence {}",
32-
observed
33-
)));
38+
missing.push(EventKey {
39+
collection,
40+
identity: identity.to_string(),
41+
signed_by: Some(other.clone()),
42+
sequence: observed,
43+
});
3444
}
3545
}
36-
Ok(())
46+
Ok(missing)
3747
}

0 commit comments

Comments
 (0)