mars_owner/
owner.rs

1use std::fmt::Debug;
2
3use cosmwasm_schema::cw_serde;
4use cosmwasm_std::{
5    Addr, Api, CustomQuery, DepsMut, MessageInfo, Response, StdError, StdResult, Storage,
6};
7use cw_storage_plus::Item;
8use schemars::JsonSchema;
9use thiserror::Error;
10
11/// Returned from Owner.query()
12#[cw_serde]
13pub struct OwnerResponse {
14    pub owner: Option<String>,
15    pub proposed: Option<String>,
16    pub initialized: bool,
17    pub abolished: bool,
18    #[cfg(feature = "emergency-owner")]
19    pub emergency_owner: Option<String>,
20}
21
22/// Errors returned from Owner state transitions
23#[derive(Error, Debug, PartialEq)]
24pub enum OwnerError {
25    #[error("{0}")]
26    Std(#[from] StdError),
27
28    #[error("Caller is not owner")]
29    NotOwner {},
30
31    #[error("Caller is not the proposed owner")]
32    NotProposedOwner {},
33
34    #[error("Owner state transition was not valid")]
35    StateTransitionError {},
36
37    #[cfg(feature = "emergency-owner")]
38    #[error("Caller is not the emergency owner")]
39    NotEmergencyOwner {},
40}
41
42type OwnerResult<T> = Result<T, OwnerError>;
43
44/// The finite states that are possible
45#[cw_serde]
46enum OwnerState {
47    Uninitialized,
48    Base {
49        owner: Addr,
50        #[cfg(feature = "emergency-owner")]
51        emergency_owner: Option<Addr>,
52    },
53    Proposed {
54        owner: Addr,
55        proposed: Addr,
56        #[cfg(feature = "emergency-owner")]
57        emergency_owner: Option<Addr>,
58    },
59    Abolished,
60}
61
62#[cw_serde]
63pub enum OwnerUpdate {
64    /// Proposes a new owner to take role. Only current owner can execute.
65    ProposeNewOwner { proposed: String },
66    /// Clears the currently proposed owner. Only current owner can execute.
67    ClearProposed,
68    /// Promotes the proposed owner to be the current one. Only the proposed owner can execute.
69    AcceptProposed,
70    /// Throws away the keys to the Owner role forever. Once done, no owner can ever be set later.
71    AbolishOwnerRole,
72    #[cfg(feature = "emergency-owner")]
73    /// A separate entity managed by Owner that can be used for granting specific emergency powers.
74    SetEmergencyOwner { emergency_owner: String },
75    #[cfg(feature = "emergency-owner")]
76    /// Remove the entity in the Emergency Owner role
77    ClearEmergencyOwner,
78}
79
80#[cw_serde]
81pub enum OwnerInit {
82    /// Sets the initial owner when none. No restrictions permissions to modify.
83    SetInitialOwner { owner: String },
84    /// Throws away the keys to the Owner role forever. Once done, no owner can ever be set later.
85    AbolishOwnerRole,
86}
87
88/// A struct designed to help facilitate a two-step transition between contract owners safely.
89/// It implements a finite state machine with dispatched events to manage state transitions.
90/// State machine visualization: https://stately.ai/registry/editor/b7e5dbac-2d33-47f7-a84b-e38dff5694ad?machineId=f8d99cd1-dd55-4506-961b-e2542480be68&mode=Simulate
91pub struct Owner<'a>(Item<'a, OwnerState>);
92
93impl<'a> Owner<'a> {
94    pub const fn new(namespace: &'a str) -> Self {
95        Self(Item::new(namespace))
96    }
97
98    fn state(&self, storage: &'a dyn Storage) -> StdResult<OwnerState> {
99        Ok(self
100            .0
101            .may_load(storage)?
102            .unwrap_or(OwnerState::Uninitialized))
103    }
104
105    //--------------------------------------------------------------------------------------------------
106    // Queries
107    //--------------------------------------------------------------------------------------------------
108    pub fn current(&self, storage: &'a dyn Storage) -> StdResult<Option<Addr>> {
109        Ok(match self.state(storage)? {
110            OwnerState::Base { owner, .. } => Some(owner),
111            OwnerState::Proposed { owner, .. } => Some(owner),
112            _ => None,
113        })
114    }
115
116    pub fn is_owner(&self, storage: &'a dyn Storage, addr: &Addr) -> StdResult<bool> {
117        match self.current(storage)? {
118            Some(owner) if owner == addr => Ok(true),
119            _ => Ok(false),
120        }
121    }
122
123    pub fn proposed(&self, storage: &'a dyn Storage) -> StdResult<Option<Addr>> {
124        Ok(match self.state(storage)? {
125            OwnerState::Proposed { proposed, .. } => Some(proposed),
126            _ => None,
127        })
128    }
129
130    pub fn is_proposed(&self, storage: &'a dyn Storage, addr: &Addr) -> StdResult<bool> {
131        match self.proposed(storage)? {
132            Some(proposed) if proposed == addr => Ok(true),
133            _ => Ok(false),
134        }
135    }
136
137    #[cfg(feature = "emergency-owner")]
138    pub fn emergency_owner(&self, storage: &'a dyn Storage) -> StdResult<Option<Addr>> {
139        Ok(match self.state(storage)? {
140            OwnerState::Base {
141                emergency_owner, ..
142            } => emergency_owner,
143            OwnerState::Proposed {
144                emergency_owner, ..
145            } => emergency_owner,
146            _ => None,
147        })
148    }
149
150    #[cfg(feature = "emergency-owner")]
151    pub fn is_emergency_owner(&self, storage: &'a dyn Storage, addr: &Addr) -> StdResult<bool> {
152        match self.emergency_owner(storage)? {
153            Some(em_owner) if em_owner == addr => Ok(true),
154            _ => Ok(false),
155        }
156    }
157
158    pub fn query(&self, storage: &'a dyn Storage) -> StdResult<OwnerResponse> {
159        Ok(OwnerResponse {
160            owner: self.current(storage)?.map(Into::into),
161            proposed: self.proposed(storage)?.map(Into::into),
162            initialized: !matches!(self.state(storage)?, OwnerState::Uninitialized),
163            abolished: matches!(self.state(storage)?, OwnerState::Abolished),
164            #[cfg(feature = "emergency-owner")]
165            emergency_owner: self.emergency_owner(storage)?.map(Into::into),
166        })
167    }
168
169    //--------------------------------------------------------------------------------------------------
170    // Mutations
171    //--------------------------------------------------------------------------------------------------
172    /// Execute inside instantiate fn
173    pub fn initialize(
174        &self,
175        storage: &'a mut dyn Storage,
176        api: &'a dyn Api,
177        init_action: OwnerInit,
178    ) -> OwnerResult<()> {
179        let initial_state = self.state(storage)?;
180        match initial_state {
181            OwnerState::Uninitialized => {
182                let new_state = match init_action {
183                    OwnerInit::SetInitialOwner { owner } => {
184                        let validated = api.addr_validate(&owner)?;
185                        OwnerState::Base {
186                            owner: validated,
187                            #[cfg(feature = "emergency-owner")]
188                            emergency_owner: None,
189                        }
190                    }
191                    OwnerInit::AbolishOwnerRole => OwnerState::Abolished,
192                };
193                self.0.save(storage, &new_state)?;
194                Ok(())
195            }
196            // Can only be in uninitialized state to call this fn
197            _ => Err(OwnerError::StateTransitionError {}),
198        }
199    }
200
201    /// Composes execute responses for owner state updates
202    pub fn update<C, Q: CustomQuery>(
203        &self,
204        deps: DepsMut<Q>,
205        info: MessageInfo,
206        update: OwnerUpdate,
207    ) -> OwnerResult<Response<C>>
208    where
209        C: Clone + Debug + PartialEq + JsonSchema,
210    {
211        let new_state = self.transition_state(deps.storage, deps.api, &info.sender, update)?;
212        self.0.save(deps.storage, &new_state)?;
213
214        let res = self.query(deps.storage)?;
215        Ok(Response::new()
216            .add_attribute("action", "update_owner")
217            .add_attribute("owner", res.owner.unwrap_or_else(|| "None".to_string()))
218            .add_attribute(
219                "proposed",
220                res.proposed.unwrap_or_else(|| "None".to_string()),
221            )
222            .add_attribute("sender", info.sender))
223    }
224
225    /// Executes owner state transitions
226    fn transition_state(
227        &self,
228        storage: &'a mut dyn Storage,
229        api: &'a dyn Api,
230        sender: &Addr,
231        event: OwnerUpdate,
232    ) -> OwnerResult<OwnerState> {
233        let state = self.state(storage)?;
234
235        let new_state = match (state, event) {
236            (
237                OwnerState::Base {
238                    owner,
239                    #[cfg(feature = "emergency-owner")]
240                    emergency_owner,
241                    ..
242                },
243                OwnerUpdate::ProposeNewOwner { proposed },
244            ) => {
245                self.assert_owner(storage, sender)?;
246                let validated = api.addr_validate(&proposed)?;
247                OwnerState::Proposed {
248                    owner,
249                    proposed: validated,
250                    #[cfg(feature = "emergency-owner")]
251                    emergency_owner,
252                }
253            }
254            #[cfg(feature = "emergency-owner")]
255            (
256                OwnerState::Base { owner, .. },
257                OwnerUpdate::SetEmergencyOwner { emergency_owner },
258            ) => {
259                self.assert_owner(storage, sender)?;
260                let validated = api.addr_validate(&emergency_owner)?;
261                OwnerState::Base {
262                    owner,
263                    emergency_owner: Some(validated),
264                }
265            }
266            #[cfg(feature = "emergency-owner")]
267            (OwnerState::Base { owner, .. }, OwnerUpdate::ClearEmergencyOwner) => {
268                self.assert_owner(storage, sender)?;
269                OwnerState::Base {
270                    owner,
271                    emergency_owner: None,
272                }
273            }
274            (OwnerState::Base { .. }, OwnerUpdate::AbolishOwnerRole) => {
275                self.assert_owner(storage, sender)?;
276                OwnerState::Abolished
277            }
278            (
279                OwnerState::Proposed {
280                    proposed,
281                    #[cfg(feature = "emergency-owner")]
282                    emergency_owner,
283                    ..
284                },
285                OwnerUpdate::AcceptProposed,
286            ) => {
287                self.assert_proposed(storage, sender)?;
288                OwnerState::Base {
289                    owner: proposed,
290                    #[cfg(feature = "emergency-owner")]
291                    emergency_owner,
292                }
293            }
294            (
295                OwnerState::Proposed {
296                    owner,
297                    #[cfg(feature = "emergency-owner")]
298                    emergency_owner,
299                    ..
300                },
301                OwnerUpdate::ClearProposed,
302            ) => {
303                self.assert_owner(storage, sender)?;
304                OwnerState::Base {
305                    owner,
306                    #[cfg(feature = "emergency-owner")]
307                    emergency_owner,
308                }
309            }
310            (_, _) => return Err(OwnerError::StateTransitionError {}),
311        };
312        Ok(new_state)
313    }
314
315    //--------------------------------------------------------------------------------------------------
316    // Assertions
317    //--------------------------------------------------------------------------------------------------
318    /// Similar to is_owner() except it raises an exception if caller is not current owner
319    pub fn assert_owner(&self, storage: &'a dyn Storage, caller: &Addr) -> OwnerResult<()> {
320        if !self.is_owner(storage, caller)? {
321            Err(OwnerError::NotOwner {})
322        } else {
323            Ok(())
324        }
325    }
326
327    pub fn assert_proposed(&self, storage: &'a dyn Storage, caller: &Addr) -> OwnerResult<()> {
328        if !self.is_proposed(storage, caller)? {
329            Err(OwnerError::NotProposedOwner {})
330        } else {
331            Ok(())
332        }
333    }
334
335    #[cfg(feature = "emergency-owner")]
336    pub fn assert_emergency_owner(
337        &self,
338        storage: &'a dyn Storage,
339        caller: &Addr,
340    ) -> OwnerResult<()> {
341        if !self.is_emergency_owner(storage, caller)? {
342            Err(OwnerError::NotEmergencyOwner {})
343        } else {
344            Ok(())
345        }
346    }
347}
348
349#[cfg(test)]
350mod tests {
351
352    //--------------------------------------------------------------------------------------------------
353    // Test invalid state transitions
354    //--------------------------------------------------------------------------------------------------
355
356    use crate::owner::OwnerState;
357    use crate::OwnerUpdate::{AbolishOwnerRole, AcceptProposed, ClearProposed, ProposeNewOwner};
358    #[cfg(feature = "emergency-owner")]
359    use crate::OwnerUpdate::{ClearEmergencyOwner, SetEmergencyOwner};
360    use crate::{Owner, OwnerError, OwnerInit, OwnerResponse};
361    use cosmwasm_std::testing::{mock_dependencies, mock_info};
362    use cosmwasm_std::{Addr, Empty, Storage};
363
364    #[test]
365    fn invalid_uninitialized_state_transitions() {
366        let mut deps = mock_dependencies();
367        let sender = Addr::unchecked("peter_parker");
368        let info = mock_info(sender.as_ref(), &[]);
369        let owner = Owner::new("xyz");
370
371        let err = owner
372            .update::<Empty, Empty>(
373                deps.as_mut(),
374                info.clone(),
375                ProposeNewOwner {
376                    proposed: "abc".to_string(),
377                },
378            )
379            .unwrap_err();
380        assert_eq!(err, OwnerError::StateTransitionError {});
381
382        let err = owner
383            .update::<Empty, Empty>(deps.as_mut(), info.clone(), ClearProposed)
384            .unwrap_err();
385        assert_eq!(err, OwnerError::StateTransitionError {});
386
387        let err = owner
388            .update::<Empty, Empty>(deps.as_mut(), info.clone(), AcceptProposed)
389            .unwrap_err();
390        assert_eq!(err, OwnerError::StateTransitionError {});
391
392        let err = owner
393            .update::<Empty, Empty>(deps.as_mut(), info.clone(), AbolishOwnerRole)
394            .unwrap_err();
395        assert_eq!(err, OwnerError::StateTransitionError {});
396
397        #[cfg(feature = "emergency-owner")]
398        {
399            let err = owner
400                .update::<Empty, Empty>(
401                    deps.as_mut(),
402                    info.clone(),
403                    SetEmergencyOwner {
404                        emergency_owner: "xyz".to_string(),
405                    },
406                )
407                .unwrap_err();
408
409            assert_eq!(err, OwnerError::StateTransitionError {});
410            let err = owner
411                .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
412                .unwrap_err();
413            assert_eq!(err, OwnerError::StateTransitionError {});
414        }
415    }
416
417    #[test]
418    fn invalid_owner_set_no_proposed_state_transitions() {
419        let mut deps = mock_dependencies();
420        let sender = Addr::unchecked("peter_parker");
421        let info = mock_info(sender.as_ref(), &[]);
422        let owner = Owner::new("xyz");
423
424        let mut_deps = deps.as_mut();
425
426        owner
427            .initialize(
428                mut_deps.storage,
429                mut_deps.api,
430                OwnerInit::SetInitialOwner {
431                    owner: sender.to_string(),
432                },
433            )
434            .unwrap();
435
436        let err = owner
437            .initialize(
438                mut_deps.storage,
439                mut_deps.api,
440                OwnerInit::SetInitialOwner {
441                    owner: "abc".to_string(),
442                },
443            )
444            .unwrap_err();
445        assert_eq!(err, OwnerError::StateTransitionError {});
446
447        let err = owner
448            .update::<Empty, Empty>(deps.as_mut(), info.clone(), ClearProposed)
449            .unwrap_err();
450        assert_eq!(err, OwnerError::StateTransitionError {});
451
452        let err = owner
453            .update::<Empty, Empty>(deps.as_mut(), info, AcceptProposed)
454            .unwrap_err();
455        assert_eq!(err, OwnerError::StateTransitionError {});
456    }
457
458    #[test]
459    fn invalid_owner_set_with_proposed_state_transitions() {
460        let mut deps = mock_dependencies();
461        let sender = Addr::unchecked("peter_parker");
462        let info = mock_info(sender.as_ref(), &[]);
463        let owner = Owner::new("xyz");
464
465        let mut_deps = deps.as_mut();
466
467        owner
468            .initialize(
469                mut_deps.storage,
470                mut_deps.api,
471                OwnerInit::SetInitialOwner {
472                    owner: sender.to_string(),
473                },
474            )
475            .unwrap();
476
477        owner
478            .update::<Empty, Empty>(
479                mut_deps,
480                info.clone(),
481                ProposeNewOwner {
482                    proposed: "abc".to_string(),
483                },
484            )
485            .unwrap();
486
487        let mut_deps = deps.as_mut();
488
489        let err = owner
490            .initialize(
491                mut_deps.storage,
492                mut_deps.api,
493                OwnerInit::SetInitialOwner {
494                    owner: "abc".to_string(),
495                },
496            )
497            .unwrap_err();
498        assert_eq!(err, OwnerError::StateTransitionError {});
499
500        let err = owner
501            .update::<Empty, Empty>(
502                deps.as_mut(),
503                info.clone(),
504                ProposeNewOwner {
505                    proposed: "efg".to_string(),
506                },
507            )
508            .unwrap_err();
509        assert_eq!(err, OwnerError::StateTransitionError {});
510
511        #[cfg(feature = "emergency-owner")]
512        {
513            let err = owner
514                .update::<Empty, Empty>(
515                    deps.as_mut(),
516                    info.clone(),
517                    SetEmergencyOwner {
518                        emergency_owner: "xyz".to_string(),
519                    },
520                )
521                .unwrap_err();
522            assert_eq!(err, OwnerError::StateTransitionError {});
523
524            let err = owner
525                .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
526                .unwrap_err();
527            assert_eq!(err, OwnerError::StateTransitionError {});
528        }
529    }
530
531    #[test]
532    fn invalid_owner_role_abolished_state_transitions() {
533        let mut deps = mock_dependencies();
534        let sender = Addr::unchecked("peter_parker");
535        let info = mock_info(sender.as_ref(), &[]);
536        let owner = Owner::new("xyz");
537
538        let mut_deps = deps.as_mut();
539
540        owner
541            .initialize(mut_deps.storage, mut_deps.api, OwnerInit::AbolishOwnerRole)
542            .unwrap();
543
544        let err = owner
545            .initialize(
546                mut_deps.storage,
547                mut_deps.api,
548                OwnerInit::SetInitialOwner {
549                    owner: "abc".to_string(),
550                },
551            )
552            .unwrap_err();
553        assert_eq!(err, OwnerError::StateTransitionError {});
554
555        let err = owner
556            .update::<Empty, Empty>(
557                deps.as_mut(),
558                info.clone(),
559                ProposeNewOwner {
560                    proposed: "efg".to_string(),
561                },
562            )
563            .unwrap_err();
564        assert_eq!(err, OwnerError::StateTransitionError {});
565
566        let err = owner
567            .update::<Empty, Empty>(deps.as_mut(), info.clone(), ClearProposed)
568            .unwrap_err();
569        assert_eq!(err, OwnerError::StateTransitionError {});
570
571        let err = owner
572            .update::<Empty, Empty>(deps.as_mut(), info.clone(), AcceptProposed)
573            .unwrap_err();
574        assert_eq!(err, OwnerError::StateTransitionError {});
575
576        let err = owner
577            .update::<Empty, Empty>(deps.as_mut(), info.clone(), AbolishOwnerRole)
578            .unwrap_err();
579        assert_eq!(err, OwnerError::StateTransitionError {});
580
581        #[cfg(feature = "emergency-owner")]
582        {
583            let err = owner
584                .update::<Empty, Empty>(
585                    deps.as_mut(),
586                    info.clone(),
587                    SetEmergencyOwner {
588                        emergency_owner: "xyz".to_string(),
589                    },
590                )
591                .unwrap_err();
592            assert_eq!(err, OwnerError::StateTransitionError {});
593
594            let err = owner
595                .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
596                .unwrap_err();
597            assert_eq!(err, OwnerError::StateTransitionError {});
598        }
599    }
600
601    //--------------------------------------------------------------------------------------------------
602    // Test permissions
603    //--------------------------------------------------------------------------------------------------
604
605    #[test]
606    fn initialize_owner_permissions() {
607        let mut deps = mock_dependencies();
608        let mut_deps = deps.as_mut();
609        let owner = Owner::new("xyz");
610
611        // Anyone can initialize
612        owner
613            .initialize(mut_deps.storage, mut_deps.api, OwnerInit::AbolishOwnerRole)
614            .unwrap();
615
616        let mut deps = mock_dependencies();
617        let mut_deps = deps.as_mut();
618
619        owner
620            .initialize(
621                mut_deps.storage,
622                mut_deps.api,
623                OwnerInit::SetInitialOwner {
624                    owner: "xyz".to_string(),
625                },
626            )
627            .unwrap();
628    }
629
630    #[test]
631    fn propose_new_owner_permissions() {
632        let mut deps = mock_dependencies();
633        let sender = Addr::unchecked("peter_parker");
634        let owner = Owner::new("xyz");
635
636        let mut_deps = deps.as_mut();
637        owner
638            .initialize(
639                mut_deps.storage,
640                mut_deps.api,
641                OwnerInit::SetInitialOwner {
642                    owner: sender.to_string(),
643                },
644            )
645            .unwrap();
646
647        let bad_guy = Addr::unchecked("doc_oc");
648        let info = mock_info(bad_guy.as_ref(), &[]);
649        let err = owner
650            .update::<Empty, Empty>(
651                mut_deps,
652                info,
653                ProposeNewOwner {
654                    proposed: bad_guy.to_string(),
655                },
656            )
657            .unwrap_err();
658
659        assert_eq!(err, OwnerError::NotOwner {})
660    }
661
662    #[test]
663    fn clear_proposed_permissions() {
664        let mut deps = mock_dependencies();
665        let sender = Addr::unchecked("peter_parker");
666        let info = mock_info(sender.as_ref(), &[]);
667        let owner = Owner::new("xyz");
668
669        let mut_deps = deps.as_mut();
670        owner
671            .initialize(
672                mut_deps.storage,
673                mut_deps.api,
674                OwnerInit::SetInitialOwner {
675                    owner: sender.to_string(),
676                },
677            )
678            .unwrap();
679        owner
680            .update::<Empty, Empty>(
681                mut_deps,
682                info,
683                ProposeNewOwner {
684                    proposed: "miles_morales".to_string(),
685                },
686            )
687            .unwrap();
688
689        let bad_guy = Addr::unchecked("doc_oc");
690        let info = mock_info(bad_guy.as_ref(), &[]);
691        let err = owner
692            .update::<Empty, Empty>(deps.as_mut(), info, ClearProposed)
693            .unwrap_err();
694
695        assert_eq!(err, OwnerError::NotOwner {})
696    }
697
698    #[test]
699    fn accept_proposed_permissions() {
700        let mut deps = mock_dependencies();
701        let sender = Addr::unchecked("peter_parker");
702        let info = mock_info(sender.as_ref(), &[]);
703        let owner = Owner::new("xyz");
704
705        let mut_deps = deps.as_mut();
706        owner
707            .initialize(
708                mut_deps.storage,
709                mut_deps.api,
710                OwnerInit::SetInitialOwner {
711                    owner: sender.to_string(),
712                },
713            )
714            .unwrap();
715        owner
716            .update::<Empty, Empty>(
717                mut_deps,
718                info,
719                ProposeNewOwner {
720                    proposed: "miles_morales".to_string(),
721                },
722            )
723            .unwrap();
724
725        let bad_guy = Addr::unchecked("doc_oc");
726        let info = mock_info(bad_guy.as_ref(), &[]);
727        let err = owner
728            .update::<Empty, Empty>(deps.as_mut(), info, AcceptProposed)
729            .unwrap_err();
730
731        assert_eq!(err, OwnerError::NotProposedOwner {})
732    }
733
734    #[test]
735    fn abolish_owner_role_permissions() {
736        let mut deps = mock_dependencies();
737        let sender = Addr::unchecked("peter_parker");
738        let owner = Owner::new("xyz");
739
740        let mut_deps = deps.as_mut();
741        owner
742            .initialize(
743                mut_deps.storage,
744                mut_deps.api,
745                OwnerInit::SetInitialOwner {
746                    owner: sender.to_string(),
747                },
748            )
749            .unwrap();
750
751        let bad_guy = Addr::unchecked("doc_oc");
752        let info = mock_info(bad_guy.as_ref(), &[]);
753        let err = owner
754            .update::<Empty, Empty>(deps.as_mut(), info, AbolishOwnerRole)
755            .unwrap_err();
756
757        assert_eq!(err, OwnerError::NotOwner {})
758    }
759
760    #[cfg(feature = "emergency-owner")]
761    #[test]
762    fn set_emergency_owner_role_permissions() {
763        let mut deps = mock_dependencies();
764        let sender = Addr::unchecked("peter_parker");
765        let owner = Owner::new("xyz");
766
767        let mut_deps = deps.as_mut();
768        owner
769            .initialize(
770                mut_deps.storage,
771                mut_deps.api,
772                OwnerInit::SetInitialOwner {
773                    owner: sender.to_string(),
774                },
775            )
776            .unwrap();
777
778        let bad_guy = Addr::unchecked("doc_oc");
779        let info = mock_info(bad_guy.as_ref(), &[]);
780        let err = owner
781            .update::<Empty, Empty>(
782                deps.as_mut(),
783                info,
784                SetEmergencyOwner {
785                    emergency_owner: bad_guy.to_string(),
786                },
787            )
788            .unwrap_err();
789
790        assert_eq!(err, OwnerError::NotOwner {})
791    }
792
793    #[cfg(feature = "emergency-owner")]
794    #[test]
795    fn clear_emergency_owner_role_permissions() {
796        let mut deps = mock_dependencies();
797        let sender = Addr::unchecked("peter_parker");
798        let owner = Owner::new("xyz");
799
800        let mut_deps = deps.as_mut();
801        owner
802            .initialize(
803                mut_deps.storage,
804                mut_deps.api,
805                OwnerInit::SetInitialOwner {
806                    owner: sender.to_string(),
807                },
808            )
809            .unwrap();
810
811        let bad_guy = Addr::unchecked("doc_oc");
812        let info = mock_info(bad_guy.as_ref(), &[]);
813        let err = owner
814            .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
815            .unwrap_err();
816
817        assert_eq!(err, OwnerError::NotOwner {})
818    }
819
820    //--------------------------------------------------------------------------------------------------
821    // Test success cases
822    //--------------------------------------------------------------------------------------------------
823
824    fn assert_uninitialized(storage: &dyn Storage, owner: &Owner) {
825        let state = owner.state(storage).unwrap();
826        match state {
827            OwnerState::Uninitialized => {}
828            _ => panic!("Should be in the Uninitialized state"),
829        }
830
831        let current = owner.current(storage).unwrap();
832        assert_eq!(current, None);
833
834        let proposed = owner.proposed(storage).unwrap();
835        assert_eq!(proposed, None);
836
837        let res = owner.query(storage).unwrap();
838        assert_eq!(
839            res,
840            OwnerResponse {
841                owner: None,
842                proposed: None,
843                initialized: false,
844                abolished: false,
845                #[cfg(feature = "emergency-owner")]
846                emergency_owner: None,
847            }
848        );
849    }
850
851    #[test]
852    fn uninitialized_state() {
853        let deps = mock_dependencies();
854        let owner = Owner::new("xyz");
855        assert_uninitialized(deps.as_ref().storage, &owner);
856    }
857
858    #[test]
859    fn initialize_owner() {
860        let mut deps = mock_dependencies();
861        let original_owner = Addr::unchecked("peter_parker");
862        let owner = Owner::new("xyz");
863
864        let mut_deps = deps.as_mut();
865        owner
866            .initialize(
867                mut_deps.storage,
868                mut_deps.api,
869                OwnerInit::SetInitialOwner {
870                    owner: original_owner.to_string(),
871                },
872            )
873            .unwrap();
874
875        let state = owner.state(mut_deps.storage).unwrap();
876        match state {
877            OwnerState::Base { .. } => {}
878            _ => panic!("Should be in the Base state"),
879        }
880
881        let current = owner.current(mut_deps.storage).unwrap();
882        assert_eq!(current, Some(original_owner.clone()));
883        assert!(owner.is_owner(mut_deps.storage, &original_owner).unwrap());
884
885        let proposed = owner.proposed(mut_deps.storage).unwrap();
886        assert_eq!(proposed, None);
887
888        let res = owner.query(mut_deps.storage).unwrap();
889        assert_eq!(
890            res,
891            OwnerResponse {
892                owner: Some(original_owner.to_string()),
893                proposed: None,
894                initialized: true,
895                abolished: false,
896                #[cfg(feature = "emergency-owner")]
897                emergency_owner: None,
898            }
899        );
900    }
901
902    #[test]
903    fn propose_new_owner() {
904        let mut deps = mock_dependencies();
905        let original_owner = Addr::unchecked("peter_parker");
906        let proposed_owner = Addr::unchecked("miles_morales");
907        let info = mock_info(original_owner.as_ref(), &[]);
908        let owner = Owner::new("xyz");
909
910        let mut_deps = deps.as_mut();
911        owner
912            .initialize(
913                mut_deps.storage,
914                mut_deps.api,
915                OwnerInit::SetInitialOwner {
916                    owner: original_owner.to_string(),
917                },
918            )
919            .unwrap();
920
921        owner
922            .update::<Empty, Empty>(
923                mut_deps,
924                info,
925                ProposeNewOwner {
926                    proposed: "miles_morales".to_string(),
927                },
928            )
929            .unwrap();
930
931        let storage = deps.as_mut().storage;
932
933        let state = owner.state(storage).unwrap();
934        match state {
935            OwnerState::Proposed { .. } => {}
936            _ => panic!("Should be in the Proposed state"),
937        }
938
939        let current = owner.current(storage).unwrap();
940        assert_eq!(current, Some(original_owner.clone()));
941        assert!(owner.is_owner(storage, &original_owner).unwrap());
942
943        let proposed = owner.proposed(storage).unwrap();
944        assert_eq!(proposed, Some(proposed_owner.clone()));
945        assert!(owner.is_proposed(storage, &proposed_owner).unwrap());
946
947        let res = owner.query(storage).unwrap();
948        assert_eq!(
949            res,
950            OwnerResponse {
951                owner: Some(original_owner.to_string()),
952                proposed: Some(proposed_owner.to_string()),
953                initialized: true,
954                abolished: false,
955                #[cfg(feature = "emergency-owner")]
956                emergency_owner: None,
957            }
958        );
959    }
960
961    #[test]
962    fn clear_proposed() {
963        let mut deps = mock_dependencies();
964        let original_owner = Addr::unchecked("peter_parker");
965        let proposed_owner = Addr::unchecked("miles_morales");
966        let info = mock_info(original_owner.as_ref(), &[]);
967        let owner = Owner::new("xyz");
968
969        let mut_deps = deps.as_mut();
970        owner
971            .initialize(
972                mut_deps.storage,
973                mut_deps.api,
974                OwnerInit::SetInitialOwner {
975                    owner: original_owner.to_string(),
976                },
977            )
978            .unwrap();
979
980        let mut_deps = deps.as_mut();
981        owner
982            .update::<Empty, Empty>(
983                mut_deps,
984                info.clone(),
985                ProposeNewOwner {
986                    proposed: "miles_morales".to_string(),
987                },
988            )
989            .unwrap();
990
991        let mut_deps = deps.as_mut();
992        owner
993            .update::<Empty, Empty>(mut_deps, info, ClearProposed)
994            .unwrap();
995
996        let storage = deps.as_mut().storage;
997
998        let state = owner.state(storage).unwrap();
999        match state {
1000            OwnerState::Base { .. } => {}
1001            _ => panic!("Should be in the Base state"),
1002        }
1003
1004        let current = owner.current(storage).unwrap();
1005        assert_eq!(current, Some(original_owner.clone()));
1006        assert!(owner.is_owner(storage, &original_owner).unwrap());
1007
1008        let proposed = owner.proposed(storage).unwrap();
1009        assert_eq!(proposed, None);
1010        assert!(!owner.is_proposed(storage, &proposed_owner).unwrap());
1011
1012        let res = owner.query(storage).unwrap();
1013        assert_eq!(
1014            res,
1015            OwnerResponse {
1016                owner: Some(original_owner.to_string()),
1017                proposed: None,
1018                initialized: true,
1019                abolished: false,
1020                #[cfg(feature = "emergency-owner")]
1021                emergency_owner: None,
1022            }
1023        );
1024    }
1025
1026    #[test]
1027    fn accept_proposed() {
1028        let mut deps = mock_dependencies();
1029        let original_owner = Addr::unchecked("peter_parker");
1030        let proposed_owner = Addr::unchecked("miles_morales");
1031        let info = mock_info(original_owner.as_ref(), &[]);
1032        let owner = Owner::new("xyz");
1033
1034        let mut_deps = deps.as_mut();
1035        owner
1036            .initialize(
1037                mut_deps.storage,
1038                mut_deps.api,
1039                OwnerInit::SetInitialOwner {
1040                    owner: original_owner.to_string(),
1041                },
1042            )
1043            .unwrap();
1044
1045        let mut_deps = deps.as_mut();
1046        owner
1047            .update::<Empty, Empty>(
1048                mut_deps,
1049                info,
1050                ProposeNewOwner {
1051                    proposed: "miles_morales".to_string(),
1052                },
1053            )
1054            .unwrap();
1055
1056        let info = mock_info(proposed_owner.as_ref(), &[]);
1057        let mut_deps = deps.as_mut();
1058        owner
1059            .update::<Empty, Empty>(mut_deps, info, AcceptProposed)
1060            .unwrap();
1061
1062        let storage = deps.as_mut().storage;
1063
1064        let state = owner.state(storage).unwrap();
1065        match state {
1066            OwnerState::Base { .. } => {}
1067            _ => panic!("Should be in the Base state"),
1068        }
1069
1070        let current = owner.current(storage).unwrap();
1071        assert_eq!(current, Some(proposed_owner.clone()));
1072        assert!(owner.is_owner(storage, &proposed_owner).unwrap());
1073
1074        let proposed = owner.proposed(storage).unwrap();
1075        assert_eq!(proposed, None);
1076        assert!(!owner.is_proposed(storage, &proposed_owner).unwrap());
1077
1078        let res = owner.query(storage).unwrap();
1079        assert_eq!(
1080            res,
1081            OwnerResponse {
1082                owner: Some(proposed_owner.to_string()),
1083                proposed: None,
1084                initialized: true,
1085                abolished: false,
1086                #[cfg(feature = "emergency-owner")]
1087                emergency_owner: None,
1088            }
1089        );
1090    }
1091
1092    #[test]
1093    fn abolish_owner_role() {
1094        let mut deps = mock_dependencies();
1095        let original_owner = Addr::unchecked("peter_parker");
1096        let info = mock_info(original_owner.as_ref(), &[]);
1097        let owner = Owner::new("xyz");
1098
1099        let mut_deps = deps.as_mut();
1100        owner
1101            .initialize(
1102                mut_deps.storage,
1103                mut_deps.api,
1104                OwnerInit::SetInitialOwner {
1105                    owner: original_owner.to_string(),
1106                },
1107            )
1108            .unwrap();
1109
1110        let mut_deps = deps.as_mut();
1111        owner
1112            .update::<Empty, Empty>(mut_deps, info, AbolishOwnerRole)
1113            .unwrap();
1114
1115        let storage = deps.as_mut().storage;
1116
1117        let state = owner.state(storage).unwrap();
1118        match state {
1119            OwnerState::Abolished => {}
1120            _ => panic!("Should be in the Abolished state"),
1121        }
1122
1123        let current = owner.current(storage).unwrap();
1124        assert_eq!(current, None);
1125        assert!(!owner.is_owner(storage, &original_owner).unwrap());
1126
1127        let proposed = owner.proposed(storage).unwrap();
1128        assert_eq!(proposed, None);
1129        assert!(!owner.is_proposed(storage, &original_owner).unwrap());
1130
1131        let res = owner.query(storage).unwrap();
1132        assert_eq!(
1133            res,
1134            OwnerResponse {
1135                owner: None,
1136                proposed: None,
1137                initialized: true,
1138                abolished: true,
1139                #[cfg(feature = "emergency-owner")]
1140                emergency_owner: None,
1141            }
1142        );
1143    }
1144
1145    #[cfg(feature = "emergency-owner")]
1146    #[test]
1147    fn set_emergency_owner() {
1148        let mut deps = mock_dependencies();
1149        let original_owner = Addr::unchecked("peter_parker");
1150        let emergency_owner = Addr::unchecked("miles_morales");
1151        let info = mock_info(original_owner.as_ref(), &[]);
1152        let owner = Owner::new("xyz");
1153
1154        let mut_deps = deps.as_mut();
1155
1156        owner
1157            .initialize(
1158                mut_deps.storage,
1159                mut_deps.api,
1160                OwnerInit::SetInitialOwner {
1161                    owner: original_owner.to_string(),
1162                },
1163            )
1164            .unwrap();
1165
1166        let current = owner.current(mut_deps.storage).unwrap();
1167        assert_eq!(current, Some(original_owner.clone()));
1168        assert!(owner.is_owner(mut_deps.storage, &original_owner).unwrap());
1169
1170        let em_owner = owner.emergency_owner(mut_deps.storage).unwrap();
1171        assert_eq!(em_owner, None);
1172        assert!(!owner
1173            .is_emergency_owner(mut_deps.storage, &emergency_owner)
1174            .unwrap());
1175
1176        let res = owner.query(mut_deps.storage).unwrap();
1177        assert_eq!(
1178            res,
1179            OwnerResponse {
1180                owner: Some(original_owner.to_string()),
1181                proposed: None,
1182                initialized: true,
1183                abolished: false,
1184                emergency_owner: None,
1185            }
1186        );
1187
1188        owner
1189            .update::<Empty, Empty>(
1190                mut_deps,
1191                info,
1192                SetEmergencyOwner {
1193                    emergency_owner: emergency_owner.to_string(),
1194                },
1195            )
1196            .unwrap();
1197
1198        let storage = deps.as_ref().storage;
1199
1200        let current = owner.current(storage).unwrap();
1201        assert_eq!(current, Some(original_owner.clone()));
1202        assert!(owner.is_owner(storage, &original_owner).unwrap());
1203
1204        let em_owner = owner.emergency_owner(storage).unwrap();
1205        assert_eq!(em_owner, Some(emergency_owner.clone()));
1206        assert!(owner.is_emergency_owner(storage, &emergency_owner).unwrap());
1207
1208        let state = owner.state(storage).unwrap();
1209        match state {
1210            OwnerState::Base { .. } => {}
1211            _ => panic!("Should be in the Base state"),
1212        }
1213
1214        let res = owner.query(storage).unwrap();
1215        assert_eq!(
1216            res,
1217            OwnerResponse {
1218                owner: Some(original_owner.to_string()),
1219                proposed: None,
1220                emergency_owner: Some(emergency_owner.to_string()),
1221                initialized: true,
1222                abolished: false,
1223            }
1224        );
1225    }
1226
1227    #[cfg(feature = "emergency-owner")]
1228    #[test]
1229    fn clear_emergency_owner() {
1230        let mut deps = mock_dependencies();
1231        let original_owner = Addr::unchecked("peter_parker");
1232        let emergency_owner = Addr::unchecked("miles_morales");
1233        let info = mock_info(original_owner.as_ref(), &[]);
1234        let owner = Owner::new("xyz");
1235
1236        let mut_deps = deps.as_mut();
1237
1238        owner
1239            .initialize(
1240                mut_deps.storage,
1241                mut_deps.api,
1242                OwnerInit::SetInitialOwner {
1243                    owner: original_owner.to_string(),
1244                },
1245            )
1246            .unwrap();
1247
1248        owner
1249            .update::<Empty, Empty>(
1250                mut_deps,
1251                info.clone(),
1252                SetEmergencyOwner {
1253                    emergency_owner: emergency_owner.to_string(),
1254                },
1255            )
1256            .unwrap();
1257
1258        owner
1259            .update::<Empty, Empty>(deps.as_mut(), info, ClearEmergencyOwner)
1260            .unwrap();
1261
1262        let storage = deps.as_ref().storage;
1263
1264        let current = owner.current(storage).unwrap();
1265        assert_eq!(current, Some(original_owner.clone()));
1266        assert!(owner.is_owner(storage, &original_owner).unwrap());
1267
1268        let em_owner = owner.emergency_owner(storage).unwrap();
1269        assert_eq!(em_owner, None);
1270        assert!(!owner.is_emergency_owner(storage, &emergency_owner).unwrap());
1271
1272        let state = owner.state(storage).unwrap();
1273        match state {
1274            OwnerState::Base { .. } => {}
1275            _ => panic!("Should be in the Base state"),
1276        }
1277
1278        let res = owner.query(storage).unwrap();
1279        assert_eq!(
1280            res,
1281            OwnerResponse {
1282                owner: Some(original_owner.to_string()),
1283                proposed: None,
1284                initialized: true,
1285                abolished: false,
1286                emergency_owner: None,
1287            }
1288        );
1289    }
1290}