iop_morpheus_node/
state_holder.rs

1use super::*;
2
3#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
4#[serde(rename_all = "camelCase")]
5pub struct TransactionIdWithHeight {
6    pub transaction_id: String,
7    pub height: BlockHeight,
8}
9
10#[derive(Default)]
11pub struct StateHolder {
12    corrupted: bool,
13    inner: Box<State>,
14}
15
16impl StateHolder {
17    pub const CORRUPTED_ERR_MSG: &'static str =
18        "Morpheus state is corrupt. All incoming changes will be ignored.";
19
20    pub fn is_corrupted(&self) -> bool {
21        self.corrupted
22    }
23
24    pub fn ensure_not_corrupted(&self) -> Result<()> {
25        ensure!(!self.corrupted, StateHolder::CORRUPTED_ERR_MSG);
26        Ok(())
27    }
28
29    pub fn state(&self) -> Result<&State> {
30        self.ensure_not_corrupted()?;
31        Ok(&self.inner)
32    }
33
34    pub fn dry_run(&self, asset: &MorpheusAsset) -> Result<Vec<OperationError>> {
35        if self.is_corrupted() {
36            return Ok(vec![OperationError {
37                invalid_operation_attempt: None,
38                message: StateHolder::CORRUPTED_ERR_MSG.to_owned(),
39            }]);
40        }
41
42        let temp = self.inner.clone();
43        let res = asset.operation_attempts.iter().try_fold(
44            temp,
45            |mut inner, op| -> Result<Box<State>, OperationError> {
46                inner.apply(Mutation::DoAttempt { txid: "dry_run", op }).map_err(|e| {
47                    OperationError {
48                        invalid_operation_attempt: Some(op.clone()),
49                        message: e.to_string(),
50                    }
51                })?;
52                Ok(inner)
53            },
54        );
55        // TODO The TS code just stopped on the 1st error, should we collect them all?
56        let errors = match res {
57            Ok(_) => vec![],
58            Err(e) => vec![e],
59        };
60        Ok(errors)
61    }
62
63    pub fn block_applying(&mut self, height: BlockHeight) -> Result<()> {
64        self.ensure_not_corrupted()?;
65        self.may_corrupt_state(|inner| inner.apply(Mutation::SetBlockHeight { height }))
66    }
67
68    pub fn apply_transaction(&mut self, txid: &str, asset: &MorpheusAsset) -> Result<()> {
69        self.ensure_not_corrupted()?;
70        asset
71            .operation_attempts
72            .iter()
73            .try_for_each(|op| self.inner.apply(Mutation::RegisterAttempt { txid, op }))?;
74        let inner_res = asset.operation_attempts.iter().try_fold(
75            self.inner.clone(),
76            |mut inner, op| -> Result<Box<State>> {
77                inner.apply(Mutation::DoAttempt { txid, op })?;
78                Ok(inner)
79            },
80        );
81        match inner_res {
82            Ok(mut inner) => {
83                inner.apply(Mutation::ConfirmTxn { txid })?;
84                self.inner = inner;
85                Ok(())
86            }
87            Err(e) => {
88                self.inner.apply(Mutation::RejectTxn { txid })?;
89                Err(e)
90            }
91        }
92    }
93
94    pub fn block_reverting(&mut self, height: BlockHeight) -> Result<()> {
95        self.ensure_not_corrupted()?;
96        self.may_corrupt_state(|inner| inner.revert(Mutation::SetBlockHeight { height }))
97    }
98
99    pub fn revert_transaction(&mut self, txid: &str, asset: &MorpheusAsset) -> Result<()> {
100        self.ensure_not_corrupted()?;
101        self.may_corrupt_state(|inner| {
102            let confirmed_opt = inner.is_confirmed(txid);
103            ensure!(
104                confirmed_opt.is_some(),
105                "Transaction {} has not been applied, cannot revert.",
106                txid
107            );
108
109            // Option::unwrap is panic-free after handling None above
110            if confirmed_opt.unwrap() {
111                inner.revert(Mutation::ConfirmTxn { txid })?;
112                asset.operation_attempts.iter().rev().try_for_each(|op| -> Result<()> {
113                    inner.revert(Mutation::DoAttempt { txid, op })?;
114                    Ok(())
115                })?;
116            } else {
117                inner.revert(Mutation::RejectTxn { txid })?;
118            }
119            asset.operation_attempts.iter().rev().try_for_each(|op| -> Result<()> {
120                inner.revert(Mutation::RegisterAttempt { txid, op })?;
121                Ok(())
122            })?;
123            Ok(())
124        })
125    }
126
127    fn may_corrupt_state(&mut self, action: impl FnOnce(&mut State) -> Result<()>) -> Result<()> {
128        if let Err(e) = action(&mut self.inner) {
129            self.corrupted = true;
130            Err(e)
131        } else {
132            Ok(())
133        }
134    }
135}