Skip to main content

defuse_core/engine/
mod.rs

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