dpp/state_transition/
mod.rs

1use derive_more::From;
2#[cfg(feature = "serde-conversion")]
3use serde::{Deserialize, Serialize};
4use state_transitions::document::batch_transition::batched_transition::document_transition::DocumentTransition;
5use std::collections::BTreeMap;
6use std::ops::RangeInclusive;
7
8pub use abstract_state_transition::state_transition_helpers;
9
10use platform_value::{BinaryData, Identifier};
11pub use state_transition_types::*;
12
13use bincode::{Decode, Encode};
14#[cfg(any(
15    feature = "state-transition-signing",
16    feature = "state-transition-validation"
17))]
18use dashcore::signer;
19#[cfg(feature = "state-transition-validation")]
20use dashcore::signer::double_sha;
21use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize, PlatformSignable};
22use platform_version::version::{PlatformVersion, ProtocolVersion, ALL_VERSIONS, LATEST_VERSION};
23
24mod abstract_state_transition;
25#[cfg(any(
26    feature = "state-transition-signing",
27    feature = "state-transition-validation"
28))]
29use crate::BlsModule;
30use crate::ProtocolError;
31
32mod state_transition_types;
33
34pub mod state_transition_factory;
35
36pub mod errors;
37#[cfg(feature = "state-transition-signing")]
38use crate::util::hash::ripemd160_sha256;
39use crate::util::hash::{hash_double_to_vec, hash_single};
40
41pub mod proof_result;
42mod serialization;
43pub mod state_transitions;
44mod traits;
45
46// pub mod state_transition_fee;
47
48#[cfg(feature = "state-transition-validation")]
49use crate::consensus::basic::UnsupportedFeatureError;
50#[cfg(feature = "state-transition-signing")]
51use crate::consensus::signature::InvalidSignaturePublicKeySecurityLevelError;
52#[cfg(feature = "state-transition-validation")]
53use crate::consensus::signature::{
54    InvalidStateTransitionSignatureError, PublicKeyIsDisabledError, SignatureError,
55};
56#[cfg(feature = "state-transition-validation")]
57use crate::consensus::ConsensusError;
58pub use traits::*;
59
60use crate::address_funds::PlatformAddress;
61use crate::data_contract::serialized_version::DataContractInSerializationFormat;
62use crate::fee::Credits;
63#[cfg(any(
64    feature = "state-transition-signing",
65    feature = "state-transition-validation"
66))]
67use crate::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
68#[cfg(feature = "state-transition-signing")]
69use crate::identity::signer::Signer;
70use crate::identity::state_transition::OptionallyAssetLockProved;
71use crate::identity::Purpose;
72#[cfg(any(
73    feature = "state-transition-signing",
74    feature = "state-transition-validation"
75))]
76use crate::identity::{IdentityPublicKey, KeyType};
77use crate::identity::{KeyID, SecurityLevel};
78use crate::prelude::{AddressNonce, AssetLockProof, UserFeeIncrease};
79use crate::serialization::{PlatformDeserializable, Signable};
80use crate::state_transition::address_credit_withdrawal_transition::{
81    AddressCreditWithdrawalTransition, AddressCreditWithdrawalTransitionSignable,
82};
83use crate::state_transition::address_funding_from_asset_lock_transition::{
84    AddressFundingFromAssetLockTransition, AddressFundingFromAssetLockTransitionSignable,
85};
86use crate::state_transition::address_funds_transfer_transition::{
87    AddressFundsTransferTransition, AddressFundsTransferTransitionSignable,
88};
89use crate::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0;
90use crate::state_transition::batch_transition::batched_transition::BatchedTransitionRef;
91#[cfg(feature = "state-transition-signing")]
92use crate::state_transition::batch_transition::resolvers::v0::BatchTransitionResolversV0;
93use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionSignable};
94use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0;
95use crate::state_transition::data_contract_create_transition::{
96    DataContractCreateTransition, DataContractCreateTransitionSignable,
97};
98use crate::state_transition::data_contract_update_transition::accessors::DataContractUpdateTransitionAccessorsV0;
99use crate::state_transition::data_contract_update_transition::{
100    DataContractUpdateTransition, DataContractUpdateTransitionSignable,
101};
102#[cfg(feature = "state-transition-signing")]
103use crate::state_transition::errors::InvalidSignaturePublicKeyError;
104#[cfg(all(feature = "state-transitions", feature = "validation"))]
105use crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError;
106#[cfg(feature = "state-transition-signing")]
107use crate::state_transition::errors::WrongPublicKeyPurposeError;
108#[cfg(feature = "state-transition-validation")]
109use crate::state_transition::errors::{
110    InvalidIdentityPublicKeyTypeError, PublicKeyMismatchError, StateTransitionIsNotSignedError,
111};
112use crate::state_transition::identity_create_from_addresses_transition::{
113    IdentityCreateFromAddressesTransition, IdentityCreateFromAddressesTransitionSignable,
114};
115use crate::state_transition::identity_create_transition::{
116    IdentityCreateTransition, IdentityCreateTransitionSignable,
117};
118use crate::state_transition::identity_credit_transfer_to_addresses_transition::{
119    IdentityCreditTransferToAddressesTransition,
120    IdentityCreditTransferToAddressesTransitionSignable,
121};
122use crate::state_transition::identity_credit_transfer_transition::{
123    IdentityCreditTransferTransition, IdentityCreditTransferTransitionSignable,
124};
125use crate::state_transition::identity_credit_withdrawal_transition::{
126    IdentityCreditWithdrawalTransition, IdentityCreditWithdrawalTransitionSignable,
127};
128use crate::state_transition::identity_topup_from_addresses_transition::{
129    IdentityTopUpFromAddressesTransition, IdentityTopUpFromAddressesTransitionSignable,
130};
131use crate::state_transition::identity_topup_transition::{
132    IdentityTopUpTransition, IdentityTopUpTransitionSignable,
133};
134use crate::state_transition::identity_update_transition::{
135    IdentityUpdateTransition, IdentityUpdateTransitionSignable,
136};
137use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
138use crate::state_transition::masternode_vote_transition::MasternodeVoteTransitionSignable;
139use crate::state_transition::shield_from_asset_lock_transition::{
140    ShieldFromAssetLockTransition, ShieldFromAssetLockTransitionSignable,
141};
142use crate::state_transition::shield_transition::{ShieldTransition, ShieldTransitionSignable};
143use crate::state_transition::shielded_transfer_transition::{
144    ShieldedTransferTransition, ShieldedTransferTransitionSignable,
145};
146use crate::state_transition::shielded_withdrawal_transition::{
147    ShieldedWithdrawalTransition, ShieldedWithdrawalTransitionSignable,
148};
149#[cfg(feature = "state-transition-signing")]
150use crate::state_transition::state_transitions::document::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0;
151use crate::state_transition::unshield_transition::{
152    UnshieldTransition, UnshieldTransitionSignable,
153};
154use state_transitions::document::batch_transition::batched_transition::token_transition::TokenTransition;
155pub use state_transitions::*;
156
157pub type GetDataContractSecurityLevelRequirementFn =
158    fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>;
159
160macro_rules! call_method {
161    ($state_transition:expr, $method:ident, $args:tt ) => {
162        match $state_transition {
163            StateTransition::DataContractCreate(st) => st.$method($args),
164            StateTransition::DataContractUpdate(st) => st.$method($args),
165            StateTransition::Batch(st) => st.$method($args),
166            StateTransition::IdentityCreate(st) => st.$method($args),
167            StateTransition::IdentityTopUp(st) => st.$method($args),
168            StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
169            StateTransition::IdentityUpdate(st) => st.$method($args),
170            StateTransition::IdentityCreditTransfer(st) => st.$method($args),
171            StateTransition::MasternodeVote(st) => st.$method($args),
172            StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
173            StateTransition::IdentityCreateFromAddresses(st) => st.$method($args),
174            StateTransition::IdentityTopUpFromAddresses(st) => st.$method($args),
175            StateTransition::AddressFundsTransfer(st) => st.$method($args),
176            StateTransition::AddressFundingFromAssetLock(st) => st.$method($args),
177            StateTransition::AddressCreditWithdrawal(st) => st.$method($args),
178            StateTransition::Shield(st) => st.$method($args),
179            StateTransition::ShieldedTransfer(st) => st.$method($args),
180            StateTransition::Unshield(st) => st.$method($args),
181            StateTransition::ShieldFromAssetLock(st) => st.$method($args),
182            StateTransition::ShieldedWithdrawal(st) => st.$method($args),
183        }
184    };
185    ($state_transition:expr, $method:ident ) => {
186        match $state_transition {
187            StateTransition::DataContractCreate(st) => st.$method(),
188            StateTransition::DataContractUpdate(st) => st.$method(),
189            StateTransition::Batch(st) => st.$method(),
190            StateTransition::IdentityCreate(st) => st.$method(),
191            StateTransition::IdentityTopUp(st) => st.$method(),
192            StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
193            StateTransition::IdentityUpdate(st) => st.$method(),
194            StateTransition::IdentityCreditTransfer(st) => st.$method(),
195            StateTransition::MasternodeVote(st) => st.$method(),
196            StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
197            StateTransition::IdentityCreateFromAddresses(st) => st.$method(),
198            StateTransition::IdentityTopUpFromAddresses(st) => st.$method(),
199            StateTransition::AddressFundsTransfer(st) => st.$method(),
200            StateTransition::AddressFundingFromAssetLock(st) => st.$method(),
201            StateTransition::AddressCreditWithdrawal(st) => st.$method(),
202            StateTransition::Shield(st) => st.$method(),
203            StateTransition::ShieldedTransfer(st) => st.$method(),
204            StateTransition::Unshield(st) => st.$method(),
205            StateTransition::ShieldFromAssetLock(st) => st.$method(),
206            StateTransition::ShieldedWithdrawal(st) => st.$method(),
207        }
208    };
209}
210
211macro_rules! call_getter_method_identity_signed {
212    ($state_transition:expr, $method:ident, $args:tt ) => {
213        match $state_transition {
214            StateTransition::DataContractCreate(st) => Some(st.$method($args)),
215            StateTransition::DataContractUpdate(st) => Some(st.$method($args)),
216            StateTransition::Batch(st) => Some(st.$method($args)),
217            StateTransition::IdentityCreate(_) => None,
218            StateTransition::IdentityTopUp(_) => None,
219            StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method($args)),
220            StateTransition::IdentityUpdate(st) => Some(st.$method($args)),
221            StateTransition::IdentityCreditTransfer(st) => Some(st.$method($args)),
222            StateTransition::MasternodeVote(st) => Some(st.$method($args)),
223            StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method($args)),
224            StateTransition::IdentityCreateFromAddresses(_) => None,
225            StateTransition::IdentityTopUpFromAddresses(_) => None,
226            StateTransition::AddressFundsTransfer(_) => None,
227            StateTransition::AddressFundingFromAssetLock(_) => None,
228            StateTransition::AddressCreditWithdrawal(_) => None,
229            StateTransition::Shield(_) => None,
230            StateTransition::ShieldedTransfer(_) => None,
231            StateTransition::Unshield(_) => None,
232            StateTransition::ShieldFromAssetLock(_) => None,
233            StateTransition::ShieldedWithdrawal(_) => None,
234        }
235    };
236    ($state_transition:expr, $method:ident ) => {
237        match $state_transition {
238            StateTransition::DataContractCreate(st) => Some(st.$method()),
239            StateTransition::DataContractUpdate(st) => Some(st.$method()),
240            StateTransition::Batch(st) => Some(st.$method()),
241            StateTransition::IdentityCreate(_) => None,
242            StateTransition::IdentityTopUp(_) => None,
243            StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method()),
244            StateTransition::IdentityUpdate(st) => Some(st.$method()),
245            StateTransition::IdentityCreditTransfer(st) => Some(st.$method()),
246            StateTransition::MasternodeVote(st) => Some(st.$method()),
247            StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method()),
248            StateTransition::IdentityCreateFromAddresses(_) => None,
249            StateTransition::IdentityTopUpFromAddresses(_) => None,
250            StateTransition::AddressFundsTransfer(_) => None,
251            StateTransition::AddressFundingFromAssetLock(_) => None,
252            StateTransition::AddressCreditWithdrawal(_) => None,
253            StateTransition::Shield(_) => None,
254            StateTransition::ShieldedTransfer(_) => None,
255            StateTransition::Unshield(_) => None,
256            StateTransition::ShieldFromAssetLock(_) => None,
257            StateTransition::ShieldedWithdrawal(_) => None,
258        }
259    };
260}
261
262macro_rules! call_method_identity_signed {
263    ($state_transition:expr, $method:ident, $args:tt ) => {
264        match $state_transition {
265            StateTransition::DataContractCreate(st) => st.$method($args),
266            StateTransition::DataContractUpdate(st) => st.$method($args),
267            StateTransition::Batch(st) => st.$method($args),
268            StateTransition::IdentityCreate(_st) => {}
269            StateTransition::IdentityTopUp(_st) => {}
270            StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
271            StateTransition::IdentityUpdate(st) => st.$method($args),
272            StateTransition::IdentityCreditTransfer(st) => st.$method($args),
273            StateTransition::MasternodeVote(st) => st.$method($args),
274            StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
275            StateTransition::IdentityCreateFromAddresses(_) => {}
276            StateTransition::IdentityTopUpFromAddresses(_) => {}
277            StateTransition::AddressFundsTransfer(_) => {}
278            StateTransition::AddressFundingFromAssetLock(_) => {}
279            StateTransition::AddressCreditWithdrawal(_) => {}
280            StateTransition::Shield(_) => {}
281            StateTransition::ShieldedTransfer(_) => {}
282            StateTransition::Unshield(_) => {}
283            StateTransition::ShieldFromAssetLock(_) => {}
284            StateTransition::ShieldedWithdrawal(_) => {}
285        }
286    };
287    ($state_transition:expr, $method:ident ) => {
288        match $state_transition {
289            StateTransition::DataContractCreate(st) => st.$method(),
290            StateTransition::DataContractUpdate(st) => st.$method(),
291            StateTransition::Batch(st) => st.$method(),
292            StateTransition::IdentityCreate(st) => {}
293            StateTransition::IdentityTopUp(st) => {}
294            StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
295            StateTransition::IdentityUpdate(st) => st.$method(),
296            StateTransition::IdentityCreditTransfer(st) => st.$method(),
297            StateTransition::MasternodeVote(st) => st.$method(),
298            StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
299            StateTransition::IdentityCreateFromAddresses(_) => {}
300            StateTransition::IdentityTopUpFromAddresses(_) => {}
301            StateTransition::AddressFundsTransfer(_) => {}
302            StateTransition::AddressFundingFromAssetLock(_) => {}
303            StateTransition::AddressCreditWithdrawal(_) => {}
304            StateTransition::Shield(_) => {}
305            StateTransition::ShieldedTransfer(_) => {}
306            StateTransition::Unshield(_) => {}
307            StateTransition::ShieldFromAssetLock(_) => {}
308            StateTransition::ShieldedWithdrawal(_) => {}
309        }
310    };
311}
312
313#[cfg(feature = "state-transition-signing")]
314macro_rules! call_errorable_method_identity_signed {
315    ($state_transition:expr, $method:ident, $( $arg:expr ),* ) => {
316        match $state_transition {
317            StateTransition::DataContractCreate(st) => st.$method($( $arg ),*),
318            StateTransition::DataContractUpdate(st) => st.$method($( $arg ),*),
319            StateTransition::Batch(st) => st.$method($( $arg ),*),
320            StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
321                "identity create can not be called for identity signing".to_string(),
322            )),
323            StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
324                "identity top up can not be called for identity signing".to_string(),
325            )),
326            StateTransition::IdentityCreditWithdrawal(st) => st.$method($( $arg ),*),
327            StateTransition::IdentityUpdate(st) => st.$method($( $arg ),*),
328            StateTransition::IdentityCreditTransfer(st) => st.$method($( $arg ),*),
329            StateTransition::MasternodeVote(st) => st.$method($( $arg ),*),
330            StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($( $arg ),*),
331            StateTransition::IdentityCreateFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
332                "identity create from addresses can not be called for identity signing".to_string(),
333            )),
334            StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
335                "identity top up from addresses can not be called for identity signing".to_string(),
336            )),
337            StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
338                "address funds transfer can not be called for identity signing".to_string(),
339            )),
340            StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
341                "address funding from asset lock can not be called for identity signing".to_string(),
342            )),
343            StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
344                "address credit withdrawal can not be called for identity signing".to_string(),
345            )),
346            StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
347                "shield transition can not be called for identity signing".to_string(),
348            )),
349            StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
350                "shielded transfer transition can not be called for identity signing".to_string(),
351            )),
352            StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
353                "unshield transition can not be called for identity signing".to_string(),
354            )),
355            StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
356                "shield from asset lock transition can not be called for identity signing".to_string(),
357            )),
358            StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
359                "shielded withdrawal transition can not be called for identity signing".to_string(),
360            )),
361        }
362    };
363    ($state_transition:expr, $method:ident) => {
364        match $state_transition {
365            StateTransition::DataContractCreate(st) => st.$method(),
366            StateTransition::DataContractUpdate(st) => st.$method(),
367            StateTransition::Batch(st) => st.$method(),
368            StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
369                "identity create can not be called for identity signing".to_string(),
370            )),
371            StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
372                "identity top up can not be called for identity signing".to_string(),
373            )),
374            StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
375            StateTransition::IdentityUpdate(st) => st.$method(),
376            StateTransition::IdentityCreditTransfer(st) => st.$method(),
377            StateTransition::MasternodeVote(st) => st.$method(),
378            StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
379            StateTransition::IdentityCreateFromAddresses(st) => Err(ProtocolError::CorruptedCodeExecution(
380                "identity create from addresses can not be called for identity signing".to_string(),
381            )),
382            StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
383                "identity top up from addresses can not be called for identity signing".to_string(),
384            )),
385            StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
386                "address funds transfer can not be called for identity signing".to_string(),
387            )),
388            StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
389                "address funding from asset lock can not be called for identity signing".to_string(),
390            )),
391            StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
392                "address credit withdrawal can not be called for identity signing".to_string(),
393            )),
394            StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
395                "shield transition can not be called for identity signing".to_string(),
396            )),
397            StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
398                "shielded transfer transition can not be called for identity signing".to_string(),
399            )),
400            StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
401                "unshield transition can not be called for identity signing".to_string(),
402            )),
403            StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
404                "shield from asset lock transition can not be called for identity signing".to_string(),
405            )),
406            StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
407                "shielded withdrawal transition can not be called for identity signing".to_string(),
408            )),
409        }
410    };
411}
412
413#[derive(
414    Debug,
415    Clone,
416    Encode,
417    Decode,
418    PlatformSerialize,
419    PlatformDeserialize,
420    PlatformSignable,
421    From,
422    PartialEq,
423)]
424#[cfg_attr(
425    feature = "serde-conversion",
426    derive(Serialize, Deserialize),
427    serde(untagged)
428)]
429#[platform_serialize(unversioned)] //versioned directly, no need to use platform_version
430#[platform_serialize(limit = 100000)]
431pub enum StateTransition {
432    DataContractCreate(DataContractCreateTransition),
433    DataContractUpdate(DataContractUpdateTransition),
434    Batch(BatchTransition),
435    IdentityCreate(IdentityCreateTransition),
436    IdentityTopUp(IdentityTopUpTransition),
437    IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition),
438    IdentityUpdate(IdentityUpdateTransition),
439    IdentityCreditTransfer(IdentityCreditTransferTransition),
440    MasternodeVote(MasternodeVoteTransition),
441    IdentityCreditTransferToAddresses(IdentityCreditTransferToAddressesTransition),
442    IdentityCreateFromAddresses(IdentityCreateFromAddressesTransition),
443    IdentityTopUpFromAddresses(IdentityTopUpFromAddressesTransition),
444    AddressFundsTransfer(AddressFundsTransferTransition),
445    AddressFundingFromAssetLock(AddressFundingFromAssetLockTransition),
446    AddressCreditWithdrawal(AddressCreditWithdrawalTransition),
447    Shield(ShieldTransition),
448    ShieldedTransfer(ShieldedTransferTransition),
449    Unshield(UnshieldTransition),
450    ShieldFromAssetLock(ShieldFromAssetLockTransition),
451    ShieldedWithdrawal(ShieldedWithdrawalTransition),
452}
453
454impl OptionallyAssetLockProved for StateTransition {
455    fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> {
456        match self {
457            StateTransition::IdentityCreate(st) => st.optional_asset_lock_proof(),
458            StateTransition::IdentityTopUp(st) => st.optional_asset_lock_proof(),
459            StateTransition::ShieldFromAssetLock(st) => st.optional_asset_lock_proof(),
460            _ => None,
461        }
462    }
463}
464
465/// The state transition signing options
466#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
467pub struct StateTransitionSigningOptions {
468    /// This will allow signing with any security level for debugging purposes
469    pub allow_signing_with_any_security_level: bool,
470    /// This will allow signing with any purpose for debugging purposes
471    pub allow_signing_with_any_purpose: bool,
472}
473
474impl StateTransition {
475    #[allow(unused_variables)]
476    pub fn deserialize_from_bytes_in_version(
477        bytes: &[u8],
478        platform_version: &PlatformVersion,
479    ) -> Result<Self, ProtocolError> {
480        let state_transition = StateTransition::deserialize_from_bytes(bytes)?;
481        #[cfg(all(feature = "state-transitions", feature = "validation"))]
482        {
483            let active_version_range = state_transition.active_version_range();
484
485            // Tests are done with very high protocol ranges, while we could put this behind a feature,
486            // that would probably be overkill.
487            if active_version_range.contains(&platform_version.protocol_version)
488                || platform_version.protocol_version > 268435456
489            {
490                Ok(state_transition)
491            } else {
492                Err(ProtocolError::StateTransitionError(
493                    StateTransitionIsNotActiveError {
494                        state_transition_type: state_transition.name(),
495                        active_version_range,
496                        current_protocol_version: platform_version.protocol_version,
497                    },
498                ))
499            }
500        }
501        #[cfg(not(all(feature = "state-transitions", feature = "validation")))]
502        Ok(state_transition)
503    }
504
505    pub fn active_version_range(&self) -> RangeInclusive<ProtocolVersion> {
506        match self {
507            StateTransition::DataContractCreate(data_contract_create_transition) => {
508                match data_contract_create_transition.data_contract() {
509                    DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
510                    DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
511                }
512            }
513            StateTransition::DataContractUpdate(data_contract_update_transition) => {
514                match data_contract_update_transition.data_contract() {
515                    DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
516                    DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
517                }
518            }
519            StateTransition::Batch(batch_transition) => match batch_transition {
520                BatchTransition::V0(_) => ALL_VERSIONS,
521                BatchTransition::V1(_) => 9..=LATEST_VERSION,
522            },
523            StateTransition::IdentityCreate(_)
524            | StateTransition::IdentityTopUp(_)
525            | StateTransition::IdentityCreditWithdrawal(_)
526            | StateTransition::IdentityUpdate(_)
527            | StateTransition::IdentityCreditTransfer(_)
528            | StateTransition::MasternodeVote(_) => ALL_VERSIONS,
529            StateTransition::IdentityCreditTransferToAddresses(_)
530            | StateTransition::IdentityCreateFromAddresses(_)
531            | StateTransition::IdentityTopUpFromAddresses(_)
532            | StateTransition::AddressFundsTransfer(_)
533            | StateTransition::AddressFundingFromAssetLock(_)
534            | StateTransition::AddressCreditWithdrawal(_) => 11..=LATEST_VERSION,
535            StateTransition::Shield(_)
536            | StateTransition::ShieldedTransfer(_)
537            | StateTransition::Unshield(_)
538            | StateTransition::ShieldFromAssetLock(_)
539            | StateTransition::ShieldedWithdrawal(_) => 12..=LATEST_VERSION,
540        }
541    }
542
543    pub fn is_identity_signed(&self) -> bool {
544        !matches!(
545            self,
546            StateTransition::IdentityCreate(_)
547                | StateTransition::IdentityTopUp(_)
548                | StateTransition::Shield(_)
549                | StateTransition::ShieldedTransfer(_)
550                | StateTransition::Unshield(_)
551                | StateTransition::ShieldFromAssetLock(_)
552                | StateTransition::ShieldedWithdrawal(_)
553        )
554    }
555
556    pub fn required_asset_lock_balance_for_processing_start(
557        &self,
558        platform_version: &PlatformVersion,
559    ) -> Result<Credits, ProtocolError> {
560        match self {
561            StateTransition::IdentityCreate(st) => {
562                st.calculate_min_required_fee(platform_version)
563            }
564            StateTransition::IdentityTopUp(st) => {
565                st.calculate_min_required_fee(platform_version)
566            }
567            StateTransition::AddressFundingFromAssetLock(st) => {
568                st.calculate_min_required_fee(platform_version)
569            }
570            StateTransition::ShieldFromAssetLock(st) => {
571                st.calculate_min_required_fee(platform_version)
572            }
573            st => Err(ProtocolError::CorruptedCodeExecution(format!("{} is not an asset lock transaction, but we are calling required_asset_lock_balance_for_processing_start", st.name()))),
574        }
575    }
576
577    fn hash(&self, skip_signature: bool) -> Result<Vec<u8>, ProtocolError> {
578        if skip_signature {
579            Ok(hash_double_to_vec(self.signable_bytes()?))
580        } else {
581            Ok(hash_double_to_vec(
582                crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
583            ))
584        }
585    }
586
587    /// Returns state transition name
588    pub fn name(&self) -> String {
589        match self {
590            Self::DataContractCreate(_) => "DataContractCreate".to_string(),
591            Self::DataContractUpdate(_) => "DataContractUpdate".to_string(),
592            Self::Batch(batch_transition) => {
593                let mut document_transition_types = vec![];
594                for transition in batch_transition.transitions_iter() {
595                    let document_transition_name = match transition {
596                        BatchedTransitionRef::Document(DocumentTransition::Create(_)) => "Create",
597                        BatchedTransitionRef::Document(DocumentTransition::Replace(_)) => "Replace",
598                        BatchedTransitionRef::Document(DocumentTransition::Delete(_)) => "Delete",
599                        BatchedTransitionRef::Document(DocumentTransition::Transfer(_)) => {
600                            "Transfer"
601                        }
602                        BatchedTransitionRef::Document(DocumentTransition::UpdatePrice(_)) => {
603                            "UpdatePrice"
604                        }
605                        BatchedTransitionRef::Document(DocumentTransition::Purchase(_)) => {
606                            "Purchase"
607                        }
608                        BatchedTransitionRef::Token(TokenTransition::Transfer(_)) => {
609                            "TokenTransfer"
610                        }
611                        BatchedTransitionRef::Token(TokenTransition::Mint(_)) => "TokenMint",
612                        BatchedTransitionRef::Token(TokenTransition::Burn(_)) => "TokenBurn",
613                        BatchedTransitionRef::Token(TokenTransition::Freeze(_)) => "TokenFreeze",
614                        BatchedTransitionRef::Token(TokenTransition::Unfreeze(_)) => {
615                            "TokenUnfreeze"
616                        }
617                        BatchedTransitionRef::Token(TokenTransition::DestroyFrozenFunds(_)) => {
618                            "TokenDestroyFrozenFunds"
619                        }
620                        BatchedTransitionRef::Token(TokenTransition::EmergencyAction(_)) => {
621                            "TokenEmergencyAction"
622                        }
623                        BatchedTransitionRef::Token(TokenTransition::ConfigUpdate(_)) => {
624                            "TokenConfigUpdate"
625                        }
626                        BatchedTransitionRef::Token(TokenTransition::Claim(_)) => "TokenClaim",
627                        BatchedTransitionRef::Token(TokenTransition::DirectPurchase(_)) => {
628                            "TokenDirectPurchase"
629                        }
630                        BatchedTransitionRef::Token(
631                            TokenTransition::SetPriceForDirectPurchase(_),
632                        ) => "SetPriceForDirectPurchase",
633                    };
634                    document_transition_types.push(document_transition_name);
635                }
636                format!("DocumentsBatch([{}])", document_transition_types.join(", "))
637            }
638            Self::IdentityCreate(_) => "IdentityCreate".to_string(),
639            Self::IdentityTopUp(_) => "IdentityTopUp".to_string(),
640            Self::IdentityCreditWithdrawal(_) => "IdentityCreditWithdrawal".to_string(),
641            Self::IdentityUpdate(_) => "IdentityUpdate".to_string(),
642            Self::IdentityCreditTransfer(_) => "IdentityCreditTransfer".to_string(),
643            Self::MasternodeVote(_) => "MasternodeVote".to_string(),
644            Self::IdentityCreditTransferToAddresses(_) => {
645                "IdentityCreditTransferToAddresses".to_string()
646            }
647            Self::IdentityCreateFromAddresses(_) => "IdentityCreateFromAddresses".to_string(),
648            Self::IdentityTopUpFromAddresses(_) => "IdentityTopUpFromAddresses".to_string(),
649            Self::AddressFundsTransfer(_) => "AddressFundsTransfer".to_string(),
650            Self::AddressFundingFromAssetLock(_) => "AddressFundingFromAssetLock".to_string(),
651            Self::AddressCreditWithdrawal(_) => "AddressCreditWithdrawal".to_string(),
652            Self::Shield(_) => "Shield".to_string(),
653            Self::ShieldedTransfer(_) => "ShieldedTransfer".to_string(),
654            Self::Unshield(_) => "Unshield".to_string(),
655            Self::ShieldFromAssetLock(_) => "ShieldFromAssetLock".to_string(),
656            Self::ShieldedWithdrawal(_) => "ShieldedWithdrawal".to_string(),
657        }
658    }
659
660    /// returns the signature as a byte-array
661    pub fn signature(&self) -> Option<&BinaryData> {
662        match self {
663            StateTransition::DataContractCreate(st) => Some(st.signature()),
664            StateTransition::DataContractUpdate(st) => Some(st.signature()),
665            StateTransition::Batch(st) => Some(st.signature()),
666            StateTransition::IdentityCreate(st) => Some(st.signature()),
667            StateTransition::IdentityTopUp(st) => Some(st.signature()),
668            StateTransition::IdentityCreditWithdrawal(st) => Some(st.signature()),
669            StateTransition::IdentityUpdate(st) => Some(st.signature()),
670            StateTransition::IdentityCreditTransfer(st) => Some(st.signature()),
671            StateTransition::MasternodeVote(st) => Some(st.signature()),
672            StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.signature()),
673            StateTransition::IdentityCreateFromAddresses(_) => None,
674            StateTransition::IdentityTopUpFromAddresses(_) => None,
675            StateTransition::AddressFundsTransfer(_) => None,
676            StateTransition::AddressFundingFromAssetLock(st) => Some(st.signature()),
677            StateTransition::AddressCreditWithdrawal(_) => None,
678            StateTransition::Shield(_) => None,
679            StateTransition::ShieldedTransfer(_) => None,
680            StateTransition::Unshield(_) => None,
681            StateTransition::ShieldFromAssetLock(st) => Some(st.signature()),
682            StateTransition::ShieldedWithdrawal(_) => None,
683        }
684    }
685
686    /// returns the number of private keys
687    pub fn required_number_of_private_keys(&self) -> u16 {
688        match self {
689            StateTransition::IdentityCreateFromAddresses(st) => st.inputs().len() as u16,
690            StateTransition::IdentityTopUpFromAddresses(st) => st.inputs().len() as u16,
691            StateTransition::AddressFundsTransfer(st) => st.inputs().len() as u16,
692            StateTransition::AddressCreditWithdrawal(st) => st.inputs().len() as u16,
693            StateTransition::Shield(st) => st.inputs().len() as u16,
694            StateTransition::ShieldedTransfer(_) => 0,
695            StateTransition::Unshield(_) => 0,
696            StateTransition::ShieldFromAssetLock(_) => 0,
697            StateTransition::ShieldedWithdrawal(_) => 0,
698            _ => 1,
699        }
700    }
701
702    /// returns the fee_increase additional percentage multiplier, it affects only processing costs
703    pub fn user_fee_increase(&self) -> UserFeeIncrease {
704        match self {
705            StateTransition::DataContractCreate(st) => st.user_fee_increase(),
706            StateTransition::DataContractUpdate(st) => st.user_fee_increase(),
707            StateTransition::Batch(st) => st.user_fee_increase(),
708            StateTransition::IdentityCreate(st) => st.user_fee_increase(),
709            StateTransition::IdentityTopUp(st) => st.user_fee_increase(),
710            StateTransition::IdentityCreditWithdrawal(st) => st.user_fee_increase(),
711            StateTransition::IdentityUpdate(st) => st.user_fee_increase(),
712            StateTransition::IdentityCreditTransfer(st) => st.user_fee_increase(),
713            StateTransition::IdentityCreditTransferToAddresses(st) => st.user_fee_increase(),
714            StateTransition::IdentityCreateFromAddresses(st) => st.user_fee_increase(),
715            StateTransition::IdentityTopUpFromAddresses(st) => st.user_fee_increase(),
716            StateTransition::AddressFundsTransfer(st) => st.user_fee_increase(),
717            StateTransition::AddressFundingFromAssetLock(st) => st.user_fee_increase(),
718            StateTransition::AddressCreditWithdrawal(st) => st.user_fee_increase(),
719            StateTransition::Shield(st) => st.user_fee_increase(),
720            // These transitions don't support user fee adjustment
721            StateTransition::ShieldFromAssetLock(_) => 0,
722            StateTransition::MasternodeVote(_) => 0,
723            StateTransition::ShieldedTransfer(_) => 0,
724            StateTransition::Unshield(_) => 0,
725            StateTransition::ShieldedWithdrawal(_) => 0,
726        }
727    }
728
729    /// Calculates the estimated minimum fee required for this state transition.
730    ///
731    /// The fee is calculated based on the number of inputs, outputs, and any
732    /// transition-specific costs (e.g., key creation costs for identity creation).
733    ///
734    /// # Arguments
735    ///
736    /// * `platform_version` - The platform version containing fee configuration.
737    ///
738    /// # Returns
739    ///
740    /// The estimated fee in credits.
741    fn calculate_estimated_fee(
742        &self,
743        platform_version: &PlatformVersion,
744    ) -> Result<Credits, ProtocolError> {
745        call_method!(self, calculate_min_required_fee, platform_version)
746    }
747
748    /// The transaction id is a single hash of the data with the signature
749    pub fn transaction_id(&self) -> Result<[u8; 32], ProtocolError> {
750        Ok(hash_single(
751            crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
752        ))
753    }
754
755    /// returns the signature as a byte-array
756    pub fn signature_public_key_id(&self) -> Option<KeyID> {
757        call_getter_method_identity_signed!(self, signature_public_key_id)
758    }
759
760    /// returns the key security level requirement for the state transition
761    pub fn security_level_requirement(&self, purpose: Purpose) -> Option<Vec<SecurityLevel>> {
762        call_getter_method_identity_signed!(self, security_level_requirement, purpose)
763    }
764
765    /// returns the key purpose requirement for the state transition
766    pub fn purpose_requirement(&self) -> Option<Vec<Purpose>> {
767        call_getter_method_identity_signed!(self, purpose_requirement)
768    }
769
770    /// returns the signature as a byte-array
771    pub fn owner_id(&self) -> Option<Identifier> {
772        match self {
773            StateTransition::DataContractCreate(st) => Some(st.owner_id()),
774            StateTransition::DataContractUpdate(st) => Some(st.owner_id()),
775            StateTransition::Batch(st) => Some(st.owner_id()),
776            StateTransition::IdentityCreate(st) => Some(st.owner_id()),
777            StateTransition::IdentityTopUp(st) => Some(st.owner_id()),
778            StateTransition::IdentityCreditWithdrawal(st) => Some(st.owner_id()),
779            StateTransition::IdentityUpdate(st) => Some(st.owner_id()),
780            StateTransition::IdentityCreditTransfer(st) => Some(st.owner_id()),
781            StateTransition::MasternodeVote(st) => Some(st.owner_id()),
782            StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.owner_id()),
783            StateTransition::IdentityCreateFromAddresses(_) => None,
784            StateTransition::IdentityTopUpFromAddresses(_) => None,
785            StateTransition::AddressFundsTransfer(_) => None,
786            StateTransition::AddressFundingFromAssetLock(_) => None,
787            StateTransition::AddressCreditWithdrawal(_) => None,
788            StateTransition::Shield(_) => None,
789            StateTransition::ShieldedTransfer(_) => None,
790            StateTransition::Unshield(_) => None,
791            StateTransition::ShieldFromAssetLock(_) => None,
792            StateTransition::ShieldedWithdrawal(_) => None,
793        }
794    }
795
796    /// returns the signature as a byte-array
797    pub fn inputs(&self) -> Option<&BTreeMap<PlatformAddress, (AddressNonce, Credits)>> {
798        match self {
799            StateTransition::DataContractCreate(_)
800            | StateTransition::DataContractUpdate(_)
801            | StateTransition::Batch(_)
802            | StateTransition::IdentityCreate(_)
803            | StateTransition::IdentityTopUp(_)
804            | StateTransition::IdentityCreditWithdrawal(_)
805            | StateTransition::IdentityUpdate(_)
806            | StateTransition::IdentityCreditTransfer(_)
807            | StateTransition::MasternodeVote(_)
808            | StateTransition::IdentityCreditTransferToAddresses(_) => None,
809            StateTransition::IdentityCreateFromAddresses(st) => Some(st.inputs()),
810            StateTransition::IdentityTopUpFromAddresses(st) => Some(st.inputs()),
811            StateTransition::AddressFundsTransfer(st) => Some(st.inputs()),
812            StateTransition::AddressFundingFromAssetLock(st) => Some(st.inputs()),
813            StateTransition::AddressCreditWithdrawal(st) => Some(st.inputs()),
814            StateTransition::Shield(st) => Some(st.inputs()),
815            StateTransition::ShieldedTransfer(_) => None,
816            StateTransition::Unshield(_) => None,
817            StateTransition::ShieldFromAssetLock(_) => None,
818            StateTransition::ShieldedWithdrawal(_) => None,
819        }
820    }
821
822    /// returns the state transition type
823    pub fn state_transition_type(&self) -> StateTransitionType {
824        call_method!(self, state_transition_type)
825    }
826
827    /// returns the unique identifiers for the state transition
828    pub fn unique_identifiers(&self) -> Vec<String> {
829        call_method!(self, unique_identifiers)
830    }
831
832    /// set a new signature
833    pub fn set_signature(&mut self, signature: BinaryData) -> bool {
834        match self {
835            StateTransition::DataContractCreate(st) => {
836                st.set_signature(signature);
837                true
838            }
839            StateTransition::DataContractUpdate(st) => {
840                st.set_signature(signature);
841                true
842            }
843            StateTransition::Batch(st) => {
844                st.set_signature(signature);
845                true
846            }
847            StateTransition::IdentityCreate(st) => {
848                st.set_signature(signature);
849                true
850            }
851            StateTransition::IdentityTopUp(st) => {
852                st.set_signature(signature);
853                true
854            }
855            StateTransition::IdentityCreditWithdrawal(st) => {
856                st.set_signature(signature);
857                true
858            }
859            StateTransition::IdentityUpdate(st) => {
860                st.set_signature(signature);
861                true
862            }
863            StateTransition::IdentityCreditTransfer(st) => {
864                st.set_signature(signature);
865                true
866            }
867            StateTransition::MasternodeVote(st) => {
868                st.set_signature(signature);
869                true
870            }
871            StateTransition::IdentityCreditTransferToAddresses(st) => {
872                st.set_signature(signature);
873                true
874            }
875            StateTransition::IdentityCreateFromAddresses(_)
876            | StateTransition::IdentityTopUpFromAddresses(_)
877            | StateTransition::AddressFundsTransfer(_)
878            | StateTransition::Shield(_)
879            | StateTransition::ShieldedTransfer(_)
880            | StateTransition::Unshield(_)
881            | StateTransition::ShieldedWithdrawal(_) => false,
882            StateTransition::AddressFundingFromAssetLock(st) => {
883                st.set_signature(signature);
884                true
885            }
886            StateTransition::ShieldFromAssetLock(st) => {
887                st.set_signature(signature);
888                true
889            }
890            StateTransition::AddressCreditWithdrawal(_) => false,
891        }
892    }
893
894    /// set fee multiplier
895    pub fn set_user_fee_increase(&mut self, user_fee_increase: UserFeeIncrease) {
896        match self {
897            StateTransition::DataContractCreate(st) => st.set_user_fee_increase(user_fee_increase),
898            StateTransition::DataContractUpdate(st) => st.set_user_fee_increase(user_fee_increase),
899            StateTransition::Batch(st) => st.set_user_fee_increase(user_fee_increase),
900            StateTransition::IdentityCreate(st) => st.set_user_fee_increase(user_fee_increase),
901            StateTransition::IdentityTopUp(st) => st.set_user_fee_increase(user_fee_increase),
902            StateTransition::IdentityCreditWithdrawal(st) => {
903                st.set_user_fee_increase(user_fee_increase)
904            }
905            StateTransition::IdentityUpdate(st) => st.set_user_fee_increase(user_fee_increase),
906            StateTransition::IdentityCreditTransfer(st) => {
907                st.set_user_fee_increase(user_fee_increase)
908            }
909            StateTransition::IdentityCreditTransferToAddresses(st) => {
910                st.set_user_fee_increase(user_fee_increase)
911            }
912            StateTransition::IdentityCreateFromAddresses(st) => {
913                st.set_user_fee_increase(user_fee_increase)
914            }
915            StateTransition::IdentityTopUpFromAddresses(st) => {
916                st.set_user_fee_increase(user_fee_increase)
917            }
918            StateTransition::AddressFundsTransfer(st) => {
919                st.set_user_fee_increase(user_fee_increase)
920            }
921            StateTransition::AddressFundingFromAssetLock(st) => {
922                st.set_user_fee_increase(user_fee_increase)
923            }
924            StateTransition::AddressCreditWithdrawal(st) => {
925                st.set_user_fee_increase(user_fee_increase)
926            }
927            StateTransition::Shield(st) => st.set_user_fee_increase(user_fee_increase),
928            // These transitions don't support user fee adjustment — no-op
929            StateTransition::ShieldFromAssetLock(_) => {}
930            StateTransition::MasternodeVote(_) => {}
931            StateTransition::ShieldedTransfer(_) => {}
932            StateTransition::Unshield(_) => {}
933            StateTransition::ShieldedWithdrawal(_) => {}
934        }
935    }
936
937    /// set a new signature
938    pub fn set_signature_public_key_id(&mut self, public_key_id: KeyID) {
939        call_method_identity_signed!(self, set_signature_public_key_id, public_key_id)
940    }
941
942    #[cfg(feature = "state-transition-signing")]
943    pub fn sign_external<S: Signer<IdentityPublicKey>>(
944        &mut self,
945        identity_public_key: &IdentityPublicKey,
946        signer: &S,
947        get_data_contract_security_level_requirement: Option<
948            impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
949        >,
950    ) -> Result<(), ProtocolError> {
951        self.sign_external_with_options(
952            identity_public_key,
953            signer,
954            get_data_contract_security_level_requirement,
955            StateTransitionSigningOptions::default(),
956        )
957    }
958
959    #[cfg(feature = "state-transition-signing")]
960    pub fn sign_external_with_options<S: Signer<IdentityPublicKey>>(
961        &mut self,
962        identity_public_key: &IdentityPublicKey,
963        signer: &S,
964        get_data_contract_security_level_requirement: Option<
965            impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
966        >,
967        options: StateTransitionSigningOptions,
968    ) -> Result<(), ProtocolError> {
969        match self {
970            StateTransition::DataContractCreate(st) => {
971                st.verify_public_key_level_and_purpose(identity_public_key, options)?;
972                st.verify_public_key_is_enabled(identity_public_key)?;
973            }
974            StateTransition::DataContractUpdate(st) => {
975                st.verify_public_key_level_and_purpose(identity_public_key, options)?;
976                st.verify_public_key_is_enabled(identity_public_key)?;
977            }
978            StateTransition::Batch(st) => {
979                let allow_token_transfer_keys = st.transitions_len() == 1
980                    && (st
981                        .first_transition()
982                        .expect("expected first transition with len 1")
983                        .as_transition_token_claim()
984                        .is_some()
985                        || st
986                            .first_transition()
987                            .expect("expected first transition with len 1")
988                            .as_transition_token_transfer()
989                            .is_some());
990                let allowed_key_purposes = if allow_token_transfer_keys {
991                    vec![Purpose::AUTHENTICATION, Purpose::TRANSFER]
992                } else {
993                    vec![Purpose::AUTHENTICATION]
994                };
995                if !options.allow_signing_with_any_purpose
996                    && !allowed_key_purposes.contains(&identity_public_key.purpose())
997                {
998                    return Err(ProtocolError::WrongPublicKeyPurposeError(
999                        WrongPublicKeyPurposeError::new(
1000                            identity_public_key.purpose(),
1001                            allowed_key_purposes,
1002                        ),
1003                    ));
1004                }
1005                if !options.allow_signing_with_any_security_level {
1006                    let security_level_requirement = st.combined_security_level_requirement(
1007                        get_data_contract_security_level_requirement,
1008                    )?;
1009                    if !security_level_requirement.contains(&identity_public_key.security_level()) {
1010                        return Err(ProtocolError::InvalidSignaturePublicKeySecurityLevelError(
1011                            InvalidSignaturePublicKeySecurityLevelError::new(
1012                                identity_public_key.security_level(),
1013                                security_level_requirement,
1014                            ),
1015                        ));
1016                    }
1017                }
1018                st.verify_public_key_is_enabled(identity_public_key)?;
1019            }
1020            StateTransition::IdentityCreditWithdrawal(st) => {
1021                st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1022                st.verify_public_key_is_enabled(identity_public_key)?;
1023            }
1024            StateTransition::IdentityUpdate(st) => {
1025                st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1026                st.verify_public_key_is_enabled(identity_public_key)?;
1027            }
1028            StateTransition::IdentityCreditTransfer(st) => {
1029                st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1030                st.verify_public_key_is_enabled(identity_public_key)?;
1031            }
1032            StateTransition::IdentityCreate(_) => {
1033                return Err(ProtocolError::CorruptedCodeExecution(
1034                    "identity create can not be called for identity signing".to_string(),
1035                ))
1036            }
1037            StateTransition::IdentityTopUp(_) => {
1038                return Err(ProtocolError::CorruptedCodeExecution(
1039                    "identity top up can not be called for identity signing".to_string(),
1040                ))
1041            }
1042            StateTransition::MasternodeVote(st) => {
1043                st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1044                st.verify_public_key_is_enabled(identity_public_key)?;
1045            }
1046            StateTransition::IdentityCreditTransferToAddresses(st) => {
1047                st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1048                st.verify_public_key_is_enabled(identity_public_key)?;
1049            }
1050            StateTransition::IdentityCreateFromAddresses(_) => {
1051                return Err(ProtocolError::CorruptedCodeExecution(
1052                    "identity create from addresses can not be called for identity signing"
1053                        .to_string(),
1054                ))
1055            }
1056            StateTransition::IdentityTopUpFromAddresses(_) => {
1057                return Err(ProtocolError::CorruptedCodeExecution(
1058                    "identity top up from addresses can not be called for identity signing"
1059                        .to_string(),
1060                ))
1061            }
1062            StateTransition::AddressFundsTransfer(_) => {
1063                return Err(ProtocolError::CorruptedCodeExecution(
1064                    "address funds transfer transition can not be called for identity signing"
1065                        .to_string(),
1066                ))
1067            }
1068            StateTransition::AddressFundingFromAssetLock(_) => {
1069                return Err(ProtocolError::CorruptedCodeExecution(
1070                    "address funding from asset lock transition can not be called for identity signing"
1071                        .to_string(),
1072                ))
1073            }
1074            StateTransition::AddressCreditWithdrawal(_) => {
1075                return Err(ProtocolError::CorruptedCodeExecution(
1076                    "address credit withdrawal transition can not be called for identity signing"
1077                        .to_string(),
1078                ))
1079            }
1080            StateTransition::Shield(_) => {
1081                return Err(ProtocolError::CorruptedCodeExecution(
1082                    "shield transition can not be called for identity signing".to_string(),
1083                ))
1084            }
1085            StateTransition::ShieldedTransfer(_) => {
1086                return Err(ProtocolError::CorruptedCodeExecution(
1087                    "shielded transfer transition can not be called for identity signing"
1088                        .to_string(),
1089                ))
1090            }
1091            StateTransition::Unshield(_) => {
1092                return Err(ProtocolError::CorruptedCodeExecution(
1093                    "unshield transition can not be called for identity signing".to_string(),
1094                ))
1095            }
1096            StateTransition::ShieldFromAssetLock(_) => {
1097                return Err(ProtocolError::CorruptedCodeExecution(
1098                    "shield from asset lock transition can not be called for identity signing"
1099                        .to_string(),
1100                ))
1101            }
1102            StateTransition::ShieldedWithdrawal(_) => {
1103                return Err(ProtocolError::CorruptedCodeExecution(
1104                    "shielded withdrawal transition can not be called for identity signing"
1105                        .to_string(),
1106                ))
1107            }
1108        }
1109        let data = self.signable_bytes()?;
1110        self.set_signature(signer.sign(identity_public_key, data.as_slice())?);
1111        self.set_signature_public_key_id(identity_public_key.id());
1112        Ok(())
1113    }
1114
1115    #[cfg(feature = "state-transition-signing")]
1116    pub fn sign(
1117        &mut self,
1118        identity_public_key: &IdentityPublicKey,
1119        private_key: &[u8],
1120        bls: &impl BlsModule,
1121    ) -> Result<(), ProtocolError> {
1122        self.sign_with_options(
1123            identity_public_key,
1124            private_key,
1125            bls,
1126            StateTransitionSigningOptions::default(),
1127        )
1128    }
1129
1130    #[cfg(feature = "state-transition-signing")]
1131    pub fn sign_with_options(
1132        &mut self,
1133        identity_public_key: &IdentityPublicKey,
1134        private_key: &[u8],
1135        bls: &impl BlsModule,
1136        options: StateTransitionSigningOptions,
1137    ) -> Result<(), ProtocolError> {
1138        call_errorable_method_identity_signed!(
1139            self,
1140            verify_public_key_level_and_purpose,
1141            identity_public_key,
1142            options
1143        )?;
1144        call_errorable_method_identity_signed!(
1145            self,
1146            verify_public_key_is_enabled,
1147            identity_public_key
1148        )?;
1149
1150        match identity_public_key.key_type() {
1151            KeyType::ECDSA_SECP256K1 => {
1152                let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1153
1154                // we store compressed public key in the identity ,
1155                // and here we compare the private key used to sing the state transition with
1156                // the compressed key stored in the identity
1157
1158                if public_key_compressed.as_slice() != identity_public_key.data().as_slice() {
1159                    return Err(ProtocolError::InvalidSignaturePublicKeyError(
1160                        InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1161                    ));
1162                }
1163
1164                self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1165            }
1166            KeyType::ECDSA_HASH160 => {
1167                let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1168                let pub_key_hash = ripemd160_sha256(&public_key_compressed);
1169
1170                if identity_public_key.data().as_slice() != pub_key_hash {
1171                    return Err(ProtocolError::InvalidSignaturePublicKeyError(
1172                        InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1173                    ));
1174                }
1175                self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1176            }
1177            KeyType::BLS12_381 => {
1178                let public_key = bls.private_key_to_public_key(private_key)?;
1179
1180                if public_key != identity_public_key.data().as_slice() {
1181                    return Err(ProtocolError::InvalidSignaturePublicKeyError(
1182                        InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1183                    ));
1184                }
1185                self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1186            }
1187
1188            // the default behavior from
1189            // https://github.com/dashevo/platform/blob/6b02b26e5cd3a7c877c5fdfe40c4a4385a8dda15/packages/js-dpp/lib/stateTransition/AbstractStateTransitionIdentitySigned.js#L108
1190            // is to return the error for the BIP13_SCRIPT_HASH
1191            KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1192                Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1193                    InvalidIdentityPublicKeyTypeError::new(identity_public_key.key_type()),
1194                ))
1195            }
1196        }?;
1197
1198        self.set_signature_public_key_id(identity_public_key.id());
1199
1200        Ok(())
1201    }
1202
1203    #[cfg(feature = "state-transition-signing")]
1204    /// Signs data with the private key
1205    pub fn sign_by_private_key(
1206        &mut self,
1207        private_key: &[u8],
1208        key_type: KeyType,
1209        bls: &impl BlsModule,
1210    ) -> Result<(), ProtocolError> {
1211        let data = self.signable_bytes()?;
1212        match key_type {
1213            KeyType::BLS12_381 => {
1214                if !self.set_signature(bls.sign(&data, private_key)?.into()) {
1215                    return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1216                        needed: self.required_number_of_private_keys(),
1217                        using: 1,
1218                        msg: "failed to set BLS signature",
1219                    });
1220                }
1221            }
1222
1223            // https://github.com/dashevo/platform/blob/9c8e6a3b6afbc330a6ab551a689de8ccd63f9120/packages/js-dpp/lib/stateTransition/AbstractStateTransition.js#L169
1224            KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => {
1225                let signature = signer::sign(&data, private_key)?;
1226                if !self.set_signature(signature.to_vec().into()) {
1227                    return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1228                        needed: self.required_number_of_private_keys(),
1229                        using: 1,
1230                        msg: "failed to set ECDSA signature",
1231                    });
1232                };
1233            }
1234
1235            // the default behavior from
1236            // https://github.com/dashevo/platform/blob/6b02b26e5cd3a7c877c5fdfe40c4a4385a8dda15/packages/js-dpp/lib/stateTransition/AbstractStateTransition.js#L187
1237            // is to return the error for the BIP13_SCRIPT_HASH
1238            KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1239                return Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1240                    InvalidIdentityPublicKeyTypeError::new(key_type),
1241                ))
1242            }
1243        };
1244        Ok(())
1245    }
1246
1247    #[cfg(feature = "state-transition-validation")]
1248    fn verify_by_raw_public_key<T: BlsModule>(
1249        &self,
1250        public_key: &[u8],
1251        public_key_type: KeyType,
1252        bls: &T,
1253    ) -> Result<(), ProtocolError> {
1254        match public_key_type {
1255            KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key),
1256            KeyType::ECDSA_HASH160 => {
1257                self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key)
1258            }
1259            KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key, bls),
1260            KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1261                Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1262                    InvalidIdentityPublicKeyTypeError::new(public_key_type),
1263                ))
1264            }
1265        }
1266    }
1267
1268    #[cfg(feature = "state-transition-validation")]
1269    pub fn verify_identity_signed_signature(
1270        &self,
1271        public_key: &IdentityPublicKey,
1272        bls: &impl BlsModule,
1273    ) -> Result<(), ProtocolError> {
1274        // self.verify_public_key_level_and_purpose(public_key)?;
1275        if public_key.disabled_at().is_some() {
1276            return Err(ProtocolError::PublicKeyIsDisabledError(
1277                PublicKeyIsDisabledError::new(public_key.id()),
1278            ));
1279        }
1280
1281        let Some(signature) = self.signature() else {
1282            return Err(ProtocolError::CorruptedCodeExecution("verifying identity signature for a state transition that doesn't use identity signatures".to_string()));
1283        };
1284        if signature.is_empty() {
1285            return Err(ProtocolError::StateTransitionIsNotSignedError(
1286                StateTransitionIsNotSignedError::new(self.clone()),
1287            ));
1288        }
1289
1290        if self.signature_public_key_id() != Some(public_key.id()) {
1291            return Err(ProtocolError::PublicKeyMismatchError(
1292                PublicKeyMismatchError::new(public_key.clone()),
1293            ));
1294        }
1295
1296        let public_key_bytes = public_key.data().as_slice();
1297        match public_key.key_type() {
1298            KeyType::ECDSA_HASH160 => {
1299                self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key_bytes)
1300            }
1301
1302            KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key_bytes),
1303
1304            KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key_bytes, bls),
1305
1306            // per https://github.com/dashevo/platform/pull/353, signing and verification is not supported
1307            KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => Ok(()),
1308        }
1309    }
1310
1311    #[cfg(feature = "state-transition-validation")]
1312    fn verify_ecdsa_hash_160_signature_by_public_key_hash(
1313        &self,
1314        public_key_hash: &[u8],
1315    ) -> Result<(), ProtocolError> {
1316        let Some(signature) = self.signature() else {
1317            return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1318                needed: self.required_number_of_private_keys(),
1319                using: 1,
1320                msg: "This state transition type should a single signature",
1321            });
1322        };
1323        if signature.is_empty() {
1324            return Err(ProtocolError::StateTransitionIsNotSignedError(
1325                StateTransitionIsNotSignedError::new(self.clone()),
1326            ));
1327        }
1328        let data = self.signable_bytes()?;
1329        let data_hash = double_sha(data);
1330        signer::verify_hash_signature(&data_hash, signature.as_slice(), public_key_hash).map_err(
1331            |e| {
1332                ProtocolError::from(ConsensusError::SignatureError(
1333                    SignatureError::InvalidStateTransitionSignatureError(
1334                        InvalidStateTransitionSignatureError::new(e.to_string()),
1335                    ),
1336                ))
1337            },
1338        )
1339    }
1340
1341    #[cfg(feature = "state-transition-validation")]
1342    /// Verifies an ECDSA signature with the public key
1343    fn verify_ecdsa_signature_by_public_key(&self, public_key: &[u8]) -> Result<(), ProtocolError> {
1344        let Some(signature) = self.signature() else {
1345            return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1346                needed: self.required_number_of_private_keys(),
1347                using: 1,
1348                msg: "This state transition type should a single signature",
1349            });
1350        };
1351        if signature.is_empty() {
1352            return Err(ProtocolError::StateTransitionIsNotSignedError(
1353                StateTransitionIsNotSignedError::new(self.clone()),
1354            ));
1355        }
1356        let data = self.signable_bytes()?;
1357        signer::verify_data_signature(&data, signature.as_slice(), public_key).map_err(|e| {
1358            // TODO: it shouldn't respond with consensus error
1359
1360            ProtocolError::from(ConsensusError::SignatureError(
1361                SignatureError::InvalidStateTransitionSignatureError(
1362                    InvalidStateTransitionSignatureError::new(e.to_string()),
1363                ),
1364            ))
1365        })
1366    }
1367
1368    #[cfg(feature = "state-transition-validation")]
1369    /// Verifies a BLS signature with the public key
1370    fn verify_bls_signature_by_public_key<T: BlsModule>(
1371        &self,
1372        public_key: &[u8],
1373        bls: &T,
1374    ) -> Result<(), ProtocolError> {
1375        let Some(signature) = self.signature() else {
1376            return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1377                needed: self.required_number_of_private_keys(),
1378                using: 1,
1379                msg: "This state transition type should a single signature",
1380            });
1381        };
1382        if signature.is_empty() {
1383            return Err(ProtocolError::StateTransitionIsNotSignedError(
1384                StateTransitionIsNotSignedError::new(self.clone()),
1385            ));
1386        }
1387
1388        let data = self.signable_bytes()?;
1389
1390        bls.verify_signature(signature.as_slice(), &data, public_key)
1391            .map(|_| ())
1392            .map_err(|e| {
1393                // TODO: it shouldn't respond with consensus error
1394                ProtocolError::from(ConsensusError::SignatureError(
1395                    SignatureError::InvalidStateTransitionSignatureError(
1396                        InvalidStateTransitionSignatureError::new(e.to_string()),
1397                    ),
1398                ))
1399            })
1400    }
1401}
1402
1403#[cfg(feature = "state-transition-validation")]
1404impl StateTransitionStructureValidation for StateTransition {
1405    fn validate_structure(
1406        &self,
1407        platform_version: &PlatformVersion,
1408    ) -> crate::validation::SimpleConsensusValidationResult {
1409        match self {
1410            StateTransition::DataContractCreate(_)
1411            | StateTransition::DataContractUpdate(_)
1412            | StateTransition::Batch(_)
1413            | StateTransition::IdentityCreate(_)
1414            | StateTransition::IdentityTopUp(_)
1415            | StateTransition::IdentityCreditWithdrawal(_)
1416            | StateTransition::IdentityUpdate(_)
1417            | StateTransition::IdentityCreditTransfer(_)
1418            | StateTransition::MasternodeVote(_) => {
1419                crate::validation::SimpleConsensusValidationResult::new_with_error(
1420                    UnsupportedFeatureError::new(
1421                        "structure validation for identity-based state transitions".to_string(),
1422                        platform_version.protocol_version,
1423                    )
1424                    .into(),
1425                )
1426            }
1427            StateTransition::IdentityCreditTransferToAddresses(transition) => {
1428                transition.validate_structure(platform_version)
1429            }
1430            StateTransition::IdentityCreateFromAddresses(transition) => {
1431                transition.validate_structure(platform_version)
1432            }
1433            StateTransition::IdentityTopUpFromAddresses(transition) => {
1434                transition.validate_structure(platform_version)
1435            }
1436            StateTransition::AddressFundsTransfer(transition) => {
1437                transition.validate_structure(platform_version)
1438            }
1439            StateTransition::AddressFundingFromAssetLock(transition) => {
1440                transition.validate_structure(platform_version)
1441            }
1442            StateTransition::AddressCreditWithdrawal(transition) => {
1443                transition.validate_structure(platform_version)
1444            }
1445            StateTransition::Shield(transition) => transition.validate_structure(platform_version),
1446            StateTransition::ShieldedTransfer(transition) => {
1447                transition.validate_structure(platform_version)
1448            }
1449            StateTransition::Unshield(transition) => {
1450                transition.validate_structure(platform_version)
1451            }
1452            StateTransition::ShieldFromAssetLock(transition) => {
1453                transition.validate_structure(platform_version)
1454            }
1455            StateTransition::ShieldedWithdrawal(transition) => {
1456                transition.validate_structure(platform_version)
1457            }
1458        }
1459    }
1460}
1461
1462#[cfg(test)]
1463mod tests {
1464    use super::*;
1465
1466    // -----------------------------------------------------------------------
1467    // StateTransitionSigningOptions tests
1468    // -----------------------------------------------------------------------
1469
1470    #[test]
1471    fn test_signing_options_default() {
1472        let opts = StateTransitionSigningOptions::default();
1473        assert!(!opts.allow_signing_with_any_security_level);
1474        assert!(!opts.allow_signing_with_any_purpose);
1475    }
1476
1477    #[test]
1478    fn test_signing_options_equality() {
1479        let a = StateTransitionSigningOptions {
1480            allow_signing_with_any_security_level: true,
1481            allow_signing_with_any_purpose: false,
1482        };
1483        let b = StateTransitionSigningOptions {
1484            allow_signing_with_any_security_level: true,
1485            allow_signing_with_any_purpose: false,
1486        };
1487        assert_eq!(a, b);
1488    }
1489
1490    #[test]
1491    fn test_signing_options_inequality() {
1492        let a = StateTransitionSigningOptions {
1493            allow_signing_with_any_security_level: true,
1494            allow_signing_with_any_purpose: false,
1495        };
1496        let b = StateTransitionSigningOptions {
1497            allow_signing_with_any_security_level: false,
1498            allow_signing_with_any_purpose: false,
1499        };
1500        assert_ne!(a, b);
1501    }
1502
1503    #[test]
1504    fn test_signing_options_clone() {
1505        let original = StateTransitionSigningOptions {
1506            allow_signing_with_any_security_level: true,
1507            allow_signing_with_any_purpose: true,
1508        };
1509        let cloned = original.clone();
1510        assert_eq!(original, cloned);
1511    }
1512
1513    #[test]
1514    fn test_signing_options_copy() {
1515        let original = StateTransitionSigningOptions {
1516            allow_signing_with_any_security_level: true,
1517            allow_signing_with_any_purpose: false,
1518        };
1519        let copied = original;
1520        assert_eq!(original, copied);
1521    }
1522
1523    #[test]
1524    fn test_signing_options_debug() {
1525        let opts = StateTransitionSigningOptions::default();
1526        let debug_str = format!("{:?}", opts);
1527        assert!(debug_str.contains("StateTransitionSigningOptions"));
1528        assert!(debug_str.contains("allow_signing_with_any_security_level"));
1529        assert!(debug_str.contains("allow_signing_with_any_purpose"));
1530    }
1531}