1use massa_time::MassaTime;
2use std::collections::BTreeMap;
3use thiserror::Error;
4
5use crate::versioning::{ComponentStateTypeId, MipComponent, MipStore};
6
7#[allow(missing_docs)]
9#[derive(Error, Debug, Clone)]
10pub enum FactoryError {
11 #[error("Unknown version, cannot build obj with version: {0}")]
12 UnknownVersion(u32),
13 #[error("Version: {0} is in versioning store but not yet implemented")]
14 UnimplementedVersion(u32), #[error("Version: {0} is not active yet")]
16 OnStateNotReady(u32),
17 #[error("Could not create object of type {0}: {1}")]
18 OnCreate(String, String),
19}
20
21#[derive(Clone, Debug)]
22pub enum FactoryStrategy {
24 Exact(u32),
28 At(MassaTime),
30}
31
32impl From<u32> for FactoryStrategy {
33 fn from(value: u32) -> Self {
34 FactoryStrategy::Exact(value)
35 }
36}
37
38pub trait VersioningFactory {
42 type Output;
45 type Error;
47 type Arguments;
49
50 fn get_component() -> MipComponent;
53 fn get_versioning_store(&self) -> MipStore;
55
56 fn get_latest_component_version_at(&self, ts: MassaTime) -> Result<u32, FactoryError> {
58 let component = Self::get_component();
59 let mi_store_ = self.get_versioning_store();
60 Ok(mi_store_.get_latest_component_version_at(&component, ts))
61 }
62
63 fn get_all_active_component_versions(&self) -> Vec<u32> {
65 let component = Self::get_component();
66 let mip_store = self.get_versioning_store();
67 mip_store.get_all_active_component_versions(&component)
68 }
69
70 fn get_all_component_versions(&self) -> BTreeMap<u32, ComponentStateTypeId> {
72 let component = Self::get_component();
73 let mip_store = self.get_versioning_store();
74 mip_store.get_all_component_versions(&component)
75 }
76
77 fn get_component_version_with_strategy(
79 &self,
80 strategy: FactoryStrategy,
81 ) -> Result<u32, FactoryError> {
82 match strategy {
83 FactoryStrategy::Exact(v) => match self.get_all_component_versions().get(&v) {
84 Some(s) if *s == ComponentStateTypeId::Active => Ok(v),
85 Some(s) if *s != ComponentStateTypeId::Active => {
86 Err(FactoryError::OnStateNotReady(v))
87 }
88 _ => Err(FactoryError::UnknownVersion(v)),
89 },
90 FactoryStrategy::At(ts) => self.get_latest_component_version_at(ts),
91 }
92 }
93
94 fn create(
96 &self,
97 args: &Self::Arguments,
98 strategy: FactoryStrategy,
99 ) -> Result<Self::Output, Self::Error>;
100}
101
102#[cfg(test)]
103mod test {
104 use super::*;
105
106 use num::rational::Ratio;
107 use std::collections::BTreeMap;
108
109 use crate::test_helpers::versioning_helpers::advance_state_until;
110 use crate::versioning::{ComponentState, MipInfo, MipState, MipStatsConfig};
111
112 use massa_time::MassaTime;
113
114 #[allow(dead_code)]
116 #[derive(Debug)]
117 struct TestAddressV0 {
118 hash: String,
119 }
120
121 #[allow(dead_code)]
122 impl TestAddressV0 {
123 fn new(hash: String) -> Self {
124 Self { hash }
125 }
126 }
127
128 #[allow(dead_code)]
129 #[derive(Debug)]
130 struct TestAddressV1 {
131 slot: String,
132 creator: String,
133 index: u32,
134 }
135
136 #[allow(dead_code)]
137 impl TestAddressV1 {
138 fn new(slot: String, creator: String, index: u32) -> Self {
139 Self {
140 slot,
141 creator,
142 index,
143 }
144 }
145 }
146
147 #[allow(dead_code)]
148 #[derive(Debug)]
149 enum TestAddress {
150 V0(TestAddressV0),
151 V1(TestAddressV1),
152 }
153
154 struct TestAddressArgs {
155 hash: Option<String>,
157 slot: Option<String>,
159 creator: Option<String>,
160 index: Option<u32>,
161 }
162
163 #[derive(Debug)]
166 struct TestAddressFactory {
167 versioning_store: MipStore,
168 }
169
170 impl VersioningFactory for TestAddressFactory {
171 type Output = TestAddress;
172 type Error = FactoryError;
173 type Arguments = TestAddressArgs;
174
175 fn get_component() -> MipComponent {
176 MipComponent::Address
177 }
178
179 fn get_versioning_store(&self) -> MipStore {
180 self.versioning_store.clone()
181 }
182
183 fn create(
184 &self,
185 args: &Self::Arguments,
186 strategy: FactoryStrategy,
187 ) -> Result<Self::Output, Self::Error> {
188 let version = self.get_component_version_with_strategy(strategy);
189
190 match version {
191 Ok(0) => Ok(TestAddress::V0(TestAddressV0 {
192 hash: args.hash.clone().ok_or(FactoryError::OnCreate(
193 stringify!(Self::Output).to_string(),
194 "Please provide hash in args".to_string(),
195 ))?,
196 })),
197 Ok(1) => Ok(TestAddress::V1(TestAddressV1 {
198 slot: args.slot.clone().ok_or(FactoryError::OnCreate(
199 stringify!(Self::Output).to_string(),
200 "Please provide 'slot' in args".to_string(),
201 ))?,
202 creator: args.creator.clone().unwrap(),
203 index: args.index.unwrap(),
204 })),
205 Ok(v) => Err(FactoryError::UnimplementedVersion(v)),
206 Err(e) => Err(e),
207 }
208 }
209 }
210
211 #[test]
212 fn test_address_factory_args() {
213 let vi_1 = MipInfo {
214 name: "MIP-0002".to_string(),
215 version: 1,
216 components: BTreeMap::from([(MipComponent::Address, 1)]),
217 start: MassaTime::from_millis(12),
218 timeout: MassaTime::from_millis(15),
219 activation_delay: MassaTime::from_millis(2),
220 };
221 let vs_1 = MipState::new(MassaTime::from_millis(10));
222
223 let vi_2 = MipInfo {
224 name: "MIP-0003".to_string(),
225 version: 2,
226 components: BTreeMap::from([(MipComponent::Address, 2)]),
227 start: MassaTime::from_millis(25),
228 timeout: MassaTime::from_millis(28),
229 activation_delay: MassaTime::from_millis(2),
230 };
231 let vs_2 = MipState::new(MassaTime::from_millis(18));
232
233 let mip_stats_cfg = MipStatsConfig {
234 block_count_considered: 10,
235 warn_announced_version_ratio: Ratio::new_raw(30, 100),
236 };
237
238 let vs = MipStore::try_from((
239 [(vi_1.clone(), vs_1), (vi_2.clone(), vs_2.clone())],
240 mip_stats_cfg,
241 ))
242 .unwrap();
243 let fa = TestAddressFactory {
244 versioning_store: vs.clone(),
245 };
246
247 let args = TestAddressArgs {
248 hash: Some("sdofjsklfhskfjl".into()),
249 slot: Some("slot_4_2".to_string()),
250 creator: Some("me_pubk".to_string()),
251 index: Some(3),
252 };
253 let args_no_v1 = TestAddressArgs {
254 hash: Some("sdofjsklfhskfjl".into()),
255 slot: None,
256 creator: Some("me_pubk".to_string()),
257 index: Some(3),
258 };
259
260 assert_eq!(fa.get_all_active_component_versions(), vec![0]);
261
262 let addr_a = fa.create(&args, 0.into());
263 assert!(matches!(addr_a, Ok(TestAddress::V0(_))));
264 let addr_ = fa.create(&args, 2.into());
267 assert!(matches!(addr_, Err(FactoryError::OnStateNotReady(2))));
268
269 let _time = MassaTime::now();
271 let vs_1_new = advance_state_until(ComponentState::active(_time), &vi_1);
272 let info = BTreeMap::from([(vi_1.clone(), vs_1_new.clone()), (vi_2.clone(), vs_2)]);
274 vs.0.write().store = info;
276
277 assert_eq!(fa.get_all_active_component_versions(), vec![0, 1]);
278 assert_eq!(
279 fa.get_all_component_versions()
280 .keys()
281 .cloned()
282 .collect::<Vec<u32>>(),
283 vec![0, 1, 2]
284 );
285 let addr_b = fa.create(&args, FactoryStrategy::At(MassaTime::now()));
287 assert!(matches!(addr_b, Ok(TestAddress::V1(_))));
288
289 let addr_ = fa.create(&args_no_v1, 1.into());
291 assert!(matches!(addr_, Err(FactoryError::OnCreate(_, _))));
292
293 let addr_c = fa.create(&args, 0.into());
295 println!("addr_c: {:?}", addr_c);
296 assert!(matches!(addr_c, Ok(TestAddress::V0(_))));
297 }
298
299 #[test]
300 fn test_factory_strategy_at() {
301 let _time = MassaTime::now();
304 let vi_1 = MipInfo {
305 name: "MIP-0002".to_string(),
306 version: 1,
307 components: BTreeMap::from([(MipComponent::Address, 1)]),
308 start: MassaTime::from_millis(12),
309 timeout: MassaTime::from_millis(15),
310 activation_delay: MassaTime::from_millis(2),
311 };
312 let vs_1 = advance_state_until(ComponentState::active(_time), &vi_1);
313
314 let vi_2 = MipInfo {
315 name: "MIP-0003".to_string(),
316 version: 2,
317 components: BTreeMap::from([(MipComponent::Address, 2)]),
318 start: MassaTime::from_millis(25),
319 timeout: MassaTime::from_millis(28),
320 activation_delay: MassaTime::from_millis(2),
321 };
322 let vs_2 = MipState::new(MassaTime::from_millis(18));
323
324 let mip_stats_cfg = MipStatsConfig {
325 block_count_considered: 10,
326 warn_announced_version_ratio: Ratio::new_raw(30, 100),
327 };
328
329 let vs = MipStore::try_from((
330 [(vi_1.clone(), vs_1.clone()), (vi_2.clone(), vs_2.clone())],
331 mip_stats_cfg,
332 ))
333 .unwrap();
334
335 let fa = TestAddressFactory {
336 versioning_store: vs.clone(),
337 };
338
339 let args = TestAddressArgs {
340 hash: Some("sdofjsklfhskfjl".into()),
341 slot: Some("slot_4_2".to_string()),
342 creator: Some("me_pubk".to_string()),
343 index: Some(3),
344 };
345
346 let st_1 = FactoryStrategy::At(MassaTime::from_millis(8)); let ts_1_2 = MassaTime::from_millis(13);
349 let st_1_2 = FactoryStrategy::At(ts_1_2); let st_2 = FactoryStrategy::At(MassaTime::from_millis(18)); let st_3 = FactoryStrategy::At(MassaTime::from_millis(27)); let st_4 = FactoryStrategy::At(MassaTime::from_millis(30)); let addr_st_1 = fa.create(&args, st_1);
355 let addr_st_1_2 = fa.create(&args, st_1_2.clone());
356 let addr_st_2 = fa.create(&args, st_2);
357 let addr_st_3 = fa.create(&args, st_3);
358 let addr_st_4 = fa.create(&args, st_4.clone());
359
360 assert!(matches!(addr_st_1, Ok(TestAddress::V0(_))));
361 assert!(matches!(addr_st_1_2, Ok(TestAddress::V0(_))));
362 assert!(matches!(addr_st_2, Ok(TestAddress::V1(_))));
363 assert!(matches!(addr_st_3, Ok(TestAddress::V1(_))));
364 assert!(matches!(addr_st_4, Ok(TestAddress::V1(_)))); let vs_2_new = advance_state_until(ComponentState::active(_time), &vi_2);
368 let info = BTreeMap::from([(vi_1.clone(), vs_1), (vi_2.clone(), vs_2_new)]);
369 vs.0.write().store = info;
371
372 assert_eq!(fa.get_all_active_component_versions(), vec![0, 1, 2]);
373 let addr_st_4 = fa.create(&args, st_4);
374 assert!(matches!(
376 addr_st_4,
377 Err(FactoryError::UnimplementedVersion(2))
378 ));
379 }
380}