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_from_shielded_pool_transition::{
116 IdentityCreateFromShieldedPoolTransition, IdentityCreateFromShieldedPoolTransitionSignable,
117};
118use crate::state_transition::identity_create_transition::{
119 IdentityCreateTransition, IdentityCreateTransitionSignable,
120};
121use crate::state_transition::identity_credit_transfer_to_addresses_transition::{
122 IdentityCreditTransferToAddressesTransition,
123 IdentityCreditTransferToAddressesTransitionSignable,
124};
125use crate::state_transition::identity_credit_transfer_transition::{
126 IdentityCreditTransferTransition, IdentityCreditTransferTransitionSignable,
127};
128use crate::state_transition::identity_credit_withdrawal_transition::{
129 IdentityCreditWithdrawalTransition, IdentityCreditWithdrawalTransitionSignable,
130};
131use crate::state_transition::identity_topup_from_addresses_transition::{
132 IdentityTopUpFromAddressesTransition, IdentityTopUpFromAddressesTransitionSignable,
133};
134use crate::state_transition::identity_topup_transition::{
135 IdentityTopUpTransition, IdentityTopUpTransitionSignable,
136};
137use crate::state_transition::identity_update_transition::{
138 IdentityUpdateTransition, IdentityUpdateTransitionSignable,
139};
140use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
141use crate::state_transition::masternode_vote_transition::MasternodeVoteTransitionSignable;
142use crate::state_transition::shield_from_asset_lock_transition::{
143 ShieldFromAssetLockTransition, ShieldFromAssetLockTransitionSignable,
144};
145use crate::state_transition::shield_transition::{ShieldTransition, ShieldTransitionSignable};
146use crate::state_transition::shielded_transfer_transition::{
147 ShieldedTransferTransition, ShieldedTransferTransitionSignable,
148};
149use crate::state_transition::shielded_withdrawal_transition::{
150 ShieldedWithdrawalTransition, ShieldedWithdrawalTransitionSignable,
151};
152#[cfg(feature = "state-transition-signing")]
153use crate::state_transition::state_transitions::document::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0;
154use crate::state_transition::unshield_transition::{
155 UnshieldTransition, UnshieldTransitionSignable,
156};
157use state_transitions::document::batch_transition::batched_transition::token_transition::TokenTransition;
158pub use state_transitions::*;
159
160pub type GetDataContractSecurityLevelRequirementFn =
161 fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>;
162
163macro_rules! call_method {
164 ($state_transition:expr, $method:ident, $args:tt ) => {
165 match $state_transition {
166 StateTransition::DataContractCreate(st) => st.$method($args),
167 StateTransition::DataContractUpdate(st) => st.$method($args),
168 StateTransition::Batch(st) => st.$method($args),
169 StateTransition::IdentityCreate(st) => st.$method($args),
170 StateTransition::IdentityTopUp(st) => st.$method($args),
171 StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
172 StateTransition::IdentityUpdate(st) => st.$method($args),
173 StateTransition::IdentityCreditTransfer(st) => st.$method($args),
174 StateTransition::MasternodeVote(st) => st.$method($args),
175 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
176 StateTransition::IdentityCreateFromAddresses(st) => st.$method($args),
177 StateTransition::IdentityTopUpFromAddresses(st) => st.$method($args),
178 StateTransition::AddressFundsTransfer(st) => st.$method($args),
179 StateTransition::AddressFundingFromAssetLock(st) => st.$method($args),
180 StateTransition::AddressCreditWithdrawal(st) => st.$method($args),
181 StateTransition::Shield(st) => st.$method($args),
182 StateTransition::ShieldedTransfer(st) => st.$method($args),
183 StateTransition::Unshield(st) => st.$method($args),
184 StateTransition::ShieldFromAssetLock(st) => st.$method($args),
185 StateTransition::ShieldedWithdrawal(st) => st.$method($args),
186 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method($args),
187 }
188 };
189 ($state_transition:expr, $method:ident ) => {
190 match $state_transition {
191 StateTransition::DataContractCreate(st) => st.$method(),
192 StateTransition::DataContractUpdate(st) => st.$method(),
193 StateTransition::Batch(st) => st.$method(),
194 StateTransition::IdentityCreate(st) => st.$method(),
195 StateTransition::IdentityTopUp(st) => st.$method(),
196 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
197 StateTransition::IdentityUpdate(st) => st.$method(),
198 StateTransition::IdentityCreditTransfer(st) => st.$method(),
199 StateTransition::MasternodeVote(st) => st.$method(),
200 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
201 StateTransition::IdentityCreateFromAddresses(st) => st.$method(),
202 StateTransition::IdentityTopUpFromAddresses(st) => st.$method(),
203 StateTransition::AddressFundsTransfer(st) => st.$method(),
204 StateTransition::AddressFundingFromAssetLock(st) => st.$method(),
205 StateTransition::AddressCreditWithdrawal(st) => st.$method(),
206 StateTransition::Shield(st) => st.$method(),
207 StateTransition::ShieldedTransfer(st) => st.$method(),
208 StateTransition::Unshield(st) => st.$method(),
209 StateTransition::ShieldFromAssetLock(st) => st.$method(),
210 StateTransition::ShieldedWithdrawal(st) => st.$method(),
211 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method(),
212 }
213 };
214}
215
216macro_rules! call_getter_method_identity_signed {
217 ($state_transition:expr, $method:ident, $args:tt ) => {
218 match $state_transition {
219 StateTransition::DataContractCreate(st) => Some(st.$method($args)),
220 StateTransition::DataContractUpdate(st) => Some(st.$method($args)),
221 StateTransition::Batch(st) => Some(st.$method($args)),
222 StateTransition::IdentityCreate(_) => None,
223 StateTransition::IdentityTopUp(_) => None,
224 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method($args)),
225 StateTransition::IdentityUpdate(st) => Some(st.$method($args)),
226 StateTransition::IdentityCreditTransfer(st) => Some(st.$method($args)),
227 StateTransition::MasternodeVote(st) => Some(st.$method($args)),
228 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method($args)),
229 StateTransition::IdentityCreateFromAddresses(_) => None,
230 StateTransition::IdentityTopUpFromAddresses(_) => None,
231 StateTransition::AddressFundsTransfer(_) => None,
232 StateTransition::AddressFundingFromAssetLock(_) => None,
233 StateTransition::AddressCreditWithdrawal(_) => None,
234 StateTransition::Shield(_) => None,
235 StateTransition::ShieldedTransfer(_) => None,
236 StateTransition::Unshield(_) => None,
237 StateTransition::ShieldFromAssetLock(_) => None,
238 StateTransition::ShieldedWithdrawal(_) => None,
239 StateTransition::IdentityCreateFromShieldedPool(_) => None,
240 }
241 };
242 ($state_transition:expr, $method:ident ) => {
243 match $state_transition {
244 StateTransition::DataContractCreate(st) => Some(st.$method()),
245 StateTransition::DataContractUpdate(st) => Some(st.$method()),
246 StateTransition::Batch(st) => Some(st.$method()),
247 StateTransition::IdentityCreate(_) => None,
248 StateTransition::IdentityTopUp(_) => None,
249 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method()),
250 StateTransition::IdentityUpdate(st) => Some(st.$method()),
251 StateTransition::IdentityCreditTransfer(st) => Some(st.$method()),
252 StateTransition::MasternodeVote(st) => Some(st.$method()),
253 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method()),
254 StateTransition::IdentityCreateFromAddresses(_) => None,
255 StateTransition::IdentityTopUpFromAddresses(_) => None,
256 StateTransition::AddressFundsTransfer(_) => None,
257 StateTransition::AddressFundingFromAssetLock(_) => None,
258 StateTransition::AddressCreditWithdrawal(_) => None,
259 StateTransition::Shield(_) => None,
260 StateTransition::ShieldedTransfer(_) => None,
261 StateTransition::Unshield(_) => None,
262 StateTransition::ShieldFromAssetLock(_) => None,
263 StateTransition::ShieldedWithdrawal(_) => None,
264 StateTransition::IdentityCreateFromShieldedPool(_) => None,
265 }
266 };
267}
268
269macro_rules! call_method_identity_signed {
270 ($state_transition:expr, $method:ident, $args:tt ) => {
271 match $state_transition {
272 StateTransition::DataContractCreate(st) => st.$method($args),
273 StateTransition::DataContractUpdate(st) => st.$method($args),
274 StateTransition::Batch(st) => st.$method($args),
275 StateTransition::IdentityCreate(_st) => {}
276 StateTransition::IdentityTopUp(_st) => {}
277 StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
278 StateTransition::IdentityUpdate(st) => st.$method($args),
279 StateTransition::IdentityCreditTransfer(st) => st.$method($args),
280 StateTransition::MasternodeVote(st) => st.$method($args),
281 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
282 StateTransition::IdentityCreateFromAddresses(_) => {}
283 StateTransition::IdentityTopUpFromAddresses(_) => {}
284 StateTransition::AddressFundsTransfer(_) => {}
285 StateTransition::AddressFundingFromAssetLock(_) => {}
286 StateTransition::AddressCreditWithdrawal(_) => {}
287 StateTransition::Shield(_) => {}
288 StateTransition::ShieldedTransfer(_) => {}
289 StateTransition::Unshield(_) => {}
290 StateTransition::ShieldFromAssetLock(_) => {}
291 StateTransition::ShieldedWithdrawal(_) => {}
292 StateTransition::IdentityCreateFromShieldedPool(_) => {}
293 }
294 };
295 ($state_transition:expr, $method:ident ) => {
296 match $state_transition {
297 StateTransition::DataContractCreate(st) => st.$method(),
298 StateTransition::DataContractUpdate(st) => st.$method(),
299 StateTransition::Batch(st) => st.$method(),
300 StateTransition::IdentityCreate(st) => {}
301 StateTransition::IdentityTopUp(st) => {}
302 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
303 StateTransition::IdentityUpdate(st) => st.$method(),
304 StateTransition::IdentityCreditTransfer(st) => st.$method(),
305 StateTransition::MasternodeVote(st) => st.$method(),
306 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
307 StateTransition::IdentityCreateFromAddresses(_) => {}
308 StateTransition::IdentityTopUpFromAddresses(_) => {}
309 StateTransition::AddressFundsTransfer(_) => {}
310 StateTransition::AddressFundingFromAssetLock(_) => {}
311 StateTransition::AddressCreditWithdrawal(_) => {}
312 StateTransition::Shield(_) => {}
313 StateTransition::ShieldedTransfer(_) => {}
314 StateTransition::Unshield(_) => {}
315 StateTransition::ShieldFromAssetLock(_) => {}
316 StateTransition::ShieldedWithdrawal(_) => {}
317 StateTransition::IdentityCreateFromShieldedPool(_) => {}
318 }
319 };
320}
321
322#[cfg(feature = "state-transition-signing")]
323macro_rules! call_errorable_method_identity_signed {
324 ($state_transition:expr, $method:ident, $( $arg:expr ),* ) => {
325 match $state_transition {
326 StateTransition::DataContractCreate(st) => st.$method($( $arg ),*),
327 StateTransition::DataContractUpdate(st) => st.$method($( $arg ),*),
328 StateTransition::Batch(st) => st.$method($( $arg ),*),
329 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
330 "identity create can not be called for identity signing".to_string(),
331 )),
332 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
333 "identity top up can not be called for identity signing".to_string(),
334 )),
335 StateTransition::IdentityCreditWithdrawal(st) => st.$method($( $arg ),*),
336 StateTransition::IdentityUpdate(st) => st.$method($( $arg ),*),
337 StateTransition::IdentityCreditTransfer(st) => st.$method($( $arg ),*),
338 StateTransition::MasternodeVote(st) => st.$method($( $arg ),*),
339 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($( $arg ),*),
340 StateTransition::IdentityCreateFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
341 "identity create from addresses can not be called for identity signing".to_string(),
342 )),
343 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
344 "identity top up from addresses can not be called for identity signing".to_string(),
345 )),
346 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
347 "address funds transfer can not be called for identity signing".to_string(),
348 )),
349 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
350 "address funding from asset lock can not be called for identity signing".to_string(),
351 )),
352 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
353 "address credit withdrawal can not be called for identity signing".to_string(),
354 )),
355 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
356 "shield transition can not be called for identity signing".to_string(),
357 )),
358 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
359 "shielded transfer transition can not be called for identity signing".to_string(),
360 )),
361 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
362 "unshield transition can not be called for identity signing".to_string(),
363 )),
364 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
365 "shield from asset lock transition can not be called for identity signing".to_string(),
366 )),
367 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
368 "shielded withdrawal transition can not be called for identity signing".to_string(),
369 )),
370 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
371 "identity create from shielded pool transition can not be called for identity signing".to_string(),
372 )),
373 }
374 };
375 ($state_transition:expr, $method:ident) => {
376 match $state_transition {
377 StateTransition::DataContractCreate(st) => st.$method(),
378 StateTransition::DataContractUpdate(st) => st.$method(),
379 StateTransition::Batch(st) => st.$method(),
380 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
381 "identity create can not be called for identity signing".to_string(),
382 )),
383 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
384 "identity top up can not be called for identity signing".to_string(),
385 )),
386 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
387 StateTransition::IdentityUpdate(st) => st.$method(),
388 StateTransition::IdentityCreditTransfer(st) => st.$method(),
389 StateTransition::MasternodeVote(st) => st.$method(),
390 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
391 StateTransition::IdentityCreateFromAddresses(st) => Err(ProtocolError::CorruptedCodeExecution(
392 "identity create from addresses can not be called for identity signing".to_string(),
393 )),
394 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
395 "identity top up from addresses can not be called for identity signing".to_string(),
396 )),
397 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
398 "address funds transfer can not be called for identity signing".to_string(),
399 )),
400 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
401 "address funding from asset lock can not be called for identity signing".to_string(),
402 )),
403 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
404 "address credit withdrawal can not be called for identity signing".to_string(),
405 )),
406 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
407 "shield transition can not be called for identity signing".to_string(),
408 )),
409 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
410 "shielded transfer transition can not be called for identity signing".to_string(),
411 )),
412 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
413 "unshield transition can not be called for identity signing".to_string(),
414 )),
415 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
416 "shield from asset lock transition can not be called for identity signing".to_string(),
417 )),
418 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
419 "shielded withdrawal transition can not be called for identity signing".to_string(),
420 )),
421 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
422 "identity create from shielded pool transition can not be called for identity signing".to_string(),
423 )),
424 }
425 };
426}
427
428#[derive(
429 Debug,
430 Clone,
431 Encode,
432 Decode,
433 PlatformSerialize,
434 PlatformDeserialize,
435 PlatformSignable,
436 From,
437 PartialEq,
438)]
439#[cfg_attr(
440 feature = "serde-conversion",
441 derive(Serialize, Deserialize),
442 serde(untagged)
443)]
444#[platform_serialize(unversioned)] #[platform_serialize(limit = 100000)]
446pub enum StateTransition {
447 DataContractCreate(DataContractCreateTransition),
448 DataContractUpdate(DataContractUpdateTransition),
449 Batch(BatchTransition),
450 IdentityCreate(IdentityCreateTransition),
451 IdentityTopUp(IdentityTopUpTransition),
452 IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition),
453 IdentityUpdate(IdentityUpdateTransition),
454 IdentityCreditTransfer(IdentityCreditTransferTransition),
455 MasternodeVote(MasternodeVoteTransition),
456 IdentityCreditTransferToAddresses(IdentityCreditTransferToAddressesTransition),
457 IdentityCreateFromAddresses(IdentityCreateFromAddressesTransition),
458 IdentityTopUpFromAddresses(IdentityTopUpFromAddressesTransition),
459 AddressFundsTransfer(AddressFundsTransferTransition),
460 AddressFundingFromAssetLock(AddressFundingFromAssetLockTransition),
461 AddressCreditWithdrawal(AddressCreditWithdrawalTransition),
462 Shield(ShieldTransition),
463 ShieldedTransfer(ShieldedTransferTransition),
464 Unshield(UnshieldTransition),
465 ShieldFromAssetLock(ShieldFromAssetLockTransition),
466 ShieldedWithdrawal(ShieldedWithdrawalTransition),
467 IdentityCreateFromShieldedPool(IdentityCreateFromShieldedPoolTransition),
468}
469
470impl OptionallyAssetLockProved for StateTransition {
471 fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> {
472 match self {
473 StateTransition::IdentityCreate(st) => st.optional_asset_lock_proof(),
474 StateTransition::IdentityTopUp(st) => st.optional_asset_lock_proof(),
475 StateTransition::ShieldFromAssetLock(st) => st.optional_asset_lock_proof(),
476 _ => None,
477 }
478 }
479}
480
481#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
483pub struct StateTransitionSigningOptions {
484 pub allow_signing_with_any_security_level: bool,
486 pub allow_signing_with_any_purpose: bool,
488}
489
490impl StateTransition {
491 #[allow(unused_variables)]
492 pub fn deserialize_from_bytes_in_version(
493 bytes: &[u8],
494 platform_version: &PlatformVersion,
495 ) -> Result<Self, ProtocolError> {
496 let state_transition = StateTransition::deserialize_from_bytes(bytes)?;
497 #[cfg(all(feature = "state-transitions", feature = "validation"))]
498 {
499 let active_version_range = state_transition.active_version_range();
500
501 if active_version_range.contains(&platform_version.protocol_version)
504 || platform_version.protocol_version > 268435456
505 {
506 Ok(state_transition)
507 } else {
508 Err(ProtocolError::StateTransitionError(
509 StateTransitionIsNotActiveError {
510 state_transition_type: state_transition.name(),
511 active_version_range,
512 current_protocol_version: platform_version.protocol_version,
513 },
514 ))
515 }
516 }
517 #[cfg(not(all(feature = "state-transitions", feature = "validation")))]
518 Ok(state_transition)
519 }
520
521 pub fn active_version_range(&self) -> RangeInclusive<ProtocolVersion> {
522 match self {
523 StateTransition::DataContractCreate(data_contract_create_transition) => {
524 match data_contract_create_transition.data_contract() {
525 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
526 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
527 }
528 }
529 StateTransition::DataContractUpdate(data_contract_update_transition) => {
530 match data_contract_update_transition.data_contract() {
531 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
532 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
533 }
534 }
535 StateTransition::Batch(batch_transition) => match batch_transition {
536 BatchTransition::V0(_) => ALL_VERSIONS,
537 BatchTransition::V1(_) => 9..=LATEST_VERSION,
538 },
539 StateTransition::IdentityCreate(_)
540 | StateTransition::IdentityTopUp(_)
541 | StateTransition::IdentityCreditWithdrawal(_)
542 | StateTransition::IdentityUpdate(_)
543 | StateTransition::IdentityCreditTransfer(_)
544 | StateTransition::MasternodeVote(_) => ALL_VERSIONS,
545 StateTransition::IdentityCreditTransferToAddresses(_)
546 | StateTransition::IdentityCreateFromAddresses(_)
547 | StateTransition::IdentityTopUpFromAddresses(_)
548 | StateTransition::AddressFundsTransfer(_)
549 | StateTransition::AddressFundingFromAssetLock(_)
550 | StateTransition::AddressCreditWithdrawal(_) => 11..=LATEST_VERSION,
551 StateTransition::Shield(_)
552 | StateTransition::ShieldedTransfer(_)
553 | StateTransition::Unshield(_)
554 | StateTransition::ShieldFromAssetLock(_)
555 | StateTransition::ShieldedWithdrawal(_)
556 | StateTransition::IdentityCreateFromShieldedPool(_) => 12..=LATEST_VERSION,
557 }
558 }
559
560 pub fn is_identity_signed(&self) -> bool {
561 !matches!(
562 self,
563 StateTransition::IdentityCreate(_)
564 | StateTransition::IdentityTopUp(_)
565 | StateTransition::Shield(_)
566 | StateTransition::ShieldedTransfer(_)
567 | StateTransition::Unshield(_)
568 | StateTransition::ShieldFromAssetLock(_)
569 | StateTransition::ShieldedWithdrawal(_)
570 | StateTransition::IdentityCreateFromShieldedPool(_)
571 )
572 }
573
574 pub fn required_asset_lock_balance_for_processing_start(
575 &self,
576 platform_version: &PlatformVersion,
577 ) -> Result<Credits, ProtocolError> {
578 match self {
579 StateTransition::IdentityCreate(st) => {
580 st.calculate_min_required_fee(platform_version)
581 }
582 StateTransition::IdentityTopUp(st) => {
583 st.calculate_min_required_fee(platform_version)
584 }
585 StateTransition::AddressFundingFromAssetLock(st) => {
586 st.calculate_min_required_fee(platform_version)
587 }
588 StateTransition::ShieldFromAssetLock(st) => {
589 st.calculate_min_required_fee(platform_version)
590 }
591 st => Err(ProtocolError::CorruptedCodeExecution(format!("{} is not an asset lock transaction, but we are calling required_asset_lock_balance_for_processing_start", st.name()))),
592 }
593 }
594
595 fn hash(&self, skip_signature: bool) -> Result<Vec<u8>, ProtocolError> {
596 if skip_signature {
597 Ok(hash_double_to_vec(self.signable_bytes()?))
598 } else {
599 Ok(hash_double_to_vec(
600 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
601 ))
602 }
603 }
604
605 pub fn name(&self) -> String {
607 match self {
608 Self::DataContractCreate(_) => "DataContractCreate".to_string(),
609 Self::DataContractUpdate(_) => "DataContractUpdate".to_string(),
610 Self::Batch(batch_transition) => {
611 let mut document_transition_types = vec![];
612 for transition in batch_transition.transitions_iter() {
613 let document_transition_name = match transition {
614 BatchedTransitionRef::Document(DocumentTransition::Create(_)) => "Create",
615 BatchedTransitionRef::Document(DocumentTransition::Replace(_)) => "Replace",
616 BatchedTransitionRef::Document(DocumentTransition::Delete(_)) => "Delete",
617 BatchedTransitionRef::Document(DocumentTransition::Transfer(_)) => {
618 "Transfer"
619 }
620 BatchedTransitionRef::Document(DocumentTransition::UpdatePrice(_)) => {
621 "UpdatePrice"
622 }
623 BatchedTransitionRef::Document(DocumentTransition::Purchase(_)) => {
624 "Purchase"
625 }
626 BatchedTransitionRef::Token(TokenTransition::Transfer(_)) => {
627 "TokenTransfer"
628 }
629 BatchedTransitionRef::Token(TokenTransition::Mint(_)) => "TokenMint",
630 BatchedTransitionRef::Token(TokenTransition::Burn(_)) => "TokenBurn",
631 BatchedTransitionRef::Token(TokenTransition::Freeze(_)) => "TokenFreeze",
632 BatchedTransitionRef::Token(TokenTransition::Unfreeze(_)) => {
633 "TokenUnfreeze"
634 }
635 BatchedTransitionRef::Token(TokenTransition::DestroyFrozenFunds(_)) => {
636 "TokenDestroyFrozenFunds"
637 }
638 BatchedTransitionRef::Token(TokenTransition::EmergencyAction(_)) => {
639 "TokenEmergencyAction"
640 }
641 BatchedTransitionRef::Token(TokenTransition::ConfigUpdate(_)) => {
642 "TokenConfigUpdate"
643 }
644 BatchedTransitionRef::Token(TokenTransition::Claim(_)) => "TokenClaim",
645 BatchedTransitionRef::Token(TokenTransition::DirectPurchase(_)) => {
646 "TokenDirectPurchase"
647 }
648 BatchedTransitionRef::Token(
649 TokenTransition::SetPriceForDirectPurchase(_),
650 ) => "SetPriceForDirectPurchase",
651 };
652 document_transition_types.push(document_transition_name);
653 }
654 format!("DocumentsBatch([{}])", document_transition_types.join(", "))
655 }
656 Self::IdentityCreate(_) => "IdentityCreate".to_string(),
657 Self::IdentityTopUp(_) => "IdentityTopUp".to_string(),
658 Self::IdentityCreditWithdrawal(_) => "IdentityCreditWithdrawal".to_string(),
659 Self::IdentityUpdate(_) => "IdentityUpdate".to_string(),
660 Self::IdentityCreditTransfer(_) => "IdentityCreditTransfer".to_string(),
661 Self::MasternodeVote(_) => "MasternodeVote".to_string(),
662 Self::IdentityCreditTransferToAddresses(_) => {
663 "IdentityCreditTransferToAddresses".to_string()
664 }
665 Self::IdentityCreateFromAddresses(_) => "IdentityCreateFromAddresses".to_string(),
666 Self::IdentityTopUpFromAddresses(_) => "IdentityTopUpFromAddresses".to_string(),
667 Self::AddressFundsTransfer(_) => "AddressFundsTransfer".to_string(),
668 Self::AddressFundingFromAssetLock(_) => "AddressFundingFromAssetLock".to_string(),
669 Self::AddressCreditWithdrawal(_) => "AddressCreditWithdrawal".to_string(),
670 Self::Shield(_) => "Shield".to_string(),
671 Self::ShieldedTransfer(_) => "ShieldedTransfer".to_string(),
672 Self::Unshield(_) => "Unshield".to_string(),
673 Self::ShieldFromAssetLock(_) => "ShieldFromAssetLock".to_string(),
674 Self::ShieldedWithdrawal(_) => "ShieldedWithdrawal".to_string(),
675 Self::IdentityCreateFromShieldedPool(_) => "IdentityCreateFromShieldedPool".to_string(),
676 }
677 }
678
679 pub fn signature(&self) -> Option<&BinaryData> {
681 match self {
682 StateTransition::DataContractCreate(st) => Some(st.signature()),
683 StateTransition::DataContractUpdate(st) => Some(st.signature()),
684 StateTransition::Batch(st) => Some(st.signature()),
685 StateTransition::IdentityCreate(st) => Some(st.signature()),
686 StateTransition::IdentityTopUp(st) => Some(st.signature()),
687 StateTransition::IdentityCreditWithdrawal(st) => Some(st.signature()),
688 StateTransition::IdentityUpdate(st) => Some(st.signature()),
689 StateTransition::IdentityCreditTransfer(st) => Some(st.signature()),
690 StateTransition::MasternodeVote(st) => Some(st.signature()),
691 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.signature()),
692 StateTransition::IdentityCreateFromAddresses(_) => None,
693 StateTransition::IdentityTopUpFromAddresses(_) => None,
694 StateTransition::AddressFundsTransfer(_) => None,
695 StateTransition::AddressFundingFromAssetLock(st) => Some(st.signature()),
696 StateTransition::AddressCreditWithdrawal(_) => None,
697 StateTransition::Shield(_) => None,
698 StateTransition::ShieldedTransfer(_) => None,
699 StateTransition::Unshield(_) => None,
700 StateTransition::ShieldFromAssetLock(st) => Some(st.signature()),
701 StateTransition::ShieldedWithdrawal(_) => None,
702 StateTransition::IdentityCreateFromShieldedPool(_) => None,
703 }
704 }
705
706 pub fn required_number_of_private_keys(&self) -> u16 {
708 match self {
709 StateTransition::IdentityCreateFromAddresses(st) => st.inputs().len() as u16,
710 StateTransition::IdentityTopUpFromAddresses(st) => st.inputs().len() as u16,
711 StateTransition::AddressFundsTransfer(st) => st.inputs().len() as u16,
712 StateTransition::AddressCreditWithdrawal(st) => st.inputs().len() as u16,
713 StateTransition::Shield(st) => st.inputs().len() as u16,
714 StateTransition::ShieldedTransfer(_) => 0,
715 StateTransition::Unshield(_) => 0,
716 StateTransition::ShieldFromAssetLock(_) => 0,
717 StateTransition::ShieldedWithdrawal(_) => 0,
718 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
719 _ => 1,
720 }
721 }
722
723 pub fn user_fee_increase(&self) -> UserFeeIncrease {
725 match self {
726 StateTransition::DataContractCreate(st) => st.user_fee_increase(),
727 StateTransition::DataContractUpdate(st) => st.user_fee_increase(),
728 StateTransition::Batch(st) => st.user_fee_increase(),
729 StateTransition::IdentityCreate(st) => st.user_fee_increase(),
730 StateTransition::IdentityTopUp(st) => st.user_fee_increase(),
731 StateTransition::IdentityCreditWithdrawal(st) => st.user_fee_increase(),
732 StateTransition::IdentityUpdate(st) => st.user_fee_increase(),
733 StateTransition::IdentityCreditTransfer(st) => st.user_fee_increase(),
734 StateTransition::IdentityCreditTransferToAddresses(st) => st.user_fee_increase(),
735 StateTransition::IdentityCreateFromAddresses(st) => st.user_fee_increase(),
736 StateTransition::IdentityTopUpFromAddresses(st) => st.user_fee_increase(),
737 StateTransition::AddressFundsTransfer(st) => st.user_fee_increase(),
738 StateTransition::AddressFundingFromAssetLock(st) => st.user_fee_increase(),
739 StateTransition::AddressCreditWithdrawal(st) => st.user_fee_increase(),
740 StateTransition::Shield(st) => st.user_fee_increase(),
741 StateTransition::ShieldFromAssetLock(_) => 0,
743 StateTransition::MasternodeVote(_) => 0,
744 StateTransition::ShieldedTransfer(_) => 0,
745 StateTransition::Unshield(_) => 0,
746 StateTransition::ShieldedWithdrawal(_) => 0,
747 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
748 }
749 }
750
751 fn calculate_estimated_fee(
764 &self,
765 platform_version: &PlatformVersion,
766 ) -> Result<Credits, ProtocolError> {
767 call_method!(self, calculate_min_required_fee, platform_version)
768 }
769
770 pub fn transaction_id(&self) -> Result<[u8; 32], ProtocolError> {
772 Ok(hash_single(
773 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
774 ))
775 }
776
777 pub fn signature_public_key_id(&self) -> Option<KeyID> {
779 call_getter_method_identity_signed!(self, signature_public_key_id)
780 }
781
782 pub fn security_level_requirement(&self, purpose: Purpose) -> Option<Vec<SecurityLevel>> {
784 call_getter_method_identity_signed!(self, security_level_requirement, purpose)
785 }
786
787 pub fn purpose_requirement(&self) -> Option<Vec<Purpose>> {
789 call_getter_method_identity_signed!(self, purpose_requirement)
790 }
791
792 pub fn owner_id(&self) -> Option<Identifier> {
794 match self {
795 StateTransition::DataContractCreate(st) => Some(st.owner_id()),
796 StateTransition::DataContractUpdate(st) => Some(st.owner_id()),
797 StateTransition::Batch(st) => Some(st.owner_id()),
798 StateTransition::IdentityCreate(st) => Some(st.owner_id()),
799 StateTransition::IdentityTopUp(st) => Some(st.owner_id()),
800 StateTransition::IdentityCreditWithdrawal(st) => Some(st.owner_id()),
801 StateTransition::IdentityUpdate(st) => Some(st.owner_id()),
802 StateTransition::IdentityCreditTransfer(st) => Some(st.owner_id()),
803 StateTransition::MasternodeVote(st) => Some(st.owner_id()),
804 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.owner_id()),
805 StateTransition::IdentityCreateFromAddresses(_) => None,
806 StateTransition::IdentityTopUpFromAddresses(_) => None,
807 StateTransition::AddressFundsTransfer(_) => None,
808 StateTransition::AddressFundingFromAssetLock(_) => None,
809 StateTransition::AddressCreditWithdrawal(_) => None,
810 StateTransition::Shield(_) => None,
811 StateTransition::ShieldedTransfer(_) => None,
812 StateTransition::Unshield(_) => None,
813 StateTransition::ShieldFromAssetLock(_) => None,
814 StateTransition::ShieldedWithdrawal(_) => None,
815 StateTransition::IdentityCreateFromShieldedPool(_) => None,
816 }
817 }
818
819 pub fn inputs(&self) -> Option<&BTreeMap<PlatformAddress, (AddressNonce, Credits)>> {
821 match self {
822 StateTransition::DataContractCreate(_)
823 | StateTransition::DataContractUpdate(_)
824 | StateTransition::Batch(_)
825 | StateTransition::IdentityCreate(_)
826 | StateTransition::IdentityTopUp(_)
827 | StateTransition::IdentityCreditWithdrawal(_)
828 | StateTransition::IdentityUpdate(_)
829 | StateTransition::IdentityCreditTransfer(_)
830 | StateTransition::MasternodeVote(_)
831 | StateTransition::IdentityCreditTransferToAddresses(_) => None,
832 StateTransition::IdentityCreateFromAddresses(st) => Some(st.inputs()),
833 StateTransition::IdentityTopUpFromAddresses(st) => Some(st.inputs()),
834 StateTransition::AddressFundsTransfer(st) => Some(st.inputs()),
835 StateTransition::AddressFundingFromAssetLock(st) => Some(st.inputs()),
836 StateTransition::AddressCreditWithdrawal(st) => Some(st.inputs()),
837 StateTransition::Shield(st) => Some(st.inputs()),
838 StateTransition::ShieldedTransfer(_) => None,
839 StateTransition::Unshield(_) => None,
840 StateTransition::ShieldFromAssetLock(_) => None,
841 StateTransition::ShieldedWithdrawal(_) => None,
842 StateTransition::IdentityCreateFromShieldedPool(_) => None,
843 }
844 }
845
846 pub fn state_transition_type(&self) -> StateTransitionType {
848 call_method!(self, state_transition_type)
849 }
850
851 pub fn unique_identifiers(&self) -> Vec<String> {
853 call_method!(self, unique_identifiers)
854 }
855
856 pub fn set_signature(&mut self, signature: BinaryData) -> bool {
858 match self {
859 StateTransition::DataContractCreate(st) => {
860 st.set_signature(signature);
861 true
862 }
863 StateTransition::DataContractUpdate(st) => {
864 st.set_signature(signature);
865 true
866 }
867 StateTransition::Batch(st) => {
868 st.set_signature(signature);
869 true
870 }
871 StateTransition::IdentityCreate(st) => {
872 st.set_signature(signature);
873 true
874 }
875 StateTransition::IdentityTopUp(st) => {
876 st.set_signature(signature);
877 true
878 }
879 StateTransition::IdentityCreditWithdrawal(st) => {
880 st.set_signature(signature);
881 true
882 }
883 StateTransition::IdentityUpdate(st) => {
884 st.set_signature(signature);
885 true
886 }
887 StateTransition::IdentityCreditTransfer(st) => {
888 st.set_signature(signature);
889 true
890 }
891 StateTransition::MasternodeVote(st) => {
892 st.set_signature(signature);
893 true
894 }
895 StateTransition::IdentityCreditTransferToAddresses(st) => {
896 st.set_signature(signature);
897 true
898 }
899 StateTransition::IdentityCreateFromAddresses(_)
900 | StateTransition::IdentityTopUpFromAddresses(_)
901 | StateTransition::AddressFundsTransfer(_)
902 | StateTransition::Shield(_)
903 | StateTransition::ShieldedTransfer(_)
904 | StateTransition::Unshield(_)
905 | StateTransition::ShieldedWithdrawal(_)
906 | StateTransition::IdentityCreateFromShieldedPool(_) => false,
907 StateTransition::AddressFundingFromAssetLock(st) => {
908 st.set_signature(signature);
909 true
910 }
911 StateTransition::ShieldFromAssetLock(st) => {
912 st.set_signature(signature);
913 true
914 }
915 StateTransition::AddressCreditWithdrawal(_) => false,
916 }
917 }
918
919 pub fn set_user_fee_increase(&mut self, user_fee_increase: UserFeeIncrease) {
921 match self {
922 StateTransition::DataContractCreate(st) => st.set_user_fee_increase(user_fee_increase),
923 StateTransition::DataContractUpdate(st) => st.set_user_fee_increase(user_fee_increase),
924 StateTransition::Batch(st) => st.set_user_fee_increase(user_fee_increase),
925 StateTransition::IdentityCreate(st) => st.set_user_fee_increase(user_fee_increase),
926 StateTransition::IdentityTopUp(st) => st.set_user_fee_increase(user_fee_increase),
927 StateTransition::IdentityCreditWithdrawal(st) => {
928 st.set_user_fee_increase(user_fee_increase)
929 }
930 StateTransition::IdentityUpdate(st) => st.set_user_fee_increase(user_fee_increase),
931 StateTransition::IdentityCreditTransfer(st) => {
932 st.set_user_fee_increase(user_fee_increase)
933 }
934 StateTransition::IdentityCreditTransferToAddresses(st) => {
935 st.set_user_fee_increase(user_fee_increase)
936 }
937 StateTransition::IdentityCreateFromAddresses(st) => {
938 st.set_user_fee_increase(user_fee_increase)
939 }
940 StateTransition::IdentityTopUpFromAddresses(st) => {
941 st.set_user_fee_increase(user_fee_increase)
942 }
943 StateTransition::AddressFundsTransfer(st) => {
944 st.set_user_fee_increase(user_fee_increase)
945 }
946 StateTransition::AddressFundingFromAssetLock(st) => {
947 st.set_user_fee_increase(user_fee_increase)
948 }
949 StateTransition::AddressCreditWithdrawal(st) => {
950 st.set_user_fee_increase(user_fee_increase)
951 }
952 StateTransition::Shield(st) => st.set_user_fee_increase(user_fee_increase),
953 StateTransition::ShieldFromAssetLock(_) => {}
955 StateTransition::MasternodeVote(_) => {}
956 StateTransition::ShieldedTransfer(_) => {}
957 StateTransition::Unshield(_) => {}
958 StateTransition::ShieldedWithdrawal(_) => {}
959 StateTransition::IdentityCreateFromShieldedPool(_) => {}
960 }
961 }
962
963 pub fn set_signature_public_key_id(&mut self, public_key_id: KeyID) {
965 call_method_identity_signed!(self, set_signature_public_key_id, public_key_id)
966 }
967
968 #[cfg(feature = "state-transition-signing")]
969 pub async fn sign_external<S: Signer<IdentityPublicKey>>(
970 &mut self,
971 identity_public_key: &IdentityPublicKey,
972 signer: &S,
973 get_data_contract_security_level_requirement: Option<
974 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
975 >,
976 ) -> Result<(), ProtocolError> {
977 self.sign_external_with_options(
978 identity_public_key,
979 signer,
980 get_data_contract_security_level_requirement,
981 StateTransitionSigningOptions::default(),
982 )
983 .await
984 }
985
986 #[cfg(feature = "state-transition-signing")]
987 pub async fn sign_external_with_options<S: Signer<IdentityPublicKey>>(
988 &mut self,
989 identity_public_key: &IdentityPublicKey,
990 signer: &S,
991 get_data_contract_security_level_requirement: Option<
992 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
993 >,
994 options: StateTransitionSigningOptions,
995 ) -> Result<(), ProtocolError> {
996 match self {
997 StateTransition::DataContractCreate(st) => {
998 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
999 st.verify_public_key_is_enabled(identity_public_key)?;
1000 }
1001 StateTransition::DataContractUpdate(st) => {
1002 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1003 st.verify_public_key_is_enabled(identity_public_key)?;
1004 }
1005 StateTransition::Batch(st) => {
1006 let allow_token_transfer_keys = st.transitions_len() == 1
1007 && (st
1008 .first_transition()
1009 .expect("expected first transition with len 1")
1010 .as_transition_token_claim()
1011 .is_some()
1012 || st
1013 .first_transition()
1014 .expect("expected first transition with len 1")
1015 .as_transition_token_transfer()
1016 .is_some());
1017 let allowed_key_purposes = if allow_token_transfer_keys {
1018 vec![Purpose::AUTHENTICATION, Purpose::TRANSFER]
1019 } else {
1020 vec![Purpose::AUTHENTICATION]
1021 };
1022 if !options.allow_signing_with_any_purpose
1023 && !allowed_key_purposes.contains(&identity_public_key.purpose())
1024 {
1025 return Err(ProtocolError::WrongPublicKeyPurposeError(
1026 WrongPublicKeyPurposeError::new(
1027 identity_public_key.purpose(),
1028 allowed_key_purposes,
1029 ),
1030 ));
1031 }
1032 if !options.allow_signing_with_any_security_level {
1033 let security_level_requirement = st.combined_security_level_requirement(
1034 get_data_contract_security_level_requirement,
1035 )?;
1036 if !security_level_requirement.contains(&identity_public_key.security_level()) {
1037 return Err(ProtocolError::InvalidSignaturePublicKeySecurityLevelError(
1038 InvalidSignaturePublicKeySecurityLevelError::new(
1039 identity_public_key.security_level(),
1040 security_level_requirement,
1041 ),
1042 ));
1043 }
1044 }
1045 st.verify_public_key_is_enabled(identity_public_key)?;
1046 }
1047 StateTransition::IdentityCreditWithdrawal(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::IdentityUpdate(st) => {
1052 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1053 st.verify_public_key_is_enabled(identity_public_key)?;
1054 }
1055 StateTransition::IdentityCreditTransfer(st) => {
1056 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1057 st.verify_public_key_is_enabled(identity_public_key)?;
1058 }
1059 StateTransition::IdentityCreate(_) => {
1060 return Err(ProtocolError::CorruptedCodeExecution(
1061 "identity create can not be called for identity signing".to_string(),
1062 ))
1063 }
1064 StateTransition::IdentityTopUp(_) => {
1065 return Err(ProtocolError::CorruptedCodeExecution(
1066 "identity top up can not be called for identity signing".to_string(),
1067 ))
1068 }
1069 StateTransition::MasternodeVote(st) => {
1070 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1071 st.verify_public_key_is_enabled(identity_public_key)?;
1072 }
1073 StateTransition::IdentityCreditTransferToAddresses(st) => {
1074 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1075 st.verify_public_key_is_enabled(identity_public_key)?;
1076 }
1077 StateTransition::IdentityCreateFromAddresses(_) => {
1078 return Err(ProtocolError::CorruptedCodeExecution(
1079 "identity create from addresses can not be called for identity signing"
1080 .to_string(),
1081 ))
1082 }
1083 StateTransition::IdentityTopUpFromAddresses(_) => {
1084 return Err(ProtocolError::CorruptedCodeExecution(
1085 "identity top up from addresses can not be called for identity signing"
1086 .to_string(),
1087 ))
1088 }
1089 StateTransition::AddressFundsTransfer(_) => {
1090 return Err(ProtocolError::CorruptedCodeExecution(
1091 "address funds transfer transition can not be called for identity signing"
1092 .to_string(),
1093 ))
1094 }
1095 StateTransition::AddressFundingFromAssetLock(_) => {
1096 return Err(ProtocolError::CorruptedCodeExecution(
1097 "address funding from asset lock transition can not be called for identity signing"
1098 .to_string(),
1099 ))
1100 }
1101 StateTransition::AddressCreditWithdrawal(_) => {
1102 return Err(ProtocolError::CorruptedCodeExecution(
1103 "address credit withdrawal transition can not be called for identity signing"
1104 .to_string(),
1105 ))
1106 }
1107 StateTransition::Shield(_) => {
1108 return Err(ProtocolError::CorruptedCodeExecution(
1109 "shield transition can not be called for identity signing".to_string(),
1110 ))
1111 }
1112 StateTransition::ShieldedTransfer(_) => {
1113 return Err(ProtocolError::CorruptedCodeExecution(
1114 "shielded transfer transition can not be called for identity signing"
1115 .to_string(),
1116 ))
1117 }
1118 StateTransition::Unshield(_) => {
1119 return Err(ProtocolError::CorruptedCodeExecution(
1120 "unshield transition can not be called for identity signing".to_string(),
1121 ))
1122 }
1123 StateTransition::ShieldFromAssetLock(_) => {
1124 return Err(ProtocolError::CorruptedCodeExecution(
1125 "shield from asset lock transition can not be called for identity signing"
1126 .to_string(),
1127 ))
1128 }
1129 StateTransition::ShieldedWithdrawal(_) => {
1130 return Err(ProtocolError::CorruptedCodeExecution(
1131 "shielded withdrawal transition can not be called for identity signing"
1132 .to_string(),
1133 ))
1134 }
1135 StateTransition::IdentityCreateFromShieldedPool(_) => {
1136 return Err(ProtocolError::CorruptedCodeExecution(
1137 "identity create from shielded pool transition can not be called for identity signing"
1138 .to_string(),
1139 ))
1140 }
1141 }
1142 let data = self.signable_bytes()?;
1143 self.set_signature(signer.sign(identity_public_key, data.as_slice()).await?);
1144 self.set_signature_public_key_id(identity_public_key.id());
1145 Ok(())
1146 }
1147
1148 #[cfg(feature = "state-transition-signing")]
1149 pub fn sign(
1150 &mut self,
1151 identity_public_key: &IdentityPublicKey,
1152 private_key: &[u8],
1153 bls: &impl BlsModule,
1154 ) -> Result<(), ProtocolError> {
1155 self.sign_with_options(
1156 identity_public_key,
1157 private_key,
1158 bls,
1159 StateTransitionSigningOptions::default(),
1160 )
1161 }
1162
1163 #[cfg(feature = "state-transition-signing")]
1164 pub fn sign_with_options(
1165 &mut self,
1166 identity_public_key: &IdentityPublicKey,
1167 private_key: &[u8],
1168 bls: &impl BlsModule,
1169 options: StateTransitionSigningOptions,
1170 ) -> Result<(), ProtocolError> {
1171 call_errorable_method_identity_signed!(
1172 self,
1173 verify_public_key_level_and_purpose,
1174 identity_public_key,
1175 options
1176 )?;
1177 call_errorable_method_identity_signed!(
1178 self,
1179 verify_public_key_is_enabled,
1180 identity_public_key
1181 )?;
1182
1183 match identity_public_key.key_type() {
1184 KeyType::ECDSA_SECP256K1 => {
1185 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1186
1187 if public_key_compressed.as_slice() != identity_public_key.data().as_slice() {
1192 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1193 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1194 ));
1195 }
1196
1197 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1198 }
1199 KeyType::ECDSA_HASH160 => {
1200 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1201 let pub_key_hash = ripemd160_sha256(&public_key_compressed);
1202
1203 if identity_public_key.data().as_slice() != pub_key_hash {
1204 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1205 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1206 ));
1207 }
1208 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1209 }
1210 KeyType::BLS12_381 => {
1211 let public_key = bls.private_key_to_public_key(private_key)?;
1212
1213 if public_key != identity_public_key.data().as_slice() {
1214 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1215 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1216 ));
1217 }
1218 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1219 }
1220
1221 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1225 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1226 InvalidIdentityPublicKeyTypeError::new(identity_public_key.key_type()),
1227 ))
1228 }
1229 }?;
1230
1231 self.set_signature_public_key_id(identity_public_key.id());
1232
1233 Ok(())
1234 }
1235
1236 #[cfg(feature = "state-transition-signing")]
1237 pub fn sign_by_private_key(
1239 &mut self,
1240 private_key: &[u8],
1241 key_type: KeyType,
1242 bls: &impl BlsModule,
1243 ) -> Result<(), ProtocolError> {
1244 let data = self.signable_bytes()?;
1245 match key_type {
1246 KeyType::BLS12_381 => {
1247 if !self.set_signature(bls.sign(&data, private_key)?.into()) {
1248 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1249 needed: self.required_number_of_private_keys(),
1250 using: 1,
1251 msg: "failed to set BLS signature",
1252 });
1253 }
1254 }
1255
1256 KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => {
1258 let signature = signer::sign(&data, private_key)?;
1259 if !self.set_signature(signature.to_vec().into()) {
1260 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1261 needed: self.required_number_of_private_keys(),
1262 using: 1,
1263 msg: "failed to set ECDSA signature",
1264 });
1265 };
1266 }
1267
1268 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1272 return Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1273 InvalidIdentityPublicKeyTypeError::new(key_type),
1274 ))
1275 }
1276 };
1277 Ok(())
1278 }
1279
1280 #[cfg(all(feature = "state-transition-signing", feature = "core_key_wallet"))]
1358 pub async fn sign_with_core_signer<S: ::key_wallet::signer::Signer>(
1359 &mut self,
1360 path: &::key_wallet::bip32::DerivationPath,
1361 signer: &S,
1362 ) -> Result<(), ProtocolError> {
1363 use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1364 use dashcore::secp256k1::{Message, Secp256k1};
1365 use dashcore::signer::{double_sha, CompactSignature};
1366
1367 let data = self.signable_bytes()?;
1368 let data_hash = double_sha(&data);
1371 let digest: [u8; 32] = data_hash.as_slice().try_into().map_err(|_| {
1372 ProtocolError::Generic("double_sha did not return 32 bytes".to_string())
1373 })?;
1374
1375 let (signature, public_key) = signer
1376 .sign_ecdsa(path, digest)
1377 .await
1378 .map_err(|e| ProtocolError::ExternalSignerError(format!("signer failed: {}", e)))?;
1379
1380 let compact_64 = signature.serialize_compact();
1389 let secp = Secp256k1::new();
1390 let msg = Message::from_digest(digest);
1391
1392 let mut found: Option<RecoverableSignature> = None;
1393 for id in 0..4i32 {
1394 let recid = match RecoveryId::try_from(id) {
1395 Ok(r) => r,
1396 Err(_) => continue,
1397 };
1398 let candidate = match RecoverableSignature::from_compact(&compact_64, recid) {
1399 Ok(s) => s,
1400 Err(_) => continue,
1401 };
1402 if let Ok(recovered) = secp.recover_ecdsa(&msg, &candidate) {
1403 if recovered == public_key {
1404 found = Some(candidate);
1405 break;
1406 }
1407 }
1408 }
1409 let recoverable = found.ok_or_else(|| {
1410 ProtocolError::ExternalSignerError(
1416 "signer returned a signature whose recovery id does not match the returned public key".to_string(),
1417 )
1418 })?;
1419
1420 let compact_65 = recoverable.to_compact_signature(true);
1425
1426 if !self.set_signature(compact_65.to_vec().into()) {
1427 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1428 needed: self.required_number_of_private_keys(),
1429 using: 1,
1430 msg: "failed to set ECDSA signature",
1431 });
1432 }
1433 Ok(())
1434 }
1435
1436 #[cfg(feature = "state-transition-validation")]
1437 fn verify_by_raw_public_key<T: BlsModule>(
1438 &self,
1439 public_key: &[u8],
1440 public_key_type: KeyType,
1441 bls: &T,
1442 ) -> Result<(), ProtocolError> {
1443 match public_key_type {
1444 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key),
1445 KeyType::ECDSA_HASH160 => {
1446 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key)
1447 }
1448 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key, bls),
1449 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1450 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1451 InvalidIdentityPublicKeyTypeError::new(public_key_type),
1452 ))
1453 }
1454 }
1455 }
1456
1457 #[cfg(feature = "state-transition-validation")]
1458 pub fn verify_identity_signed_signature(
1459 &self,
1460 public_key: &IdentityPublicKey,
1461 bls: &impl BlsModule,
1462 ) -> Result<(), ProtocolError> {
1463 if public_key.disabled_at().is_some() {
1465 return Err(ProtocolError::PublicKeyIsDisabledError(
1466 PublicKeyIsDisabledError::new(public_key.id()),
1467 ));
1468 }
1469
1470 let Some(signature) = self.signature() else {
1471 return Err(ProtocolError::CorruptedCodeExecution("verifying identity signature for a state transition that doesn't use identity signatures".to_string()));
1472 };
1473 if signature.is_empty() {
1474 return Err(ProtocolError::StateTransitionIsNotSignedError(
1475 StateTransitionIsNotSignedError::new(self.clone()),
1476 ));
1477 }
1478
1479 if self.signature_public_key_id() != Some(public_key.id()) {
1480 return Err(ProtocolError::PublicKeyMismatchError(
1481 PublicKeyMismatchError::new(public_key.clone()),
1482 ));
1483 }
1484
1485 let public_key_bytes = public_key.data().as_slice();
1486 match public_key.key_type() {
1487 KeyType::ECDSA_HASH160 => {
1488 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key_bytes)
1489 }
1490
1491 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key_bytes),
1492
1493 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key_bytes, bls),
1494
1495 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => Ok(()),
1497 }
1498 }
1499
1500 #[cfg(feature = "state-transition-validation")]
1501 fn verify_ecdsa_hash_160_signature_by_public_key_hash(
1502 &self,
1503 public_key_hash: &[u8],
1504 ) -> Result<(), ProtocolError> {
1505 let Some(signature) = self.signature() else {
1506 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1507 needed: self.required_number_of_private_keys(),
1508 using: 1,
1509 msg: "This state transition type should a single signature",
1510 });
1511 };
1512 if signature.is_empty() {
1513 return Err(ProtocolError::StateTransitionIsNotSignedError(
1514 StateTransitionIsNotSignedError::new(self.clone()),
1515 ));
1516 }
1517 let data = self.signable_bytes()?;
1518 let data_hash = double_sha(data);
1519 signer::verify_hash_signature(&data_hash, signature.as_slice(), public_key_hash).map_err(
1520 |e| {
1521 ProtocolError::from(ConsensusError::SignatureError(
1522 SignatureError::InvalidStateTransitionSignatureError(
1523 InvalidStateTransitionSignatureError::new(e.to_string()),
1524 ),
1525 ))
1526 },
1527 )
1528 }
1529
1530 #[cfg(feature = "state-transition-validation")]
1531 fn verify_ecdsa_signature_by_public_key(&self, public_key: &[u8]) -> Result<(), ProtocolError> {
1533 let Some(signature) = self.signature() else {
1534 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1535 needed: self.required_number_of_private_keys(),
1536 using: 1,
1537 msg: "This state transition type should a single signature",
1538 });
1539 };
1540 if signature.is_empty() {
1541 return Err(ProtocolError::StateTransitionIsNotSignedError(
1542 StateTransitionIsNotSignedError::new(self.clone()),
1543 ));
1544 }
1545 let data = self.signable_bytes()?;
1546 signer::verify_data_signature(&data, signature.as_slice(), public_key).map_err(|e| {
1547 ProtocolError::from(ConsensusError::SignatureError(
1550 SignatureError::InvalidStateTransitionSignatureError(
1551 InvalidStateTransitionSignatureError::new(e.to_string()),
1552 ),
1553 ))
1554 })
1555 }
1556
1557 #[cfg(feature = "state-transition-validation")]
1558 fn verify_bls_signature_by_public_key<T: BlsModule>(
1560 &self,
1561 public_key: &[u8],
1562 bls: &T,
1563 ) -> Result<(), ProtocolError> {
1564 let Some(signature) = self.signature() else {
1565 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1566 needed: self.required_number_of_private_keys(),
1567 using: 1,
1568 msg: "This state transition type should a single signature",
1569 });
1570 };
1571 if signature.is_empty() {
1572 return Err(ProtocolError::StateTransitionIsNotSignedError(
1573 StateTransitionIsNotSignedError::new(self.clone()),
1574 ));
1575 }
1576
1577 let data = self.signable_bytes()?;
1578
1579 bls.verify_signature(signature.as_slice(), &data, public_key)
1580 .map(|_| ())
1581 .map_err(|e| {
1582 ProtocolError::from(ConsensusError::SignatureError(
1584 SignatureError::InvalidStateTransitionSignatureError(
1585 InvalidStateTransitionSignatureError::new(e.to_string()),
1586 ),
1587 ))
1588 })
1589 }
1590}
1591
1592#[cfg(feature = "state-transition-validation")]
1593impl StateTransitionStructureValidation for StateTransition {
1594 fn validate_structure(
1595 &self,
1596 platform_version: &PlatformVersion,
1597 ) -> crate::validation::SimpleConsensusValidationResult {
1598 match self {
1599 StateTransition::DataContractCreate(_)
1600 | StateTransition::DataContractUpdate(_)
1601 | StateTransition::Batch(_)
1602 | StateTransition::IdentityCreate(_)
1603 | StateTransition::IdentityTopUp(_)
1604 | StateTransition::IdentityCreditWithdrawal(_)
1605 | StateTransition::IdentityUpdate(_)
1606 | StateTransition::IdentityCreditTransfer(_)
1607 | StateTransition::MasternodeVote(_) => {
1608 crate::validation::SimpleConsensusValidationResult::new_with_error(
1609 UnsupportedFeatureError::new(
1610 "structure validation for identity-based state transitions".to_string(),
1611 platform_version.protocol_version,
1612 )
1613 .into(),
1614 )
1615 }
1616 StateTransition::IdentityCreditTransferToAddresses(transition) => {
1617 transition.validate_structure(platform_version)
1618 }
1619 StateTransition::IdentityCreateFromAddresses(transition) => {
1620 transition.validate_structure(platform_version)
1621 }
1622 StateTransition::IdentityTopUpFromAddresses(transition) => {
1623 transition.validate_structure(platform_version)
1624 }
1625 StateTransition::AddressFundsTransfer(transition) => {
1626 transition.validate_structure(platform_version)
1627 }
1628 StateTransition::AddressFundingFromAssetLock(transition) => {
1629 transition.validate_structure(platform_version)
1630 }
1631 StateTransition::AddressCreditWithdrawal(transition) => {
1632 transition.validate_structure(platform_version)
1633 }
1634 StateTransition::Shield(transition) => transition.validate_structure(platform_version),
1635 StateTransition::ShieldedTransfer(transition) => {
1636 transition.validate_structure(platform_version)
1637 }
1638 StateTransition::Unshield(transition) => {
1639 transition.validate_structure(platform_version)
1640 }
1641 StateTransition::ShieldFromAssetLock(transition) => {
1642 transition.validate_structure(platform_version)
1643 }
1644 StateTransition::ShieldedWithdrawal(transition) => {
1645 transition.validate_structure(platform_version)
1646 }
1647 StateTransition::IdentityCreateFromShieldedPool(transition) => {
1648 transition.validate_structure(platform_version)
1649 }
1650 }
1651 }
1652}
1653
1654#[cfg(test)]
1655mod tests {
1656 use super::*;
1657
1658 #[test]
1663 fn test_signing_options_default() {
1664 let opts = StateTransitionSigningOptions::default();
1665 assert!(!opts.allow_signing_with_any_security_level);
1666 assert!(!opts.allow_signing_with_any_purpose);
1667 }
1668
1669 #[test]
1670 fn test_signing_options_equality() {
1671 let a = StateTransitionSigningOptions {
1672 allow_signing_with_any_security_level: true,
1673 allow_signing_with_any_purpose: false,
1674 };
1675 let b = StateTransitionSigningOptions {
1676 allow_signing_with_any_security_level: true,
1677 allow_signing_with_any_purpose: false,
1678 };
1679 assert_eq!(a, b);
1680 }
1681
1682 #[test]
1683 fn test_signing_options_inequality() {
1684 let a = StateTransitionSigningOptions {
1685 allow_signing_with_any_security_level: true,
1686 allow_signing_with_any_purpose: false,
1687 };
1688 let b = StateTransitionSigningOptions {
1689 allow_signing_with_any_security_level: false,
1690 allow_signing_with_any_purpose: false,
1691 };
1692 assert_ne!(a, b);
1693 }
1694
1695 #[test]
1696 #[allow(clippy::clone_on_copy)]
1697 fn test_signing_options_clone() {
1698 let original = StateTransitionSigningOptions {
1699 allow_signing_with_any_security_level: true,
1700 allow_signing_with_any_purpose: true,
1701 };
1702 let cloned = original.clone();
1703 assert_eq!(original, cloned);
1704 }
1705
1706 #[test]
1707 fn test_signing_options_copy() {
1708 let original = StateTransitionSigningOptions {
1709 allow_signing_with_any_security_level: true,
1710 allow_signing_with_any_purpose: false,
1711 };
1712 let copied = original;
1713 assert_eq!(original, copied);
1714 }
1715
1716 #[test]
1717 fn test_signing_options_debug() {
1718 let opts = StateTransitionSigningOptions::default();
1719 let debug_str = format!("{:?}", opts);
1720 assert!(debug_str.contains("StateTransitionSigningOptions"));
1721 assert!(debug_str.contains("allow_signing_with_any_security_level"));
1722 assert!(debug_str.contains("allow_signing_with_any_purpose"));
1723 }
1724
1725 use crate::identity::core_script::CoreScript;
1735 use crate::identity::{Purpose, SecurityLevel};
1736 use crate::prelude::Identifier;
1737 use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0;
1738 use crate::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition;
1739 use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0;
1740 use crate::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition;
1741 use crate::state_transition::masternode_vote_transition::v0::MasternodeVoteTransitionV0;
1742 use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
1743 use crate::withdrawal::Pooling;
1744
1745 fn sample_transfer_st() -> StateTransition {
1746 let v0 = IdentityCreditTransferTransitionV0 {
1747 identity_id: Identifier::from([1u8; 32]),
1748 recipient_id: Identifier::from([2u8; 32]),
1749 amount: 1_000,
1750 nonce: 7,
1751 user_fee_increase: 3,
1752 signature_public_key_id: 11,
1753 signature: BinaryData::new(vec![0u8; 65]),
1754 };
1755 StateTransition::IdentityCreditTransfer(IdentityCreditTransferTransition::V0(v0))
1756 }
1757
1758 fn sample_masternode_vote_st() -> StateTransition {
1759 let v0 = MasternodeVoteTransitionV0 {
1760 pro_tx_hash: Identifier::from([3u8; 32]),
1761 voter_identity_id: Identifier::from([4u8; 32]),
1762 vote: Default::default(),
1763 nonce: 2,
1764 signature_public_key_id: 5,
1765 signature: BinaryData::new(vec![9u8; 10]),
1766 };
1767 StateTransition::MasternodeVote(MasternodeVoteTransition::V0(v0))
1768 }
1769
1770 fn sample_withdrawal_st() -> StateTransition {
1771 let v0 = IdentityCreditWithdrawalTransitionV0 {
1772 identity_id: Identifier::from([5u8; 32]),
1773 amount: 42,
1774 core_fee_per_byte: 1,
1775 pooling: Pooling::Never,
1776 output_script: CoreScript::from_bytes(vec![0x76, 0xa9]),
1777 nonce: 4,
1778 user_fee_increase: 1,
1779 signature_public_key_id: 3,
1780 signature: BinaryData::new(vec![8u8; 65]),
1781 };
1782 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V0(v0))
1783 }
1784
1785 #[test]
1786 fn test_name_returns_variant_names() {
1787 assert_eq!(sample_transfer_st().name(), "IdentityCreditTransfer");
1788 assert_eq!(sample_masternode_vote_st().name(), "MasternodeVote");
1789 assert_eq!(sample_withdrawal_st().name(), "IdentityCreditWithdrawal");
1790 }
1791
1792 #[test]
1793 fn test_state_transition_type_matches_variant() {
1794 assert_eq!(
1795 sample_transfer_st().state_transition_type(),
1796 StateTransitionType::IdentityCreditTransfer
1797 );
1798 assert_eq!(
1799 sample_masternode_vote_st().state_transition_type(),
1800 StateTransitionType::MasternodeVote
1801 );
1802 assert_eq!(
1803 sample_withdrawal_st().state_transition_type(),
1804 StateTransitionType::IdentityCreditWithdrawal
1805 );
1806 }
1807
1808 #[test]
1809 fn test_is_identity_signed_excludes_asset_lock_and_shielded() {
1810 assert!(sample_transfer_st().is_identity_signed());
1811 assert!(sample_masternode_vote_st().is_identity_signed());
1812 assert!(sample_withdrawal_st().is_identity_signed());
1813 }
1814
1815 #[test]
1816 fn test_signature_accessor() {
1817 let st = sample_transfer_st();
1818 let sig = st.signature().expect("transfer should expose signature");
1819 assert_eq!(sig.len(), 65);
1820
1821 let st = sample_masternode_vote_st();
1822 let sig = st.signature().expect("masternode vote has signature");
1823 assert_eq!(sig.as_slice(), &[9u8; 10]);
1824 }
1825
1826 #[test]
1827 fn test_owner_id_accessor() {
1828 let transfer = sample_transfer_st();
1829 assert_eq!(transfer.owner_id(), Some(Identifier::from([1u8; 32])));
1830
1831 let vote = sample_masternode_vote_st();
1832 assert_eq!(vote.owner_id(), Some(Identifier::from([4u8; 32])));
1833
1834 let withdraw = sample_withdrawal_st();
1835 assert_eq!(withdraw.owner_id(), Some(Identifier::from([5u8; 32])));
1836 }
1837
1838 #[test]
1839 fn test_signature_public_key_id_accessor() {
1840 assert_eq!(sample_transfer_st().signature_public_key_id(), Some(11));
1841 assert_eq!(
1842 sample_masternode_vote_st().signature_public_key_id(),
1843 Some(5)
1844 );
1845 assert_eq!(sample_withdrawal_st().signature_public_key_id(), Some(3));
1846 }
1847
1848 #[test]
1849 fn test_user_fee_increase_for_various_variants() {
1850 assert_eq!(sample_transfer_st().user_fee_increase(), 3);
1852 assert_eq!(sample_masternode_vote_st().user_fee_increase(), 0);
1854 assert_eq!(sample_withdrawal_st().user_fee_increase(), 1);
1856 }
1857
1858 #[test]
1859 fn test_set_signature_returns_true_for_supported() {
1860 let mut st = sample_transfer_st();
1861 let ok = st.set_signature(BinaryData::new(vec![0xaa; 65]));
1862 assert!(ok);
1863 assert_eq!(st.signature().unwrap().as_slice(), &[0xaa; 65]);
1864 }
1865
1866 #[test]
1867 fn test_set_user_fee_increase_updates_value() {
1868 let mut st = sample_transfer_st();
1869 st.set_user_fee_increase(42);
1870 assert_eq!(st.user_fee_increase(), 42);
1871
1872 let mut vote = sample_masternode_vote_st();
1874 vote.set_user_fee_increase(99);
1875 assert_eq!(vote.user_fee_increase(), 0);
1876 }
1877
1878 #[test]
1879 fn test_set_signature_public_key_id() {
1880 let mut st = sample_transfer_st();
1881 st.set_signature_public_key_id(1234);
1882 assert_eq!(st.signature_public_key_id(), Some(1234));
1883 }
1884
1885 #[test]
1886 fn test_required_number_of_private_keys_default() {
1887 assert_eq!(sample_transfer_st().required_number_of_private_keys(), 1);
1889 assert_eq!(
1890 sample_masternode_vote_st().required_number_of_private_keys(),
1891 1
1892 );
1893 assert_eq!(sample_withdrawal_st().required_number_of_private_keys(), 1);
1894 }
1895
1896 #[test]
1897 fn test_inputs_none_for_legacy_variants() {
1898 assert!(sample_transfer_st().inputs().is_none());
1900 assert!(sample_masternode_vote_st().inputs().is_none());
1901 assert!(sample_withdrawal_st().inputs().is_none());
1902 }
1903
1904 #[test]
1905 fn test_active_version_range_legacy_transitions() {
1906 assert_eq!(sample_transfer_st().active_version_range(), ALL_VERSIONS);
1908 assert_eq!(
1909 sample_masternode_vote_st().active_version_range(),
1910 ALL_VERSIONS
1911 );
1912 assert_eq!(sample_withdrawal_st().active_version_range(), ALL_VERSIONS);
1913 }
1914
1915 #[test]
1916 fn test_unique_identifiers_non_empty() {
1917 let ids = sample_transfer_st().unique_identifiers();
1918 assert_eq!(ids.len(), 1);
1919 assert!(!ids[0].is_empty());
1920 }
1921
1922 #[test]
1923 fn test_required_asset_lock_balance_rejects_non_asset_lock() {
1924 let platform_version = PlatformVersion::latest();
1925 let st = sample_transfer_st();
1926 let err = st
1927 .required_asset_lock_balance_for_processing_start(platform_version)
1928 .expect_err("credit transfer is not an asset lock state transition");
1929 match err {
1930 ProtocolError::CorruptedCodeExecution(msg) => {
1931 assert!(
1932 msg.contains("is not an asset lock transaction"),
1933 "unexpected error message: {msg}"
1934 );
1935 }
1936 other => panic!("expected CorruptedCodeExecution, got {other:?}"),
1937 }
1938 }
1939
1940 #[test]
1941 fn test_security_level_requirement_for_transfer() {
1942 let st = sample_transfer_st();
1944 let levels = st
1945 .security_level_requirement(Purpose::TRANSFER)
1946 .expect("transfer state transition should return a requirement");
1947 assert_eq!(levels, vec![SecurityLevel::CRITICAL]);
1948 }
1949
1950 #[test]
1951 fn test_purpose_requirement_for_transfer() {
1952 let st = sample_transfer_st();
1953 let purposes = st
1954 .purpose_requirement()
1955 .expect("transfer state transition should have a purpose");
1956 assert_eq!(purposes, vec![Purpose::TRANSFER]);
1957 }
1958
1959 #[test]
1960 fn test_optional_asset_lock_proof_none_for_transfer() {
1961 let st = sample_transfer_st();
1962 assert!(st.optional_asset_lock_proof().is_none());
1963 }
1964
1965 #[test]
1970 fn test_from_outer_enum_into_state_transition() {
1971 let outer: IdentityCreditTransferTransition =
1972 IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0::default());
1973 let st: StateTransition = outer.into();
1974 assert!(matches!(st, StateTransition::IdentityCreditTransfer(_)));
1975 }
1976
1977 #[test]
1978 fn test_from_masternode_vote_outer_into_state_transition() {
1979 let outer: MasternodeVoteTransition =
1980 MasternodeVoteTransition::V0(MasternodeVoteTransitionV0::default());
1981 let st: StateTransition = outer.into();
1982 assert!(matches!(st, StateTransition::MasternodeVote(_)));
1983 }
1984
1985 #[test]
1991 fn test_state_transition_platform_serialize_roundtrip() {
1992 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
1993 let original = sample_transfer_st();
1994 let bytes =
1995 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
1996 let restored =
1997 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
1998 assert_eq!(original, restored);
1999 }
2000
2001 #[test]
2002 fn test_deserialize_from_bytes_in_version_succeeds_for_latest() {
2003 use crate::serialization::PlatformSerializable;
2004 let original = sample_transfer_st();
2005 let bytes =
2006 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2007 let restored =
2008 StateTransition::deserialize_from_bytes_in_version(&bytes, PlatformVersion::latest())
2009 .expect("deserialize_from_bytes_in_version should succeed");
2010 assert_eq!(original, restored);
2011 }
2012
2013 #[test]
2014 fn test_transaction_id_is_deterministic() {
2015 let st = sample_transfer_st();
2016 let a = st.transaction_id().expect("hash should succeed");
2017 let b = st.transaction_id().expect("hash should succeed");
2018 assert_eq!(a, b);
2019 assert_eq!(a.len(), 32);
2020 }
2021
2022 #[test]
2023 fn test_transaction_id_changes_on_signature_change() {
2024 let mut st = sample_transfer_st();
2025 let before = st.transaction_id().expect("hash should succeed");
2026 st.set_signature(BinaryData::new(vec![0xbb; 65]));
2027 let after = st.transaction_id().expect("hash should succeed");
2028 assert_ne!(before, after);
2030 }
2031
2032 #[test]
2033 fn test_clone_preserves_inner_state() {
2034 let st = sample_transfer_st();
2035 let cloned = st.clone();
2036 assert_eq!(st, cloned);
2037 }
2038
2039 use crate::data_contract::serialized_version::DataContractInSerializationFormat;
2050 use crate::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0;
2051 use crate::state_transition::batch_transition::document_base_transition::DocumentBaseTransition;
2052 use crate::state_transition::batch_transition::document_delete_transition::{
2053 DocumentDeleteTransition, DocumentDeleteTransitionV0,
2054 };
2055 use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionV0};
2056 use crate::state_transition::data_contract_create_transition::{
2057 DataContractCreateTransition, DataContractCreateTransitionV0,
2058 };
2059 use crate::state_transition::data_contract_update_transition::{
2060 DataContractUpdateTransition, DataContractUpdateTransitionV0,
2061 };
2062 use crate::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0;
2063 use crate::state_transition::identity_create_transition::IdentityCreateTransition;
2064 use crate::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0;
2065 use crate::state_transition::identity_topup_transition::IdentityTopUpTransition;
2066 use crate::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0;
2067 use crate::state_transition::identity_update_transition::IdentityUpdateTransition;
2068 use crate::state_transition::shielded_transfer_transition::v0::ShieldedTransferTransitionV0;
2069 use crate::state_transition::shielded_transfer_transition::ShieldedTransferTransition;
2070 use crate::state_transition::shielded_withdrawal_transition::v0::ShieldedWithdrawalTransitionV0;
2071 use crate::state_transition::shielded_withdrawal_transition::ShieldedWithdrawalTransition;
2072 use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
2073 use crate::state_transition::unshield_transition::UnshieldTransition;
2074
2075 fn sample_data_contract_in_serialization_format() -> DataContractInSerializationFormat {
2078 use crate::data_contract::config::v0::DataContractConfigV0;
2079 use crate::data_contract::config::DataContractConfig;
2080 use crate::data_contract::v1::DataContractV1;
2081 use crate::data_contract::DataContract;
2082 use platform_version::TryIntoPlatformVersioned;
2083 use std::collections::BTreeMap;
2084
2085 let contract = DataContract::V1(DataContractV1 {
2086 id: Identifier::from([9u8; 32]),
2087 version: 1,
2088 owner_id: Identifier::from([7u8; 32]),
2089 document_types: BTreeMap::new(),
2090 config: DataContractConfig::V0(DataContractConfigV0 {
2091 can_be_deleted: false,
2092 readonly: false,
2093 keeps_history: false,
2094 documents_keep_history_contract_default: false,
2095 documents_mutable_contract_default: false,
2096 documents_can_be_deleted_contract_default: false,
2097 requires_identity_encryption_bounded_key: None,
2098 requires_identity_decryption_bounded_key: None,
2099 }),
2100 schema_defs: None,
2101 created_at: None,
2102 updated_at: None,
2103 created_at_block_height: None,
2104 updated_at_block_height: None,
2105 created_at_epoch: None,
2106 updated_at_epoch: None,
2107 groups: BTreeMap::new(),
2108 tokens: BTreeMap::new(),
2109 keywords: Vec::new(),
2110 description: None,
2111 });
2112
2113 contract
2114 .try_into_platform_versioned(PlatformVersion::latest())
2115 .expect("expected to serialize a trivial contract")
2116 }
2117
2118 fn sample_data_contract_create_st() -> StateTransition {
2119 StateTransition::DataContractCreate(DataContractCreateTransition::V0(
2120 DataContractCreateTransitionV0 {
2121 data_contract: sample_data_contract_in_serialization_format(),
2122 identity_nonce: 1,
2123 user_fee_increase: 5,
2124 signature_public_key_id: 2,
2125 signature: BinaryData::new(vec![0xAB; 65]),
2126 },
2127 ))
2128 }
2129
2130 fn sample_data_contract_update_st() -> StateTransition {
2131 StateTransition::DataContractUpdate(DataContractUpdateTransition::V0(
2132 DataContractUpdateTransitionV0 {
2133 identity_contract_nonce: 4,
2134 data_contract: sample_data_contract_in_serialization_format(),
2135 user_fee_increase: 9,
2136 signature_public_key_id: 6,
2137 signature: BinaryData::new(vec![0xCD; 65]),
2138 },
2139 ))
2140 }
2141
2142 fn sample_batch_st_with_delete() -> StateTransition {
2143 let base = DocumentBaseTransition::V0(DocumentBaseTransitionV0 {
2144 id: Identifier::from([1u8; 32]),
2145 identity_contract_nonce: 3,
2146 document_type_name: "preorder".to_string(),
2147 data_contract_id: Identifier::from([2u8; 32]),
2148 });
2149 let delete =
2150 DocumentTransition::Delete(DocumentDeleteTransition::V0(DocumentDeleteTransitionV0 {
2151 base,
2152 }));
2153 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2154 owner_id: Identifier::from([8u8; 32]),
2155 transitions: vec![delete],
2156 user_fee_increase: 2,
2157 signature_public_key_id: 7,
2158 signature: BinaryData::new(vec![0xEE; 65]),
2159 }))
2160 }
2161
2162 fn sample_batch_st_empty() -> StateTransition {
2163 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2164 owner_id: Identifier::from([1u8; 32]),
2165 transitions: vec![],
2166 user_fee_increase: 0,
2167 signature_public_key_id: 0,
2168 signature: BinaryData::new(vec![]),
2169 }))
2170 }
2171
2172 fn sample_identity_create_st() -> StateTransition {
2173 StateTransition::IdentityCreate(IdentityCreateTransition::V0(IdentityCreateTransitionV0 {
2174 identity_id: Identifier::from([3u8; 32]),
2175 ..Default::default()
2176 }))
2177 }
2178
2179 fn sample_identity_top_up_st() -> StateTransition {
2180 StateTransition::IdentityTopUp(IdentityTopUpTransition::V0(IdentityTopUpTransitionV0 {
2181 identity_id: Identifier::from([4u8; 32]),
2182 ..Default::default()
2183 }))
2184 }
2185
2186 fn sample_identity_update_st() -> StateTransition {
2187 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
2188 identity_id: Identifier::from([5u8; 32]),
2189 revision: 1,
2190 nonce: 2,
2191 add_public_keys: vec![],
2192 disable_public_keys: vec![],
2193 user_fee_increase: 11,
2194 signature_public_key_id: 33,
2195 signature: BinaryData::new(vec![0xFF; 65]),
2196 }))
2197 }
2198
2199 fn sample_unshield_st() -> StateTransition {
2200 StateTransition::Unshield(UnshieldTransition::V0(UnshieldTransitionV0 {
2201 output_address: Default::default(),
2202 actions: vec![],
2203 unshielding_amount: 0,
2204 anchor: [0u8; 32],
2205 proof: vec![],
2206 binding_signature: [0u8; 64],
2207 }))
2208 }
2209
2210 fn sample_shielded_transfer_st() -> StateTransition {
2211 StateTransition::ShieldedTransfer(ShieldedTransferTransition::V0(
2212 ShieldedTransferTransitionV0 {
2213 actions: vec![],
2214 value_balance: 0,
2215 anchor: [0u8; 32],
2216 proof: vec![],
2217 binding_signature: [0u8; 64],
2218 },
2219 ))
2220 }
2221
2222 fn sample_shielded_withdrawal_st() -> StateTransition {
2223 use crate::identity::core_script::CoreScript;
2224 use crate::withdrawal::Pooling;
2225 StateTransition::ShieldedWithdrawal(ShieldedWithdrawalTransition::V0(
2226 ShieldedWithdrawalTransitionV0 {
2227 actions: vec![],
2228 unshielding_amount: 0,
2229 anchor: [0u8; 32],
2230 proof: vec![],
2231 binding_signature: [0u8; 64],
2232 core_fee_per_byte: 1,
2233 pooling: Pooling::Never,
2234 output_script: CoreScript::from_bytes(vec![]),
2235 },
2236 ))
2237 }
2238
2239 #[test]
2242 fn test_name_for_newly_covered_variants() {
2243 assert_eq!(
2244 sample_data_contract_create_st().name(),
2245 "DataContractCreate"
2246 );
2247 assert_eq!(
2248 sample_data_contract_update_st().name(),
2249 "DataContractUpdate"
2250 );
2251 assert_eq!(sample_identity_create_st().name(), "IdentityCreate");
2252 assert_eq!(sample_identity_top_up_st().name(), "IdentityTopUp");
2253 assert_eq!(sample_identity_update_st().name(), "IdentityUpdate");
2254 assert_eq!(sample_unshield_st().name(), "Unshield");
2255 assert_eq!(sample_shielded_transfer_st().name(), "ShieldedTransfer");
2256 assert_eq!(sample_shielded_withdrawal_st().name(), "ShieldedWithdrawal");
2257
2258 let batch_name = sample_batch_st_with_delete().name();
2261 assert_eq!(batch_name, "DocumentsBatch([Delete])");
2262
2263 let empty_name = sample_batch_st_empty().name();
2265 assert_eq!(empty_name, "DocumentsBatch([])");
2266 }
2267
2268 #[test]
2270 fn test_state_transition_type_for_newly_covered_variants() {
2271 assert_eq!(
2272 sample_data_contract_create_st().state_transition_type(),
2273 StateTransitionType::DataContractCreate
2274 );
2275 assert_eq!(
2276 sample_data_contract_update_st().state_transition_type(),
2277 StateTransitionType::DataContractUpdate
2278 );
2279 assert_eq!(
2280 sample_batch_st_with_delete().state_transition_type(),
2281 StateTransitionType::Batch
2282 );
2283 assert_eq!(
2284 sample_identity_create_st().state_transition_type(),
2285 StateTransitionType::IdentityCreate
2286 );
2287 assert_eq!(
2288 sample_identity_top_up_st().state_transition_type(),
2289 StateTransitionType::IdentityTopUp
2290 );
2291 assert_eq!(
2292 sample_identity_update_st().state_transition_type(),
2293 StateTransitionType::IdentityUpdate
2294 );
2295 assert_eq!(
2296 sample_unshield_st().state_transition_type(),
2297 StateTransitionType::Unshield
2298 );
2299 assert_eq!(
2300 sample_shielded_transfer_st().state_transition_type(),
2301 StateTransitionType::ShieldedTransfer
2302 );
2303 assert_eq!(
2304 sample_shielded_withdrawal_st().state_transition_type(),
2305 StateTransitionType::ShieldedWithdrawal
2306 );
2307 }
2308
2309 #[test]
2314 fn test_active_version_range_contract_and_shielded_branches() {
2315 let contract_v1_range = 9..=LATEST_VERSION;
2318 assert_eq!(
2319 sample_data_contract_create_st().active_version_range(),
2320 contract_v1_range
2321 );
2322 let contract_v1_range = 9..=LATEST_VERSION;
2323 assert_eq!(
2324 sample_data_contract_update_st().active_version_range(),
2325 contract_v1_range
2326 );
2327 assert_eq!(
2329 sample_batch_st_with_delete().active_version_range(),
2330 ALL_VERSIONS
2331 );
2332 assert_eq!(
2334 sample_identity_create_st().active_version_range(),
2335 ALL_VERSIONS
2336 );
2337 assert_eq!(
2338 sample_identity_top_up_st().active_version_range(),
2339 ALL_VERSIONS
2340 );
2341 assert_eq!(
2342 sample_identity_update_st().active_version_range(),
2343 ALL_VERSIONS
2344 );
2345 let shielded_range = 12..=LATEST_VERSION;
2347 assert_eq!(
2348 sample_shielded_transfer_st().active_version_range(),
2349 shielded_range.clone()
2350 );
2351 assert_eq!(
2352 sample_unshield_st().active_version_range(),
2353 shielded_range.clone()
2354 );
2355 assert_eq!(
2356 sample_shielded_withdrawal_st().active_version_range(),
2357 shielded_range
2358 );
2359 }
2360
2361 #[test]
2364 fn test_is_identity_signed_false_for_identity_create_topup_and_shielded() {
2365 assert!(!sample_identity_create_st().is_identity_signed());
2366 assert!(!sample_identity_top_up_st().is_identity_signed());
2367 assert!(!sample_unshield_st().is_identity_signed());
2368 assert!(!sample_shielded_transfer_st().is_identity_signed());
2369 assert!(!sample_shielded_withdrawal_st().is_identity_signed());
2370 }
2371
2372 #[test]
2376 fn test_signature_accessor_for_other_variants() {
2377 assert_eq!(
2379 sample_data_contract_create_st().signature().unwrap().len(),
2380 65
2381 );
2382 assert_eq!(
2383 sample_data_contract_update_st().signature().unwrap().len(),
2384 65
2385 );
2386 assert_eq!(sample_batch_st_with_delete().signature().unwrap().len(), 65);
2387 assert_eq!(sample_identity_update_st().signature().unwrap().len(), 65);
2388
2389 assert!(sample_unshield_st().signature().is_none());
2391 assert!(sample_shielded_transfer_st().signature().is_none());
2392 assert!(sample_shielded_withdrawal_st().signature().is_none());
2393 }
2394
2395 #[test]
2397 fn test_owner_id_accessor_for_other_variants() {
2398 assert_eq!(
2399 sample_data_contract_create_st().owner_id(),
2400 Some(Identifier::from([7u8; 32]))
2401 );
2402 assert_eq!(
2403 sample_data_contract_update_st().owner_id(),
2404 Some(Identifier::from([7u8; 32]))
2405 );
2406 assert_eq!(
2407 sample_batch_st_with_delete().owner_id(),
2408 Some(Identifier::from([8u8; 32]))
2409 );
2410 assert_eq!(
2411 sample_identity_update_st().owner_id(),
2412 Some(Identifier::from([5u8; 32]))
2413 );
2414 assert!(sample_unshield_st().owner_id().is_none());
2416 assert!(sample_shielded_transfer_st().owner_id().is_none());
2417 assert!(sample_shielded_withdrawal_st().owner_id().is_none());
2418 }
2419
2420 #[test]
2423 fn test_user_fee_increase_for_newly_covered_variants() {
2424 assert_eq!(sample_data_contract_create_st().user_fee_increase(), 5);
2425 assert_eq!(sample_data_contract_update_st().user_fee_increase(), 9);
2426 assert_eq!(sample_batch_st_with_delete().user_fee_increase(), 2);
2427 assert_eq!(sample_identity_update_st().user_fee_increase(), 11);
2428 assert_eq!(sample_shielded_transfer_st().user_fee_increase(), 0);
2430 assert_eq!(sample_shielded_withdrawal_st().user_fee_increase(), 0);
2431 assert_eq!(sample_unshield_st().user_fee_increase(), 0);
2432 }
2433
2434 #[test]
2437 fn test_set_user_fee_increase_for_newly_covered_variants() {
2438 let mut st = sample_data_contract_create_st();
2439 st.set_user_fee_increase(42);
2440 assert_eq!(st.user_fee_increase(), 42);
2441
2442 let mut st = sample_data_contract_update_st();
2443 st.set_user_fee_increase(13);
2444 assert_eq!(st.user_fee_increase(), 13);
2445
2446 let mut st = sample_batch_st_with_delete();
2447 st.set_user_fee_increase(101);
2448 assert_eq!(st.user_fee_increase(), 101);
2449
2450 let mut st = sample_identity_update_st();
2451 st.set_user_fee_increase(77);
2452 assert_eq!(st.user_fee_increase(), 77);
2453
2454 let mut shielded = sample_shielded_transfer_st();
2456 shielded.set_user_fee_increase(99);
2457 assert_eq!(shielded.user_fee_increase(), 0);
2458
2459 let mut withdrawal = sample_shielded_withdrawal_st();
2460 withdrawal.set_user_fee_increase(99);
2461 assert_eq!(withdrawal.user_fee_increase(), 0);
2462
2463 let mut unshield = sample_unshield_st();
2464 unshield.set_user_fee_increase(99);
2465 assert_eq!(unshield.user_fee_increase(), 0);
2466 }
2467
2468 #[test]
2472 fn test_set_signature_false_for_shielded_and_identity_create_topup() {
2473 let mut st = sample_unshield_st();
2475 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2476 let mut st = sample_shielded_transfer_st();
2477 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2478 let mut st = sample_shielded_withdrawal_st();
2479 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2480 }
2481
2482 #[test]
2483 fn test_set_signature_true_for_newly_covered_variants() {
2484 let mut st = sample_data_contract_create_st();
2485 assert!(st.set_signature(BinaryData::new(vec![0x11; 65])));
2486 assert_eq!(st.signature().unwrap().as_slice(), &[0x11; 65]);
2487
2488 let mut st = sample_data_contract_update_st();
2489 assert!(st.set_signature(BinaryData::new(vec![0x22; 65])));
2490 assert_eq!(st.signature().unwrap().as_slice(), &[0x22; 65]);
2491
2492 let mut st = sample_batch_st_with_delete();
2493 assert!(st.set_signature(BinaryData::new(vec![0x33; 65])));
2494 assert_eq!(st.signature().unwrap().as_slice(), &[0x33; 65]);
2495
2496 let mut st = sample_identity_update_st();
2497 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
2498 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
2499 }
2500
2501 #[test]
2504 fn test_signature_public_key_id_returns_none_for_non_signed() {
2505 assert!(sample_identity_create_st()
2508 .signature_public_key_id()
2509 .is_none());
2510 assert!(sample_identity_top_up_st()
2511 .signature_public_key_id()
2512 .is_none());
2513 assert!(sample_unshield_st().signature_public_key_id().is_none());
2514 assert!(sample_shielded_transfer_st()
2515 .signature_public_key_id()
2516 .is_none());
2517 assert!(sample_shielded_withdrawal_st()
2518 .signature_public_key_id()
2519 .is_none());
2520 }
2521
2522 #[test]
2523 fn test_signature_public_key_id_for_signed_variants() {
2524 assert_eq!(
2525 sample_data_contract_create_st().signature_public_key_id(),
2526 Some(2)
2527 );
2528 assert_eq!(
2529 sample_data_contract_update_st().signature_public_key_id(),
2530 Some(6)
2531 );
2532 assert_eq!(
2533 sample_batch_st_with_delete().signature_public_key_id(),
2534 Some(7)
2535 );
2536 assert_eq!(
2537 sample_identity_update_st().signature_public_key_id(),
2538 Some(33)
2539 );
2540 }
2541
2542 #[test]
2545 fn test_set_signature_public_key_id_noop_for_non_signed() {
2546 let mut st = sample_identity_create_st();
2549 st.set_signature_public_key_id(100);
2550 assert_eq!(st.signature_public_key_id(), None);
2551
2552 let mut st = sample_identity_top_up_st();
2553 st.set_signature_public_key_id(100);
2554 assert_eq!(st.signature_public_key_id(), None);
2555
2556 let mut st = sample_unshield_st();
2557 st.set_signature_public_key_id(100);
2558 assert_eq!(st.signature_public_key_id(), None);
2559 }
2560
2561 #[test]
2562 fn test_set_signature_public_key_id_updates_for_signed_variants() {
2563 let mut st = sample_data_contract_create_st();
2564 st.set_signature_public_key_id(42);
2565 assert_eq!(st.signature_public_key_id(), Some(42));
2566
2567 let mut st = sample_batch_st_with_delete();
2568 st.set_signature_public_key_id(43);
2569 assert_eq!(st.signature_public_key_id(), Some(43));
2570
2571 let mut st = sample_identity_update_st();
2572 st.set_signature_public_key_id(44);
2573 assert_eq!(st.signature_public_key_id(), Some(44));
2574 }
2575
2576 #[test]
2579 fn test_required_number_of_private_keys_various_variants() {
2580 assert_eq!(
2581 sample_data_contract_create_st().required_number_of_private_keys(),
2582 1
2583 );
2584 assert_eq!(
2585 sample_data_contract_update_st().required_number_of_private_keys(),
2586 1
2587 );
2588 assert_eq!(
2589 sample_batch_st_with_delete().required_number_of_private_keys(),
2590 1
2591 );
2592 assert_eq!(
2593 sample_identity_update_st().required_number_of_private_keys(),
2594 1
2595 );
2596 assert_eq!(
2597 sample_identity_create_st().required_number_of_private_keys(),
2598 1
2599 );
2600 assert_eq!(
2602 sample_shielded_transfer_st().required_number_of_private_keys(),
2603 0
2604 );
2605 assert_eq!(
2606 sample_shielded_withdrawal_st().required_number_of_private_keys(),
2607 0
2608 );
2609 assert_eq!(sample_unshield_st().required_number_of_private_keys(), 0);
2610 }
2611
2612 #[test]
2615 fn test_inputs_none_for_many_variants() {
2616 assert!(sample_data_contract_create_st().inputs().is_none());
2617 assert!(sample_data_contract_update_st().inputs().is_none());
2618 assert!(sample_batch_st_with_delete().inputs().is_none());
2619 assert!(sample_identity_create_st().inputs().is_none());
2620 assert!(sample_identity_top_up_st().inputs().is_none());
2621 assert!(sample_identity_update_st().inputs().is_none());
2622 assert!(sample_unshield_st().inputs().is_none());
2624 assert!(sample_shielded_transfer_st().inputs().is_none());
2625 assert!(sample_shielded_withdrawal_st().inputs().is_none());
2626 }
2627
2628 #[test]
2633 fn test_optional_asset_lock_proof_returns_none_for_wildcard_arms() {
2634 assert!(sample_data_contract_create_st()
2635 .optional_asset_lock_proof()
2636 .is_none());
2637 assert!(sample_data_contract_update_st()
2638 .optional_asset_lock_proof()
2639 .is_none());
2640 assert!(sample_batch_st_with_delete()
2641 .optional_asset_lock_proof()
2642 .is_none());
2643 assert!(sample_identity_update_st()
2644 .optional_asset_lock_proof()
2645 .is_none());
2646 assert!(sample_unshield_st().optional_asset_lock_proof().is_none());
2647 assert!(sample_shielded_transfer_st()
2648 .optional_asset_lock_proof()
2649 .is_none());
2650 assert!(sample_shielded_withdrawal_st()
2651 .optional_asset_lock_proof()
2652 .is_none());
2653 }
2654
2655 #[test]
2659 fn test_required_asset_lock_balance_errors_for_other_non_asset_lock_variants() {
2660 let platform_version = PlatformVersion::latest();
2661
2662 let cases: Vec<(&str, StateTransition)> = vec![
2663 ("DataContractCreate", sample_data_contract_create_st()),
2664 ("DataContractUpdate", sample_data_contract_update_st()),
2665 ("Batch", sample_batch_st_with_delete()),
2666 ("IdentityUpdate", sample_identity_update_st()),
2667 ("MasternodeVote", sample_masternode_vote_st()),
2668 ("Unshield", sample_unshield_st()),
2669 ("ShieldedTransfer", sample_shielded_transfer_st()),
2670 ("ShieldedWithdrawal", sample_shielded_withdrawal_st()),
2671 ];
2672
2673 for (label, st) in cases {
2674 let err = st
2675 .required_asset_lock_balance_for_processing_start(platform_version)
2676 .expect_err(&format!("expected error for {label}"));
2677 match err {
2678 ProtocolError::CorruptedCodeExecution(msg) => {
2679 assert!(
2680 msg.contains("is not an asset lock transaction"),
2681 "unexpected error for {label}: {msg}"
2682 );
2683 }
2684 other => panic!("expected CorruptedCodeExecution for {label}, got {other:?}"),
2685 }
2686 }
2687 }
2688
2689 #[test]
2695 fn test_unique_identifiers_non_empty_for_other_variants() {
2696 for st in [
2697 sample_data_contract_create_st(),
2698 sample_data_contract_update_st(),
2699 sample_batch_st_with_delete(),
2700 sample_identity_create_st(),
2701 sample_identity_top_up_st(),
2702 sample_identity_update_st(),
2703 ] {
2704 let ids = st.unique_identifiers();
2705 assert!(!ids.is_empty(), "unique_identifiers should not be empty");
2706 }
2707 }
2708
2709 #[test]
2713 fn test_security_level_requirement_returns_none_for_non_signed_variants() {
2714 let purpose = Purpose::AUTHENTICATION;
2715 assert!(sample_identity_create_st()
2716 .security_level_requirement(purpose)
2717 .is_none());
2718 assert!(sample_identity_top_up_st()
2719 .security_level_requirement(purpose)
2720 .is_none());
2721 assert!(sample_unshield_st()
2722 .security_level_requirement(purpose)
2723 .is_none());
2724 assert!(sample_shielded_transfer_st()
2725 .security_level_requirement(purpose)
2726 .is_none());
2727 assert!(sample_shielded_withdrawal_st()
2728 .security_level_requirement(purpose)
2729 .is_none());
2730 }
2731
2732 #[test]
2733 fn test_purpose_requirement_returns_none_for_non_signed_variants() {
2734 assert!(sample_identity_create_st().purpose_requirement().is_none());
2735 assert!(sample_identity_top_up_st().purpose_requirement().is_none());
2736 assert!(sample_unshield_st().purpose_requirement().is_none());
2737 assert!(sample_shielded_transfer_st()
2738 .purpose_requirement()
2739 .is_none());
2740 assert!(sample_shielded_withdrawal_st()
2741 .purpose_requirement()
2742 .is_none());
2743 }
2744
2745 #[test]
2747 fn test_from_outer_data_contract_create_into_state_transition() {
2748 let outer: DataContractCreateTransition =
2749 DataContractCreateTransition::V0(DataContractCreateTransitionV0 {
2750 data_contract: sample_data_contract_in_serialization_format(),
2751 identity_nonce: 1,
2752 user_fee_increase: 0,
2753 signature_public_key_id: 0,
2754 signature: Default::default(),
2755 });
2756 let st: StateTransition = outer.into();
2757 assert!(matches!(st, StateTransition::DataContractCreate(_)));
2758 }
2759
2760 #[test]
2761 fn test_from_outer_data_contract_update_into_state_transition() {
2762 let outer: DataContractUpdateTransition =
2763 DataContractUpdateTransition::V0(DataContractUpdateTransitionV0 {
2764 identity_contract_nonce: 2,
2765 data_contract: sample_data_contract_in_serialization_format(),
2766 user_fee_increase: 0,
2767 signature_public_key_id: 0,
2768 signature: Default::default(),
2769 });
2770 let st: StateTransition = outer.into();
2771 assert!(matches!(st, StateTransition::DataContractUpdate(_)));
2772 }
2773
2774 #[test]
2775 fn test_from_outer_batch_into_state_transition() {
2776 let outer: BatchTransition = BatchTransition::V0(BatchTransitionV0::default());
2777 let st: StateTransition = outer.into();
2778 assert!(matches!(st, StateTransition::Batch(_)));
2779 }
2780
2781 #[test]
2782 fn test_from_outer_identity_create_into_state_transition() {
2783 let outer: IdentityCreateTransition =
2784 IdentityCreateTransition::V0(IdentityCreateTransitionV0::default());
2785 let st: StateTransition = outer.into();
2786 assert!(matches!(st, StateTransition::IdentityCreate(_)));
2787 }
2788
2789 #[test]
2790 fn test_from_outer_identity_update_into_state_transition() {
2791 let outer: IdentityUpdateTransition =
2792 IdentityUpdateTransition::V0(IdentityUpdateTransitionV0::default());
2793 let st: StateTransition = outer.into();
2794 assert!(matches!(st, StateTransition::IdentityUpdate(_)));
2795 }
2796
2797 #[test]
2800 fn test_transaction_id_and_clone_for_identity_update() {
2801 let st = sample_identity_update_st();
2802 let id_a = st.transaction_id().expect("hash should succeed");
2803 let cloned = st.clone();
2804 let id_b = cloned.transaction_id().expect("hash should succeed");
2805 assert_eq!(id_a, id_b);
2806 assert_eq!(id_a.len(), 32);
2807 }
2808
2809 #[test]
2810 fn test_transaction_id_and_clone_for_data_contract_create() {
2811 let st = sample_data_contract_create_st();
2812 let id_a = st.transaction_id().expect("hash should succeed");
2813 let cloned = st.clone();
2814 let id_b = cloned.transaction_id().expect("hash should succeed");
2815 assert_eq!(id_a, id_b);
2816 assert_eq!(id_a.len(), 32);
2817 }
2818
2819 #[test]
2821 fn test_serialize_roundtrip_identity_update() {
2822 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2823 let original = sample_identity_update_st();
2824 let bytes =
2825 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2826 let restored =
2827 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2828 assert_eq!(original, restored);
2829 }
2830
2831 #[test]
2832 fn test_serialize_roundtrip_data_contract_update() {
2833 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2834 let original = sample_data_contract_update_st();
2835 let bytes =
2836 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2837 let restored =
2838 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2839 assert_eq!(original, restored);
2840 }
2841
2842 #[test]
2843 fn test_serialize_roundtrip_batch_empty() {
2844 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2845 let original = sample_batch_st_empty();
2846 let bytes =
2847 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2848 let restored =
2849 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2850 assert_eq!(original, restored);
2851 }
2852
2853 #[cfg(all(feature = "state-transitions", feature = "validation"))]
2860 #[test]
2861 fn test_deserialize_from_bytes_in_version_returns_not_active_error() {
2862 use crate::serialization::PlatformSerializable;
2863
2864 let original = sample_shielded_transfer_st();
2866 let bytes =
2867 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2868
2869 let low_version = PlatformVersion::get(1).expect("platform version 1 exists");
2873 assert!(
2874 low_version.protocol_version < 12,
2875 "expected sub-12 version for this test, got {}",
2876 low_version.protocol_version
2877 );
2878
2879 let err = StateTransition::deserialize_from_bytes_in_version(&bytes, low_version)
2880 .expect_err("expected StateTransitionIsNotActiveError for sub-12 protocol");
2881 match err {
2882 ProtocolError::StateTransitionError(
2883 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
2884 state_transition_type,
2885 active_version_range,
2886 current_protocol_version,
2887 },
2888 ) => {
2889 assert_eq!(state_transition_type, "ShieldedTransfer");
2890 assert_eq!(current_protocol_version, low_version.protocol_version);
2891 assert!(active_version_range.start() >= &12);
2892 }
2893 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
2894 }
2895 }
2896
2897 use crate::state_transition::identity_credit_transfer_to_addresses_transition::v0::IdentityCreditTransferToAddressesTransitionV0;
2910 use crate::state_transition::identity_credit_transfer_to_addresses_transition::IdentityCreditTransferToAddressesTransition;
2911 use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1;
2912 use crate::withdrawal::Pooling as WithdrawalPooling;
2913
2914 fn sample_withdrawal_v1_st() -> StateTransition {
2915 let v1 = IdentityCreditWithdrawalTransitionV1 {
2916 identity_id: Identifier::from([12u8; 32]),
2917 amount: 777,
2918 core_fee_per_byte: 2,
2919 pooling: WithdrawalPooling::Standard,
2920 output_script: None,
2921 nonce: 9,
2922 user_fee_increase: 4,
2923 signature_public_key_id: 21,
2924 signature: BinaryData::new(vec![0x12; 65]),
2925 };
2926 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(v1))
2927 }
2928
2929 fn sample_credit_transfer_to_addresses_st() -> StateTransition {
2930 let v0 = IdentityCreditTransferToAddressesTransitionV0 {
2931 identity_id: Identifier::from([13u8; 32]),
2932 ..Default::default()
2933 };
2934 StateTransition::IdentityCreditTransferToAddresses(
2935 IdentityCreditTransferToAddressesTransition::V0(v0),
2936 )
2937 }
2938
2939 fn sample_address_credit_withdrawal_st() -> StateTransition {
2940 use crate::state_transition::address_credit_withdrawal_transition::v0::AddressCreditWithdrawalTransitionV0;
2941 StateTransition::AddressCreditWithdrawal(AddressCreditWithdrawalTransition::V0(
2942 AddressCreditWithdrawalTransitionV0::default(),
2943 ))
2944 }
2945
2946 fn sample_shield_from_asset_lock_st() -> StateTransition {
2947 use crate::state_transition::shield_from_asset_lock_transition::v0::ShieldFromAssetLockTransitionV0;
2948 StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V0(
2949 ShieldFromAssetLockTransitionV0 {
2950 asset_lock_proof: Default::default(),
2951 actions: vec![],
2952 value_balance: 100,
2953 anchor: [0u8; 32],
2954 proof: vec![],
2955 binding_signature: [0u8; 64],
2956 surplus_output: None,
2957 signature: BinaryData::new(vec![0x55; 65]),
2958 },
2959 ))
2960 }
2961
2962 #[test]
2965 fn test_withdrawal_v1_name_and_type() {
2966 let st = sample_withdrawal_v1_st();
2967 assert_eq!(st.name(), "IdentityCreditWithdrawal");
2968 assert_eq!(
2969 st.state_transition_type(),
2970 StateTransitionType::IdentityCreditWithdrawal
2971 );
2972 }
2973
2974 #[test]
2975 fn test_withdrawal_v1_is_identity_signed_true() {
2976 assert!(sample_withdrawal_v1_st().is_identity_signed());
2977 }
2978
2979 #[test]
2980 fn test_withdrawal_v1_signature_and_owner_and_key_id() {
2981 let st = sample_withdrawal_v1_st();
2982 let sig = st.signature().expect("V1 withdrawal has a signature");
2984 assert_eq!(sig.as_slice(), &[0x12; 65]);
2985 assert_eq!(st.owner_id(), Some(Identifier::from([12u8; 32])));
2987 assert_eq!(st.signature_public_key_id(), Some(21));
2988 assert_eq!(st.user_fee_increase(), 4);
2989 }
2990
2991 #[test]
2992 fn test_withdrawal_v1_set_signature_and_fee_and_key_id() {
2993 let mut st = sample_withdrawal_v1_st();
2994 assert!(st.set_signature(BinaryData::new(vec![0x99; 65])));
2995 assert_eq!(st.signature().unwrap().as_slice(), &[0x99; 65]);
2996
2997 st.set_user_fee_increase(33);
2998 assert_eq!(st.user_fee_increase(), 33);
2999
3000 st.set_signature_public_key_id(64);
3001 assert_eq!(st.signature_public_key_id(), Some(64));
3002 }
3003
3004 #[test]
3005 fn test_withdrawal_v1_serialize_roundtrip_via_state_transition() {
3006 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3007 let original = sample_withdrawal_v1_st();
3008 let bytes = PlatformSerializable::serialize_to_bytes(&original).expect("serialize ok");
3009 let restored = StateTransition::deserialize_from_bytes(&bytes).expect("deserialize ok");
3010 assert_eq!(original, restored);
3011 match restored {
3014 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(
3015 _,
3016 )) => {}
3017 other => panic!("expected V1 inner variant, got: {:?}", other),
3018 }
3019 }
3020
3021 #[test]
3022 fn test_withdrawal_v1_transaction_id_differs_from_v0() {
3023 let v0 = sample_withdrawal_st();
3025 let v1 = sample_withdrawal_v1_st();
3026 let id_v0 = v0.transaction_id().expect("v0 hash");
3027 let id_v1 = v1.transaction_id().expect("v1 hash");
3028 assert_ne!(id_v0, id_v1);
3029 }
3030
3031 #[test]
3032 fn test_withdrawal_v1_required_asset_lock_balance_errors() {
3033 let err = sample_withdrawal_v1_st()
3034 .required_asset_lock_balance_for_processing_start(PlatformVersion::latest())
3035 .expect_err("withdrawal is not an asset lock ST");
3036 matches!(err, ProtocolError::CorruptedCodeExecution(_));
3037 }
3038
3039 #[test]
3042 fn test_credit_transfer_to_addresses_name_and_type() {
3043 let st = sample_credit_transfer_to_addresses_st();
3044 assert_eq!(st.name(), "IdentityCreditTransferToAddresses");
3045 assert_eq!(
3046 st.state_transition_type(),
3047 StateTransitionType::IdentityCreditTransferToAddresses
3048 );
3049 }
3050
3051 #[test]
3052 fn test_credit_transfer_to_addresses_signature_some_and_owner_some() {
3053 let st = sample_credit_transfer_to_addresses_st();
3054 assert!(st.signature().is_some(), "has a signature field");
3055 assert_eq!(st.owner_id(), Some(Identifier::from([13u8; 32])));
3056 }
3057
3058 #[test]
3059 fn test_credit_transfer_to_addresses_is_identity_signed_true() {
3060 assert!(sample_credit_transfer_to_addresses_st().is_identity_signed());
3062 }
3063
3064 #[test]
3065 fn test_credit_transfer_to_addresses_inputs_none_active_range_11_latest() {
3066 let st = sample_credit_transfer_to_addresses_st();
3067 assert!(st.inputs().is_none());
3068 let range = st.active_version_range();
3070 assert_eq!(*range.start(), 11);
3071 assert_eq!(*range.end(), LATEST_VERSION);
3072 }
3073
3074 #[test]
3075 fn test_credit_transfer_to_addresses_set_signature_returns_true() {
3076 let mut st = sample_credit_transfer_to_addresses_st();
3077 let ok = st.set_signature(BinaryData::new(vec![0x77; 65]));
3078 assert!(ok);
3079 assert_eq!(st.signature().unwrap().as_slice(), &[0x77; 65]);
3080 }
3081
3082 #[test]
3083 fn test_credit_transfer_to_addresses_from_outer_enum() {
3084 let outer = IdentityCreditTransferToAddressesTransition::V0(
3085 IdentityCreditTransferToAddressesTransitionV0::default(),
3086 );
3087 let st: StateTransition = outer.into();
3088 assert!(matches!(
3089 st,
3090 StateTransition::IdentityCreditTransferToAddresses(_)
3091 ));
3092 }
3093
3094 #[test]
3095 fn test_credit_transfer_to_addresses_user_fee_increase_setter() {
3096 let mut st = sample_credit_transfer_to_addresses_st();
3097 st.set_user_fee_increase(55);
3098 assert_eq!(st.user_fee_increase(), 55);
3099 }
3100
3101 #[test]
3104 fn test_address_credit_withdrawal_name_type_and_accessors() {
3105 let st = sample_address_credit_withdrawal_st();
3106 assert_eq!(st.name(), "AddressCreditWithdrawal");
3107 assert_eq!(
3108 st.state_transition_type(),
3109 StateTransitionType::AddressCreditWithdrawal
3110 );
3111 assert!(st.signature().is_none());
3113 assert!(st.owner_id().is_none());
3115 assert!(st.inputs().is_some());
3117 }
3118
3119 #[test]
3120 fn test_address_credit_withdrawal_set_signature_returns_false() {
3121 let mut st = sample_address_credit_withdrawal_st();
3122 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3123 }
3124
3125 #[test]
3126 fn test_address_credit_withdrawal_is_identity_signed_true() {
3127 assert!(sample_address_credit_withdrawal_st().is_identity_signed());
3130 }
3131
3132 #[test]
3133 fn test_address_credit_withdrawal_active_range_is_11_latest() {
3134 let range = sample_address_credit_withdrawal_st().active_version_range();
3135 assert_eq!(*range.start(), 11);
3136 assert_eq!(*range.end(), LATEST_VERSION);
3137 }
3138
3139 #[test]
3142 fn test_shield_from_asset_lock_name_type_and_accessors() {
3143 let st = sample_shield_from_asset_lock_st();
3144 assert_eq!(st.name(), "ShieldFromAssetLock");
3145 assert_eq!(
3146 st.state_transition_type(),
3147 StateTransitionType::ShieldFromAssetLock
3148 );
3149 let sig = st
3151 .signature()
3152 .expect("shield-from-asset-lock has signature");
3153 assert_eq!(sig.as_slice(), &[0x55; 65]);
3154 assert!(st.owner_id().is_none());
3156 }
3157
3158 #[test]
3159 fn test_shield_from_asset_lock_is_not_identity_signed() {
3160 assert!(!sample_shield_from_asset_lock_st().is_identity_signed());
3161 }
3162
3163 #[test]
3164 fn test_shield_from_asset_lock_optional_asset_lock_proof_some() {
3165 let st = sample_shield_from_asset_lock_st();
3169 assert!(st.optional_asset_lock_proof().is_some());
3170 }
3171
3172 #[test]
3173 fn test_shield_from_asset_lock_user_fee_increase_is_zero_and_setter_noop() {
3174 let mut st = sample_shield_from_asset_lock_st();
3175 assert_eq!(st.user_fee_increase(), 0);
3176 st.set_user_fee_increase(123);
3177 assert_eq!(st.user_fee_increase(), 0);
3179 }
3180
3181 #[test]
3182 fn test_shield_from_asset_lock_set_signature_returns_true() {
3183 let mut st = sample_shield_from_asset_lock_st();
3184 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
3185 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
3186 }
3187
3188 #[test]
3189 fn test_shield_from_asset_lock_required_asset_lock_balance_succeeds() {
3190 let st = sample_shield_from_asset_lock_st();
3193 let result = st.required_asset_lock_balance_for_processing_start(PlatformVersion::latest());
3194 assert!(
3195 result.is_ok(),
3196 "ShieldFromAssetLock should return Ok, got {:?}",
3197 result
3198 );
3199 }
3200
3201 #[test]
3202 fn test_shield_from_asset_lock_active_range_12_latest() {
3203 let range = sample_shield_from_asset_lock_st().active_version_range();
3204 assert_eq!(*range.start(), 12);
3205 assert_eq!(*range.end(), LATEST_VERSION);
3206 }
3207
3208 #[test]
3212 fn test_batch_with_token_transfer_name_contains_token_transfer() {
3213 use crate::state_transition::batch_transition::batched_transition::token_transition::TokenTransition as TT;
3214 use crate::state_transition::batch_transition::batched_transition::BatchedTransition;
3215 use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0;
3216 use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition;
3217 use crate::state_transition::batch_transition::token_transfer_transition::v0::TokenTransferTransitionV0;
3218 use crate::state_transition::batch_transition::token_transfer_transition::TokenTransferTransition;
3219 use crate::state_transition::batch_transition::BatchTransitionV1;
3220
3221 let base = TokenBaseTransition::V0(TokenBaseTransitionV0 {
3222 identity_contract_nonce: 1,
3223 token_contract_position: 0,
3224 data_contract_id: Identifier::from([1u8; 32]),
3225 token_id: Identifier::from([2u8; 32]),
3226 using_group_info: None,
3227 });
3228 let token_transfer = TokenTransferTransition::V0(TokenTransferTransitionV0 {
3229 base,
3230 amount: 100,
3231 recipient_id: Identifier::from([3u8; 32]),
3232 public_note: None,
3233 shared_encrypted_note: None,
3234 private_encrypted_note: None,
3235 });
3236
3237 let batch = BatchTransition::V1(BatchTransitionV1 {
3239 owner_id: Identifier::from([9u8; 32]),
3240 transitions: vec![BatchedTransition::Token(TT::Transfer(token_transfer))],
3241 user_fee_increase: 0,
3242 signature_public_key_id: 0,
3243 signature: BinaryData::new(vec![0u8; 65]),
3244 });
3245 let st = StateTransition::Batch(batch);
3246
3247 assert_eq!(st.name(), "DocumentsBatch([TokenTransfer])");
3248 }
3249
3250 #[test]
3257 fn test_transaction_id_length_32_for_new_variants() {
3258 for st in [
3259 sample_withdrawal_v1_st(),
3260 sample_credit_transfer_to_addresses_st(),
3261 sample_shield_from_asset_lock_st(),
3262 ] {
3263 let id = st.transaction_id().expect("hash");
3264 assert_eq!(id.len(), 32);
3265 }
3266 }
3267
3268 #[test]
3274 fn test_clone_eq_for_new_variants() {
3275 let cases = [
3276 sample_withdrawal_v1_st(),
3277 sample_credit_transfer_to_addresses_st(),
3278 sample_address_credit_withdrawal_st(),
3279 sample_shield_from_asset_lock_st(),
3280 ];
3281 for st in cases {
3282 let cloned = st.clone();
3283 assert_eq!(st, cloned, "clone must be equal for {}", st.name());
3284 }
3285 }
3286
3287 #[test]
3293 fn test_unique_identifiers_for_address_and_shielded_variants() {
3294 for st in [
3299 sample_address_credit_withdrawal_st(),
3300 sample_shield_from_asset_lock_st(),
3301 sample_credit_transfer_to_addresses_st(),
3302 sample_withdrawal_v1_st(),
3303 ] {
3304 let _ids = st.unique_identifiers();
3308 }
3309 assert!(!sample_withdrawal_v1_st().unique_identifiers().is_empty());
3311 assert!(!sample_credit_transfer_to_addresses_st()
3312 .unique_identifiers()
3313 .is_empty());
3314 }
3315
3316 #[cfg(all(
3328 feature = "state-transition-signing",
3329 feature = "core_key_wallet",
3330 feature = "bls-signatures"
3331 ))]
3332 #[tokio::test]
3333 async fn sign_with_core_signer_matches_sign_by_private_key_byte_for_byte() {
3334 use async_trait::async_trait;
3335 use dashcore::secp256k1::{
3336 ecdsa, rand::rngs::OsRng, Message, PublicKey, Secp256k1, SecretKey,
3337 };
3338 use key_wallet::bip32::DerivationPath;
3339 use key_wallet::signer::{Signer as KwSigner, SignerMethod};
3340
3341 #[derive(Debug)]
3346 struct FixedKeySigner {
3347 secret: SecretKey,
3348 public: PublicKey,
3349 }
3350
3351 #[async_trait]
3352 impl KwSigner for FixedKeySigner {
3353 type Error = String;
3354
3355 fn supported_methods(&self) -> &[SignerMethod] {
3356 &[SignerMethod::Digest]
3357 }
3358
3359 async fn sign_ecdsa(
3360 &self,
3361 _path: &DerivationPath,
3362 sighash: [u8; 32],
3363 ) -> Result<(ecdsa::Signature, PublicKey), Self::Error> {
3364 let secp = Secp256k1::new();
3365 let msg = Message::from_digest(sighash);
3366 let sig = secp.sign_ecdsa(&msg, &self.secret);
3367 Ok((sig, self.public))
3368 }
3369
3370 async fn public_key(&self, _path: &DerivationPath) -> Result<PublicKey, Self::Error> {
3371 Ok(self.public)
3372 }
3373 }
3374
3375 let secp = Secp256k1::new();
3380 let (secret_key, public_key) = secp.generate_keypair(&mut OsRng);
3381 let private_key_bytes = secret_key.secret_bytes();
3382
3383 let signer = FixedKeySigner {
3384 secret: secret_key,
3385 public: public_key,
3386 };
3387 let path = DerivationPath::default();
3388
3389 let mut st_legacy = sample_transfer_st();
3393 let mut st_signer = sample_transfer_st();
3394
3395 assert_eq!(
3397 st_legacy.signable_bytes().expect("legacy signable_bytes"),
3398 st_signer.signable_bytes().expect("signer signable_bytes"),
3399 "signable_bytes pre-image must match across copies"
3400 );
3401
3402 let bls = crate::bls::native_bls::NativeBlsModule;
3407 st_legacy
3408 .sign_by_private_key(&private_key_bytes, KeyType::ECDSA_HASH160, &bls)
3409 .expect("sign_by_private_key");
3410
3411 st_signer
3414 .sign_with_core_signer(&path, &signer)
3415 .await
3416 .expect("sign_with_core_signer");
3417
3418 let sig_legacy = st_legacy.signature().expect("legacy signature set");
3419 let sig_signer = st_signer.signature().expect("signer signature set");
3420
3421 assert_eq!(
3422 sig_legacy.as_slice().len(),
3423 65,
3424 "legacy ECDSA signature must be 65 bytes (recoverable compact)"
3425 );
3426 assert_eq!(
3427 sig_signer.as_slice().len(),
3428 65,
3429 "signer ECDSA signature must be 65 bytes (recoverable compact)"
3430 );
3431 assert_eq!(
3432 sig_legacy.as_slice(),
3433 sig_signer.as_slice(),
3434 "sign_with_core_signer must produce byte-identical output to sign_by_private_key"
3435 );
3436 }
3437}