coven_protocol/store_commit/
owner_promotion_publication.rs1use super::*;
2
3impl SignedBody for OwnerPromotionRequestActivation {
4 const DOMAIN: &'static [u8] = b"coven.owner-promotion-request-publication.v1\0";
5}
6
7pub type OwnerPromotionRequestPublication = Signed<OwnerPromotionRequestActivation>;
10
11impl OwnerPromotionRequestActivation {
12 pub fn validate_for_request_commit(
13 &self,
14 commit: &StoreBatchCommit,
15 ) -> Result<(), StoreProtocolError> {
16 let request = commit
17 .owner_promotion_request()
18 .ok_or(StoreProtocolError::OwnerPromotionMismatch)?;
19 self.commit.verify_commit(commit)?;
20 self.commit.object.verify(&commit.to_bytes())?;
21 self.publication.validate_slot()?;
22 StorePublicationPosition::new(self.publication.position.get())?;
23 if self.publication.store_root_hash != request.store_root_hash
24 || commit.store_root_hash != request.store_root_hash
25 || commit.author_registration != request.promoter_registration
26 || commit.membership_state != request.predecessor_membership
27 || commit.device_state != request.predecessor_devices
28 {
29 return Err(StoreProtocolError::OwnerPromotionMismatch);
30 }
31 Ok(())
32 }
33}
34
35impl OwnerPromotionRequestPublication {
36 pub fn signed(
37 commit: &StoreBatchCommit,
38 accepted_entry: &StorePublicationEntry,
39 accepted_reference: &StorePublicationRef,
40 author: &StoreDeviceRegistration,
41 device_signer: &UserKeypair,
42 ) -> Result<Self, StoreProtocolError> {
43 let StorePublicationPayload::Commit(reference) = &accepted_entry.payload else {
44 return Err(StoreProtocolError::OwnerPromotionMismatch);
45 };
46 StorePublicationEntry::parse_at(
47 &accepted_entry.to_bytes(),
48 commit.store_root_hash,
49 accepted_reference,
50 &author.device_signing_pubkey,
51 )?;
52 if accepted_entry.author_registration != commit.author_registration {
53 return Err(StoreProtocolError::OwnerPromotionMismatch);
54 }
55 let value = Signed::sign(
56 OwnerPromotionRequestActivation {
57 commit: reference.clone(),
58 publication: accepted_reference.clone(),
59 },
60 device_signer,
61 );
62 value.verify_for(commit, author)?;
63 Ok(value)
64 }
65
66 pub fn verify_for(
67 &self,
68 commit: &StoreBatchCommit,
69 author: &StoreDeviceRegistration,
70 ) -> Result<(), StoreProtocolError> {
71 self.body().validate_for_request_commit(commit)?;
72 let request = commit
73 .owner_promotion_request()
74 .ok_or(StoreProtocolError::OwnerPromotionMismatch)?;
75 request.verify(&author.store_root, author)?;
76 commit.verify_by(&author.device_signing_pubkey)?;
77 self.verify_by(&author.device_signing_pubkey)
78 }
79}
80
81#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
83#[serde(deny_unknown_fields)]
84pub struct RetainedOwnerPromotionRequestPublication {
85 pub value: OwnerPromotionRequestPublication,
86 pub object: ExactObjectRef,
87}
88
89impl RetainedOwnerPromotionRequestPublication {
90 pub fn validate_for(&self, commit: &StoreBatchCommit) -> Result<(), StoreProtocolError> {
91 self.value.require_version()?;
92 self.value.body().validate_for_request_commit(commit)?;
93 self.object.verify(&self.value.to_bytes())?;
94 let request = commit
95 .owner_promotion_request()
96 .ok_or(StoreProtocolError::OwnerPromotionMismatch)?;
97 let expected = format!(
98 "{}.json",
99 owner_promotion_request_publication_semantic_prefix(request.promotion_id),
100 );
101 if self.object.slot() != &request.publication_slot
102 || self.object.slot().logical_key() != expected
103 {
104 return Err(StoreProtocolError::OwnerPromotionMismatch);
105 }
106 Ok(())
107 }
108}
109
110#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
114#[serde(deny_unknown_fields)]
115pub struct RetainedOwnerPromotionRequest {
116 pub commit: StoreBatchCommit,
117 pub publication: RetainedOwnerPromotionRequestPublication,
118 pub predecessor_state: ResolvedStoreDeviceState,
119}
120
121impl RetainedOwnerPromotionRequest {
122 pub fn request(&self) -> Result<&OwnerPromotionRequest, StoreProtocolError> {
123 self.commit
124 .owner_promotion_request()
125 .ok_or(StoreProtocolError::OwnerPromotionMismatch)
126 }
127
128 pub fn validate_shape(&self) -> Result<(), StoreProtocolError> {
129 self.publication.validate_for(&self.commit)?;
130 self.predecessor_state.validate_canonical()?;
131 let predecessor = StoreDeviceStateRef::from_resolved(
132 self.commit.order.predecessor_cut()?.frontier(),
133 &self.predecessor_state,
134 )?;
135 if predecessor != self.request()?.predecessor_devices {
136 return Err(StoreProtocolError::OwnerPromotionMismatch);
137 }
138 Ok(())
139 }
140}
141
142pub fn owner_promotion_request_publication_semantic_prefix(id: OwnerPromotionId) -> String {
143 format!("store-v1/owner-promotion-publications/{id}")
144}