feat(fof-layer2): wire types — WrapSlot, FoFCommentGating, CommentPermission::FriendsOfFriends
Adds the on-wire shapes for FoF Mode 2 comment-gating per
docs/fof-spec/layer-2-mode2-fof-comments.md:
- WrapSlot: per-V_x slot with 2B prefilter_tag + 48B read_ciphertext
+ 48B sign_ciphertext (sealed CEK + sealed priv_x_seed). 98 bytes
total per slot. Receiver trial-decrypts via prefilter match.
- FoFCommentGating: author-published gating block embedded in
Post.fof_gating. Carries slot_binder_nonce (32B random; replaces
spec's circular "post_id in HKDF info"), pub_post_set (1:1 with
wrap_slots, includes dummy pubkeys), wrap_slots, and revocation_list
(initially empty; revocation diffs accumulate on the BlobHeader copy).
- RevocationEntry: author-signed entry triggering retroactive comment
delete + pub_post_set removal on every file-holder that receives it.
- CommentPermission gains FriendsOfFriends variant. Existing match arm
in connection.rs handle-incoming-diff path is extended with a
"drop pending CDN four-check verification" stub (full verify in a
later slice).
- InlineComment extended with three optional fields:
pub_x_index: index into parent post's pub_post_set
group_sig: 64B ed25519 sig under priv_x
encrypted_payload: ChaCha20-Poly1305 ciphertext under CEK_comments
All #[serde(default)] for back-compat. Old comments deserialize
cleanly with None.
- Post gains optional fof_gating field for the author-signed snapshot
at publish time. PostId = BLAKE3(Post) covers it, so any tampering
is detectable. Mutations (revocation, access-grant) arrive later as
diffs against the local BlobHeader copy.
All 21 existing Post construction sites + 4 existing InlineComment
sites updated via perl -0pe sweeps to pass None for the new fields.
Full test suite: 134/134 pass (4 new slot crypto + 130 existing).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
74fec3b1fb
commit
0f5147a31c
11 changed files with 148 additions and 0 deletions
|
|
@ -893,6 +893,7 @@ impl Storage {
|
|||
content: row.get(1)?,
|
||||
attachments,
|
||||
timestamp_ms: row.get::<_, i64>(3)? as u64,
|
||||
fof_gating: None,
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
|
|
@ -919,6 +920,7 @@ impl Storage {
|
|||
content: row.get(1)?,
|
||||
attachments,
|
||||
timestamp_ms: row.get::<_, i64>(3)? as u64,
|
||||
fof_gating: None,
|
||||
},
|
||||
visibility,
|
||||
)))
|
||||
|
|
@ -1012,6 +1014,7 @@ impl Storage {
|
|||
content,
|
||||
attachments,
|
||||
timestamp_ms: timestamp_ms as u64,
|
||||
fof_gating: None,
|
||||
},
|
||||
visibility,
|
||||
));
|
||||
|
|
@ -1050,6 +1053,7 @@ impl Storage {
|
|||
content,
|
||||
attachments,
|
||||
timestamp_ms: timestamp_ms as u64,
|
||||
fof_gating: None,
|
||||
},
|
||||
visibility,
|
||||
));
|
||||
|
|
@ -1212,6 +1216,7 @@ impl Storage {
|
|||
content,
|
||||
attachments,
|
||||
timestamp_ms: timestamp_ms as u64,
|
||||
fof_gating: None,
|
||||
},
|
||||
visibility,
|
||||
));
|
||||
|
|
@ -1247,6 +1252,7 @@ impl Storage {
|
|||
content,
|
||||
attachments,
|
||||
timestamp_ms: timestamp_ms as u64,
|
||||
fof_gating: None,
|
||||
},
|
||||
visibility,
|
||||
));
|
||||
|
|
@ -2926,6 +2932,7 @@ impl Storage {
|
|||
content: row.get(2)?,
|
||||
attachments,
|
||||
timestamp_ms: row.get::<_, i64>(4)? as u64,
|
||||
fof_gating: None,
|
||||
},
|
||||
visibility,
|
||||
));
|
||||
|
|
@ -5302,6 +5309,9 @@ impl Storage {
|
|||
signature: sig,
|
||||
deleted_at: None,
|
||||
ref_post_id,
|
||||
pub_x_index: None,
|
||||
group_sig: None,
|
||||
encrypted_payload: None,
|
||||
});
|
||||
}
|
||||
Ok(result)
|
||||
|
|
@ -5341,6 +5351,9 @@ impl Storage {
|
|||
signature: sig,
|
||||
deleted_at: del.map(|v| v as u64),
|
||||
ref_post_id,
|
||||
pub_x_index: None,
|
||||
group_sig: None,
|
||||
encrypted_payload: None,
|
||||
});
|
||||
}
|
||||
Ok(result)
|
||||
|
|
@ -6100,6 +6113,7 @@ mod tests {
|
|||
content: format!("post at {}", ts),
|
||||
attachments: vec![],
|
||||
timestamp_ms: ts,
|
||||
fof_gating: None,
|
||||
};
|
||||
let id = blake3::hash(&serde_json::to_vec(&post).unwrap());
|
||||
s.store_post(id.as_bytes(), &post).unwrap();
|
||||
|
|
@ -6797,6 +6811,9 @@ mod tests {
|
|||
signature: vec![0u8; 64],
|
||||
deleted_at: None,
|
||||
ref_post_id: None,
|
||||
pub_x_index: None,
|
||||
group_sig: None,
|
||||
encrypted_payload: None,
|
||||
}).unwrap();
|
||||
|
||||
s.store_comment(&InlineComment {
|
||||
|
|
@ -6807,6 +6824,9 @@ mod tests {
|
|||
signature: vec![1u8; 64],
|
||||
deleted_at: None,
|
||||
ref_post_id: None,
|
||||
pub_x_index: None,
|
||||
group_sig: None,
|
||||
encrypted_payload: None,
|
||||
}).unwrap();
|
||||
|
||||
let comments = s.get_comments(&post_id).unwrap();
|
||||
|
|
@ -6832,6 +6852,9 @@ mod tests {
|
|||
signature: vec![9u8; 64],
|
||||
deleted_at: None,
|
||||
ref_post_id: Some(ref_post),
|
||||
pub_x_index: None,
|
||||
group_sig: None,
|
||||
encrypted_payload: None,
|
||||
}).unwrap();
|
||||
|
||||
let live = s.get_comments(&post_id).unwrap();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue