defuse_core/engine/
mod.rs

1mod inspector;
2mod state;
3
4pub use self::{inspector::*, state::*};
5
6use defuse_crypto::{Payload, SignedPayload};
7
8use crate::{
9    Deadline, DefuseError, ExpirableNonce, Nonce, Result, SaltedNonce, VersionedNonce,
10    intents::{DefuseIntents, ExecutableIntent},
11    payload::{DefusePayload, ExtractDefusePayload, multi::MultiPayload},
12};
13
14use self::deltas::{Deltas, Transfers};
15
16pub struct Engine<S, I> {
17    pub state: Deltas<S>,
18    pub inspector: I,
19}
20
21impl<S, I> Engine<S, I>
22where
23    S: State,
24    I: Inspector,
25{
26    #[inline]
27    pub fn new(state: S, inspector: I) -> Self {
28        Self {
29            state: Deltas::new(state),
30            inspector,
31        }
32    }
33
34    pub fn execute_signed_intents(
35        mut self,
36        signed: impl IntoIterator<Item = MultiPayload>,
37    ) -> Result<Transfers> {
38        for signed in signed {
39            self.execute_signed_intent(signed)?;
40        }
41        self.finalize()
42    }
43
44    fn execute_signed_intent(&mut self, signed: MultiPayload) -> Result<()> {
45        // verify signed payload and get public key
46        let public_key = signed.verify().ok_or(DefuseError::InvalidSignature)?;
47
48        // calculate intent hash
49        let hash = signed.hash();
50
51        // extract NEP-413 payload
52        let DefusePayload::<DefuseIntents> {
53            signer_id,
54            verifying_contract,
55            deadline,
56            nonce,
57            message: intents,
58        } = signed.extract_defuse_payload()?;
59
60        // check recipient
61        if verifying_contract != *self.state.verifying_contract() {
62            return Err(DefuseError::WrongVerifyingContract);
63        }
64
65        self.inspector.on_deadline(deadline);
66
67        // make sure message is still valid
68        if deadline.has_expired() {
69            return Err(DefuseError::DeadlineExpired);
70        }
71
72        // make sure the account has this public key
73        if !self.state.has_public_key(&signer_id, &public_key) {
74            return Err(DefuseError::PublicKeyNotExist(signer_id, public_key));
75        }
76
77        // commit nonce
78        self.verify_intent_nonce(nonce, deadline)?;
79        self.state.commit_nonce(signer_id.clone(), nonce)?;
80
81        intents.execute_intent(&signer_id, self, hash)?;
82        self.inspector.on_intent_executed(&signer_id, hash, nonce);
83
84        Ok(())
85    }
86
87    #[inline]
88    fn verify_intent_nonce(&self, nonce: Nonce, intent_deadline: Deadline) -> Result<()> {
89        let Some(nonce) = VersionedNonce::maybe_from(nonce) else {
90            return Ok(());
91        };
92
93        match nonce {
94            VersionedNonce::V1(SaltedNonce {
95                salt,
96                nonce: ExpirableNonce { deadline, .. },
97            }) => {
98                if !self.state.is_valid_salt(salt) {
99                    return Err(DefuseError::InvalidSalt);
100                }
101
102                if intent_deadline > deadline {
103                    return Err(DefuseError::DeadlineGreaterThanNonce);
104                }
105
106                if deadline.has_expired() {
107                    return Err(DefuseError::NonceExpired);
108                }
109            }
110        }
111
112        Ok(())
113    }
114
115    #[inline]
116    fn finalize(self) -> Result<Transfers> {
117        self.state
118            .finalize()
119            .map_err(DefuseError::InvariantViolated)
120    }
121}