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#[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)] #[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#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
467pub struct StateTransitionSigningOptions {
468 pub allow_signing_with_any_security_level: bool,
470 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 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 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 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 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 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 StateTransition::ShieldFromAssetLock(_) => 0,
722 StateTransition::MasternodeVote(_) => 0,
723 StateTransition::ShieldedTransfer(_) => 0,
724 StateTransition::Unshield(_) => 0,
725 StateTransition::ShieldedWithdrawal(_) => 0,
726 }
727 }
728
729 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 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 pub fn signature_public_key_id(&self) -> Option<KeyID> {
757 call_getter_method_identity_signed!(self, signature_public_key_id)
758 }
759
760 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 pub fn purpose_requirement(&self) -> Option<Vec<Purpose>> {
767 call_getter_method_identity_signed!(self, purpose_requirement)
768 }
769
770 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 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 pub fn state_transition_type(&self) -> StateTransitionType {
824 call_method!(self, state_transition_type)
825 }
826
827 pub fn unique_identifiers(&self) -> Vec<String> {
829 call_method!(self, unique_identifiers)
830 }
831
832 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 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 StateTransition::ShieldFromAssetLock(_) => {}
930 StateTransition::MasternodeVote(_) => {}
931 StateTransition::ShieldedTransfer(_) => {}
932 StateTransition::Unshield(_) => {}
933 StateTransition::ShieldedWithdrawal(_) => {}
934 }
935 }
936
937 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 async 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 .await
958 }
959
960 #[cfg(feature = "state-transition-signing")]
961 pub async fn sign_external_with_options<S: Signer<IdentityPublicKey>>(
962 &mut self,
963 identity_public_key: &IdentityPublicKey,
964 signer: &S,
965 get_data_contract_security_level_requirement: Option<
966 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
967 >,
968 options: StateTransitionSigningOptions,
969 ) -> Result<(), ProtocolError> {
970 match self {
971 StateTransition::DataContractCreate(st) => {
972 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
973 st.verify_public_key_is_enabled(identity_public_key)?;
974 }
975 StateTransition::DataContractUpdate(st) => {
976 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
977 st.verify_public_key_is_enabled(identity_public_key)?;
978 }
979 StateTransition::Batch(st) => {
980 let allow_token_transfer_keys = st.transitions_len() == 1
981 && (st
982 .first_transition()
983 .expect("expected first transition with len 1")
984 .as_transition_token_claim()
985 .is_some()
986 || st
987 .first_transition()
988 .expect("expected first transition with len 1")
989 .as_transition_token_transfer()
990 .is_some());
991 let allowed_key_purposes = if allow_token_transfer_keys {
992 vec![Purpose::AUTHENTICATION, Purpose::TRANSFER]
993 } else {
994 vec![Purpose::AUTHENTICATION]
995 };
996 if !options.allow_signing_with_any_purpose
997 && !allowed_key_purposes.contains(&identity_public_key.purpose())
998 {
999 return Err(ProtocolError::WrongPublicKeyPurposeError(
1000 WrongPublicKeyPurposeError::new(
1001 identity_public_key.purpose(),
1002 allowed_key_purposes,
1003 ),
1004 ));
1005 }
1006 if !options.allow_signing_with_any_security_level {
1007 let security_level_requirement = st.combined_security_level_requirement(
1008 get_data_contract_security_level_requirement,
1009 )?;
1010 if !security_level_requirement.contains(&identity_public_key.security_level()) {
1011 return Err(ProtocolError::InvalidSignaturePublicKeySecurityLevelError(
1012 InvalidSignaturePublicKeySecurityLevelError::new(
1013 identity_public_key.security_level(),
1014 security_level_requirement,
1015 ),
1016 ));
1017 }
1018 }
1019 st.verify_public_key_is_enabled(identity_public_key)?;
1020 }
1021 StateTransition::IdentityCreditWithdrawal(st) => {
1022 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1023 st.verify_public_key_is_enabled(identity_public_key)?;
1024 }
1025 StateTransition::IdentityUpdate(st) => {
1026 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1027 st.verify_public_key_is_enabled(identity_public_key)?;
1028 }
1029 StateTransition::IdentityCreditTransfer(st) => {
1030 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1031 st.verify_public_key_is_enabled(identity_public_key)?;
1032 }
1033 StateTransition::IdentityCreate(_) => {
1034 return Err(ProtocolError::CorruptedCodeExecution(
1035 "identity create can not be called for identity signing".to_string(),
1036 ))
1037 }
1038 StateTransition::IdentityTopUp(_) => {
1039 return Err(ProtocolError::CorruptedCodeExecution(
1040 "identity top up can not be called for identity signing".to_string(),
1041 ))
1042 }
1043 StateTransition::MasternodeVote(st) => {
1044 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1045 st.verify_public_key_is_enabled(identity_public_key)?;
1046 }
1047 StateTransition::IdentityCreditTransferToAddresses(st) => {
1048 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1049 st.verify_public_key_is_enabled(identity_public_key)?;
1050 }
1051 StateTransition::IdentityCreateFromAddresses(_) => {
1052 return Err(ProtocolError::CorruptedCodeExecution(
1053 "identity create from addresses can not be called for identity signing"
1054 .to_string(),
1055 ))
1056 }
1057 StateTransition::IdentityTopUpFromAddresses(_) => {
1058 return Err(ProtocolError::CorruptedCodeExecution(
1059 "identity top up from addresses can not be called for identity signing"
1060 .to_string(),
1061 ))
1062 }
1063 StateTransition::AddressFundsTransfer(_) => {
1064 return Err(ProtocolError::CorruptedCodeExecution(
1065 "address funds transfer transition can not be called for identity signing"
1066 .to_string(),
1067 ))
1068 }
1069 StateTransition::AddressFundingFromAssetLock(_) => {
1070 return Err(ProtocolError::CorruptedCodeExecution(
1071 "address funding from asset lock transition can not be called for identity signing"
1072 .to_string(),
1073 ))
1074 }
1075 StateTransition::AddressCreditWithdrawal(_) => {
1076 return Err(ProtocolError::CorruptedCodeExecution(
1077 "address credit withdrawal transition can not be called for identity signing"
1078 .to_string(),
1079 ))
1080 }
1081 StateTransition::Shield(_) => {
1082 return Err(ProtocolError::CorruptedCodeExecution(
1083 "shield transition can not be called for identity signing".to_string(),
1084 ))
1085 }
1086 StateTransition::ShieldedTransfer(_) => {
1087 return Err(ProtocolError::CorruptedCodeExecution(
1088 "shielded transfer transition can not be called for identity signing"
1089 .to_string(),
1090 ))
1091 }
1092 StateTransition::Unshield(_) => {
1093 return Err(ProtocolError::CorruptedCodeExecution(
1094 "unshield transition can not be called for identity signing".to_string(),
1095 ))
1096 }
1097 StateTransition::ShieldFromAssetLock(_) => {
1098 return Err(ProtocolError::CorruptedCodeExecution(
1099 "shield from asset lock transition can not be called for identity signing"
1100 .to_string(),
1101 ))
1102 }
1103 StateTransition::ShieldedWithdrawal(_) => {
1104 return Err(ProtocolError::CorruptedCodeExecution(
1105 "shielded withdrawal transition can not be called for identity signing"
1106 .to_string(),
1107 ))
1108 }
1109 }
1110 let data = self.signable_bytes()?;
1111 self.set_signature(signer.sign(identity_public_key, data.as_slice()).await?);
1112 self.set_signature_public_key_id(identity_public_key.id());
1113 Ok(())
1114 }
1115
1116 #[cfg(feature = "state-transition-signing")]
1117 pub fn sign(
1118 &mut self,
1119 identity_public_key: &IdentityPublicKey,
1120 private_key: &[u8],
1121 bls: &impl BlsModule,
1122 ) -> Result<(), ProtocolError> {
1123 self.sign_with_options(
1124 identity_public_key,
1125 private_key,
1126 bls,
1127 StateTransitionSigningOptions::default(),
1128 )
1129 }
1130
1131 #[cfg(feature = "state-transition-signing")]
1132 pub fn sign_with_options(
1133 &mut self,
1134 identity_public_key: &IdentityPublicKey,
1135 private_key: &[u8],
1136 bls: &impl BlsModule,
1137 options: StateTransitionSigningOptions,
1138 ) -> Result<(), ProtocolError> {
1139 call_errorable_method_identity_signed!(
1140 self,
1141 verify_public_key_level_and_purpose,
1142 identity_public_key,
1143 options
1144 )?;
1145 call_errorable_method_identity_signed!(
1146 self,
1147 verify_public_key_is_enabled,
1148 identity_public_key
1149 )?;
1150
1151 match identity_public_key.key_type() {
1152 KeyType::ECDSA_SECP256K1 => {
1153 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1154
1155 if public_key_compressed.as_slice() != identity_public_key.data().as_slice() {
1160 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1161 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1162 ));
1163 }
1164
1165 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1166 }
1167 KeyType::ECDSA_HASH160 => {
1168 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1169 let pub_key_hash = ripemd160_sha256(&public_key_compressed);
1170
1171 if identity_public_key.data().as_slice() != pub_key_hash {
1172 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1173 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1174 ));
1175 }
1176 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1177 }
1178 KeyType::BLS12_381 => {
1179 let public_key = bls.private_key_to_public_key(private_key)?;
1180
1181 if public_key != identity_public_key.data().as_slice() {
1182 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1183 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1184 ));
1185 }
1186 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1187 }
1188
1189 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1193 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1194 InvalidIdentityPublicKeyTypeError::new(identity_public_key.key_type()),
1195 ))
1196 }
1197 }?;
1198
1199 self.set_signature_public_key_id(identity_public_key.id());
1200
1201 Ok(())
1202 }
1203
1204 #[cfg(feature = "state-transition-signing")]
1205 pub fn sign_by_private_key(
1207 &mut self,
1208 private_key: &[u8],
1209 key_type: KeyType,
1210 bls: &impl BlsModule,
1211 ) -> Result<(), ProtocolError> {
1212 let data = self.signable_bytes()?;
1213 match key_type {
1214 KeyType::BLS12_381 => {
1215 if !self.set_signature(bls.sign(&data, private_key)?.into()) {
1216 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1217 needed: self.required_number_of_private_keys(),
1218 using: 1,
1219 msg: "failed to set BLS signature",
1220 });
1221 }
1222 }
1223
1224 KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => {
1226 let signature = signer::sign(&data, private_key)?;
1227 if !self.set_signature(signature.to_vec().into()) {
1228 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1229 needed: self.required_number_of_private_keys(),
1230 using: 1,
1231 msg: "failed to set ECDSA signature",
1232 });
1233 };
1234 }
1235
1236 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1240 return Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1241 InvalidIdentityPublicKeyTypeError::new(key_type),
1242 ))
1243 }
1244 };
1245 Ok(())
1246 }
1247
1248 #[cfg(feature = "state-transition-validation")]
1249 fn verify_by_raw_public_key<T: BlsModule>(
1250 &self,
1251 public_key: &[u8],
1252 public_key_type: KeyType,
1253 bls: &T,
1254 ) -> Result<(), ProtocolError> {
1255 match public_key_type {
1256 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key),
1257 KeyType::ECDSA_HASH160 => {
1258 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key)
1259 }
1260 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key, bls),
1261 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1262 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1263 InvalidIdentityPublicKeyTypeError::new(public_key_type),
1264 ))
1265 }
1266 }
1267 }
1268
1269 #[cfg(feature = "state-transition-validation")]
1270 pub fn verify_identity_signed_signature(
1271 &self,
1272 public_key: &IdentityPublicKey,
1273 bls: &impl BlsModule,
1274 ) -> Result<(), ProtocolError> {
1275 if public_key.disabled_at().is_some() {
1277 return Err(ProtocolError::PublicKeyIsDisabledError(
1278 PublicKeyIsDisabledError::new(public_key.id()),
1279 ));
1280 }
1281
1282 let Some(signature) = self.signature() else {
1283 return Err(ProtocolError::CorruptedCodeExecution("verifying identity signature for a state transition that doesn't use identity signatures".to_string()));
1284 };
1285 if signature.is_empty() {
1286 return Err(ProtocolError::StateTransitionIsNotSignedError(
1287 StateTransitionIsNotSignedError::new(self.clone()),
1288 ));
1289 }
1290
1291 if self.signature_public_key_id() != Some(public_key.id()) {
1292 return Err(ProtocolError::PublicKeyMismatchError(
1293 PublicKeyMismatchError::new(public_key.clone()),
1294 ));
1295 }
1296
1297 let public_key_bytes = public_key.data().as_slice();
1298 match public_key.key_type() {
1299 KeyType::ECDSA_HASH160 => {
1300 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key_bytes)
1301 }
1302
1303 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key_bytes),
1304
1305 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key_bytes, bls),
1306
1307 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => Ok(()),
1309 }
1310 }
1311
1312 #[cfg(feature = "state-transition-validation")]
1313 fn verify_ecdsa_hash_160_signature_by_public_key_hash(
1314 &self,
1315 public_key_hash: &[u8],
1316 ) -> Result<(), ProtocolError> {
1317 let Some(signature) = self.signature() else {
1318 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1319 needed: self.required_number_of_private_keys(),
1320 using: 1,
1321 msg: "This state transition type should a single signature",
1322 });
1323 };
1324 if signature.is_empty() {
1325 return Err(ProtocolError::StateTransitionIsNotSignedError(
1326 StateTransitionIsNotSignedError::new(self.clone()),
1327 ));
1328 }
1329 let data = self.signable_bytes()?;
1330 let data_hash = double_sha(data);
1331 signer::verify_hash_signature(&data_hash, signature.as_slice(), public_key_hash).map_err(
1332 |e| {
1333 ProtocolError::from(ConsensusError::SignatureError(
1334 SignatureError::InvalidStateTransitionSignatureError(
1335 InvalidStateTransitionSignatureError::new(e.to_string()),
1336 ),
1337 ))
1338 },
1339 )
1340 }
1341
1342 #[cfg(feature = "state-transition-validation")]
1343 fn verify_ecdsa_signature_by_public_key(&self, public_key: &[u8]) -> Result<(), ProtocolError> {
1345 let Some(signature) = self.signature() else {
1346 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1347 needed: self.required_number_of_private_keys(),
1348 using: 1,
1349 msg: "This state transition type should a single signature",
1350 });
1351 };
1352 if signature.is_empty() {
1353 return Err(ProtocolError::StateTransitionIsNotSignedError(
1354 StateTransitionIsNotSignedError::new(self.clone()),
1355 ));
1356 }
1357 let data = self.signable_bytes()?;
1358 signer::verify_data_signature(&data, signature.as_slice(), public_key).map_err(|e| {
1359 ProtocolError::from(ConsensusError::SignatureError(
1362 SignatureError::InvalidStateTransitionSignatureError(
1363 InvalidStateTransitionSignatureError::new(e.to_string()),
1364 ),
1365 ))
1366 })
1367 }
1368
1369 #[cfg(feature = "state-transition-validation")]
1370 fn verify_bls_signature_by_public_key<T: BlsModule>(
1372 &self,
1373 public_key: &[u8],
1374 bls: &T,
1375 ) -> Result<(), ProtocolError> {
1376 let Some(signature) = self.signature() else {
1377 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1378 needed: self.required_number_of_private_keys(),
1379 using: 1,
1380 msg: "This state transition type should a single signature",
1381 });
1382 };
1383 if signature.is_empty() {
1384 return Err(ProtocolError::StateTransitionIsNotSignedError(
1385 StateTransitionIsNotSignedError::new(self.clone()),
1386 ));
1387 }
1388
1389 let data = self.signable_bytes()?;
1390
1391 bls.verify_signature(signature.as_slice(), &data, public_key)
1392 .map(|_| ())
1393 .map_err(|e| {
1394 ProtocolError::from(ConsensusError::SignatureError(
1396 SignatureError::InvalidStateTransitionSignatureError(
1397 InvalidStateTransitionSignatureError::new(e.to_string()),
1398 ),
1399 ))
1400 })
1401 }
1402}
1403
1404#[cfg(feature = "state-transition-validation")]
1405impl StateTransitionStructureValidation for StateTransition {
1406 fn validate_structure(
1407 &self,
1408 platform_version: &PlatformVersion,
1409 ) -> crate::validation::SimpleConsensusValidationResult {
1410 match self {
1411 StateTransition::DataContractCreate(_)
1412 | StateTransition::DataContractUpdate(_)
1413 | StateTransition::Batch(_)
1414 | StateTransition::IdentityCreate(_)
1415 | StateTransition::IdentityTopUp(_)
1416 | StateTransition::IdentityCreditWithdrawal(_)
1417 | StateTransition::IdentityUpdate(_)
1418 | StateTransition::IdentityCreditTransfer(_)
1419 | StateTransition::MasternodeVote(_) => {
1420 crate::validation::SimpleConsensusValidationResult::new_with_error(
1421 UnsupportedFeatureError::new(
1422 "structure validation for identity-based state transitions".to_string(),
1423 platform_version.protocol_version,
1424 )
1425 .into(),
1426 )
1427 }
1428 StateTransition::IdentityCreditTransferToAddresses(transition) => {
1429 transition.validate_structure(platform_version)
1430 }
1431 StateTransition::IdentityCreateFromAddresses(transition) => {
1432 transition.validate_structure(platform_version)
1433 }
1434 StateTransition::IdentityTopUpFromAddresses(transition) => {
1435 transition.validate_structure(platform_version)
1436 }
1437 StateTransition::AddressFundsTransfer(transition) => {
1438 transition.validate_structure(platform_version)
1439 }
1440 StateTransition::AddressFundingFromAssetLock(transition) => {
1441 transition.validate_structure(platform_version)
1442 }
1443 StateTransition::AddressCreditWithdrawal(transition) => {
1444 transition.validate_structure(platform_version)
1445 }
1446 StateTransition::Shield(transition) => transition.validate_structure(platform_version),
1447 StateTransition::ShieldedTransfer(transition) => {
1448 transition.validate_structure(platform_version)
1449 }
1450 StateTransition::Unshield(transition) => {
1451 transition.validate_structure(platform_version)
1452 }
1453 StateTransition::ShieldFromAssetLock(transition) => {
1454 transition.validate_structure(platform_version)
1455 }
1456 StateTransition::ShieldedWithdrawal(transition) => {
1457 transition.validate_structure(platform_version)
1458 }
1459 }
1460 }
1461}
1462
1463#[cfg(test)]
1464mod tests {
1465 use super::*;
1466
1467 #[test]
1472 fn test_signing_options_default() {
1473 let opts = StateTransitionSigningOptions::default();
1474 assert!(!opts.allow_signing_with_any_security_level);
1475 assert!(!opts.allow_signing_with_any_purpose);
1476 }
1477
1478 #[test]
1479 fn test_signing_options_equality() {
1480 let a = StateTransitionSigningOptions {
1481 allow_signing_with_any_security_level: true,
1482 allow_signing_with_any_purpose: false,
1483 };
1484 let b = StateTransitionSigningOptions {
1485 allow_signing_with_any_security_level: true,
1486 allow_signing_with_any_purpose: false,
1487 };
1488 assert_eq!(a, b);
1489 }
1490
1491 #[test]
1492 fn test_signing_options_inequality() {
1493 let a = StateTransitionSigningOptions {
1494 allow_signing_with_any_security_level: true,
1495 allow_signing_with_any_purpose: false,
1496 };
1497 let b = StateTransitionSigningOptions {
1498 allow_signing_with_any_security_level: false,
1499 allow_signing_with_any_purpose: false,
1500 };
1501 assert_ne!(a, b);
1502 }
1503
1504 #[test]
1505 #[allow(clippy::clone_on_copy)]
1506 fn test_signing_options_clone() {
1507 let original = StateTransitionSigningOptions {
1508 allow_signing_with_any_security_level: true,
1509 allow_signing_with_any_purpose: true,
1510 };
1511 let cloned = original.clone();
1512 assert_eq!(original, cloned);
1513 }
1514
1515 #[test]
1516 fn test_signing_options_copy() {
1517 let original = StateTransitionSigningOptions {
1518 allow_signing_with_any_security_level: true,
1519 allow_signing_with_any_purpose: false,
1520 };
1521 let copied = original;
1522 assert_eq!(original, copied);
1523 }
1524
1525 #[test]
1526 fn test_signing_options_debug() {
1527 let opts = StateTransitionSigningOptions::default();
1528 let debug_str = format!("{:?}", opts);
1529 assert!(debug_str.contains("StateTransitionSigningOptions"));
1530 assert!(debug_str.contains("allow_signing_with_any_security_level"));
1531 assert!(debug_str.contains("allow_signing_with_any_purpose"));
1532 }
1533
1534 use crate::identity::core_script::CoreScript;
1544 use crate::identity::{Purpose, SecurityLevel};
1545 use crate::prelude::Identifier;
1546 use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0;
1547 use crate::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition;
1548 use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0;
1549 use crate::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition;
1550 use crate::state_transition::masternode_vote_transition::v0::MasternodeVoteTransitionV0;
1551 use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
1552 use crate::withdrawal::Pooling;
1553
1554 fn sample_transfer_st() -> StateTransition {
1555 let v0 = IdentityCreditTransferTransitionV0 {
1556 identity_id: Identifier::from([1u8; 32]),
1557 recipient_id: Identifier::from([2u8; 32]),
1558 amount: 1_000,
1559 nonce: 7,
1560 user_fee_increase: 3,
1561 signature_public_key_id: 11,
1562 signature: BinaryData::new(vec![0u8; 65]),
1563 };
1564 StateTransition::IdentityCreditTransfer(IdentityCreditTransferTransition::V0(v0))
1565 }
1566
1567 fn sample_masternode_vote_st() -> StateTransition {
1568 let v0 = MasternodeVoteTransitionV0 {
1569 pro_tx_hash: Identifier::from([3u8; 32]),
1570 voter_identity_id: Identifier::from([4u8; 32]),
1571 vote: Default::default(),
1572 nonce: 2,
1573 signature_public_key_id: 5,
1574 signature: BinaryData::new(vec![9u8; 10]),
1575 };
1576 StateTransition::MasternodeVote(MasternodeVoteTransition::V0(v0))
1577 }
1578
1579 fn sample_withdrawal_st() -> StateTransition {
1580 let v0 = IdentityCreditWithdrawalTransitionV0 {
1581 identity_id: Identifier::from([5u8; 32]),
1582 amount: 42,
1583 core_fee_per_byte: 1,
1584 pooling: Pooling::Never,
1585 output_script: CoreScript::from_bytes(vec![0x76, 0xa9]),
1586 nonce: 4,
1587 user_fee_increase: 1,
1588 signature_public_key_id: 3,
1589 signature: BinaryData::new(vec![8u8; 65]),
1590 };
1591 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V0(v0))
1592 }
1593
1594 #[test]
1595 fn test_name_returns_variant_names() {
1596 assert_eq!(sample_transfer_st().name(), "IdentityCreditTransfer");
1597 assert_eq!(sample_masternode_vote_st().name(), "MasternodeVote");
1598 assert_eq!(sample_withdrawal_st().name(), "IdentityCreditWithdrawal");
1599 }
1600
1601 #[test]
1602 fn test_state_transition_type_matches_variant() {
1603 assert_eq!(
1604 sample_transfer_st().state_transition_type(),
1605 StateTransitionType::IdentityCreditTransfer
1606 );
1607 assert_eq!(
1608 sample_masternode_vote_st().state_transition_type(),
1609 StateTransitionType::MasternodeVote
1610 );
1611 assert_eq!(
1612 sample_withdrawal_st().state_transition_type(),
1613 StateTransitionType::IdentityCreditWithdrawal
1614 );
1615 }
1616
1617 #[test]
1618 fn test_is_identity_signed_excludes_asset_lock_and_shielded() {
1619 assert!(sample_transfer_st().is_identity_signed());
1620 assert!(sample_masternode_vote_st().is_identity_signed());
1621 assert!(sample_withdrawal_st().is_identity_signed());
1622 }
1623
1624 #[test]
1625 fn test_signature_accessor() {
1626 let st = sample_transfer_st();
1627 let sig = st.signature().expect("transfer should expose signature");
1628 assert_eq!(sig.len(), 65);
1629
1630 let st = sample_masternode_vote_st();
1631 let sig = st.signature().expect("masternode vote has signature");
1632 assert_eq!(sig.as_slice(), &[9u8; 10]);
1633 }
1634
1635 #[test]
1636 fn test_owner_id_accessor() {
1637 let transfer = sample_transfer_st();
1638 assert_eq!(transfer.owner_id(), Some(Identifier::from([1u8; 32])));
1639
1640 let vote = sample_masternode_vote_st();
1641 assert_eq!(vote.owner_id(), Some(Identifier::from([4u8; 32])));
1642
1643 let withdraw = sample_withdrawal_st();
1644 assert_eq!(withdraw.owner_id(), Some(Identifier::from([5u8; 32])));
1645 }
1646
1647 #[test]
1648 fn test_signature_public_key_id_accessor() {
1649 assert_eq!(sample_transfer_st().signature_public_key_id(), Some(11));
1650 assert_eq!(
1651 sample_masternode_vote_st().signature_public_key_id(),
1652 Some(5)
1653 );
1654 assert_eq!(sample_withdrawal_st().signature_public_key_id(), Some(3));
1655 }
1656
1657 #[test]
1658 fn test_user_fee_increase_for_various_variants() {
1659 assert_eq!(sample_transfer_st().user_fee_increase(), 3);
1661 assert_eq!(sample_masternode_vote_st().user_fee_increase(), 0);
1663 assert_eq!(sample_withdrawal_st().user_fee_increase(), 1);
1665 }
1666
1667 #[test]
1668 fn test_set_signature_returns_true_for_supported() {
1669 let mut st = sample_transfer_st();
1670 let ok = st.set_signature(BinaryData::new(vec![0xaa; 65]));
1671 assert!(ok);
1672 assert_eq!(st.signature().unwrap().as_slice(), &[0xaa; 65]);
1673 }
1674
1675 #[test]
1676 fn test_set_user_fee_increase_updates_value() {
1677 let mut st = sample_transfer_st();
1678 st.set_user_fee_increase(42);
1679 assert_eq!(st.user_fee_increase(), 42);
1680
1681 let mut vote = sample_masternode_vote_st();
1683 vote.set_user_fee_increase(99);
1684 assert_eq!(vote.user_fee_increase(), 0);
1685 }
1686
1687 #[test]
1688 fn test_set_signature_public_key_id() {
1689 let mut st = sample_transfer_st();
1690 st.set_signature_public_key_id(1234);
1691 assert_eq!(st.signature_public_key_id(), Some(1234));
1692 }
1693
1694 #[test]
1695 fn test_required_number_of_private_keys_default() {
1696 assert_eq!(sample_transfer_st().required_number_of_private_keys(), 1);
1698 assert_eq!(
1699 sample_masternode_vote_st().required_number_of_private_keys(),
1700 1
1701 );
1702 assert_eq!(sample_withdrawal_st().required_number_of_private_keys(), 1);
1703 }
1704
1705 #[test]
1706 fn test_inputs_none_for_legacy_variants() {
1707 assert!(sample_transfer_st().inputs().is_none());
1709 assert!(sample_masternode_vote_st().inputs().is_none());
1710 assert!(sample_withdrawal_st().inputs().is_none());
1711 }
1712
1713 #[test]
1714 fn test_active_version_range_legacy_transitions() {
1715 assert_eq!(sample_transfer_st().active_version_range(), ALL_VERSIONS);
1717 assert_eq!(
1718 sample_masternode_vote_st().active_version_range(),
1719 ALL_VERSIONS
1720 );
1721 assert_eq!(sample_withdrawal_st().active_version_range(), ALL_VERSIONS);
1722 }
1723
1724 #[test]
1725 fn test_unique_identifiers_non_empty() {
1726 let ids = sample_transfer_st().unique_identifiers();
1727 assert_eq!(ids.len(), 1);
1728 assert!(!ids[0].is_empty());
1729 }
1730
1731 #[test]
1732 fn test_required_asset_lock_balance_rejects_non_asset_lock() {
1733 let platform_version = PlatformVersion::latest();
1734 let st = sample_transfer_st();
1735 let err = st
1736 .required_asset_lock_balance_for_processing_start(platform_version)
1737 .expect_err("credit transfer is not an asset lock state transition");
1738 match err {
1739 ProtocolError::CorruptedCodeExecution(msg) => {
1740 assert!(
1741 msg.contains("is not an asset lock transaction"),
1742 "unexpected error message: {msg}"
1743 );
1744 }
1745 other => panic!("expected CorruptedCodeExecution, got {other:?}"),
1746 }
1747 }
1748
1749 #[test]
1750 fn test_security_level_requirement_for_transfer() {
1751 let st = sample_transfer_st();
1753 let levels = st
1754 .security_level_requirement(Purpose::TRANSFER)
1755 .expect("transfer state transition should return a requirement");
1756 assert_eq!(levels, vec![SecurityLevel::CRITICAL]);
1757 }
1758
1759 #[test]
1760 fn test_purpose_requirement_for_transfer() {
1761 let st = sample_transfer_st();
1762 let purposes = st
1763 .purpose_requirement()
1764 .expect("transfer state transition should have a purpose");
1765 assert_eq!(purposes, vec![Purpose::TRANSFER]);
1766 }
1767
1768 #[test]
1769 fn test_optional_asset_lock_proof_none_for_transfer() {
1770 let st = sample_transfer_st();
1771 assert!(st.optional_asset_lock_proof().is_none());
1772 }
1773
1774 #[test]
1779 fn test_from_outer_enum_into_state_transition() {
1780 let outer: IdentityCreditTransferTransition =
1781 IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0::default());
1782 let st: StateTransition = outer.into();
1783 assert!(matches!(st, StateTransition::IdentityCreditTransfer(_)));
1784 }
1785
1786 #[test]
1787 fn test_from_masternode_vote_outer_into_state_transition() {
1788 let outer: MasternodeVoteTransition =
1789 MasternodeVoteTransition::V0(MasternodeVoteTransitionV0::default());
1790 let st: StateTransition = outer.into();
1791 assert!(matches!(st, StateTransition::MasternodeVote(_)));
1792 }
1793
1794 #[test]
1800 fn test_state_transition_platform_serialize_roundtrip() {
1801 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
1802 let original = sample_transfer_st();
1803 let bytes =
1804 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
1805 let restored =
1806 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
1807 assert_eq!(original, restored);
1808 }
1809
1810 #[test]
1811 fn test_deserialize_from_bytes_in_version_succeeds_for_latest() {
1812 use crate::serialization::PlatformSerializable;
1813 let original = sample_transfer_st();
1814 let bytes =
1815 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
1816 let restored =
1817 StateTransition::deserialize_from_bytes_in_version(&bytes, PlatformVersion::latest())
1818 .expect("deserialize_from_bytes_in_version should succeed");
1819 assert_eq!(original, restored);
1820 }
1821
1822 #[test]
1823 fn test_transaction_id_is_deterministic() {
1824 let st = sample_transfer_st();
1825 let a = st.transaction_id().expect("hash should succeed");
1826 let b = st.transaction_id().expect("hash should succeed");
1827 assert_eq!(a, b);
1828 assert_eq!(a.len(), 32);
1829 }
1830
1831 #[test]
1832 fn test_transaction_id_changes_on_signature_change() {
1833 let mut st = sample_transfer_st();
1834 let before = st.transaction_id().expect("hash should succeed");
1835 st.set_signature(BinaryData::new(vec![0xbb; 65]));
1836 let after = st.transaction_id().expect("hash should succeed");
1837 assert_ne!(before, after);
1839 }
1840
1841 #[test]
1842 fn test_clone_preserves_inner_state() {
1843 let st = sample_transfer_st();
1844 let cloned = st.clone();
1845 assert_eq!(st, cloned);
1846 }
1847
1848 use crate::data_contract::serialized_version::DataContractInSerializationFormat;
1859 use crate::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0;
1860 use crate::state_transition::batch_transition::document_base_transition::DocumentBaseTransition;
1861 use crate::state_transition::batch_transition::document_delete_transition::{
1862 DocumentDeleteTransition, DocumentDeleteTransitionV0,
1863 };
1864 use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionV0};
1865 use crate::state_transition::data_contract_create_transition::{
1866 DataContractCreateTransition, DataContractCreateTransitionV0,
1867 };
1868 use crate::state_transition::data_contract_update_transition::{
1869 DataContractUpdateTransition, DataContractUpdateTransitionV0,
1870 };
1871 use crate::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0;
1872 use crate::state_transition::identity_create_transition::IdentityCreateTransition;
1873 use crate::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0;
1874 use crate::state_transition::identity_topup_transition::IdentityTopUpTransition;
1875 use crate::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0;
1876 use crate::state_transition::identity_update_transition::IdentityUpdateTransition;
1877 use crate::state_transition::shielded_transfer_transition::v0::ShieldedTransferTransitionV0;
1878 use crate::state_transition::shielded_transfer_transition::ShieldedTransferTransition;
1879 use crate::state_transition::shielded_withdrawal_transition::v0::ShieldedWithdrawalTransitionV0;
1880 use crate::state_transition::shielded_withdrawal_transition::ShieldedWithdrawalTransition;
1881 use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
1882 use crate::state_transition::unshield_transition::UnshieldTransition;
1883
1884 fn sample_data_contract_in_serialization_format() -> DataContractInSerializationFormat {
1887 use crate::data_contract::config::v0::DataContractConfigV0;
1888 use crate::data_contract::config::DataContractConfig;
1889 use crate::data_contract::v1::DataContractV1;
1890 use crate::data_contract::DataContract;
1891 use platform_version::TryIntoPlatformVersioned;
1892 use std::collections::BTreeMap;
1893
1894 let contract = DataContract::V1(DataContractV1 {
1895 id: Identifier::from([9u8; 32]),
1896 version: 1,
1897 owner_id: Identifier::from([7u8; 32]),
1898 document_types: BTreeMap::new(),
1899 config: DataContractConfig::V0(DataContractConfigV0 {
1900 can_be_deleted: false,
1901 readonly: false,
1902 keeps_history: false,
1903 documents_keep_history_contract_default: false,
1904 documents_mutable_contract_default: false,
1905 documents_can_be_deleted_contract_default: false,
1906 requires_identity_encryption_bounded_key: None,
1907 requires_identity_decryption_bounded_key: None,
1908 }),
1909 schema_defs: None,
1910 created_at: None,
1911 updated_at: None,
1912 created_at_block_height: None,
1913 updated_at_block_height: None,
1914 created_at_epoch: None,
1915 updated_at_epoch: None,
1916 groups: BTreeMap::new(),
1917 tokens: BTreeMap::new(),
1918 keywords: Vec::new(),
1919 description: None,
1920 });
1921
1922 contract
1923 .try_into_platform_versioned(PlatformVersion::latest())
1924 .expect("expected to serialize a trivial contract")
1925 }
1926
1927 fn sample_data_contract_create_st() -> StateTransition {
1928 StateTransition::DataContractCreate(DataContractCreateTransition::V0(
1929 DataContractCreateTransitionV0 {
1930 data_contract: sample_data_contract_in_serialization_format(),
1931 identity_nonce: 1,
1932 user_fee_increase: 5,
1933 signature_public_key_id: 2,
1934 signature: BinaryData::new(vec![0xAB; 65]),
1935 },
1936 ))
1937 }
1938
1939 fn sample_data_contract_update_st() -> StateTransition {
1940 StateTransition::DataContractUpdate(DataContractUpdateTransition::V0(
1941 DataContractUpdateTransitionV0 {
1942 identity_contract_nonce: 4,
1943 data_contract: sample_data_contract_in_serialization_format(),
1944 user_fee_increase: 9,
1945 signature_public_key_id: 6,
1946 signature: BinaryData::new(vec![0xCD; 65]),
1947 },
1948 ))
1949 }
1950
1951 fn sample_batch_st_with_delete() -> StateTransition {
1952 let base = DocumentBaseTransition::V0(DocumentBaseTransitionV0 {
1953 id: Identifier::from([1u8; 32]),
1954 identity_contract_nonce: 3,
1955 document_type_name: "preorder".to_string(),
1956 data_contract_id: Identifier::from([2u8; 32]),
1957 });
1958 let delete =
1959 DocumentTransition::Delete(DocumentDeleteTransition::V0(DocumentDeleteTransitionV0 {
1960 base,
1961 }));
1962 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
1963 owner_id: Identifier::from([8u8; 32]),
1964 transitions: vec![delete],
1965 user_fee_increase: 2,
1966 signature_public_key_id: 7,
1967 signature: BinaryData::new(vec![0xEE; 65]),
1968 }))
1969 }
1970
1971 fn sample_batch_st_empty() -> StateTransition {
1972 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
1973 owner_id: Identifier::from([1u8; 32]),
1974 transitions: vec![],
1975 user_fee_increase: 0,
1976 signature_public_key_id: 0,
1977 signature: BinaryData::new(vec![]),
1978 }))
1979 }
1980
1981 fn sample_identity_create_st() -> StateTransition {
1982 StateTransition::IdentityCreate(IdentityCreateTransition::V0(IdentityCreateTransitionV0 {
1983 identity_id: Identifier::from([3u8; 32]),
1984 ..Default::default()
1985 }))
1986 }
1987
1988 fn sample_identity_top_up_st() -> StateTransition {
1989 StateTransition::IdentityTopUp(IdentityTopUpTransition::V0(IdentityTopUpTransitionV0 {
1990 identity_id: Identifier::from([4u8; 32]),
1991 ..Default::default()
1992 }))
1993 }
1994
1995 fn sample_identity_update_st() -> StateTransition {
1996 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
1997 identity_id: Identifier::from([5u8; 32]),
1998 revision: 1,
1999 nonce: 2,
2000 add_public_keys: vec![],
2001 disable_public_keys: vec![],
2002 user_fee_increase: 11,
2003 signature_public_key_id: 33,
2004 signature: BinaryData::new(vec![0xFF; 65]),
2005 }))
2006 }
2007
2008 fn sample_unshield_st() -> StateTransition {
2009 StateTransition::Unshield(UnshieldTransition::V0(UnshieldTransitionV0 {
2010 output_address: Default::default(),
2011 actions: vec![],
2012 unshielding_amount: 0,
2013 anchor: [0u8; 32],
2014 proof: vec![],
2015 binding_signature: [0u8; 64],
2016 }))
2017 }
2018
2019 fn sample_shielded_transfer_st() -> StateTransition {
2020 StateTransition::ShieldedTransfer(ShieldedTransferTransition::V0(
2021 ShieldedTransferTransitionV0 {
2022 actions: vec![],
2023 value_balance: 0,
2024 anchor: [0u8; 32],
2025 proof: vec![],
2026 binding_signature: [0u8; 64],
2027 },
2028 ))
2029 }
2030
2031 fn sample_shielded_withdrawal_st() -> StateTransition {
2032 use crate::identity::core_script::CoreScript;
2033 use crate::withdrawal::Pooling;
2034 StateTransition::ShieldedWithdrawal(ShieldedWithdrawalTransition::V0(
2035 ShieldedWithdrawalTransitionV0 {
2036 actions: vec![],
2037 unshielding_amount: 0,
2038 anchor: [0u8; 32],
2039 proof: vec![],
2040 binding_signature: [0u8; 64],
2041 core_fee_per_byte: 1,
2042 pooling: Pooling::Never,
2043 output_script: CoreScript::from_bytes(vec![]),
2044 },
2045 ))
2046 }
2047
2048 #[test]
2051 fn test_name_for_newly_covered_variants() {
2052 assert_eq!(
2053 sample_data_contract_create_st().name(),
2054 "DataContractCreate"
2055 );
2056 assert_eq!(
2057 sample_data_contract_update_st().name(),
2058 "DataContractUpdate"
2059 );
2060 assert_eq!(sample_identity_create_st().name(), "IdentityCreate");
2061 assert_eq!(sample_identity_top_up_st().name(), "IdentityTopUp");
2062 assert_eq!(sample_identity_update_st().name(), "IdentityUpdate");
2063 assert_eq!(sample_unshield_st().name(), "Unshield");
2064 assert_eq!(sample_shielded_transfer_st().name(), "ShieldedTransfer");
2065 assert_eq!(sample_shielded_withdrawal_st().name(), "ShieldedWithdrawal");
2066
2067 let batch_name = sample_batch_st_with_delete().name();
2070 assert_eq!(batch_name, "DocumentsBatch([Delete])");
2071
2072 let empty_name = sample_batch_st_empty().name();
2074 assert_eq!(empty_name, "DocumentsBatch([])");
2075 }
2076
2077 #[test]
2079 fn test_state_transition_type_for_newly_covered_variants() {
2080 assert_eq!(
2081 sample_data_contract_create_st().state_transition_type(),
2082 StateTransitionType::DataContractCreate
2083 );
2084 assert_eq!(
2085 sample_data_contract_update_st().state_transition_type(),
2086 StateTransitionType::DataContractUpdate
2087 );
2088 assert_eq!(
2089 sample_batch_st_with_delete().state_transition_type(),
2090 StateTransitionType::Batch
2091 );
2092 assert_eq!(
2093 sample_identity_create_st().state_transition_type(),
2094 StateTransitionType::IdentityCreate
2095 );
2096 assert_eq!(
2097 sample_identity_top_up_st().state_transition_type(),
2098 StateTransitionType::IdentityTopUp
2099 );
2100 assert_eq!(
2101 sample_identity_update_st().state_transition_type(),
2102 StateTransitionType::IdentityUpdate
2103 );
2104 assert_eq!(
2105 sample_unshield_st().state_transition_type(),
2106 StateTransitionType::Unshield
2107 );
2108 assert_eq!(
2109 sample_shielded_transfer_st().state_transition_type(),
2110 StateTransitionType::ShieldedTransfer
2111 );
2112 assert_eq!(
2113 sample_shielded_withdrawal_st().state_transition_type(),
2114 StateTransitionType::ShieldedWithdrawal
2115 );
2116 }
2117
2118 #[test]
2123 fn test_active_version_range_contract_and_shielded_branches() {
2124 let contract_v1_range = 9..=LATEST_VERSION;
2127 assert_eq!(
2128 sample_data_contract_create_st().active_version_range(),
2129 contract_v1_range
2130 );
2131 let contract_v1_range = 9..=LATEST_VERSION;
2132 assert_eq!(
2133 sample_data_contract_update_st().active_version_range(),
2134 contract_v1_range
2135 );
2136 assert_eq!(
2138 sample_batch_st_with_delete().active_version_range(),
2139 ALL_VERSIONS
2140 );
2141 assert_eq!(
2143 sample_identity_create_st().active_version_range(),
2144 ALL_VERSIONS
2145 );
2146 assert_eq!(
2147 sample_identity_top_up_st().active_version_range(),
2148 ALL_VERSIONS
2149 );
2150 assert_eq!(
2151 sample_identity_update_st().active_version_range(),
2152 ALL_VERSIONS
2153 );
2154 let shielded_range = 12..=LATEST_VERSION;
2156 assert_eq!(
2157 sample_shielded_transfer_st().active_version_range(),
2158 shielded_range.clone()
2159 );
2160 assert_eq!(
2161 sample_unshield_st().active_version_range(),
2162 shielded_range.clone()
2163 );
2164 assert_eq!(
2165 sample_shielded_withdrawal_st().active_version_range(),
2166 shielded_range
2167 );
2168 }
2169
2170 #[test]
2173 fn test_is_identity_signed_false_for_identity_create_topup_and_shielded() {
2174 assert!(!sample_identity_create_st().is_identity_signed());
2175 assert!(!sample_identity_top_up_st().is_identity_signed());
2176 assert!(!sample_unshield_st().is_identity_signed());
2177 assert!(!sample_shielded_transfer_st().is_identity_signed());
2178 assert!(!sample_shielded_withdrawal_st().is_identity_signed());
2179 }
2180
2181 #[test]
2185 fn test_signature_accessor_for_other_variants() {
2186 assert_eq!(
2188 sample_data_contract_create_st().signature().unwrap().len(),
2189 65
2190 );
2191 assert_eq!(
2192 sample_data_contract_update_st().signature().unwrap().len(),
2193 65
2194 );
2195 assert_eq!(sample_batch_st_with_delete().signature().unwrap().len(), 65);
2196 assert_eq!(sample_identity_update_st().signature().unwrap().len(), 65);
2197
2198 assert!(sample_unshield_st().signature().is_none());
2200 assert!(sample_shielded_transfer_st().signature().is_none());
2201 assert!(sample_shielded_withdrawal_st().signature().is_none());
2202 }
2203
2204 #[test]
2206 fn test_owner_id_accessor_for_other_variants() {
2207 assert_eq!(
2208 sample_data_contract_create_st().owner_id(),
2209 Some(Identifier::from([7u8; 32]))
2210 );
2211 assert_eq!(
2212 sample_data_contract_update_st().owner_id(),
2213 Some(Identifier::from([7u8; 32]))
2214 );
2215 assert_eq!(
2216 sample_batch_st_with_delete().owner_id(),
2217 Some(Identifier::from([8u8; 32]))
2218 );
2219 assert_eq!(
2220 sample_identity_update_st().owner_id(),
2221 Some(Identifier::from([5u8; 32]))
2222 );
2223 assert!(sample_unshield_st().owner_id().is_none());
2225 assert!(sample_shielded_transfer_st().owner_id().is_none());
2226 assert!(sample_shielded_withdrawal_st().owner_id().is_none());
2227 }
2228
2229 #[test]
2232 fn test_user_fee_increase_for_newly_covered_variants() {
2233 assert_eq!(sample_data_contract_create_st().user_fee_increase(), 5);
2234 assert_eq!(sample_data_contract_update_st().user_fee_increase(), 9);
2235 assert_eq!(sample_batch_st_with_delete().user_fee_increase(), 2);
2236 assert_eq!(sample_identity_update_st().user_fee_increase(), 11);
2237 assert_eq!(sample_shielded_transfer_st().user_fee_increase(), 0);
2239 assert_eq!(sample_shielded_withdrawal_st().user_fee_increase(), 0);
2240 assert_eq!(sample_unshield_st().user_fee_increase(), 0);
2241 }
2242
2243 #[test]
2246 fn test_set_user_fee_increase_for_newly_covered_variants() {
2247 let mut st = sample_data_contract_create_st();
2248 st.set_user_fee_increase(42);
2249 assert_eq!(st.user_fee_increase(), 42);
2250
2251 let mut st = sample_data_contract_update_st();
2252 st.set_user_fee_increase(13);
2253 assert_eq!(st.user_fee_increase(), 13);
2254
2255 let mut st = sample_batch_st_with_delete();
2256 st.set_user_fee_increase(101);
2257 assert_eq!(st.user_fee_increase(), 101);
2258
2259 let mut st = sample_identity_update_st();
2260 st.set_user_fee_increase(77);
2261 assert_eq!(st.user_fee_increase(), 77);
2262
2263 let mut shielded = sample_shielded_transfer_st();
2265 shielded.set_user_fee_increase(99);
2266 assert_eq!(shielded.user_fee_increase(), 0);
2267
2268 let mut withdrawal = sample_shielded_withdrawal_st();
2269 withdrawal.set_user_fee_increase(99);
2270 assert_eq!(withdrawal.user_fee_increase(), 0);
2271
2272 let mut unshield = sample_unshield_st();
2273 unshield.set_user_fee_increase(99);
2274 assert_eq!(unshield.user_fee_increase(), 0);
2275 }
2276
2277 #[test]
2281 fn test_set_signature_false_for_shielded_and_identity_create_topup() {
2282 let mut st = sample_unshield_st();
2284 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2285 let mut st = sample_shielded_transfer_st();
2286 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2287 let mut st = sample_shielded_withdrawal_st();
2288 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2289 }
2290
2291 #[test]
2292 fn test_set_signature_true_for_newly_covered_variants() {
2293 let mut st = sample_data_contract_create_st();
2294 assert!(st.set_signature(BinaryData::new(vec![0x11; 65])));
2295 assert_eq!(st.signature().unwrap().as_slice(), &[0x11; 65]);
2296
2297 let mut st = sample_data_contract_update_st();
2298 assert!(st.set_signature(BinaryData::new(vec![0x22; 65])));
2299 assert_eq!(st.signature().unwrap().as_slice(), &[0x22; 65]);
2300
2301 let mut st = sample_batch_st_with_delete();
2302 assert!(st.set_signature(BinaryData::new(vec![0x33; 65])));
2303 assert_eq!(st.signature().unwrap().as_slice(), &[0x33; 65]);
2304
2305 let mut st = sample_identity_update_st();
2306 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
2307 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
2308 }
2309
2310 #[test]
2313 fn test_signature_public_key_id_returns_none_for_non_signed() {
2314 assert!(sample_identity_create_st()
2317 .signature_public_key_id()
2318 .is_none());
2319 assert!(sample_identity_top_up_st()
2320 .signature_public_key_id()
2321 .is_none());
2322 assert!(sample_unshield_st().signature_public_key_id().is_none());
2323 assert!(sample_shielded_transfer_st()
2324 .signature_public_key_id()
2325 .is_none());
2326 assert!(sample_shielded_withdrawal_st()
2327 .signature_public_key_id()
2328 .is_none());
2329 }
2330
2331 #[test]
2332 fn test_signature_public_key_id_for_signed_variants() {
2333 assert_eq!(
2334 sample_data_contract_create_st().signature_public_key_id(),
2335 Some(2)
2336 );
2337 assert_eq!(
2338 sample_data_contract_update_st().signature_public_key_id(),
2339 Some(6)
2340 );
2341 assert_eq!(
2342 sample_batch_st_with_delete().signature_public_key_id(),
2343 Some(7)
2344 );
2345 assert_eq!(
2346 sample_identity_update_st().signature_public_key_id(),
2347 Some(33)
2348 );
2349 }
2350
2351 #[test]
2354 fn test_set_signature_public_key_id_noop_for_non_signed() {
2355 let mut st = sample_identity_create_st();
2358 st.set_signature_public_key_id(100);
2359 assert_eq!(st.signature_public_key_id(), None);
2360
2361 let mut st = sample_identity_top_up_st();
2362 st.set_signature_public_key_id(100);
2363 assert_eq!(st.signature_public_key_id(), None);
2364
2365 let mut st = sample_unshield_st();
2366 st.set_signature_public_key_id(100);
2367 assert_eq!(st.signature_public_key_id(), None);
2368 }
2369
2370 #[test]
2371 fn test_set_signature_public_key_id_updates_for_signed_variants() {
2372 let mut st = sample_data_contract_create_st();
2373 st.set_signature_public_key_id(42);
2374 assert_eq!(st.signature_public_key_id(), Some(42));
2375
2376 let mut st = sample_batch_st_with_delete();
2377 st.set_signature_public_key_id(43);
2378 assert_eq!(st.signature_public_key_id(), Some(43));
2379
2380 let mut st = sample_identity_update_st();
2381 st.set_signature_public_key_id(44);
2382 assert_eq!(st.signature_public_key_id(), Some(44));
2383 }
2384
2385 #[test]
2388 fn test_required_number_of_private_keys_various_variants() {
2389 assert_eq!(
2390 sample_data_contract_create_st().required_number_of_private_keys(),
2391 1
2392 );
2393 assert_eq!(
2394 sample_data_contract_update_st().required_number_of_private_keys(),
2395 1
2396 );
2397 assert_eq!(
2398 sample_batch_st_with_delete().required_number_of_private_keys(),
2399 1
2400 );
2401 assert_eq!(
2402 sample_identity_update_st().required_number_of_private_keys(),
2403 1
2404 );
2405 assert_eq!(
2406 sample_identity_create_st().required_number_of_private_keys(),
2407 1
2408 );
2409 assert_eq!(
2411 sample_shielded_transfer_st().required_number_of_private_keys(),
2412 0
2413 );
2414 assert_eq!(
2415 sample_shielded_withdrawal_st().required_number_of_private_keys(),
2416 0
2417 );
2418 assert_eq!(sample_unshield_st().required_number_of_private_keys(), 0);
2419 }
2420
2421 #[test]
2424 fn test_inputs_none_for_many_variants() {
2425 assert!(sample_data_contract_create_st().inputs().is_none());
2426 assert!(sample_data_contract_update_st().inputs().is_none());
2427 assert!(sample_batch_st_with_delete().inputs().is_none());
2428 assert!(sample_identity_create_st().inputs().is_none());
2429 assert!(sample_identity_top_up_st().inputs().is_none());
2430 assert!(sample_identity_update_st().inputs().is_none());
2431 assert!(sample_unshield_st().inputs().is_none());
2433 assert!(sample_shielded_transfer_st().inputs().is_none());
2434 assert!(sample_shielded_withdrawal_st().inputs().is_none());
2435 }
2436
2437 #[test]
2442 fn test_optional_asset_lock_proof_returns_none_for_wildcard_arms() {
2443 assert!(sample_data_contract_create_st()
2444 .optional_asset_lock_proof()
2445 .is_none());
2446 assert!(sample_data_contract_update_st()
2447 .optional_asset_lock_proof()
2448 .is_none());
2449 assert!(sample_batch_st_with_delete()
2450 .optional_asset_lock_proof()
2451 .is_none());
2452 assert!(sample_identity_update_st()
2453 .optional_asset_lock_proof()
2454 .is_none());
2455 assert!(sample_unshield_st().optional_asset_lock_proof().is_none());
2456 assert!(sample_shielded_transfer_st()
2457 .optional_asset_lock_proof()
2458 .is_none());
2459 assert!(sample_shielded_withdrawal_st()
2460 .optional_asset_lock_proof()
2461 .is_none());
2462 }
2463
2464 #[test]
2468 fn test_required_asset_lock_balance_errors_for_other_non_asset_lock_variants() {
2469 let platform_version = PlatformVersion::latest();
2470
2471 let cases: Vec<(&str, StateTransition)> = vec![
2472 ("DataContractCreate", sample_data_contract_create_st()),
2473 ("DataContractUpdate", sample_data_contract_update_st()),
2474 ("Batch", sample_batch_st_with_delete()),
2475 ("IdentityUpdate", sample_identity_update_st()),
2476 ("MasternodeVote", sample_masternode_vote_st()),
2477 ("Unshield", sample_unshield_st()),
2478 ("ShieldedTransfer", sample_shielded_transfer_st()),
2479 ("ShieldedWithdrawal", sample_shielded_withdrawal_st()),
2480 ];
2481
2482 for (label, st) in cases {
2483 let err = st
2484 .required_asset_lock_balance_for_processing_start(platform_version)
2485 .expect_err(&format!("expected error for {label}"));
2486 match err {
2487 ProtocolError::CorruptedCodeExecution(msg) => {
2488 assert!(
2489 msg.contains("is not an asset lock transaction"),
2490 "unexpected error for {label}: {msg}"
2491 );
2492 }
2493 other => panic!("expected CorruptedCodeExecution for {label}, got {other:?}"),
2494 }
2495 }
2496 }
2497
2498 #[test]
2504 fn test_unique_identifiers_non_empty_for_other_variants() {
2505 for st in [
2506 sample_data_contract_create_st(),
2507 sample_data_contract_update_st(),
2508 sample_batch_st_with_delete(),
2509 sample_identity_create_st(),
2510 sample_identity_top_up_st(),
2511 sample_identity_update_st(),
2512 ] {
2513 let ids = st.unique_identifiers();
2514 assert!(!ids.is_empty(), "unique_identifiers should not be empty");
2515 }
2516 }
2517
2518 #[test]
2522 fn test_security_level_requirement_returns_none_for_non_signed_variants() {
2523 let purpose = Purpose::AUTHENTICATION;
2524 assert!(sample_identity_create_st()
2525 .security_level_requirement(purpose)
2526 .is_none());
2527 assert!(sample_identity_top_up_st()
2528 .security_level_requirement(purpose)
2529 .is_none());
2530 assert!(sample_unshield_st()
2531 .security_level_requirement(purpose)
2532 .is_none());
2533 assert!(sample_shielded_transfer_st()
2534 .security_level_requirement(purpose)
2535 .is_none());
2536 assert!(sample_shielded_withdrawal_st()
2537 .security_level_requirement(purpose)
2538 .is_none());
2539 }
2540
2541 #[test]
2542 fn test_purpose_requirement_returns_none_for_non_signed_variants() {
2543 assert!(sample_identity_create_st().purpose_requirement().is_none());
2544 assert!(sample_identity_top_up_st().purpose_requirement().is_none());
2545 assert!(sample_unshield_st().purpose_requirement().is_none());
2546 assert!(sample_shielded_transfer_st()
2547 .purpose_requirement()
2548 .is_none());
2549 assert!(sample_shielded_withdrawal_st()
2550 .purpose_requirement()
2551 .is_none());
2552 }
2553
2554 #[test]
2556 fn test_from_outer_data_contract_create_into_state_transition() {
2557 let outer: DataContractCreateTransition =
2558 DataContractCreateTransition::V0(DataContractCreateTransitionV0 {
2559 data_contract: sample_data_contract_in_serialization_format(),
2560 identity_nonce: 1,
2561 user_fee_increase: 0,
2562 signature_public_key_id: 0,
2563 signature: Default::default(),
2564 });
2565 let st: StateTransition = outer.into();
2566 assert!(matches!(st, StateTransition::DataContractCreate(_)));
2567 }
2568
2569 #[test]
2570 fn test_from_outer_data_contract_update_into_state_transition() {
2571 let outer: DataContractUpdateTransition =
2572 DataContractUpdateTransition::V0(DataContractUpdateTransitionV0 {
2573 identity_contract_nonce: 2,
2574 data_contract: sample_data_contract_in_serialization_format(),
2575 user_fee_increase: 0,
2576 signature_public_key_id: 0,
2577 signature: Default::default(),
2578 });
2579 let st: StateTransition = outer.into();
2580 assert!(matches!(st, StateTransition::DataContractUpdate(_)));
2581 }
2582
2583 #[test]
2584 fn test_from_outer_batch_into_state_transition() {
2585 let outer: BatchTransition = BatchTransition::V0(BatchTransitionV0::default());
2586 let st: StateTransition = outer.into();
2587 assert!(matches!(st, StateTransition::Batch(_)));
2588 }
2589
2590 #[test]
2591 fn test_from_outer_identity_create_into_state_transition() {
2592 let outer: IdentityCreateTransition =
2593 IdentityCreateTransition::V0(IdentityCreateTransitionV0::default());
2594 let st: StateTransition = outer.into();
2595 assert!(matches!(st, StateTransition::IdentityCreate(_)));
2596 }
2597
2598 #[test]
2599 fn test_from_outer_identity_update_into_state_transition() {
2600 let outer: IdentityUpdateTransition =
2601 IdentityUpdateTransition::V0(IdentityUpdateTransitionV0::default());
2602 let st: StateTransition = outer.into();
2603 assert!(matches!(st, StateTransition::IdentityUpdate(_)));
2604 }
2605
2606 #[test]
2609 fn test_transaction_id_and_clone_for_identity_update() {
2610 let st = sample_identity_update_st();
2611 let id_a = st.transaction_id().expect("hash should succeed");
2612 let cloned = st.clone();
2613 let id_b = cloned.transaction_id().expect("hash should succeed");
2614 assert_eq!(id_a, id_b);
2615 assert_eq!(id_a.len(), 32);
2616 }
2617
2618 #[test]
2619 fn test_transaction_id_and_clone_for_data_contract_create() {
2620 let st = sample_data_contract_create_st();
2621 let id_a = st.transaction_id().expect("hash should succeed");
2622 let cloned = st.clone();
2623 let id_b = cloned.transaction_id().expect("hash should succeed");
2624 assert_eq!(id_a, id_b);
2625 assert_eq!(id_a.len(), 32);
2626 }
2627
2628 #[test]
2630 fn test_serialize_roundtrip_identity_update() {
2631 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2632 let original = sample_identity_update_st();
2633 let bytes =
2634 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2635 let restored =
2636 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2637 assert_eq!(original, restored);
2638 }
2639
2640 #[test]
2641 fn test_serialize_roundtrip_data_contract_update() {
2642 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2643 let original = sample_data_contract_update_st();
2644 let bytes =
2645 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2646 let restored =
2647 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2648 assert_eq!(original, restored);
2649 }
2650
2651 #[test]
2652 fn test_serialize_roundtrip_batch_empty() {
2653 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2654 let original = sample_batch_st_empty();
2655 let bytes =
2656 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2657 let restored =
2658 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2659 assert_eq!(original, restored);
2660 }
2661
2662 #[cfg(all(feature = "state-transitions", feature = "validation"))]
2669 #[test]
2670 fn test_deserialize_from_bytes_in_version_returns_not_active_error() {
2671 use crate::serialization::PlatformSerializable;
2672
2673 let original = sample_shielded_transfer_st();
2675 let bytes =
2676 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2677
2678 let low_version = PlatformVersion::get(1).expect("platform version 1 exists");
2682 assert!(
2683 low_version.protocol_version < 12,
2684 "expected sub-12 version for this test, got {}",
2685 low_version.protocol_version
2686 );
2687
2688 let err = StateTransition::deserialize_from_bytes_in_version(&bytes, low_version)
2689 .expect_err("expected StateTransitionIsNotActiveError for sub-12 protocol");
2690 match err {
2691 ProtocolError::StateTransitionError(
2692 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
2693 state_transition_type,
2694 active_version_range,
2695 current_protocol_version,
2696 },
2697 ) => {
2698 assert_eq!(state_transition_type, "ShieldedTransfer");
2699 assert_eq!(current_protocol_version, low_version.protocol_version);
2700 assert!(active_version_range.start() >= &12);
2701 }
2702 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
2703 }
2704 }
2705
2706 use crate::state_transition::identity_credit_transfer_to_addresses_transition::v0::IdentityCreditTransferToAddressesTransitionV0;
2719 use crate::state_transition::identity_credit_transfer_to_addresses_transition::IdentityCreditTransferToAddressesTransition;
2720 use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1;
2721 use crate::withdrawal::Pooling as WithdrawalPooling;
2722
2723 fn sample_withdrawal_v1_st() -> StateTransition {
2724 let v1 = IdentityCreditWithdrawalTransitionV1 {
2725 identity_id: Identifier::from([12u8; 32]),
2726 amount: 777,
2727 core_fee_per_byte: 2,
2728 pooling: WithdrawalPooling::Standard,
2729 output_script: None,
2730 nonce: 9,
2731 user_fee_increase: 4,
2732 signature_public_key_id: 21,
2733 signature: BinaryData::new(vec![0x12; 65]),
2734 };
2735 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(v1))
2736 }
2737
2738 fn sample_credit_transfer_to_addresses_st() -> StateTransition {
2739 let v0 = IdentityCreditTransferToAddressesTransitionV0 {
2740 identity_id: Identifier::from([13u8; 32]),
2741 ..Default::default()
2742 };
2743 StateTransition::IdentityCreditTransferToAddresses(
2744 IdentityCreditTransferToAddressesTransition::V0(v0),
2745 )
2746 }
2747
2748 fn sample_address_credit_withdrawal_st() -> StateTransition {
2749 use crate::state_transition::address_credit_withdrawal_transition::v0::AddressCreditWithdrawalTransitionV0;
2750 StateTransition::AddressCreditWithdrawal(AddressCreditWithdrawalTransition::V0(
2751 AddressCreditWithdrawalTransitionV0::default(),
2752 ))
2753 }
2754
2755 fn sample_shield_from_asset_lock_st() -> StateTransition {
2756 use crate::state_transition::shield_from_asset_lock_transition::v0::ShieldFromAssetLockTransitionV0;
2757 StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V0(
2758 ShieldFromAssetLockTransitionV0 {
2759 asset_lock_proof: Default::default(),
2760 actions: vec![],
2761 value_balance: 100,
2762 anchor: [0u8; 32],
2763 proof: vec![],
2764 binding_signature: [0u8; 64],
2765 signature: BinaryData::new(vec![0x55; 65]),
2766 },
2767 ))
2768 }
2769
2770 #[test]
2773 fn test_withdrawal_v1_name_and_type() {
2774 let st = sample_withdrawal_v1_st();
2775 assert_eq!(st.name(), "IdentityCreditWithdrawal");
2776 assert_eq!(
2777 st.state_transition_type(),
2778 StateTransitionType::IdentityCreditWithdrawal
2779 );
2780 }
2781
2782 #[test]
2783 fn test_withdrawal_v1_is_identity_signed_true() {
2784 assert!(sample_withdrawal_v1_st().is_identity_signed());
2785 }
2786
2787 #[test]
2788 fn test_withdrawal_v1_signature_and_owner_and_key_id() {
2789 let st = sample_withdrawal_v1_st();
2790 let sig = st.signature().expect("V1 withdrawal has a signature");
2792 assert_eq!(sig.as_slice(), &[0x12; 65]);
2793 assert_eq!(st.owner_id(), Some(Identifier::from([12u8; 32])));
2795 assert_eq!(st.signature_public_key_id(), Some(21));
2796 assert_eq!(st.user_fee_increase(), 4);
2797 }
2798
2799 #[test]
2800 fn test_withdrawal_v1_set_signature_and_fee_and_key_id() {
2801 let mut st = sample_withdrawal_v1_st();
2802 assert!(st.set_signature(BinaryData::new(vec![0x99; 65])));
2803 assert_eq!(st.signature().unwrap().as_slice(), &[0x99; 65]);
2804
2805 st.set_user_fee_increase(33);
2806 assert_eq!(st.user_fee_increase(), 33);
2807
2808 st.set_signature_public_key_id(64);
2809 assert_eq!(st.signature_public_key_id(), Some(64));
2810 }
2811
2812 #[test]
2813 fn test_withdrawal_v1_serialize_roundtrip_via_state_transition() {
2814 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2815 let original = sample_withdrawal_v1_st();
2816 let bytes = PlatformSerializable::serialize_to_bytes(&original).expect("serialize ok");
2817 let restored = StateTransition::deserialize_from_bytes(&bytes).expect("deserialize ok");
2818 assert_eq!(original, restored);
2819 match restored {
2822 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(
2823 _,
2824 )) => {}
2825 other => panic!("expected V1 inner variant, got: {:?}", other),
2826 }
2827 }
2828
2829 #[test]
2830 fn test_withdrawal_v1_transaction_id_differs_from_v0() {
2831 let v0 = sample_withdrawal_st();
2833 let v1 = sample_withdrawal_v1_st();
2834 let id_v0 = v0.transaction_id().expect("v0 hash");
2835 let id_v1 = v1.transaction_id().expect("v1 hash");
2836 assert_ne!(id_v0, id_v1);
2837 }
2838
2839 #[test]
2840 fn test_withdrawal_v1_required_asset_lock_balance_errors() {
2841 let err = sample_withdrawal_v1_st()
2842 .required_asset_lock_balance_for_processing_start(PlatformVersion::latest())
2843 .expect_err("withdrawal is not an asset lock ST");
2844 matches!(err, ProtocolError::CorruptedCodeExecution(_));
2845 }
2846
2847 #[test]
2850 fn test_credit_transfer_to_addresses_name_and_type() {
2851 let st = sample_credit_transfer_to_addresses_st();
2852 assert_eq!(st.name(), "IdentityCreditTransferToAddresses");
2853 assert_eq!(
2854 st.state_transition_type(),
2855 StateTransitionType::IdentityCreditTransferToAddresses
2856 );
2857 }
2858
2859 #[test]
2860 fn test_credit_transfer_to_addresses_signature_some_and_owner_some() {
2861 let st = sample_credit_transfer_to_addresses_st();
2862 assert!(st.signature().is_some(), "has a signature field");
2863 assert_eq!(st.owner_id(), Some(Identifier::from([13u8; 32])));
2864 }
2865
2866 #[test]
2867 fn test_credit_transfer_to_addresses_is_identity_signed_true() {
2868 assert!(sample_credit_transfer_to_addresses_st().is_identity_signed());
2870 }
2871
2872 #[test]
2873 fn test_credit_transfer_to_addresses_inputs_none_active_range_11_latest() {
2874 let st = sample_credit_transfer_to_addresses_st();
2875 assert!(st.inputs().is_none());
2876 let range = st.active_version_range();
2878 assert_eq!(*range.start(), 11);
2879 assert_eq!(*range.end(), LATEST_VERSION);
2880 }
2881
2882 #[test]
2883 fn test_credit_transfer_to_addresses_set_signature_returns_true() {
2884 let mut st = sample_credit_transfer_to_addresses_st();
2885 let ok = st.set_signature(BinaryData::new(vec![0x77; 65]));
2886 assert!(ok);
2887 assert_eq!(st.signature().unwrap().as_slice(), &[0x77; 65]);
2888 }
2889
2890 #[test]
2891 fn test_credit_transfer_to_addresses_from_outer_enum() {
2892 let outer = IdentityCreditTransferToAddressesTransition::V0(
2893 IdentityCreditTransferToAddressesTransitionV0::default(),
2894 );
2895 let st: StateTransition = outer.into();
2896 assert!(matches!(
2897 st,
2898 StateTransition::IdentityCreditTransferToAddresses(_)
2899 ));
2900 }
2901
2902 #[test]
2903 fn test_credit_transfer_to_addresses_user_fee_increase_setter() {
2904 let mut st = sample_credit_transfer_to_addresses_st();
2905 st.set_user_fee_increase(55);
2906 assert_eq!(st.user_fee_increase(), 55);
2907 }
2908
2909 #[test]
2912 fn test_address_credit_withdrawal_name_type_and_accessors() {
2913 let st = sample_address_credit_withdrawal_st();
2914 assert_eq!(st.name(), "AddressCreditWithdrawal");
2915 assert_eq!(
2916 st.state_transition_type(),
2917 StateTransitionType::AddressCreditWithdrawal
2918 );
2919 assert!(st.signature().is_none());
2921 assert!(st.owner_id().is_none());
2923 assert!(st.inputs().is_some());
2925 }
2926
2927 #[test]
2928 fn test_address_credit_withdrawal_set_signature_returns_false() {
2929 let mut st = sample_address_credit_withdrawal_st();
2930 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2931 }
2932
2933 #[test]
2934 fn test_address_credit_withdrawal_is_identity_signed_true() {
2935 assert!(sample_address_credit_withdrawal_st().is_identity_signed());
2938 }
2939
2940 #[test]
2941 fn test_address_credit_withdrawal_active_range_is_11_latest() {
2942 let range = sample_address_credit_withdrawal_st().active_version_range();
2943 assert_eq!(*range.start(), 11);
2944 assert_eq!(*range.end(), LATEST_VERSION);
2945 }
2946
2947 #[test]
2950 fn test_shield_from_asset_lock_name_type_and_accessors() {
2951 let st = sample_shield_from_asset_lock_st();
2952 assert_eq!(st.name(), "ShieldFromAssetLock");
2953 assert_eq!(
2954 st.state_transition_type(),
2955 StateTransitionType::ShieldFromAssetLock
2956 );
2957 let sig = st
2959 .signature()
2960 .expect("shield-from-asset-lock has signature");
2961 assert_eq!(sig.as_slice(), &[0x55; 65]);
2962 assert!(st.owner_id().is_none());
2964 }
2965
2966 #[test]
2967 fn test_shield_from_asset_lock_is_not_identity_signed() {
2968 assert!(!sample_shield_from_asset_lock_st().is_identity_signed());
2969 }
2970
2971 #[test]
2972 fn test_shield_from_asset_lock_optional_asset_lock_proof_some() {
2973 let st = sample_shield_from_asset_lock_st();
2977 assert!(st.optional_asset_lock_proof().is_some());
2978 }
2979
2980 #[test]
2981 fn test_shield_from_asset_lock_user_fee_increase_is_zero_and_setter_noop() {
2982 let mut st = sample_shield_from_asset_lock_st();
2983 assert_eq!(st.user_fee_increase(), 0);
2984 st.set_user_fee_increase(123);
2985 assert_eq!(st.user_fee_increase(), 0);
2987 }
2988
2989 #[test]
2990 fn test_shield_from_asset_lock_set_signature_returns_true() {
2991 let mut st = sample_shield_from_asset_lock_st();
2992 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
2993 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
2994 }
2995
2996 #[test]
2997 fn test_shield_from_asset_lock_required_asset_lock_balance_succeeds() {
2998 let st = sample_shield_from_asset_lock_st();
3001 let result = st.required_asset_lock_balance_for_processing_start(PlatformVersion::latest());
3002 assert!(
3003 result.is_ok(),
3004 "ShieldFromAssetLock should return Ok, got {:?}",
3005 result
3006 );
3007 }
3008
3009 #[test]
3010 fn test_shield_from_asset_lock_active_range_12_latest() {
3011 let range = sample_shield_from_asset_lock_st().active_version_range();
3012 assert_eq!(*range.start(), 12);
3013 assert_eq!(*range.end(), LATEST_VERSION);
3014 }
3015
3016 #[test]
3020 fn test_batch_with_token_transfer_name_contains_token_transfer() {
3021 use crate::state_transition::batch_transition::batched_transition::token_transition::TokenTransition as TT;
3022 use crate::state_transition::batch_transition::batched_transition::BatchedTransition;
3023 use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0;
3024 use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition;
3025 use crate::state_transition::batch_transition::token_transfer_transition::v0::TokenTransferTransitionV0;
3026 use crate::state_transition::batch_transition::token_transfer_transition::TokenTransferTransition;
3027 use crate::state_transition::batch_transition::BatchTransitionV1;
3028
3029 let base = TokenBaseTransition::V0(TokenBaseTransitionV0 {
3030 identity_contract_nonce: 1,
3031 token_contract_position: 0,
3032 data_contract_id: Identifier::from([1u8; 32]),
3033 token_id: Identifier::from([2u8; 32]),
3034 using_group_info: None,
3035 });
3036 let token_transfer = TokenTransferTransition::V0(TokenTransferTransitionV0 {
3037 base,
3038 amount: 100,
3039 recipient_id: Identifier::from([3u8; 32]),
3040 public_note: None,
3041 shared_encrypted_note: None,
3042 private_encrypted_note: None,
3043 });
3044
3045 let batch = BatchTransition::V1(BatchTransitionV1 {
3047 owner_id: Identifier::from([9u8; 32]),
3048 transitions: vec![BatchedTransition::Token(TT::Transfer(token_transfer))],
3049 user_fee_increase: 0,
3050 signature_public_key_id: 0,
3051 signature: BinaryData::new(vec![0u8; 65]),
3052 });
3053 let st = StateTransition::Batch(batch);
3054
3055 assert_eq!(st.name(), "DocumentsBatch([TokenTransfer])");
3056 }
3057
3058 #[test]
3065 fn test_transaction_id_length_32_for_new_variants() {
3066 for st in [
3067 sample_withdrawal_v1_st(),
3068 sample_credit_transfer_to_addresses_st(),
3069 sample_shield_from_asset_lock_st(),
3070 ] {
3071 let id = st.transaction_id().expect("hash");
3072 assert_eq!(id.len(), 32);
3073 }
3074 }
3075
3076 #[test]
3082 fn test_clone_eq_for_new_variants() {
3083 let cases = [
3084 sample_withdrawal_v1_st(),
3085 sample_credit_transfer_to_addresses_st(),
3086 sample_address_credit_withdrawal_st(),
3087 sample_shield_from_asset_lock_st(),
3088 ];
3089 for st in cases {
3090 let cloned = st.clone();
3091 assert_eq!(st, cloned, "clone must be equal for {}", st.name());
3092 }
3093 }
3094
3095 #[test]
3101 fn test_unique_identifiers_for_address_and_shielded_variants() {
3102 for st in [
3107 sample_address_credit_withdrawal_st(),
3108 sample_shield_from_asset_lock_st(),
3109 sample_credit_transfer_to_addresses_st(),
3110 sample_withdrawal_v1_st(),
3111 ] {
3112 let _ids = st.unique_identifiers();
3116 }
3117 assert!(!sample_withdrawal_v1_st().unique_identifiers().is_empty());
3119 assert!(!sample_credit_transfer_to_addresses_st()
3120 .unique_identifiers()
3121 .is_empty());
3122 }
3123}