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