Skip to main content

drive_abci/execution/validation/state_transition/transformer/
mod.rs

1use crate::error::execution::ExecutionError;
2use crate::error::Error;
3use crate::execution::types::state_transition_execution_context::StateTransitionExecutionContext;
4use crate::execution::validation::state_transition::address_credit_withdrawal::StateTransitionAddressCreditWithdrawalTransitionActionTransformer;
5use crate::execution::validation::state_transition::address_funding_from_asset_lock::StateTransitionAddressFundingFromAssetLockTransitionActionTransformer;
6use crate::execution::validation::state_transition::address_funds_transfer::StateTransitionAddressFundsTransferTransitionActionTransformer;
7use crate::execution::validation::state_transition::identity_create::StateTransitionActionTransformerForIdentityCreateTransitionV0;
8use crate::execution::validation::state_transition::identity_create_from_addresses::StateTransitionActionTransformerForIdentityCreateFromAddressesTransitionV0;
9use crate::execution::validation::state_transition::identity_create_from_shielded_pool::StateTransitionIdentityCreateFromShieldedPoolTransitionActionTransformer;
10use crate::execution::validation::state_transition::identity_top_up::StateTransitionIdentityTopUpTransitionActionTransformer;
11use crate::execution::validation::state_transition::identity_top_up_from_shielded_pool::StateTransitionIdentityTopUpFromShieldedPoolTransitionActionTransformer;
12use crate::execution::validation::state_transition::shield::StateTransitionShieldTransitionActionTransformer;
13use crate::execution::validation::state_transition::shield_from_asset_lock::StateTransitionShieldFromAssetLockTransitionActionTransformer;
14use crate::execution::validation::state_transition::shielded_transfer::StateTransitionShieldedTransferTransitionActionTransformer;
15use crate::execution::validation::state_transition::shielded_withdrawal::StateTransitionShieldedWithdrawalTransitionActionTransformer;
16use crate::execution::validation::state_transition::unshield::StateTransitionUnshieldTransitionActionTransformer;
17use crate::execution::validation::state_transition::ValidationMode;
18use crate::platform_types::platform::PlatformRef;
19use crate::rpc::core::CoreRPCLike;
20use dpp::address_funds::PlatformAddress;
21use dpp::block::block_info::BlockInfo;
22use dpp::fee::Credits;
23use dpp::identity::PartialIdentity;
24use dpp::prelude::{AddressNonce, ConsensusValidationResult};
25use dpp::serialization::Signable;
26use dpp::state_transition::StateTransition;
27use drive::grovedb::TransactionArg;
28use drive::state_transition_action::identity::identity_topup_from_addresses::IdentityTopUpFromAddressesTransitionAction;
29use drive::state_transition_action::StateTransitionAction;
30use std::collections::BTreeMap;
31
32/// A trait for validating state transitions within a blockchain.
33pub trait StateTransitionActionTransformer {
34    /// A trait for transforming a raw `TransactionArg` into a typed `StateTransitionAction`.
35    ///
36    /// This is primarily intended for **testing**, internal tooling, and controlled validation flows.
37    /// In production, the transformation is normally performed within `validate_state`, and this
38    /// method should not be invoked directly.
39    ///
40    /// ## Versioning Note
41    /// This trait is intentionally **not versioned**.
42    /// If the structure of `transform_into_action` ever needs to change, a new trait should be
43    /// introduced rather than modifying this one to preserve API stability.
44    ///
45    /// # Type Parameters
46    /// * `C` – A type implementing the [`CoreRPCLike`] trait.
47    ///
48    /// # Arguments
49    /// * `platform` – A platform reference implementing `CoreRPCLike`.
50    /// * `block_info` – Information about the current block.
51    /// * `remaining_address_input_balances` – A map of input addresses to their (nonce, balance)
52    ///   tuples used during partial validation.
53    /// * `validation_mode` – The current validation mode controlling the strictness of checks.
54    /// * `execution_context` – The execution context for the state transition.
55    /// * `tx` – The raw transaction argument to be transformed.
56    ///
57    /// # Returns
58    /// A `Result` containing:
59    /// * `ConsensusValidationResult<StateTransitionAction>` on success, or
60    /// * `Error` if the action could not be created or validated.
61    fn transform_into_action<C: CoreRPCLike>(
62        &self,
63        platform: &PlatformRef<C>,
64        block_info: &BlockInfo,
65        remaining_address_input_balances: &Option<
66            BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
67        >,
68        validation_mode: ValidationMode,
69        execution_context: &mut StateTransitionExecutionContext,
70        tx: TransactionArg,
71    ) -> Result<ConsensusValidationResult<StateTransitionAction>, Error>;
72}
73
74/// Transforms a state transition into its action when the translation depends on who signed it.
75///
76/// The action is the state-based translation of the transition, and part of that state can hang
77/// on the signing key: a batch signed by an AUTHENTICATION key bound to a contract group needs
78/// the group memberships of the contracts it touches, and no other batch does. This trait hands
79/// the transformer the signer's already loaded identity, so that state is read only when it is
80/// needed. [`StateTransitionActionTransformer`] stays as it is, per its versioning note; every
81/// transition other than a batch is transformed through it.
82pub trait StateTransitionSignerAwareActionTransformer {
83    /// Like [`StateTransitionActionTransformer::transform_into_action`], with the identity the
84    /// signature was validated against, when the caller holds one.
85    #[allow(clippy::too_many_arguments)] // The unversioned trait's inputs plus the signer.
86    fn transform_into_action_for_signer<C: CoreRPCLike>(
87        &self,
88        platform: &PlatformRef<C>,
89        block_info: &BlockInfo,
90        remaining_address_input_balances: &Option<
91            BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
92        >,
93        signer_identity: Option<&PartialIdentity>,
94        validation_mode: ValidationMode,
95        execution_context: &mut StateTransitionExecutionContext,
96        tx: TransactionArg,
97    ) -> Result<ConsensusValidationResult<StateTransitionAction>, Error>;
98}
99
100impl StateTransitionSignerAwareActionTransformer for StateTransition {
101    fn transform_into_action_for_signer<C: CoreRPCLike>(
102        &self,
103        platform: &PlatformRef<C>,
104        block_info: &BlockInfo,
105        remaining_address_input_balances: &Option<
106            BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
107        >,
108        signer_identity: Option<&PartialIdentity>,
109        validation_mode: ValidationMode,
110        execution_context: &mut StateTransitionExecutionContext,
111        tx: TransactionArg,
112    ) -> Result<ConsensusValidationResult<StateTransitionAction>, Error> {
113        match self {
114            StateTransition::Batch(st) => st.transform_into_action_for_signer(
115                platform,
116                block_info,
117                remaining_address_input_balances,
118                signer_identity,
119                validation_mode,
120                execution_context,
121                tx,
122            ),
123            _ => self.transform_into_action(
124                platform,
125                block_info,
126                remaining_address_input_balances,
127                validation_mode,
128                execution_context,
129                tx,
130            ),
131        }
132    }
133}
134
135impl StateTransitionActionTransformer for StateTransition {
136    fn transform_into_action<C: CoreRPCLike>(
137        &self,
138        platform: &PlatformRef<C>,
139        block_info: &BlockInfo,
140        remaining_address_input_balances: &Option<
141            BTreeMap<PlatformAddress, (AddressNonce, Credits)>,
142        >,
143        validation_mode: ValidationMode,
144        execution_context: &mut StateTransitionExecutionContext,
145        tx: TransactionArg,
146    ) -> Result<ConsensusValidationResult<StateTransitionAction>, Error> {
147        match self {
148            StateTransition::DataContractCreate(st) => st.transform_into_action(
149                platform,
150                block_info,
151                remaining_address_input_balances,
152                validation_mode,
153                execution_context,
154                tx,
155            ),
156            StateTransition::DataContractUpdate(st) => st.transform_into_action(
157                platform,
158                block_info,
159                remaining_address_input_balances,
160                validation_mode,
161                execution_context,
162                tx,
163            ),
164            StateTransition::IdentityCreate(st) => {
165                let signable_bytes = self.signable_bytes()?;
166                st.transform_into_action_for_identity_create_transition(
167                    platform,
168                    signable_bytes,
169                    validation_mode,
170                    execution_context,
171                    tx,
172                )
173            }
174            StateTransition::IdentityUpdate(st) => st.transform_into_action(
175                platform,
176                block_info,
177                remaining_address_input_balances,
178                validation_mode,
179                execution_context,
180                tx,
181            ),
182            StateTransition::IdentityKeyLimitsUpdate(st) => st.transform_into_action(
183                platform,
184                block_info,
185                remaining_address_input_balances,
186                validation_mode,
187                execution_context,
188                tx,
189            ),
190            StateTransition::IdentityTopUp(st) => {
191                let signable_bytes = self.signable_bytes()?;
192                st.transform_into_action_for_identity_top_up_transition(
193                    platform,
194                    signable_bytes,
195                    validation_mode,
196                    execution_context,
197                    tx,
198                )
199            }
200            StateTransition::IdentityCreditWithdrawal(st) => st.transform_into_action(
201                platform,
202                block_info,
203                remaining_address_input_balances,
204                validation_mode,
205                execution_context,
206                tx,
207            ),
208            StateTransition::Batch(st) => st.transform_into_action(
209                platform,
210                block_info,
211                remaining_address_input_balances,
212                validation_mode,
213                execution_context,
214                tx,
215            ),
216            StateTransition::IdentityCreditTransfer(st) => st.transform_into_action(
217                platform,
218                block_info,
219                remaining_address_input_balances,
220                validation_mode,
221                execution_context,
222                tx,
223            ),
224            StateTransition::MasternodeVote(st) => st.transform_into_action(
225                platform,
226                block_info,
227                remaining_address_input_balances,
228                validation_mode,
229                execution_context,
230                tx,
231            ),
232            StateTransition::IdentityCreditTransferToAddresses(st) => st.transform_into_action(
233                platform,
234                block_info,
235                remaining_address_input_balances,
236                validation_mode,
237                execution_context,
238                tx,
239            ),
240            StateTransition::IdentityCreateFromAddresses(st) => {
241                let Some(remaining_address_input_balances) = remaining_address_input_balances
242                else {
243                    return Err(Error::Execution(ExecutionError::CorruptedCodeExecution(
244                        "we must have remaining address input balances",
245                    )));
246                };
247                st.transform_into_action_for_identity_create_from_addresses_transition(
248                    platform,
249                    remaining_address_input_balances.clone(),
250                )
251            }
252            StateTransition::IdentityTopUpFromAddresses(st) => {
253                let Some(remaining_address_input_balances) = remaining_address_input_balances
254                else {
255                    return Err(Error::Execution(ExecutionError::CorruptedCodeExecution(
256                        "we must have remaining address input balances",
257                    )));
258                };
259                Ok(
260                    IdentityTopUpFromAddressesTransitionAction::try_from_transition(
261                        st,
262                        remaining_address_input_balances.clone(),
263                    )
264                    .map(|action| action.into()),
265                )
266            }
267            StateTransition::AddressFundsTransfer(st) => {
268                let Some(remaining_address_input_balances) = remaining_address_input_balances
269                else {
270                    return Err(Error::Execution(ExecutionError::CorruptedCodeExecution(
271                        "we must have remaining address input balances",
272                    )));
273                };
274                st.transform_into_action_for_address_funds_transfer_transition(
275                    platform,
276                    remaining_address_input_balances.clone(),
277                )
278            }
279            StateTransition::AddressFundingFromAssetLock(st) => {
280                let Some(remaining_address_input_balances) = remaining_address_input_balances
281                else {
282                    return Err(Error::Execution(ExecutionError::CorruptedCodeExecution(
283                        "we must have remaining address input balances",
284                    )));
285                };
286                let signable_bytes = self.signable_bytes()?;
287                st.transform_into_action_for_address_funding_from_asset_lock_transition(
288                    platform,
289                    signable_bytes,
290                    remaining_address_input_balances.clone(),
291                    validation_mode,
292                    execution_context,
293                    tx,
294                )
295            }
296            StateTransition::AddressCreditWithdrawal(st) => {
297                let Some(remaining_address_input_balances) = remaining_address_input_balances
298                else {
299                    return Err(Error::Execution(ExecutionError::CorruptedCodeExecution(
300                        "we must have remaining address input balances",
301                    )));
302                };
303                st.transform_into_action_for_address_credit_withdrawal_transition(
304                    platform,
305                    block_info,
306                    remaining_address_input_balances.clone(),
307                )
308            }
309            StateTransition::Shield(st) => {
310                let Some(remaining_address_input_balances) = remaining_address_input_balances
311                else {
312                    return Err(Error::Execution(ExecutionError::CorruptedCodeExecution(
313                        "we must have remaining address input balances",
314                    )));
315                };
316                st.transform_into_action_for_shield_transition(
317                    platform,
318                    remaining_address_input_balances.clone(),
319                    block_info,
320                    execution_context,
321                    tx,
322                )
323            }
324            StateTransition::ShieldedTransfer(st) => {
325                st.transform_into_action_for_shielded_transfer_transition(platform, tx)
326            }
327            StateTransition::ShieldFromIdentity(st) => st.transform_into_action(
328                platform,
329                block_info,
330                remaining_address_input_balances,
331                validation_mode,
332                execution_context,
333                tx,
334            ),
335            StateTransition::Unshield(st) => {
336                st.transform_into_action_for_unshield_transition(platform, tx)
337            }
338            StateTransition::IdentityTopUpFromShieldedPool(st) => st
339                .transform_into_action_for_identity_top_up_from_shielded_pool_transition(
340                    platform, tx,
341                ),
342            StateTransition::ShieldFromAssetLock(st) => {
343                let signable_bytes = self.signable_bytes()?;
344                st.transform_into_action_for_shield_from_asset_lock_transition(
345                    platform,
346                    signable_bytes,
347                    validation_mode,
348                    block_info,
349                    execution_context,
350                    None,
351                    tx,
352                )
353            }
354            StateTransition::ShieldedWithdrawal(st) => st
355                .transform_into_action_for_shielded_withdrawal_transition(platform, block_info, tx),
356            StateTransition::IdentityCreateFromShieldedPool(st) => {
357                // Key structure + per-key proof-of-possession are *verified* earlier (in
358                // `validate_shielded_proof`, ahead of Halo 2); the transformer does the stateful
359                // pool checks and records the per-key signature-verification ops for fee accounting.
360                st.transform_into_action_for_identity_create_from_shielded_pool_transition(
361                    platform,
362                    execution_context,
363                    tx,
364                )
365            }
366        }
367    }
368}