dsf_core/service/
subscriber.rs

1use core::convert::TryInto;
2
3#[cfg(feature = "alloc")]
4use alloc::prelude::v1::*;
5
6use crate::crypto;
7use crate::error::Error;
8use crate::page::{Page, PageInfo};
9use crate::service::Service;
10use crate::types::*;
11
12pub trait Subscriber {
13    /// Create a service instance (or replica) from a given primary service page
14    fn load(page: &Page) -> Result<Service, Error>;
15
16    /// Apply an updated primary page to an existing service instance
17    fn apply_primary(&mut self, primary: &Page) -> Result<bool, Error>;
18
19    /// Validate a given secondary page published by this service
20    fn validate_page(&mut self, page: &Page) -> Result<(), Error>;
21}
22
23impl Subscriber for Service {
24    /// Create a service instance from a given page
25    fn load(page: &Page) -> Result<Service, Error> {
26        let header = page.header();
27        let flags = header.flags();
28
29        let public_key = match page.info() {
30            PageInfo::Primary(primary) => primary.pub_key.clone(),
31            _ => {
32                error!("Attempted to load service from secondary page");
33                return Err(Error::UnexpectedPageType);
34            }
35        };
36
37        let body = page.body();
38
39        let public_options = page.public_options();
40        let private_options = page.private_options();
41
42        Ok(Service {
43            id: page.id().clone(),
44
45            application_id: header.application_id(),
46            kind: header.kind().try_into().unwrap(),
47
48            version: header.index(),
49            data_index: 0,
50
51            body: body.clone(),
52
53            public_options: public_options.to_vec(),
54            private_options: private_options.clone(),
55
56            public_key,
57            private_key: None,
58
59            encrypted: flags.contains(Flags::ENCRYPTED),
60            secret_key: None,
61
62            last_sig: page.signature(),
63        })
64    }
65
66    /// Apply an upgrade to an existing service.
67    /// This consumes a new page and updates the service instance
68    fn apply_primary(&mut self, update: &Page) -> Result<bool, Error> {
69        let header = update.header();
70
71        let flags = header.flags();
72        let body = update.body();
73
74        let public_options = update.public_options();
75        let private_options = update.private_options();
76
77        self.validate_primary(update)?;
78
79        if header.index() == self.version {
80            return Ok(false);
81        }
82        if header.index() <= self.version {
83            return Err(Error::InvalidServiceVersion);
84        }
85
86        self.version = header.index();
87        self.encrypted = flags.contains(Flags::ENCRYPTED);
88        self.body = body.clone();
89        self.public_options = public_options.to_vec();
90        self.private_options = private_options.clone();
91
92        Ok(true)
93    }
94
95    fn validate_page(&mut self, page: &Page) -> Result<(), Error> {
96        let header = page.header();
97
98        if header.kind().is_page() {
99            if !header.flags().contains(Flags::SECONDARY) {
100                self.validate_primary(page)
101            } else {
102                self.validate_secondary(page)
103            }
104        } else if header.kind().is_data() {
105            self.validate_data(page)
106        } else {
107            Err(Error::UnexpectedPageKind)
108        }
109    }
110}
111
112impl Service {
113    /// Validate a primary page
114    pub(crate) fn validate_primary(&mut self, page: &Page) -> Result<(), Error> {
115        let header = page.header();
116
117        if !header.kind().is_page() {
118            return Err(Error::ExpectedPrimaryPage);
119        }
120        if header.flags().contains(Flags::SECONDARY) {
121            return Err(Error::ExpectedPrimaryPage);
122        }
123
124        if page.id() != &self.id {
125            return Err(Error::UnexpectedServiceId);
126        }
127        if header.application_id() != self.application_id {
128            return Err(Error::UnexpectedApplicationId);
129        }
130        if header.kind() != self.kind.into() {
131            return Err(Error::InvalidPageKind);
132        }
133
134        // Fetch public key from options
135        let public_key: PublicKey = match page.info() {
136            PageInfo::Primary(primary) => primary.pub_key.clone(),
137            _ => {
138                error!("Attempted to update service from secondary page");
139                return Err(Error::ExpectedPrimaryPage);
140            }
141        };
142
143        // Check public key and ID match
144        if self.id != crypto::hash(&public_key).unwrap() {
145            return Err(Error::KeyIdMismatch);
146        }
147
148        // Check public key hasn't changed
149        if self.public_key != public_key {
150            return Err(Error::PublicKeyChanged);
151        }
152
153        Ok(())
154    }
155
156    /// Validate a secondary page
157    pub(crate) fn validate_secondary(&mut self, secondary: &Page) -> Result<(), Error> {
158        let header = secondary.header();
159
160        if !header.kind().is_page() {
161            return Err(Error::ExpectedPrimaryPage);
162        }
163        if !header.flags().contains(Flags::SECONDARY) {
164            return Err(Error::ExpectedSecondaryPage);
165        }
166
167        let publisher_id = match secondary.info().peer_id() {
168            Some(p) => p,
169            None => return Err(Error::NoPeerId),
170        };
171        if publisher_id != self.id {
172            return Err(Error::UnexpectedPeerId);
173        }
174
175        if header.application_id() != self.application_id {
176            return Err(Error::UnexpectedApplicationId);
177        }
178
179        Ok(())
180    }
181
182    /// Validate a data objects
183    pub(crate) fn validate_data(&mut self, data: &Page) -> Result<(), Error> {
184        let header = data.header();
185
186        if !header.kind().is_data() {
187            return Err(Error::ExpectedDataObject);
188        }
189
190        if data.id() != &self.id {
191            return Err(Error::UnexpectedServiceId);
192        }
193        if header.application_id() != self.application_id {
194            return Err(Error::UnexpectedApplicationId);
195        }
196
197        Ok(())
198    }
199}