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