机密副本与定向发布
发件人把一份文书寄给一位收件人。中继经手人在途中读了文书,并摘抄了一份副本,原件照常送到 收件人手中。之后,经手人把副本发布给两名指定读者,随后又撤回这次发布。撤回之后,其中一名读者 再想打开副本,请求被拒绝。
这个示例回答的问题是:副本、限定范围的发布和撤回应当怎样记录,才能让每个持有人只知道真正到达 自己的信息,同时让撤回挡住之后的阅读,而此前的阅读仍留有记录?
所有持有人、时间和内容代码都是合成的测试值。
示例涉及的引擎部件
Section titled “示例涉及的引擎部件”canwu-information,负责信息流转的模拟领域扩展。谁产生、发送、 阅读、发布了一份信息,都以类型化的领域记录保存。InformationLifecycle::plan:按当前记录校验一条LifecycleRequest,返回InformationMutationPlan,其中包括记录变更和知识发布,每条知识发布只面向一个持有人 (持有人相对知识)。- 通过
ContentDerivation和RepresentationSourceEdge记录副本的来源。 - 由成员名单、成员数和成员根(membership root)固定下来的受众(
AudiencePayload),以及从Active转为Withdrawn的发布(ReleasePayload)。 InformationPlugin在Canwu中执行其中两条请求,即面向受众的发布激活和撤回,每次之后都 验证快照恢复、精确重放和紧凑检查点恢复。其余步骤只在脱离运行时的 账本中执行。
cargo run -p canwu-information --example confidential_copy_release所有检查通过后,程序打印一行结果;任何一项检查失败,程序都会提前停止:
confidential_copy_release: hidden-operation holder isolation, derived copy, restricted release, withdrawal, retained knowledge, authoritative save/load, exact replay, and compact reconstruction verified每个短语对应源码中的一组检查。hidden-operation holder isolation(隐蔽操作的持有人隔离)是指:
每次阅读只向阅读者本人发布知识,其他人不会得知经手人暗中的阅读;并且在权威运行中,只有
H-404 和 H-505 从这次发布中得到知识。
场景代码在
confidential_copy_release.rs,
共用的辅助函数在
support/mod.rs。
持有人都是 KnowledgeHolderRef::Person,时间由 SimTime::from_minutes 给出。
| 持有人 | 角色 |
|---|---|
H-101 |
发件人 |
H-202 |
指定收件人 |
H-303 |
中继经手人,阅读并摘抄原件,之后发布副本 |
H-404、H-505 |
受众成员 |
H-606 |
无关持有人,用来检查没有任何知识发布落到他身上 |
1. 逐条规划并应用请求
Section titled “1. 逐条规划并应用请求”DetachedCaseLedger::plan_and_apply 把记录放在 Canwu 之外的一个普通映射里,方便逐步检查:
let plan = InformationLifecycle::plan( &self.record_set()?, request, InformationLimitsV1::canonical(),)?;self.apply(&plan)?;self.requests.push(request.clone());Ok(plan)单独调用 plan 只校验、不应用,示例用它检查应当失败的请求。
2. 创建原件并开始投递(第 0–7 分钟)
Section titled “2. 创建原件并开始投递(第 0–7 分钟)”发件人创建内容代码为 SYN-C4-17 的 Content 记录、格式为 sealed_text_v1 的
Representation(表现形式),以及 Instance(实物载体)。带有 AddressedDelivery 能力的
Channel 承载一条发往收件人的 Dispatch:
target: DispatchTarget::Addressed(vec![destination_holder.clone()]),发送记录从 Prepared 转为 Active,第一次 DeliveryAttempt(投递尝试)以 relay 引用角色
记下 H-303。
3. 经手人阅读并摘抄(第 12–14 分钟)
Section titled “3. 经手人阅读并摘抄(第 12–14 分钟)”经手人的 RecordAccess 引用载体实例和原始表现形式。副本是一份新内容,其衍生信息指回原件:
derivation: Some(ContentDerivation { operation: "select_and_copy".to_owned(), sources: vec![ContentSourceEdge { source: content, role: ContentSourceRole::Quotation, completeness_per_mille: 700, fidelity_per_mille: 970, }],}),副本的表现形式使用 ContentRelation::DerivedContent,并用 RepresentationSourceEdge 指向
原始表现形式。生命周期校验要求 source_content 和 parent_representation 引用与这些来源边
一一对应,衍生内容还必须把父表现形式的内容列为来源(见
lifecycle.rs
中的 validate_content_lineage 和 validate_representation_lineage)。
4. 原件送达收件人(第 25–27 分钟)
Section titled “4. 原件送达收件人(第 25–27 分钟)”投递尝试转为 Delivered,并向收件人发布 RepresentationAvailable。收件人的访问引用
delivery_attempt 和 dispatch,随后发送记录转为 Completed。定向发送必须等每个收件人
最近一次投递尝试进入终态才能完成(validate_dispatch_completion)。第 3 步的副本自成一条
记录链。
5. 冻结受众并发布副本(第 40–43 分钟)
Section titled “5. 冻结受众并发布副本(第 40–43 分钟)”payload: AudiencePayload { membership: AudienceMembership::ExplicitMembers, resolved_at: minute(40), resolution_version: 1, resolved_boundary: None, member_count: 2, membership_root: audience_membership_root_v1( &[audience_member_a.clone(), audience_member_b.clone()], InformationLimitsV1::canonical(), )?,},列出的成员必须与 member_count 和 membership_root 一致。ReleaseScope::Audience 的
Release 指向这个受众和副本的表现形式,publisher 是经手人。发布转为 Active 时,每个成员
各得到一条 ReleaseAvailable。成员 A 在第 43 分钟阅读副本,引用这次发布并附上
AudienceAccessEvidence::ListedMember。
激活还会通过 verify_authoritative_operation_roundtrip 在 Canwu 中执行一遍。这个辅助函数
把账本中的记录放进 Scenario,以插件命令的形式把请求交给 InformationPlugin,反复结算边界直到
操作进入终态,再逐条比对记录与账本。之后,它用 Canwu::admin_query_knowledge 读取每个
持有人的知识:
assert_authoritative_knowledge(canwu, &audience_member_a, &["release_available"])?;assert_authoritative_knowledge(canwu, &audience_member_b, &["release_available"])?;assert_authoritative_knowledge(canwu, &origin_holder, &[])?;assert_authoritative_knowledge(canwu, &unrelated_holder, &[])?;assert_authoritative_knowledge(canwu, &relay_holder, &[])这个模拟以账本中的记录为初始状态、知识库为空,所以这些检查只能看到这一次操作发布的知识。
因此第 12 分钟读过原件的 H-303 在这里什么也没有。副本是经手人发布的,但他不在受众名单里,
这次激活也不会给他任何知识。最后,辅助函数恢复快照、用 Canwu::replay_from_journal 重放日志、
恢复紧凑检查点,并把每个结果与原快照比对。
6. 撤回并拒绝迟到的阅读
Section titled “6. 撤回并拒绝迟到的阅读”proposed: ReleasePayload { status: ReleaseStatus::Withdrawn, active_at: Some(minute(42)), ..prepared_release},撤回同样经过 verify_authoritative_operation_roundtrip。撤回只更新发布记录,不产生任何
知识发布,所以在它的权威运行中没有持有人拥有知识;这里要确认的是此前三条访问记录仍与账本一致。
Withdrawn 是终态,而经由发布的阅读要求发布处于 Active,所以成员 B 在第 51 分钟的阅读失败:
assert!(ledger.plan(&access_after_withdrawal).is_err());值得注意的地方
Section titled “值得注意的地方”- 阅读(
Access)和摘抄是两种记录。副本是带有自己来源链的新内容,原件内容保持原有记录。 - 每条知识发布只面向一个持有人:阅读者、收件人,或受众名单中的每个成员。
- 发布者只有在自己也列入受众时,才会从这次发布中得到知识。
- 撤回管的是之后的阅读。此前的访问记录,以及引用它们的知识,都保持原样。
- 脱离运行时的账本和
InformationPlugin都调用InformationLifecycle::plan,同一请求得到 同样的记录。
- 把第 43 分钟那次阅读中的
audience_member_a换成unrelated_holder。规划会失败,报错access holder is not an explicit audience member。 - 删掉副本内容绑定中的
source_content引用。规划会失败,报错content source references must exactly match derivation source edges。 - 撤回之后,用
expected_version: 3规划一条把发布改回ReleaseStatus::Active的TransitionRelease。它会失败,报错terminal release cannot reopen。
sealed_text_v1、bounded_carrier、select_and_copy、temporary_read这类标签都是 应用自定的字符串,扩展只检查它们是去掉首尾空白后的非空文本。时间安排、渠道行为和后果由你自己的 插件负责。- 由群组解析出的受众使用
AudienceMembership::ResolvedGroupSnapshot,读者可以用AudienceAccessEvidence::MembershipProof对照成员根证明自己的成员身份。发布也可以使用ReleaseScope::OpenAvailability,处于Active的发布还可以转为Expired。 - 按路线投递的通信在同样的发送记录和投递尝试 记录之上,构建沿路线传递的信件。
打开可运行示例
阅读示例共用的辅助函数
阅读生命周期测试