Skip to main content

drive/state_transition_action/batch/
mod.rs

1use crate::state_transition_action::batch::batched_transition::BatchedTransitionAction;
2use crate::state_transition_action::batch::v0::BatchTransitionActionV0;
3use derive_more::From;
4use dpp::data_contract::accessors::v0::DataContractV0Getters;
5use dpp::data_contract::document_type::accessors::DocumentTypeV0Getters;
6use crate::drive::contract_groups::types::ContractGroupMembershipsForContract;
7use dpp::fee::fee_result::FeeResult;
8use dpp::fee::Credits;
9use dpp::identity::SecurityLevel;
10use dpp::platform_value::Identifier;
11use dpp::prelude::UserFeeIncrease;
12use dpp::ProtocolError;
13use crate::state_transition_action::batch::batched_transition::document_transition::document_base_transition_action::DocumentBaseTransitionActionAccessorsV0;
14
15/// batched transition
16pub mod batched_transition;
17/// v0
18pub mod v0;
19
20#[cfg(test)]
21mod tests;
22
23/// A contract's group memberships as the batch transformer read them, with the fee of that
24/// read. The fee travels with the data, like a contract's fetch info: the transformer does not
25/// bill it, the check that uses the answer does, so a batch signed by an ordinary key pays
26/// nothing for it.
27#[derive(Debug, Clone, Default)]
28pub struct ResolvedContractGroupMemberships {
29    /// The groups the contract, its document types and its tokens belong to
30    pub memberships: ContractGroupMembershipsForContract,
31    /// What reading them cost
32    pub fee: FeeResult,
33}
34
35/// documents batch transition action
36#[derive(Debug, Clone, From)]
37pub enum BatchTransitionAction {
38    /// v0
39    V0(BatchTransitionActionV0),
40}
41
42impl BatchTransitionAction {
43    /// owner id
44    pub fn owner_id(&self) -> Identifier {
45        match self {
46            BatchTransitionAction::V0(v0) => v0.owner_id,
47        }
48    }
49
50    /// transitions
51    pub fn transitions(&self) -> &Vec<BatchedTransitionAction> {
52        match self {
53            BatchTransitionAction::V0(v0) => &v0.transitions,
54        }
55    }
56
57    /// transitions
58    pub fn transitions_mut(&mut self) -> &mut Vec<BatchedTransitionAction> {
59        match self {
60            BatchTransitionAction::V0(v0) => &mut v0.transitions,
61        }
62    }
63
64    /// transitions
65    pub fn transitions_take(&mut self) -> Vec<BatchedTransitionAction> {
66        match self {
67            BatchTransitionAction::V0(v0) => std::mem::take(&mut v0.transitions),
68        }
69    }
70
71    /// transitions owned
72    pub fn transitions_owned(self) -> Vec<BatchedTransitionAction> {
73        match self {
74            BatchTransitionAction::V0(v0) => v0.transitions,
75        }
76    }
77
78    /// set transitions
79    pub fn set_transitions(&mut self, transitions: Vec<BatchedTransitionAction>) {
80        match self {
81            BatchTransitionAction::V0(v0) => v0.transitions = transitions,
82        }
83    }
84
85    /// fee multiplier
86    pub fn user_fee_increase(&self) -> UserFeeIncrease {
87        match self {
88            BatchTransitionAction::V0(transition) => transition.user_fee_increase,
89        }
90    }
91
92    /// The group memberships the transformer resolved for a contract the batch touches
93    pub fn contract_group_memberships(
94        &self,
95        contract_id: &Identifier,
96    ) -> Option<&ResolvedContractGroupMemberships> {
97        match self {
98            BatchTransitionAction::V0(v0) => v0.contract_group_memberships.get(contract_id),
99        }
100    }
101
102    /// Records the group memberships of a contract the batch touches
103    pub fn set_contract_group_memberships(
104        &mut self,
105        contract_id: Identifier,
106        resolved: ResolvedContractGroupMemberships,
107    ) {
108        match self {
109            BatchTransitionAction::V0(v0) => {
110                v0.contract_group_memberships.insert(contract_id, resolved);
111            }
112        }
113    }
114}
115
116impl BatchTransitionAction {
117    /// The sum of all purchases amount and all conflicting index collateral voting funds
118    pub fn all_used_balances(&self) -> Result<Option<Credits>, ProtocolError> {
119        match self {
120            BatchTransitionAction::V0(v0) => v0.all_used_balances(),
121        }
122    }
123
124    /// The sum of all purchases amounts for all purchase transitions in the batch
125    pub fn all_purchases_amount(&self) -> Result<Option<Credits>, ProtocolError> {
126        match self {
127            BatchTransitionAction::V0(v0) => v0.all_purchases_amount(),
128        }
129    }
130
131    /// The sum of all conflicting index collateral voting funds for all document create transitions in the batch
132    pub fn all_conflicting_index_collateral_voting_funds(
133        &self,
134    ) -> Result<Option<Credits>, ProtocolError> {
135        match self {
136            BatchTransitionAction::V0(v0) => v0.all_conflicting_index_collateral_voting_funds(),
137        }
138    }
139
140    /// Determines the security level requirements for the batch transition action.
141    ///
142    /// This method performs the following steps:
143    ///
144    /// 1. Retrieves all document types associated with the state transitions (STs) in the batch.
145    /// 2. For each document type, fetches its schema to determine its security level requirement.
146    ///    - If the schema specifies a security level, that is used.
147    ///    - Otherwise, a default security level is used.
148    ///
149    /// The method then determines the highest security level (which corresponds to the lowest
150    /// integer value of the `SecurityLevel` enum) across all documents affected by the state transitions.
151    /// This highest level becomes the signature requirement for the entire batch transition action.
152    ///
153    /// # Returns
154    ///
155    /// - Returns a `Result` containing a `Vec<SecurityLevel>` which is the list of security
156    ///   levels required for the batch transition action.
157    /// - Returns an `Err` of type `ProtocolError` if any error occurs during the process.
158    ///
159    /// # Examples
160    ///
161    /// ```ignore
162    /// // Assuming `batch_transition_action` is an instance of `DocumentsBatchTransitionAction`
163    /// let required_levels = batch_transition_action.contract_based_security_level_requirement()?;
164    /// ```
165    ///
166    pub fn combined_security_level_requirement(&self) -> Result<Vec<SecurityLevel>, ProtocolError> {
167        // Step 1: Get all document types for the ST
168        // Step 2: Get document schema for every type
169        // If schema has security level, use that, if not, use the default security level
170        // Find the highest level (lowest int value) of all documents - the ST's signature
171        // requirement is the highest level across all documents affected by the ST./
172        let mut highest_security_level = SecurityLevel::lowest_level();
173
174        for transition in self.transitions().iter() {
175            match transition {
176                BatchedTransitionAction::DocumentAction(document_transition) => {
177                    let document_type_name = document_transition.base().document_type_name();
178                    let data_contract_info = document_transition.base().data_contract_fetch_info();
179
180                    let document_type = data_contract_info
181                        .contract
182                        .document_type_for_name(document_type_name)?;
183
184                    let document_security_level = document_type.security_level_requirement();
185
186                    // lower enum representation means higher in security
187                    if document_security_level < highest_security_level {
188                        highest_security_level = document_security_level
189                    }
190                }
191                BatchedTransitionAction::TokenAction(_) => {
192                    // lower enum representation means higher in security
193                    if highest_security_level != SecurityLevel::MASTER {
194                        highest_security_level = SecurityLevel::CRITICAL
195                    }
196                }
197                BatchedTransitionAction::BumpIdentityDataContractNonce(_) => {}
198            }
199        }
200        Ok(if highest_security_level == SecurityLevel::MASTER {
201            vec![SecurityLevel::MASTER]
202        } else {
203            // this might seem wrong until you realize that master is 0, critical 1, etc
204            (SecurityLevel::CRITICAL as u8..=highest_security_level as u8)
205                .map(|security_level| SecurityLevel::try_from(security_level).unwrap())
206                .collect()
207        })
208    }
209}