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
8use platform_value::{BinaryData, Identifier};
9pub use state_transition_types::*;
10
11use bincode::{Decode, Encode};
12#[cfg(any(
13 feature = "state-transition-signing",
14 feature = "state-transition-validation"
15))]
16use dashcore::signer;
17#[cfg(feature = "state-transition-validation")]
18use dashcore::signer::double_sha;
19use platform_serialization_derive::{PlatformDeserialize, PlatformSerialize, PlatformSignable};
20use platform_version::version::{PlatformVersion, ProtocolVersion, ALL_VERSIONS, LATEST_VERSION};
21
22#[cfg(any(
23 feature = "state-transition-signing",
24 feature = "state-transition-validation"
25))]
26use crate::BlsModule;
27use crate::ProtocolError;
28
29mod state_transition_types;
30
31pub mod state_transition_factory;
32
33pub mod errors;
34#[cfg(feature = "state-transition-signing")]
35use crate::util::hash::ripemd160_sha256;
36use crate::util::hash::{hash_double_to_vec, hash_single};
37
38pub mod proof_result;
39mod serialization;
40pub mod state_transitions;
41mod traits;
42
43#[cfg(feature = "state-transition-validation")]
46use crate::consensus::basic::UnsupportedFeatureError;
47#[cfg(feature = "state-transition-signing")]
48use crate::consensus::signature::InvalidSignaturePublicKeySecurityLevelError;
49#[cfg(feature = "state-transition-validation")]
50use crate::consensus::signature::{
51 InvalidStateTransitionSignatureError, PublicKeyIsDisabledError, SignatureError,
52};
53#[cfg(feature = "state-transition-validation")]
54use crate::consensus::ConsensusError;
55pub use traits::*;
56
57use crate::address_funds::PlatformAddress;
58use crate::data_contract::serialized_version::DataContractInSerializationFormat;
59use crate::fee::Credits;
60#[cfg(any(
61 feature = "state-transition-signing",
62 feature = "state-transition-validation"
63))]
64use crate::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
65#[cfg(feature = "state-transition-signing")]
66use crate::identity::signer::Signer;
67use crate::identity::state_transition::OptionallyAssetLockProved;
68use crate::identity::Purpose;
69#[cfg(any(
70 feature = "state-transition-signing",
71 feature = "state-transition-validation"
72))]
73use crate::identity::{IdentityPublicKey, KeyType};
74use crate::identity::{KeyID, SecurityLevel};
75use crate::prelude::{AddressNonce, AssetLockProof, UserFeeIncrease};
76use crate::serialization::{PlatformDeserializable, Signable};
77use crate::state_transition::address_credit_withdrawal_transition::{
78 AddressCreditWithdrawalTransition, AddressCreditWithdrawalTransitionSignable,
79};
80use crate::state_transition::address_funding_from_asset_lock_transition::{
81 AddressFundingFromAssetLockTransition, AddressFundingFromAssetLockTransitionSignable,
82};
83use crate::state_transition::address_funds_transfer_transition::{
84 AddressFundsTransferTransition, AddressFundsTransferTransitionSignable,
85};
86use crate::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0;
87use crate::state_transition::batch_transition::batched_transition::BatchedTransitionRef;
88#[cfg(feature = "state-transition-signing")]
89use crate::state_transition::batch_transition::resolvers::v0::BatchTransitionResolversV0;
90use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionSignable};
91use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0;
92use crate::state_transition::data_contract_create_transition::{
93 DataContractCreateTransition, DataContractCreateTransitionSignable,
94};
95use crate::state_transition::data_contract_update_transition::accessors::DataContractUpdateTransitionAccessorsV0;
96use crate::state_transition::data_contract_update_transition::{
97 DataContractUpdateTransition, DataContractUpdateTransitionSignable,
98};
99#[cfg(feature = "state-transition-signing")]
100use crate::state_transition::errors::InvalidSignaturePublicKeyError;
101#[cfg(all(feature = "state-transitions", feature = "validation"))]
102use crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError;
103#[cfg(feature = "state-transition-signing")]
104use crate::state_transition::errors::WrongPublicKeyPurposeError;
105#[cfg(feature = "state-transition-validation")]
106use crate::state_transition::errors::{
107 InvalidIdentityPublicKeyTypeError, PublicKeyMismatchError, StateTransitionIsNotSignedError,
108};
109use crate::state_transition::identity_create_from_addresses_transition::{
110 IdentityCreateFromAddressesTransition, IdentityCreateFromAddressesTransitionSignable,
111};
112use crate::state_transition::identity_create_from_shielded_pool_transition::{
113 IdentityCreateFromShieldedPoolTransition, IdentityCreateFromShieldedPoolTransitionSignable,
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 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method($args),
184 }
185 };
186 ($state_transition:expr, $method:ident ) => {
187 match $state_transition {
188 StateTransition::DataContractCreate(st) => st.$method(),
189 StateTransition::DataContractUpdate(st) => st.$method(),
190 StateTransition::Batch(st) => st.$method(),
191 StateTransition::IdentityCreate(st) => st.$method(),
192 StateTransition::IdentityTopUp(st) => st.$method(),
193 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
194 StateTransition::IdentityUpdate(st) => st.$method(),
195 StateTransition::IdentityCreditTransfer(st) => st.$method(),
196 StateTransition::MasternodeVote(st) => st.$method(),
197 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
198 StateTransition::IdentityCreateFromAddresses(st) => st.$method(),
199 StateTransition::IdentityTopUpFromAddresses(st) => st.$method(),
200 StateTransition::AddressFundsTransfer(st) => st.$method(),
201 StateTransition::AddressFundingFromAssetLock(st) => st.$method(),
202 StateTransition::AddressCreditWithdrawal(st) => st.$method(),
203 StateTransition::Shield(st) => st.$method(),
204 StateTransition::ShieldedTransfer(st) => st.$method(),
205 StateTransition::Unshield(st) => st.$method(),
206 StateTransition::ShieldFromAssetLock(st) => st.$method(),
207 StateTransition::ShieldedWithdrawal(st) => st.$method(),
208 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method(),
209 }
210 };
211}
212
213macro_rules! call_getter_method_identity_signed {
214 ($state_transition:expr, $method:ident, $args:tt ) => {
215 match $state_transition {
216 StateTransition::DataContractCreate(st) => Some(st.$method($args)),
217 StateTransition::DataContractUpdate(st) => Some(st.$method($args)),
218 StateTransition::Batch(st) => Some(st.$method($args)),
219 StateTransition::IdentityCreate(_) => None,
220 StateTransition::IdentityTopUp(_) => None,
221 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method($args)),
222 StateTransition::IdentityUpdate(st) => Some(st.$method($args)),
223 StateTransition::IdentityCreditTransfer(st) => Some(st.$method($args)),
224 StateTransition::MasternodeVote(st) => Some(st.$method($args)),
225 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method($args)),
226 StateTransition::IdentityCreateFromAddresses(_) => None,
227 StateTransition::IdentityTopUpFromAddresses(_) => None,
228 StateTransition::AddressFundsTransfer(_) => None,
229 StateTransition::AddressFundingFromAssetLock(_) => None,
230 StateTransition::AddressCreditWithdrawal(_) => None,
231 StateTransition::Shield(_) => None,
232 StateTransition::ShieldedTransfer(_) => None,
233 StateTransition::Unshield(_) => None,
234 StateTransition::ShieldFromAssetLock(_) => None,
235 StateTransition::ShieldedWithdrawal(_) => None,
236 StateTransition::IdentityCreateFromShieldedPool(_) => None,
237 }
238 };
239 ($state_transition:expr, $method:ident ) => {
240 match $state_transition {
241 StateTransition::DataContractCreate(st) => Some(st.$method()),
242 StateTransition::DataContractUpdate(st) => Some(st.$method()),
243 StateTransition::Batch(st) => Some(st.$method()),
244 StateTransition::IdentityCreate(_) => None,
245 StateTransition::IdentityTopUp(_) => None,
246 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method()),
247 StateTransition::IdentityUpdate(st) => Some(st.$method()),
248 StateTransition::IdentityCreditTransfer(st) => Some(st.$method()),
249 StateTransition::MasternodeVote(st) => Some(st.$method()),
250 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method()),
251 StateTransition::IdentityCreateFromAddresses(_) => None,
252 StateTransition::IdentityTopUpFromAddresses(_) => None,
253 StateTransition::AddressFundsTransfer(_) => None,
254 StateTransition::AddressFundingFromAssetLock(_) => None,
255 StateTransition::AddressCreditWithdrawal(_) => None,
256 StateTransition::Shield(_) => None,
257 StateTransition::ShieldedTransfer(_) => None,
258 StateTransition::Unshield(_) => None,
259 StateTransition::ShieldFromAssetLock(_) => None,
260 StateTransition::ShieldedWithdrawal(_) => None,
261 StateTransition::IdentityCreateFromShieldedPool(_) => None,
262 }
263 };
264}
265
266macro_rules! call_method_identity_signed {
267 ($state_transition:expr, $method:ident, $args:tt ) => {
268 match $state_transition {
269 StateTransition::DataContractCreate(st) => st.$method($args),
270 StateTransition::DataContractUpdate(st) => st.$method($args),
271 StateTransition::Batch(st) => st.$method($args),
272 StateTransition::IdentityCreate(_st) => {}
273 StateTransition::IdentityTopUp(_st) => {}
274 StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
275 StateTransition::IdentityUpdate(st) => st.$method($args),
276 StateTransition::IdentityCreditTransfer(st) => st.$method($args),
277 StateTransition::MasternodeVote(st) => st.$method($args),
278 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
279 StateTransition::IdentityCreateFromAddresses(_) => {}
280 StateTransition::IdentityTopUpFromAddresses(_) => {}
281 StateTransition::AddressFundsTransfer(_) => {}
282 StateTransition::AddressFundingFromAssetLock(_) => {}
283 StateTransition::AddressCreditWithdrawal(_) => {}
284 StateTransition::Shield(_) => {}
285 StateTransition::ShieldedTransfer(_) => {}
286 StateTransition::Unshield(_) => {}
287 StateTransition::ShieldFromAssetLock(_) => {}
288 StateTransition::ShieldedWithdrawal(_) => {}
289 StateTransition::IdentityCreateFromShieldedPool(_) => {}
290 }
291 };
292 ($state_transition:expr, $method:ident ) => {
293 match $state_transition {
294 StateTransition::DataContractCreate(st) => st.$method(),
295 StateTransition::DataContractUpdate(st) => st.$method(),
296 StateTransition::Batch(st) => st.$method(),
297 StateTransition::IdentityCreate(st) => {}
298 StateTransition::IdentityTopUp(st) => {}
299 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
300 StateTransition::IdentityUpdate(st) => st.$method(),
301 StateTransition::IdentityCreditTransfer(st) => st.$method(),
302 StateTransition::MasternodeVote(st) => st.$method(),
303 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
304 StateTransition::IdentityCreateFromAddresses(_) => {}
305 StateTransition::IdentityTopUpFromAddresses(_) => {}
306 StateTransition::AddressFundsTransfer(_) => {}
307 StateTransition::AddressFundingFromAssetLock(_) => {}
308 StateTransition::AddressCreditWithdrawal(_) => {}
309 StateTransition::Shield(_) => {}
310 StateTransition::ShieldedTransfer(_) => {}
311 StateTransition::Unshield(_) => {}
312 StateTransition::ShieldFromAssetLock(_) => {}
313 StateTransition::ShieldedWithdrawal(_) => {}
314 StateTransition::IdentityCreateFromShieldedPool(_) => {}
315 }
316 };
317}
318
319#[cfg(feature = "state-transition-signing")]
320macro_rules! call_errorable_method_identity_signed {
321 ($state_transition:expr, $method:ident, $( $arg:expr ),* ) => {
322 match $state_transition {
323 StateTransition::DataContractCreate(st) => st.$method($( $arg ),*),
324 StateTransition::DataContractUpdate(st) => st.$method($( $arg ),*),
325 StateTransition::Batch(st) => st.$method($( $arg ),*),
326 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
327 "identity create can not be called for identity signing".to_string(),
328 )),
329 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
330 "identity top up can not be called for identity signing".to_string(),
331 )),
332 StateTransition::IdentityCreditWithdrawal(st) => st.$method($( $arg ),*),
333 StateTransition::IdentityUpdate(st) => st.$method($( $arg ),*),
334 StateTransition::IdentityCreditTransfer(st) => st.$method($( $arg ),*),
335 StateTransition::MasternodeVote(st) => st.$method($( $arg ),*),
336 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($( $arg ),*),
337 StateTransition::IdentityCreateFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
338 "identity create from addresses can not be called for identity signing".to_string(),
339 )),
340 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
341 "identity top up from addresses can not be called for identity signing".to_string(),
342 )),
343 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
344 "address funds transfer can not be called for identity signing".to_string(),
345 )),
346 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
347 "address funding from asset lock can not be called for identity signing".to_string(),
348 )),
349 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
350 "address credit withdrawal can not be called for identity signing".to_string(),
351 )),
352 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
353 "shield transition can not be called for identity signing".to_string(),
354 )),
355 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
356 "shielded transfer transition can not be called for identity signing".to_string(),
357 )),
358 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
359 "unshield transition can not be called for identity signing".to_string(),
360 )),
361 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
362 "shield from asset lock transition can not be called for identity signing".to_string(),
363 )),
364 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
365 "shielded withdrawal transition can not be called for identity signing".to_string(),
366 )),
367 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
368 "identity create from shielded pool transition can not be called for identity signing".to_string(),
369 )),
370 }
371 };
372 ($state_transition:expr, $method:ident) => {
373 match $state_transition {
374 StateTransition::DataContractCreate(st) => st.$method(),
375 StateTransition::DataContractUpdate(st) => st.$method(),
376 StateTransition::Batch(st) => st.$method(),
377 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
378 "identity create can not be called for identity signing".to_string(),
379 )),
380 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
381 "identity top up can not be called for identity signing".to_string(),
382 )),
383 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
384 StateTransition::IdentityUpdate(st) => st.$method(),
385 StateTransition::IdentityCreditTransfer(st) => st.$method(),
386 StateTransition::MasternodeVote(st) => st.$method(),
387 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
388 StateTransition::IdentityCreateFromAddresses(st) => Err(ProtocolError::CorruptedCodeExecution(
389 "identity create from addresses can not be called for identity signing".to_string(),
390 )),
391 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
392 "identity top up from addresses can not be called for identity signing".to_string(),
393 )),
394 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
395 "address funds transfer can not be called for identity signing".to_string(),
396 )),
397 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
398 "address funding from asset lock can not be called for identity signing".to_string(),
399 )),
400 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
401 "address credit withdrawal can not be called for identity signing".to_string(),
402 )),
403 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
404 "shield transition can not be called for identity signing".to_string(),
405 )),
406 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
407 "shielded transfer transition can not be called for identity signing".to_string(),
408 )),
409 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
410 "unshield transition can not be called for identity signing".to_string(),
411 )),
412 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
413 "shield from asset lock transition can not be called for identity signing".to_string(),
414 )),
415 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
416 "shielded withdrawal transition can not be called for identity signing".to_string(),
417 )),
418 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
419 "identity create from shielded pool transition can not be called for identity signing".to_string(),
420 )),
421 }
422 };
423}
424
425#[derive(
426 Debug,
427 Clone,
428 Encode,
429 Decode,
430 PlatformSerialize,
431 PlatformDeserialize,
432 PlatformSignable,
433 From,
434 PartialEq,
435)]
436#[cfg_attr(
456 feature = "serde-conversion",
457 derive(Serialize, Deserialize),
458 serde(tag = "$type", rename_all = "camelCase")
459)]
460#[platform_serialize(unversioned)] #[platform_serialize(limit = 100000)]
462pub enum StateTransition {
463 DataContractCreate(DataContractCreateTransition),
464 DataContractUpdate(DataContractUpdateTransition),
465 Batch(BatchTransition),
466 IdentityCreate(IdentityCreateTransition),
467 IdentityTopUp(IdentityTopUpTransition),
468 IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition),
469 IdentityUpdate(IdentityUpdateTransition),
470 IdentityCreditTransfer(IdentityCreditTransferTransition),
471 MasternodeVote(MasternodeVoteTransition),
472 IdentityCreditTransferToAddresses(IdentityCreditTransferToAddressesTransition),
473 IdentityCreateFromAddresses(IdentityCreateFromAddressesTransition),
474 IdentityTopUpFromAddresses(IdentityTopUpFromAddressesTransition),
475 AddressFundsTransfer(AddressFundsTransferTransition),
476 AddressFundingFromAssetLock(AddressFundingFromAssetLockTransition),
477 AddressCreditWithdrawal(AddressCreditWithdrawalTransition),
478 Shield(ShieldTransition),
479 ShieldedTransfer(ShieldedTransferTransition),
480 Unshield(UnshieldTransition),
481 ShieldFromAssetLock(ShieldFromAssetLockTransition),
482 ShieldedWithdrawal(ShieldedWithdrawalTransition),
483 IdentityCreateFromShieldedPool(IdentityCreateFromShieldedPoolTransition),
484}
485
486#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
487impl crate::serialization::JsonConvertible for StateTransition {}
488
489#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
490impl crate::serialization::ValueConvertible for StateTransition {}
491
492#[cfg(all(
493 test,
494 feature = "json-conversion",
495 feature = "value-conversion",
496 feature = "serde-conversion"
497))]
498mod json_convertible_tests {
499 use super::*;
500
501 fn assert_umbrella_round_trip_inner(
520 original: StateTransition,
521 expected_type_tag: &str,
522 lossy_json_int_variants: bool,
523 ) {
524 use crate::serialization::{JsonConvertible, ValueConvertible};
525
526 let json = original.to_json().expect("to_json");
528 assert_eq!(
529 json["$type"], expected_type_tag,
530 "json type tag for {expected_type_tag}",
531 );
532 let recovered = StateTransition::from_json(json).expect("from_json round-trip");
533 assert_eq!(
534 std::mem::discriminant(&original),
535 std::mem::discriminant(&recovered),
536 "json round-trip variant for {expected_type_tag}",
537 );
538 if lossy_json_int_variants {
539 use crate::tests::utils::normalize_integer_variants_for_json_round_trip;
540 let mut original_canon = original.to_object().expect("to_object");
541 let mut recovered_canon = recovered.to_object().expect("to_object");
542 normalize_integer_variants_for_json_round_trip(&mut original_canon);
543 normalize_integer_variants_for_json_round_trip(&mut recovered_canon);
544 assert_eq!(
545 original_canon, recovered_canon,
546 "json round-trip equality (modulo int-variant) for {expected_type_tag}",
547 );
548 } else {
549 assert_eq!(
550 original, recovered,
551 "json round-trip equality for {expected_type_tag}"
552 );
553 }
554
555 let value = original.to_object().expect("to_object");
557 let map = value.as_map().expect("Value::Map");
558 let tag = map
559 .iter()
560 .find(|(k, _)| k.as_text() == Some("$type"))
561 .map(|(_, v)| v)
562 .unwrap_or_else(|| panic!("type tag missing for {expected_type_tag}"));
563 assert_eq!(
564 *tag,
565 platform_value::Value::Text(expected_type_tag.to_string()),
566 "value type tag for {expected_type_tag}",
567 );
568 let recovered = StateTransition::from_object(value).expect("from_object round-trip");
569 assert_eq!(
570 std::mem::discriminant(&original),
571 std::mem::discriminant(&recovered),
572 "value round-trip variant for {expected_type_tag}",
573 );
574 assert_eq!(
575 original, recovered,
576 "value round-trip equality for {expected_type_tag}"
577 );
578 }
579
580 fn assert_umbrella_round_trip(original: StateTransition, expected_type_tag: &str) {
581 assert_umbrella_round_trip_inner(original, expected_type_tag, false);
582 }
583
584 fn assert_umbrella_round_trip_lossy_json_int_variants(
591 original: StateTransition,
592 expected_type_tag: &str,
593 ) {
594 assert_umbrella_round_trip_inner(original, expected_type_tag, true);
595 }
596
597 #[test]
603 fn umbrella_data_contract_create() {
604 let inner = crate::state_transition::data_contract_create_transition::json_convertible_tests::fixture();
605 assert_umbrella_round_trip_lossy_json_int_variants(
606 StateTransition::DataContractCreate(inner),
607 "dataContractCreate",
608 );
609 }
610
611 #[test]
612 fn umbrella_data_contract_update() {
613 let inner = crate::state_transition::data_contract_update_transition::json_convertible_tests::fixture();
614 assert_umbrella_round_trip_lossy_json_int_variants(
615 StateTransition::DataContractUpdate(inner),
616 "dataContractUpdate",
617 );
618 }
619
620 #[test]
621 fn umbrella_batch() {
622 let inner = crate::state_transition::batch_transition::json_convertible_tests::fixture();
623 assert_umbrella_round_trip(StateTransition::Batch(inner), "batch");
624 }
625
626 #[test]
627 fn umbrella_identity_create() {
628 let inner =
629 crate::state_transition::identity_create_transition::json_convertible_tests::fixture();
630 assert_umbrella_round_trip(StateTransition::IdentityCreate(inner), "identityCreate");
631 }
632
633 #[test]
634 fn umbrella_identity_top_up() {
635 let inner =
636 crate::state_transition::identity_topup_transition::json_convertible_tests::fixture();
637 assert_umbrella_round_trip(StateTransition::IdentityTopUp(inner), "identityTopUp");
638 }
639
640 #[test]
641 fn umbrella_identity_credit_withdrawal() {
642 let inner = crate::state_transition::identity_credit_withdrawal_transition::json_convertible_tests::fixture();
643 assert_umbrella_round_trip(
644 StateTransition::IdentityCreditWithdrawal(inner),
645 "identityCreditWithdrawal",
646 );
647 }
648
649 #[test]
650 fn umbrella_identity_update() {
651 let inner =
652 crate::state_transition::identity_update_transition::json_convertible_tests::fixture();
653 assert_umbrella_round_trip(StateTransition::IdentityUpdate(inner), "identityUpdate");
654 }
655
656 #[test]
657 fn umbrella_identity_credit_transfer() {
658 let inner = crate::state_transition::identity_credit_transfer_transition::json_convertible_tests::fixture();
659 assert_umbrella_round_trip(
660 StateTransition::IdentityCreditTransfer(inner),
661 "identityCreditTransfer",
662 );
663 }
664
665 #[test]
666 fn umbrella_masternode_vote() {
667 let inner =
668 crate::state_transition::masternode_vote_transition::json_convertible_tests::fixture();
669 assert_umbrella_round_trip(StateTransition::MasternodeVote(inner), "masternodeVote");
670 }
671
672 #[test]
673 fn umbrella_identity_credit_transfer_to_addresses() {
674 let inner = crate::state_transition::identity_credit_transfer_to_addresses_transition::json_convertible_tests::fixture();
675 assert_umbrella_round_trip(
676 StateTransition::IdentityCreditTransferToAddresses(inner),
677 "identityCreditTransferToAddresses",
678 );
679 }
680
681 #[test]
682 fn umbrella_identity_create_from_addresses() {
683 let inner = crate::state_transition::identity_create_from_addresses_transition::json_convertible_tests::fixture();
684 assert_umbrella_round_trip(
685 StateTransition::IdentityCreateFromAddresses(inner),
686 "identityCreateFromAddresses",
687 );
688 }
689
690 #[test]
691 fn umbrella_identity_top_up_from_addresses() {
692 let inner = crate::state_transition::identity_topup_from_addresses_transition::json_convertible_tests::fixture();
693 assert_umbrella_round_trip(
694 StateTransition::IdentityTopUpFromAddresses(inner),
695 "identityTopUpFromAddresses",
696 );
697 }
698
699 #[test]
700 fn umbrella_address_funds_transfer() {
701 let inner = crate::state_transition::address_funds_transfer_transition::json_convertible_tests::fixture();
702 assert_umbrella_round_trip(
703 StateTransition::AddressFundsTransfer(inner),
704 "addressFundsTransfer",
705 );
706 }
707
708 #[test]
709 fn umbrella_address_funding_from_asset_lock() {
710 let inner = crate::state_transition::address_funding_from_asset_lock_transition::json_convertible_tests::fixture();
711 assert_umbrella_round_trip(
712 StateTransition::AddressFundingFromAssetLock(inner),
713 "addressFundingFromAssetLock",
714 );
715 }
716
717 #[test]
718 fn umbrella_address_credit_withdrawal() {
719 let inner = crate::state_transition::address_credit_withdrawal_transition::json_convertible_tests::fixture();
720 assert_umbrella_round_trip(
721 StateTransition::AddressCreditWithdrawal(inner),
722 "addressCreditWithdrawal",
723 );
724 }
725
726 #[test]
727 fn umbrella_shield() {
728 let inner = crate::state_transition::shield_transition::json_convertible_tests::fixture();
729 assert_umbrella_round_trip(StateTransition::Shield(inner), "shield");
730 }
731
732 #[test]
733 fn umbrella_shielded_transfer() {
734 let inner =
735 crate::state_transition::shielded_transfer_transition::json_convertible_tests::fixture(
736 );
737 assert_umbrella_round_trip(StateTransition::ShieldedTransfer(inner), "shieldedTransfer");
738 }
739
740 #[test]
741 fn umbrella_unshield() {
742 let inner = crate::state_transition::unshield_transition::json_convertible_tests::fixture();
743 assert_umbrella_round_trip(StateTransition::Unshield(inner), "unshield");
744 }
745
746 #[test]
747 fn umbrella_shield_from_asset_lock() {
748 let inner = crate::state_transition::shield_from_asset_lock_transition::json_convertible_tests::fixture();
749 assert_umbrella_round_trip(
750 StateTransition::ShieldFromAssetLock(inner),
751 "shieldFromAssetLock",
752 );
753 }
754
755 #[test]
756 fn umbrella_shielded_withdrawal() {
757 let inner = crate::state_transition::shielded_withdrawal_transition::json_convertible_tests::fixture();
758 assert_umbrella_round_trip(
759 StateTransition::ShieldedWithdrawal(inner),
760 "shieldedWithdrawal",
761 );
762 }
763
764 #[test]
765 fn umbrella_identity_create_from_shielded_pool() {
766 let inner = crate::state_transition::identity_create_from_shielded_pool_transition::json_convertible_tests::fixture();
767 assert_umbrella_round_trip(
768 StateTransition::IdentityCreateFromShieldedPool(inner),
769 "identityCreateFromShieldedPool",
770 );
771 }
772}
773
774impl OptionallyAssetLockProved for StateTransition {
775 fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> {
776 match self {
777 StateTransition::IdentityCreate(st) => st.optional_asset_lock_proof(),
778 StateTransition::IdentityTopUp(st) => st.optional_asset_lock_proof(),
779 StateTransition::ShieldFromAssetLock(st) => st.optional_asset_lock_proof(),
780 _ => None,
781 }
782 }
783}
784
785#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
787pub struct StateTransitionSigningOptions {
788 pub allow_signing_with_any_security_level: bool,
790 pub allow_signing_with_any_purpose: bool,
792}
793
794impl StateTransition {
795 #[allow(unused_variables)]
796 pub fn deserialize_from_bytes_in_version(
797 bytes: &[u8],
798 platform_version: &PlatformVersion,
799 ) -> Result<Self, ProtocolError> {
800 let state_transition = StateTransition::deserialize_from_bytes(bytes)?;
801 #[cfg(all(feature = "state-transitions", feature = "validation"))]
802 {
803 let active_version_range = state_transition.active_version_range();
804
805 if active_version_range.contains(&platform_version.protocol_version)
808 || platform_version.protocol_version > 268435456
809 {
810 Ok(state_transition)
811 } else {
812 Err(ProtocolError::StateTransitionError(
813 StateTransitionIsNotActiveError {
814 state_transition_type: state_transition.name(),
815 active_version_range,
816 current_protocol_version: platform_version.protocol_version,
817 },
818 ))
819 }
820 }
821 #[cfg(not(all(feature = "state-transitions", feature = "validation")))]
822 Ok(state_transition)
823 }
824
825 pub fn active_version_range(&self) -> RangeInclusive<ProtocolVersion> {
826 match self {
827 StateTransition::DataContractCreate(data_contract_create_transition) => {
828 match data_contract_create_transition.data_contract() {
829 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
830 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
831 }
832 }
833 StateTransition::DataContractUpdate(data_contract_update_transition) => {
834 match data_contract_update_transition.data_contract() {
835 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
836 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
837 }
838 }
839 StateTransition::Batch(batch_transition) => match batch_transition {
840 BatchTransition::V0(_) => ALL_VERSIONS,
841 BatchTransition::V1(_) => 9..=LATEST_VERSION,
842 },
843 StateTransition::IdentityCreate(_)
844 | StateTransition::IdentityTopUp(_)
845 | StateTransition::IdentityCreditWithdrawal(_)
846 | StateTransition::IdentityUpdate(_)
847 | StateTransition::IdentityCreditTransfer(_)
848 | StateTransition::MasternodeVote(_) => ALL_VERSIONS,
849 StateTransition::IdentityCreditTransferToAddresses(_)
850 | StateTransition::IdentityCreateFromAddresses(_)
851 | StateTransition::IdentityTopUpFromAddresses(_)
852 | StateTransition::AddressFundsTransfer(_)
853 | StateTransition::AddressFundingFromAssetLock(_)
854 | StateTransition::AddressCreditWithdrawal(_) => 11..=LATEST_VERSION,
855 StateTransition::Shield(_)
856 | StateTransition::ShieldedTransfer(_)
857 | StateTransition::Unshield(_)
858 | StateTransition::ShieldFromAssetLock(_)
859 | StateTransition::ShieldedWithdrawal(_)
860 | StateTransition::IdentityCreateFromShieldedPool(_) => 12..=LATEST_VERSION,
861 }
862 }
863
864 pub fn is_identity_signed(&self) -> bool {
865 !matches!(
866 self,
867 StateTransition::IdentityCreate(_)
868 | StateTransition::IdentityTopUp(_)
869 | StateTransition::Shield(_)
870 | StateTransition::ShieldedTransfer(_)
871 | StateTransition::Unshield(_)
872 | StateTransition::ShieldFromAssetLock(_)
873 | StateTransition::ShieldedWithdrawal(_)
874 | StateTransition::IdentityCreateFromShieldedPool(_)
875 )
876 }
877
878 pub fn required_asset_lock_balance_for_processing_start(
879 &self,
880 platform_version: &PlatformVersion,
881 ) -> Result<Credits, ProtocolError> {
882 match self {
883 StateTransition::IdentityCreate(st) => {
884 st.calculate_min_required_fee(platform_version)
885 }
886 StateTransition::IdentityTopUp(st) => {
887 st.calculate_min_required_fee(platform_version)
888 }
889 StateTransition::AddressFundingFromAssetLock(st) => {
890 st.calculate_min_required_fee(platform_version)
891 }
892 StateTransition::ShieldFromAssetLock(st) => {
893 st.calculate_min_required_fee(platform_version)
894 }
895 st => Err(ProtocolError::CorruptedCodeExecution(format!("{} is not an asset lock transaction, but we are calling required_asset_lock_balance_for_processing_start", st.name()))),
896 }
897 }
898
899 fn hash(&self, skip_signature: bool) -> Result<Vec<u8>, ProtocolError> {
900 if skip_signature {
901 Ok(hash_double_to_vec(self.signable_bytes()?))
902 } else {
903 Ok(hash_double_to_vec(
904 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
905 ))
906 }
907 }
908
909 pub fn name(&self) -> String {
911 match self {
912 Self::DataContractCreate(_) => "DataContractCreate".to_string(),
913 Self::DataContractUpdate(_) => "DataContractUpdate".to_string(),
914 Self::Batch(batch_transition) => {
915 let mut document_transition_types = vec![];
916 for transition in batch_transition.transitions_iter() {
917 let document_transition_name = match transition {
918 BatchedTransitionRef::Document(DocumentTransition::Create(_)) => "Create",
919 BatchedTransitionRef::Document(DocumentTransition::Replace(_)) => "Replace",
920 BatchedTransitionRef::Document(DocumentTransition::Delete(_)) => "Delete",
921 BatchedTransitionRef::Document(DocumentTransition::Transfer(_)) => {
922 "Transfer"
923 }
924 BatchedTransitionRef::Document(DocumentTransition::UpdatePrice(_)) => {
925 "UpdatePrice"
926 }
927 BatchedTransitionRef::Document(DocumentTransition::Purchase(_)) => {
928 "Purchase"
929 }
930 BatchedTransitionRef::Token(TokenTransition::Transfer(_)) => {
931 "TokenTransfer"
932 }
933 BatchedTransitionRef::Token(TokenTransition::Mint(_)) => "TokenMint",
934 BatchedTransitionRef::Token(TokenTransition::Burn(_)) => "TokenBurn",
935 BatchedTransitionRef::Token(TokenTransition::Freeze(_)) => "TokenFreeze",
936 BatchedTransitionRef::Token(TokenTransition::Unfreeze(_)) => {
937 "TokenUnfreeze"
938 }
939 BatchedTransitionRef::Token(TokenTransition::DestroyFrozenFunds(_)) => {
940 "TokenDestroyFrozenFunds"
941 }
942 BatchedTransitionRef::Token(TokenTransition::EmergencyAction(_)) => {
943 "TokenEmergencyAction"
944 }
945 BatchedTransitionRef::Token(TokenTransition::ConfigUpdate(_)) => {
946 "TokenConfigUpdate"
947 }
948 BatchedTransitionRef::Token(TokenTransition::Claim(_)) => "TokenClaim",
949 BatchedTransitionRef::Token(TokenTransition::DirectPurchase(_)) => {
950 "TokenDirectPurchase"
951 }
952 BatchedTransitionRef::Token(
953 TokenTransition::SetPriceForDirectPurchase(_),
954 ) => "SetPriceForDirectPurchase",
955 };
956 document_transition_types.push(document_transition_name);
957 }
958 format!("DocumentsBatch([{}])", document_transition_types.join(", "))
959 }
960 Self::IdentityCreate(_) => "IdentityCreate".to_string(),
961 Self::IdentityTopUp(_) => "IdentityTopUp".to_string(),
962 Self::IdentityCreditWithdrawal(_) => "IdentityCreditWithdrawal".to_string(),
963 Self::IdentityUpdate(_) => "IdentityUpdate".to_string(),
964 Self::IdentityCreditTransfer(_) => "IdentityCreditTransfer".to_string(),
965 Self::MasternodeVote(_) => "MasternodeVote".to_string(),
966 Self::IdentityCreditTransferToAddresses(_) => {
967 "IdentityCreditTransferToAddresses".to_string()
968 }
969 Self::IdentityCreateFromAddresses(_) => "IdentityCreateFromAddresses".to_string(),
970 Self::IdentityTopUpFromAddresses(_) => "IdentityTopUpFromAddresses".to_string(),
971 Self::AddressFundsTransfer(_) => "AddressFundsTransfer".to_string(),
972 Self::AddressFundingFromAssetLock(_) => "AddressFundingFromAssetLock".to_string(),
973 Self::AddressCreditWithdrawal(_) => "AddressCreditWithdrawal".to_string(),
974 Self::Shield(_) => "Shield".to_string(),
975 Self::ShieldedTransfer(_) => "ShieldedTransfer".to_string(),
976 Self::Unshield(_) => "Unshield".to_string(),
977 Self::ShieldFromAssetLock(_) => "ShieldFromAssetLock".to_string(),
978 Self::ShieldedWithdrawal(_) => "ShieldedWithdrawal".to_string(),
979 Self::IdentityCreateFromShieldedPool(_) => "IdentityCreateFromShieldedPool".to_string(),
980 }
981 }
982
983 pub fn signature(&self) -> Option<&BinaryData> {
985 match self {
986 StateTransition::DataContractCreate(st) => Some(st.signature()),
987 StateTransition::DataContractUpdate(st) => Some(st.signature()),
988 StateTransition::Batch(st) => Some(st.signature()),
989 StateTransition::IdentityCreate(st) => Some(st.signature()),
990 StateTransition::IdentityTopUp(st) => Some(st.signature()),
991 StateTransition::IdentityCreditWithdrawal(st) => Some(st.signature()),
992 StateTransition::IdentityUpdate(st) => Some(st.signature()),
993 StateTransition::IdentityCreditTransfer(st) => Some(st.signature()),
994 StateTransition::MasternodeVote(st) => Some(st.signature()),
995 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.signature()),
996 StateTransition::IdentityCreateFromAddresses(_) => None,
997 StateTransition::IdentityTopUpFromAddresses(_) => None,
998 StateTransition::AddressFundsTransfer(_) => None,
999 StateTransition::AddressFundingFromAssetLock(st) => Some(st.signature()),
1000 StateTransition::AddressCreditWithdrawal(_) => None,
1001 StateTransition::Shield(_) => None,
1002 StateTransition::ShieldedTransfer(_) => None,
1003 StateTransition::Unshield(_) => None,
1004 StateTransition::ShieldFromAssetLock(st) => Some(st.signature()),
1005 StateTransition::ShieldedWithdrawal(_) => None,
1006 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1007 }
1008 }
1009
1010 pub fn required_number_of_private_keys(&self) -> u16 {
1012 match self {
1013 StateTransition::IdentityCreateFromAddresses(st) => st.inputs().len() as u16,
1014 StateTransition::IdentityTopUpFromAddresses(st) => st.inputs().len() as u16,
1015 StateTransition::AddressFundsTransfer(st) => st.inputs().len() as u16,
1016 StateTransition::AddressCreditWithdrawal(st) => st.inputs().len() as u16,
1017 StateTransition::Shield(st) => st.inputs().len() as u16,
1018 StateTransition::ShieldedTransfer(_) => 0,
1019 StateTransition::Unshield(_) => 0,
1020 StateTransition::ShieldFromAssetLock(_) => 0,
1021 StateTransition::ShieldedWithdrawal(_) => 0,
1022 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1023 _ => 1,
1024 }
1025 }
1026
1027 pub fn user_fee_increase(&self) -> UserFeeIncrease {
1029 match self {
1030 StateTransition::DataContractCreate(st) => st.user_fee_increase(),
1031 StateTransition::DataContractUpdate(st) => st.user_fee_increase(),
1032 StateTransition::Batch(st) => st.user_fee_increase(),
1033 StateTransition::IdentityCreate(st) => st.user_fee_increase(),
1034 StateTransition::IdentityTopUp(st) => st.user_fee_increase(),
1035 StateTransition::IdentityCreditWithdrawal(st) => st.user_fee_increase(),
1036 StateTransition::IdentityUpdate(st) => st.user_fee_increase(),
1037 StateTransition::IdentityCreditTransfer(st) => st.user_fee_increase(),
1038 StateTransition::IdentityCreditTransferToAddresses(st) => st.user_fee_increase(),
1039 StateTransition::IdentityCreateFromAddresses(st) => st.user_fee_increase(),
1040 StateTransition::IdentityTopUpFromAddresses(st) => st.user_fee_increase(),
1041 StateTransition::AddressFundsTransfer(st) => st.user_fee_increase(),
1042 StateTransition::AddressFundingFromAssetLock(st) => st.user_fee_increase(),
1043 StateTransition::AddressCreditWithdrawal(st) => st.user_fee_increase(),
1044 StateTransition::Shield(st) => st.user_fee_increase(),
1045 StateTransition::ShieldFromAssetLock(_) => 0,
1047 StateTransition::MasternodeVote(_) => 0,
1048 StateTransition::ShieldedTransfer(_) => 0,
1049 StateTransition::Unshield(_) => 0,
1050 StateTransition::ShieldedWithdrawal(_) => 0,
1051 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1052 }
1053 }
1054
1055 fn calculate_estimated_fee(
1068 &self,
1069 platform_version: &PlatformVersion,
1070 ) -> Result<Credits, ProtocolError> {
1071 call_method!(self, calculate_min_required_fee, platform_version)
1072 }
1073
1074 pub fn transaction_id(&self) -> Result<[u8; 32], ProtocolError> {
1076 Ok(hash_single(
1077 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
1078 ))
1079 }
1080
1081 pub fn signature_public_key_id(&self) -> Option<KeyID> {
1083 call_getter_method_identity_signed!(self, signature_public_key_id)
1084 }
1085
1086 pub fn security_level_requirement(&self, purpose: Purpose) -> Option<Vec<SecurityLevel>> {
1088 call_getter_method_identity_signed!(self, security_level_requirement, purpose)
1089 }
1090
1091 pub fn purpose_requirement(&self) -> Option<Vec<Purpose>> {
1093 call_getter_method_identity_signed!(self, purpose_requirement)
1094 }
1095
1096 pub fn owner_id(&self) -> Option<Identifier> {
1098 match self {
1099 StateTransition::DataContractCreate(st) => Some(st.owner_id()),
1100 StateTransition::DataContractUpdate(st) => Some(st.owner_id()),
1101 StateTransition::Batch(st) => Some(st.owner_id()),
1102 StateTransition::IdentityCreate(st) => Some(st.owner_id()),
1103 StateTransition::IdentityTopUp(st) => Some(st.owner_id()),
1104 StateTransition::IdentityCreditWithdrawal(st) => Some(st.owner_id()),
1105 StateTransition::IdentityUpdate(st) => Some(st.owner_id()),
1106 StateTransition::IdentityCreditTransfer(st) => Some(st.owner_id()),
1107 StateTransition::MasternodeVote(st) => Some(st.owner_id()),
1108 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.owner_id()),
1109 StateTransition::IdentityCreateFromAddresses(_) => None,
1110 StateTransition::IdentityTopUpFromAddresses(_) => None,
1111 StateTransition::AddressFundsTransfer(_) => None,
1112 StateTransition::AddressFundingFromAssetLock(_) => None,
1113 StateTransition::AddressCreditWithdrawal(_) => None,
1114 StateTransition::Shield(_) => None,
1115 StateTransition::ShieldedTransfer(_) => None,
1116 StateTransition::Unshield(_) => None,
1117 StateTransition::ShieldFromAssetLock(_) => None,
1118 StateTransition::ShieldedWithdrawal(_) => None,
1119 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1120 }
1121 }
1122
1123 pub fn inputs(&self) -> Option<&BTreeMap<PlatformAddress, (AddressNonce, Credits)>> {
1125 match self {
1126 StateTransition::DataContractCreate(_)
1127 | StateTransition::DataContractUpdate(_)
1128 | StateTransition::Batch(_)
1129 | StateTransition::IdentityCreate(_)
1130 | StateTransition::IdentityTopUp(_)
1131 | StateTransition::IdentityCreditWithdrawal(_)
1132 | StateTransition::IdentityUpdate(_)
1133 | StateTransition::IdentityCreditTransfer(_)
1134 | StateTransition::MasternodeVote(_)
1135 | StateTransition::IdentityCreditTransferToAddresses(_) => None,
1136 StateTransition::IdentityCreateFromAddresses(st) => Some(st.inputs()),
1137 StateTransition::IdentityTopUpFromAddresses(st) => Some(st.inputs()),
1138 StateTransition::AddressFundsTransfer(st) => Some(st.inputs()),
1139 StateTransition::AddressFundingFromAssetLock(st) => Some(st.inputs()),
1140 StateTransition::AddressCreditWithdrawal(st) => Some(st.inputs()),
1141 StateTransition::Shield(st) => Some(st.inputs()),
1142 StateTransition::ShieldedTransfer(_) => None,
1143 StateTransition::Unshield(_) => None,
1144 StateTransition::ShieldFromAssetLock(_) => None,
1145 StateTransition::ShieldedWithdrawal(_) => None,
1146 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1147 }
1148 }
1149
1150 pub fn state_transition_type(&self) -> StateTransitionType {
1152 call_method!(self, state_transition_type)
1153 }
1154
1155 pub fn unique_identifiers(&self) -> Vec<String> {
1157 call_method!(self, unique_identifiers)
1158 }
1159
1160 pub fn set_signature(&mut self, signature: BinaryData) -> bool {
1162 match self {
1163 StateTransition::DataContractCreate(st) => {
1164 st.set_signature(signature);
1165 true
1166 }
1167 StateTransition::DataContractUpdate(st) => {
1168 st.set_signature(signature);
1169 true
1170 }
1171 StateTransition::Batch(st) => {
1172 st.set_signature(signature);
1173 true
1174 }
1175 StateTransition::IdentityCreate(st) => {
1176 st.set_signature(signature);
1177 true
1178 }
1179 StateTransition::IdentityTopUp(st) => {
1180 st.set_signature(signature);
1181 true
1182 }
1183 StateTransition::IdentityCreditWithdrawal(st) => {
1184 st.set_signature(signature);
1185 true
1186 }
1187 StateTransition::IdentityUpdate(st) => {
1188 st.set_signature(signature);
1189 true
1190 }
1191 StateTransition::IdentityCreditTransfer(st) => {
1192 st.set_signature(signature);
1193 true
1194 }
1195 StateTransition::MasternodeVote(st) => {
1196 st.set_signature(signature);
1197 true
1198 }
1199 StateTransition::IdentityCreditTransferToAddresses(st) => {
1200 st.set_signature(signature);
1201 true
1202 }
1203 StateTransition::IdentityCreateFromAddresses(_)
1204 | StateTransition::IdentityTopUpFromAddresses(_)
1205 | StateTransition::AddressFundsTransfer(_)
1206 | StateTransition::Shield(_)
1207 | StateTransition::ShieldedTransfer(_)
1208 | StateTransition::Unshield(_)
1209 | StateTransition::ShieldedWithdrawal(_)
1210 | StateTransition::IdentityCreateFromShieldedPool(_) => false,
1211 StateTransition::AddressFundingFromAssetLock(st) => {
1212 st.set_signature(signature);
1213 true
1214 }
1215 StateTransition::ShieldFromAssetLock(st) => {
1216 st.set_signature(signature);
1217 true
1218 }
1219 StateTransition::AddressCreditWithdrawal(_) => false,
1220 }
1221 }
1222
1223 pub fn set_user_fee_increase(&mut self, user_fee_increase: UserFeeIncrease) {
1225 match self {
1226 StateTransition::DataContractCreate(st) => st.set_user_fee_increase(user_fee_increase),
1227 StateTransition::DataContractUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1228 StateTransition::Batch(st) => st.set_user_fee_increase(user_fee_increase),
1229 StateTransition::IdentityCreate(st) => st.set_user_fee_increase(user_fee_increase),
1230 StateTransition::IdentityTopUp(st) => st.set_user_fee_increase(user_fee_increase),
1231 StateTransition::IdentityCreditWithdrawal(st) => {
1232 st.set_user_fee_increase(user_fee_increase)
1233 }
1234 StateTransition::IdentityUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1235 StateTransition::IdentityCreditTransfer(st) => {
1236 st.set_user_fee_increase(user_fee_increase)
1237 }
1238 StateTransition::IdentityCreditTransferToAddresses(st) => {
1239 st.set_user_fee_increase(user_fee_increase)
1240 }
1241 StateTransition::IdentityCreateFromAddresses(st) => {
1242 st.set_user_fee_increase(user_fee_increase)
1243 }
1244 StateTransition::IdentityTopUpFromAddresses(st) => {
1245 st.set_user_fee_increase(user_fee_increase)
1246 }
1247 StateTransition::AddressFundsTransfer(st) => {
1248 st.set_user_fee_increase(user_fee_increase)
1249 }
1250 StateTransition::AddressFundingFromAssetLock(st) => {
1251 st.set_user_fee_increase(user_fee_increase)
1252 }
1253 StateTransition::AddressCreditWithdrawal(st) => {
1254 st.set_user_fee_increase(user_fee_increase)
1255 }
1256 StateTransition::Shield(st) => st.set_user_fee_increase(user_fee_increase),
1257 StateTransition::ShieldFromAssetLock(_) => {}
1259 StateTransition::MasternodeVote(_) => {}
1260 StateTransition::ShieldedTransfer(_) => {}
1261 StateTransition::Unshield(_) => {}
1262 StateTransition::ShieldedWithdrawal(_) => {}
1263 StateTransition::IdentityCreateFromShieldedPool(_) => {}
1264 }
1265 }
1266
1267 pub fn set_signature_public_key_id(&mut self, public_key_id: KeyID) {
1269 call_method_identity_signed!(self, set_signature_public_key_id, public_key_id)
1270 }
1271
1272 #[cfg(feature = "state-transition-signing")]
1273 pub async fn sign_external<S: Signer<IdentityPublicKey>>(
1274 &mut self,
1275 identity_public_key: &IdentityPublicKey,
1276 signer: &S,
1277 get_data_contract_security_level_requirement: Option<
1278 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1279 >,
1280 ) -> Result<(), ProtocolError> {
1281 self.sign_external_with_options(
1282 identity_public_key,
1283 signer,
1284 get_data_contract_security_level_requirement,
1285 StateTransitionSigningOptions::default(),
1286 )
1287 .await
1288 }
1289
1290 #[cfg(feature = "state-transition-signing")]
1291 pub async fn sign_external_with_options<S: Signer<IdentityPublicKey>>(
1292 &mut self,
1293 identity_public_key: &IdentityPublicKey,
1294 signer: &S,
1295 get_data_contract_security_level_requirement: Option<
1296 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1297 >,
1298 options: StateTransitionSigningOptions,
1299 ) -> Result<(), ProtocolError> {
1300 match self {
1301 StateTransition::DataContractCreate(st) => {
1302 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1303 st.verify_public_key_is_enabled(identity_public_key)?;
1304 }
1305 StateTransition::DataContractUpdate(st) => {
1306 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1307 st.verify_public_key_is_enabled(identity_public_key)?;
1308 }
1309 StateTransition::Batch(st) => {
1310 let allow_token_transfer_keys = st.transitions_len() == 1
1311 && (st
1312 .first_transition()
1313 .expect("expected first transition with len 1")
1314 .as_transition_token_claim()
1315 .is_some()
1316 || st
1317 .first_transition()
1318 .expect("expected first transition with len 1")
1319 .as_transition_token_transfer()
1320 .is_some());
1321 let allowed_key_purposes = if allow_token_transfer_keys {
1322 vec![Purpose::AUTHENTICATION, Purpose::TRANSFER]
1323 } else {
1324 vec![Purpose::AUTHENTICATION]
1325 };
1326 if !options.allow_signing_with_any_purpose
1327 && !allowed_key_purposes.contains(&identity_public_key.purpose())
1328 {
1329 return Err(ProtocolError::WrongPublicKeyPurposeError(
1330 WrongPublicKeyPurposeError::new(
1331 identity_public_key.purpose(),
1332 allowed_key_purposes,
1333 ),
1334 ));
1335 }
1336 if !options.allow_signing_with_any_security_level {
1337 let security_level_requirement = st.combined_security_level_requirement(
1338 get_data_contract_security_level_requirement,
1339 )?;
1340 if !security_level_requirement.contains(&identity_public_key.security_level()) {
1341 return Err(ProtocolError::InvalidSignaturePublicKeySecurityLevelError(
1342 InvalidSignaturePublicKeySecurityLevelError::new(
1343 identity_public_key.security_level(),
1344 security_level_requirement,
1345 ),
1346 ));
1347 }
1348 }
1349 st.verify_public_key_is_enabled(identity_public_key)?;
1350 }
1351 StateTransition::IdentityCreditWithdrawal(st) => {
1352 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1353 st.verify_public_key_is_enabled(identity_public_key)?;
1354 }
1355 StateTransition::IdentityUpdate(st) => {
1356 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1357 st.verify_public_key_is_enabled(identity_public_key)?;
1358 }
1359 StateTransition::IdentityCreditTransfer(st) => {
1360 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1361 st.verify_public_key_is_enabled(identity_public_key)?;
1362 }
1363 StateTransition::IdentityCreate(_) => {
1364 return Err(ProtocolError::CorruptedCodeExecution(
1365 "identity create can not be called for identity signing".to_string(),
1366 ))
1367 }
1368 StateTransition::IdentityTopUp(_) => {
1369 return Err(ProtocolError::CorruptedCodeExecution(
1370 "identity top up can not be called for identity signing".to_string(),
1371 ))
1372 }
1373 StateTransition::MasternodeVote(st) => {
1374 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1375 st.verify_public_key_is_enabled(identity_public_key)?;
1376 }
1377 StateTransition::IdentityCreditTransferToAddresses(st) => {
1378 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1379 st.verify_public_key_is_enabled(identity_public_key)?;
1380 }
1381 StateTransition::IdentityCreateFromAddresses(_) => {
1382 return Err(ProtocolError::CorruptedCodeExecution(
1383 "identity create from addresses can not be called for identity signing"
1384 .to_string(),
1385 ))
1386 }
1387 StateTransition::IdentityTopUpFromAddresses(_) => {
1388 return Err(ProtocolError::CorruptedCodeExecution(
1389 "identity top up from addresses can not be called for identity signing"
1390 .to_string(),
1391 ))
1392 }
1393 StateTransition::AddressFundsTransfer(_) => {
1394 return Err(ProtocolError::CorruptedCodeExecution(
1395 "address funds transfer transition can not be called for identity signing"
1396 .to_string(),
1397 ))
1398 }
1399 StateTransition::AddressFundingFromAssetLock(_) => {
1400 return Err(ProtocolError::CorruptedCodeExecution(
1401 "address funding from asset lock transition can not be called for identity signing"
1402 .to_string(),
1403 ))
1404 }
1405 StateTransition::AddressCreditWithdrawal(_) => {
1406 return Err(ProtocolError::CorruptedCodeExecution(
1407 "address credit withdrawal transition can not be called for identity signing"
1408 .to_string(),
1409 ))
1410 }
1411 StateTransition::Shield(_) => {
1412 return Err(ProtocolError::CorruptedCodeExecution(
1413 "shield transition can not be called for identity signing".to_string(),
1414 ))
1415 }
1416 StateTransition::ShieldedTransfer(_) => {
1417 return Err(ProtocolError::CorruptedCodeExecution(
1418 "shielded transfer transition can not be called for identity signing"
1419 .to_string(),
1420 ))
1421 }
1422 StateTransition::Unshield(_) => {
1423 return Err(ProtocolError::CorruptedCodeExecution(
1424 "unshield transition can not be called for identity signing".to_string(),
1425 ))
1426 }
1427 StateTransition::ShieldFromAssetLock(_) => {
1428 return Err(ProtocolError::CorruptedCodeExecution(
1429 "shield from asset lock transition can not be called for identity signing"
1430 .to_string(),
1431 ))
1432 }
1433 StateTransition::ShieldedWithdrawal(_) => {
1434 return Err(ProtocolError::CorruptedCodeExecution(
1435 "shielded withdrawal transition can not be called for identity signing"
1436 .to_string(),
1437 ))
1438 }
1439 StateTransition::IdentityCreateFromShieldedPool(_) => {
1440 return Err(ProtocolError::CorruptedCodeExecution(
1441 "identity create from shielded pool transition can not be called for identity signing"
1442 .to_string(),
1443 ))
1444 }
1445 }
1446 let data = self.signable_bytes()?;
1447 self.set_signature(signer.sign(identity_public_key, data.as_slice()).await?);
1448 self.set_signature_public_key_id(identity_public_key.id());
1449 Ok(())
1450 }
1451
1452 #[cfg(feature = "state-transition-signing")]
1453 pub fn sign(
1454 &mut self,
1455 identity_public_key: &IdentityPublicKey,
1456 private_key: &[u8],
1457 bls: &impl BlsModule,
1458 ) -> Result<(), ProtocolError> {
1459 self.sign_with_options(
1460 identity_public_key,
1461 private_key,
1462 bls,
1463 StateTransitionSigningOptions::default(),
1464 )
1465 }
1466
1467 #[cfg(feature = "state-transition-signing")]
1468 pub fn sign_with_options(
1469 &mut self,
1470 identity_public_key: &IdentityPublicKey,
1471 private_key: &[u8],
1472 bls: &impl BlsModule,
1473 options: StateTransitionSigningOptions,
1474 ) -> Result<(), ProtocolError> {
1475 call_errorable_method_identity_signed!(
1476 self,
1477 verify_public_key_level_and_purpose,
1478 identity_public_key,
1479 options
1480 )?;
1481 call_errorable_method_identity_signed!(
1482 self,
1483 verify_public_key_is_enabled,
1484 identity_public_key
1485 )?;
1486
1487 match identity_public_key.key_type() {
1488 KeyType::ECDSA_SECP256K1 => {
1489 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1490
1491 if public_key_compressed.as_slice() != identity_public_key.data().as_slice() {
1496 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1497 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1498 ));
1499 }
1500
1501 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1502 }
1503 KeyType::ECDSA_HASH160 => {
1504 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1505 let pub_key_hash = ripemd160_sha256(&public_key_compressed);
1506
1507 if identity_public_key.data().as_slice() != pub_key_hash {
1508 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1509 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1510 ));
1511 }
1512 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1513 }
1514 KeyType::BLS12_381 => {
1515 let public_key = bls.private_key_to_public_key(private_key)?;
1516
1517 if public_key != identity_public_key.data().as_slice() {
1518 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1519 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1520 ));
1521 }
1522 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1523 }
1524
1525 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1529 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1530 InvalidIdentityPublicKeyTypeError::new(identity_public_key.key_type()),
1531 ))
1532 }
1533 }?;
1534
1535 self.set_signature_public_key_id(identity_public_key.id());
1536
1537 Ok(())
1538 }
1539
1540 #[cfg(feature = "state-transition-signing")]
1541 pub fn sign_by_private_key(
1543 &mut self,
1544 private_key: &[u8],
1545 key_type: KeyType,
1546 bls: &impl BlsModule,
1547 ) -> Result<(), ProtocolError> {
1548 let data = self.signable_bytes()?;
1549 match key_type {
1550 KeyType::BLS12_381 => {
1551 if !self.set_signature(bls.sign(&data, private_key)?.into()) {
1552 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1553 needed: self.required_number_of_private_keys(),
1554 using: 1,
1555 msg: "failed to set BLS signature",
1556 });
1557 }
1558 }
1559
1560 KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => {
1562 let signature = signer::sign(&data, private_key)?;
1563 if !self.set_signature(signature.to_vec().into()) {
1564 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1565 needed: self.required_number_of_private_keys(),
1566 using: 1,
1567 msg: "failed to set ECDSA signature",
1568 });
1569 };
1570 }
1571
1572 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1576 return Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1577 InvalidIdentityPublicKeyTypeError::new(key_type),
1578 ))
1579 }
1580 };
1581 Ok(())
1582 }
1583
1584 #[cfg(all(feature = "state-transition-signing", feature = "core_key_wallet"))]
1662 pub async fn sign_with_core_signer<S: ::key_wallet::signer::Signer>(
1663 &mut self,
1664 path: &::key_wallet::bip32::DerivationPath,
1665 signer: &S,
1666 ) -> Result<(), ProtocolError> {
1667 use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1668 use dashcore::secp256k1::{Message, Secp256k1};
1669 use dashcore::signer::{double_sha, CompactSignature};
1670
1671 let data = self.signable_bytes()?;
1672 let data_hash = double_sha(&data);
1675 let digest: [u8; 32] = data_hash.as_slice().try_into().map_err(|_| {
1676 ProtocolError::Generic("double_sha did not return 32 bytes".to_string())
1677 })?;
1678
1679 let (signature, public_key) = signer
1680 .sign_ecdsa(path, digest)
1681 .await
1682 .map_err(|e| ProtocolError::ExternalSignerError(format!("signer failed: {}", e)))?;
1683
1684 let compact_64 = signature.serialize_compact();
1693 let secp = Secp256k1::new();
1694 let msg = Message::from_digest(digest);
1695
1696 let mut found: Option<RecoverableSignature> = None;
1697 for id in 0..4i32 {
1698 let recid = match RecoveryId::try_from(id) {
1699 Ok(r) => r,
1700 Err(_) => continue,
1701 };
1702 let candidate = match RecoverableSignature::from_compact(&compact_64, recid) {
1703 Ok(s) => s,
1704 Err(_) => continue,
1705 };
1706 if let Ok(recovered) = secp.recover_ecdsa(&msg, &candidate) {
1707 if recovered == public_key {
1708 found = Some(candidate);
1709 break;
1710 }
1711 }
1712 }
1713 let recoverable = found.ok_or_else(|| {
1714 ProtocolError::ExternalSignerError(
1720 "signer returned a signature whose recovery id does not match the returned public key".to_string(),
1721 )
1722 })?;
1723
1724 let compact_65 = recoverable.to_compact_signature(true);
1729
1730 if !self.set_signature(compact_65.to_vec().into()) {
1731 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1732 needed: self.required_number_of_private_keys(),
1733 using: 1,
1734 msg: "failed to set ECDSA signature",
1735 });
1736 }
1737 Ok(())
1738 }
1739
1740 #[cfg(feature = "state-transition-validation")]
1741 fn verify_by_raw_public_key<T: BlsModule>(
1742 &self,
1743 public_key: &[u8],
1744 public_key_type: KeyType,
1745 bls: &T,
1746 ) -> Result<(), ProtocolError> {
1747 match public_key_type {
1748 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key),
1749 KeyType::ECDSA_HASH160 => {
1750 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key)
1751 }
1752 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key, bls),
1753 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1754 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1755 InvalidIdentityPublicKeyTypeError::new(public_key_type),
1756 ))
1757 }
1758 }
1759 }
1760
1761 #[cfg(feature = "state-transition-validation")]
1762 pub fn verify_identity_signed_signature(
1763 &self,
1764 public_key: &IdentityPublicKey,
1765 bls: &impl BlsModule,
1766 ) -> Result<(), ProtocolError> {
1767 if public_key.disabled_at().is_some() {
1769 return Err(ProtocolError::PublicKeyIsDisabledError(
1770 PublicKeyIsDisabledError::new(public_key.id()),
1771 ));
1772 }
1773
1774 let Some(signature) = self.signature() else {
1775 return Err(ProtocolError::CorruptedCodeExecution("verifying identity signature for a state transition that doesn't use identity signatures".to_string()));
1776 };
1777 if signature.is_empty() {
1778 return Err(ProtocolError::StateTransitionIsNotSignedError(
1779 StateTransitionIsNotSignedError::new(self.clone()),
1780 ));
1781 }
1782
1783 if self.signature_public_key_id() != Some(public_key.id()) {
1784 return Err(ProtocolError::PublicKeyMismatchError(
1785 PublicKeyMismatchError::new(public_key.clone()),
1786 ));
1787 }
1788
1789 let public_key_bytes = public_key.data().as_slice();
1790 match public_key.key_type() {
1791 KeyType::ECDSA_HASH160 => {
1792 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key_bytes)
1793 }
1794
1795 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key_bytes),
1796
1797 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key_bytes, bls),
1798
1799 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => Ok(()),
1801 }
1802 }
1803
1804 #[cfg(feature = "state-transition-validation")]
1805 fn verify_ecdsa_hash_160_signature_by_public_key_hash(
1806 &self,
1807 public_key_hash: &[u8],
1808 ) -> Result<(), ProtocolError> {
1809 let Some(signature) = self.signature() else {
1810 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1811 needed: self.required_number_of_private_keys(),
1812 using: 1,
1813 msg: "This state transition type should a single signature",
1814 });
1815 };
1816 if signature.is_empty() {
1817 return Err(ProtocolError::StateTransitionIsNotSignedError(
1818 StateTransitionIsNotSignedError::new(self.clone()),
1819 ));
1820 }
1821 let data = self.signable_bytes()?;
1822 let data_hash = double_sha(data);
1823 signer::verify_hash_signature(&data_hash, signature.as_slice(), public_key_hash).map_err(
1824 |e| {
1825 ProtocolError::from(ConsensusError::SignatureError(
1826 SignatureError::InvalidStateTransitionSignatureError(
1827 InvalidStateTransitionSignatureError::new(e.to_string()),
1828 ),
1829 ))
1830 },
1831 )
1832 }
1833
1834 #[cfg(feature = "state-transition-validation")]
1835 fn verify_ecdsa_signature_by_public_key(&self, public_key: &[u8]) -> Result<(), ProtocolError> {
1837 let Some(signature) = self.signature() else {
1838 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1839 needed: self.required_number_of_private_keys(),
1840 using: 1,
1841 msg: "This state transition type should a single signature",
1842 });
1843 };
1844 if signature.is_empty() {
1845 return Err(ProtocolError::StateTransitionIsNotSignedError(
1846 StateTransitionIsNotSignedError::new(self.clone()),
1847 ));
1848 }
1849 let data = self.signable_bytes()?;
1850 signer::verify_data_signature(&data, signature.as_slice(), public_key).map_err(|e| {
1851 ProtocolError::from(ConsensusError::SignatureError(
1854 SignatureError::InvalidStateTransitionSignatureError(
1855 InvalidStateTransitionSignatureError::new(e.to_string()),
1856 ),
1857 ))
1858 })
1859 }
1860
1861 #[cfg(feature = "state-transition-validation")]
1862 fn verify_bls_signature_by_public_key<T: BlsModule>(
1864 &self,
1865 public_key: &[u8],
1866 bls: &T,
1867 ) -> Result<(), ProtocolError> {
1868 let Some(signature) = self.signature() else {
1869 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1870 needed: self.required_number_of_private_keys(),
1871 using: 1,
1872 msg: "This state transition type should a single signature",
1873 });
1874 };
1875 if signature.is_empty() {
1876 return Err(ProtocolError::StateTransitionIsNotSignedError(
1877 StateTransitionIsNotSignedError::new(self.clone()),
1878 ));
1879 }
1880
1881 let data = self.signable_bytes()?;
1882
1883 bls.verify_signature(signature.as_slice(), &data, public_key)
1884 .map(|_| ())
1885 .map_err(|e| {
1886 ProtocolError::from(ConsensusError::SignatureError(
1888 SignatureError::InvalidStateTransitionSignatureError(
1889 InvalidStateTransitionSignatureError::new(e.to_string()),
1890 ),
1891 ))
1892 })
1893 }
1894}
1895
1896#[cfg(feature = "state-transition-validation")]
1897impl StateTransitionStructureValidation for StateTransition {
1898 fn validate_structure(
1899 &self,
1900 platform_version: &PlatformVersion,
1901 ) -> crate::validation::SimpleConsensusValidationResult {
1902 match self {
1903 StateTransition::DataContractCreate(_)
1904 | StateTransition::DataContractUpdate(_)
1905 | StateTransition::Batch(_)
1906 | StateTransition::IdentityCreate(_)
1907 | StateTransition::IdentityTopUp(_)
1908 | StateTransition::IdentityCreditWithdrawal(_)
1909 | StateTransition::IdentityUpdate(_)
1910 | StateTransition::IdentityCreditTransfer(_)
1911 | StateTransition::MasternodeVote(_) => {
1912 crate::validation::SimpleConsensusValidationResult::new_with_error(
1913 UnsupportedFeatureError::new(
1914 "structure validation for identity-based state transitions".to_string(),
1915 platform_version.protocol_version,
1916 )
1917 .into(),
1918 )
1919 }
1920 StateTransition::IdentityCreditTransferToAddresses(transition) => {
1921 transition.validate_structure(platform_version)
1922 }
1923 StateTransition::IdentityCreateFromAddresses(transition) => {
1924 transition.validate_structure(platform_version)
1925 }
1926 StateTransition::IdentityTopUpFromAddresses(transition) => {
1927 transition.validate_structure(platform_version)
1928 }
1929 StateTransition::AddressFundsTransfer(transition) => {
1930 transition.validate_structure(platform_version)
1931 }
1932 StateTransition::AddressFundingFromAssetLock(transition) => {
1933 transition.validate_structure(platform_version)
1934 }
1935 StateTransition::AddressCreditWithdrawal(transition) => {
1936 transition.validate_structure(platform_version)
1937 }
1938 StateTransition::Shield(transition) => transition.validate_structure(platform_version),
1939 StateTransition::ShieldedTransfer(transition) => {
1940 transition.validate_structure(platform_version)
1941 }
1942 StateTransition::Unshield(transition) => {
1943 transition.validate_structure(platform_version)
1944 }
1945 StateTransition::ShieldFromAssetLock(transition) => {
1946 transition.validate_structure(platform_version)
1947 }
1948 StateTransition::ShieldedWithdrawal(transition) => {
1949 transition.validate_structure(platform_version)
1950 }
1951 StateTransition::IdentityCreateFromShieldedPool(transition) => {
1952 transition.validate_structure(platform_version)
1953 }
1954 }
1955 }
1956}
1957
1958#[cfg(test)]
1959mod tests {
1960 use super::*;
1961
1962 #[test]
1967 fn test_signing_options_default() {
1968 let opts = StateTransitionSigningOptions::default();
1969 assert!(!opts.allow_signing_with_any_security_level);
1970 assert!(!opts.allow_signing_with_any_purpose);
1971 }
1972
1973 #[test]
1974 fn test_signing_options_equality() {
1975 let a = StateTransitionSigningOptions {
1976 allow_signing_with_any_security_level: true,
1977 allow_signing_with_any_purpose: false,
1978 };
1979 let b = StateTransitionSigningOptions {
1980 allow_signing_with_any_security_level: true,
1981 allow_signing_with_any_purpose: false,
1982 };
1983 assert_eq!(a, b);
1984 }
1985
1986 #[test]
1987 fn test_signing_options_inequality() {
1988 let a = StateTransitionSigningOptions {
1989 allow_signing_with_any_security_level: true,
1990 allow_signing_with_any_purpose: false,
1991 };
1992 let b = StateTransitionSigningOptions {
1993 allow_signing_with_any_security_level: false,
1994 allow_signing_with_any_purpose: false,
1995 };
1996 assert_ne!(a, b);
1997 }
1998
1999 #[test]
2000 #[allow(clippy::clone_on_copy)]
2001 fn test_signing_options_clone() {
2002 let original = StateTransitionSigningOptions {
2003 allow_signing_with_any_security_level: true,
2004 allow_signing_with_any_purpose: true,
2005 };
2006 let cloned = original.clone();
2007 assert_eq!(original, cloned);
2008 }
2009
2010 #[test]
2011 fn test_signing_options_copy() {
2012 let original = StateTransitionSigningOptions {
2013 allow_signing_with_any_security_level: true,
2014 allow_signing_with_any_purpose: false,
2015 };
2016 let copied = original;
2017 assert_eq!(original, copied);
2018 }
2019
2020 #[test]
2021 fn test_signing_options_debug() {
2022 let opts = StateTransitionSigningOptions::default();
2023 let debug_str = format!("{:?}", opts);
2024 assert!(debug_str.contains("StateTransitionSigningOptions"));
2025 assert!(debug_str.contains("allow_signing_with_any_security_level"));
2026 assert!(debug_str.contains("allow_signing_with_any_purpose"));
2027 }
2028
2029 use crate::identity::core_script::CoreScript;
2039 use crate::identity::{Purpose, SecurityLevel};
2040 use crate::prelude::Identifier;
2041 use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0;
2042 use crate::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition;
2043 use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0;
2044 use crate::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition;
2045 use crate::state_transition::masternode_vote_transition::v0::MasternodeVoteTransitionV0;
2046 use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
2047 use crate::withdrawal::Pooling;
2048
2049 fn sample_transfer_st() -> StateTransition {
2050 let v0 = IdentityCreditTransferTransitionV0 {
2051 identity_id: Identifier::from([1u8; 32]),
2052 recipient_id: Identifier::from([2u8; 32]),
2053 amount: 1_000,
2054 nonce: 7,
2055 user_fee_increase: 3,
2056 signature_public_key_id: 11,
2057 signature: BinaryData::new(vec![0u8; 65]),
2058 };
2059 StateTransition::IdentityCreditTransfer(IdentityCreditTransferTransition::V0(v0))
2060 }
2061
2062 fn sample_masternode_vote_st() -> StateTransition {
2063 let v0 = MasternodeVoteTransitionV0 {
2064 pro_tx_hash: Identifier::from([3u8; 32]),
2065 voter_identity_id: Identifier::from([4u8; 32]),
2066 vote: Default::default(),
2067 nonce: 2,
2068 signature_public_key_id: 5,
2069 signature: BinaryData::new(vec![9u8; 10]),
2070 };
2071 StateTransition::MasternodeVote(MasternodeVoteTransition::V0(v0))
2072 }
2073
2074 fn sample_withdrawal_st() -> StateTransition {
2075 let v0 = IdentityCreditWithdrawalTransitionV0 {
2076 identity_id: Identifier::from([5u8; 32]),
2077 amount: 42,
2078 core_fee_per_byte: 1,
2079 pooling: Pooling::Never,
2080 output_script: CoreScript::from_bytes(vec![0x76, 0xa9]),
2081 nonce: 4,
2082 user_fee_increase: 1,
2083 signature_public_key_id: 3,
2084 signature: BinaryData::new(vec![8u8; 65]),
2085 };
2086 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V0(v0))
2087 }
2088
2089 #[test]
2090 fn test_name_returns_variant_names() {
2091 assert_eq!(sample_transfer_st().name(), "IdentityCreditTransfer");
2092 assert_eq!(sample_masternode_vote_st().name(), "MasternodeVote");
2093 assert_eq!(sample_withdrawal_st().name(), "IdentityCreditWithdrawal");
2094 }
2095
2096 #[test]
2097 fn test_state_transition_type_matches_variant() {
2098 assert_eq!(
2099 sample_transfer_st().state_transition_type(),
2100 StateTransitionType::IdentityCreditTransfer
2101 );
2102 assert_eq!(
2103 sample_masternode_vote_st().state_transition_type(),
2104 StateTransitionType::MasternodeVote
2105 );
2106 assert_eq!(
2107 sample_withdrawal_st().state_transition_type(),
2108 StateTransitionType::IdentityCreditWithdrawal
2109 );
2110 }
2111
2112 #[test]
2113 fn test_is_identity_signed_excludes_asset_lock_and_shielded() {
2114 assert!(sample_transfer_st().is_identity_signed());
2115 assert!(sample_masternode_vote_st().is_identity_signed());
2116 assert!(sample_withdrawal_st().is_identity_signed());
2117 }
2118
2119 #[test]
2120 fn test_signature_accessor() {
2121 let st = sample_transfer_st();
2122 let sig = st.signature().expect("transfer should expose signature");
2123 assert_eq!(sig.len(), 65);
2124
2125 let st = sample_masternode_vote_st();
2126 let sig = st.signature().expect("masternode vote has signature");
2127 assert_eq!(sig.as_slice(), &[9u8; 10]);
2128 }
2129
2130 #[test]
2131 fn test_owner_id_accessor() {
2132 let transfer = sample_transfer_st();
2133 assert_eq!(transfer.owner_id(), Some(Identifier::from([1u8; 32])));
2134
2135 let vote = sample_masternode_vote_st();
2136 assert_eq!(vote.owner_id(), Some(Identifier::from([4u8; 32])));
2137
2138 let withdraw = sample_withdrawal_st();
2139 assert_eq!(withdraw.owner_id(), Some(Identifier::from([5u8; 32])));
2140 }
2141
2142 #[test]
2143 fn test_signature_public_key_id_accessor() {
2144 assert_eq!(sample_transfer_st().signature_public_key_id(), Some(11));
2145 assert_eq!(
2146 sample_masternode_vote_st().signature_public_key_id(),
2147 Some(5)
2148 );
2149 assert_eq!(sample_withdrawal_st().signature_public_key_id(), Some(3));
2150 }
2151
2152 #[test]
2153 fn test_user_fee_increase_for_various_variants() {
2154 assert_eq!(sample_transfer_st().user_fee_increase(), 3);
2156 assert_eq!(sample_masternode_vote_st().user_fee_increase(), 0);
2158 assert_eq!(sample_withdrawal_st().user_fee_increase(), 1);
2160 }
2161
2162 #[test]
2163 fn test_set_signature_returns_true_for_supported() {
2164 let mut st = sample_transfer_st();
2165 let ok = st.set_signature(BinaryData::new(vec![0xaa; 65]));
2166 assert!(ok);
2167 assert_eq!(st.signature().unwrap().as_slice(), &[0xaa; 65]);
2168 }
2169
2170 #[test]
2171 fn test_set_user_fee_increase_updates_value() {
2172 let mut st = sample_transfer_st();
2173 st.set_user_fee_increase(42);
2174 assert_eq!(st.user_fee_increase(), 42);
2175
2176 let mut vote = sample_masternode_vote_st();
2178 vote.set_user_fee_increase(99);
2179 assert_eq!(vote.user_fee_increase(), 0);
2180 }
2181
2182 #[test]
2183 fn test_set_signature_public_key_id() {
2184 let mut st = sample_transfer_st();
2185 st.set_signature_public_key_id(1234);
2186 assert_eq!(st.signature_public_key_id(), Some(1234));
2187 }
2188
2189 #[test]
2190 fn test_required_number_of_private_keys_default() {
2191 assert_eq!(sample_transfer_st().required_number_of_private_keys(), 1);
2193 assert_eq!(
2194 sample_masternode_vote_st().required_number_of_private_keys(),
2195 1
2196 );
2197 assert_eq!(sample_withdrawal_st().required_number_of_private_keys(), 1);
2198 }
2199
2200 #[test]
2201 fn test_inputs_none_for_legacy_variants() {
2202 assert!(sample_transfer_st().inputs().is_none());
2204 assert!(sample_masternode_vote_st().inputs().is_none());
2205 assert!(sample_withdrawal_st().inputs().is_none());
2206 }
2207
2208 #[test]
2209 fn test_active_version_range_legacy_transitions() {
2210 assert_eq!(sample_transfer_st().active_version_range(), ALL_VERSIONS);
2212 assert_eq!(
2213 sample_masternode_vote_st().active_version_range(),
2214 ALL_VERSIONS
2215 );
2216 assert_eq!(sample_withdrawal_st().active_version_range(), ALL_VERSIONS);
2217 }
2218
2219 #[test]
2220 fn test_unique_identifiers_non_empty() {
2221 let ids = sample_transfer_st().unique_identifiers();
2222 assert_eq!(ids.len(), 1);
2223 assert!(!ids[0].is_empty());
2224 }
2225
2226 #[test]
2227 fn test_required_asset_lock_balance_rejects_non_asset_lock() {
2228 let platform_version = PlatformVersion::latest();
2229 let st = sample_transfer_st();
2230 let err = st
2231 .required_asset_lock_balance_for_processing_start(platform_version)
2232 .expect_err("credit transfer is not an asset lock state transition");
2233 match err {
2234 ProtocolError::CorruptedCodeExecution(msg) => {
2235 assert!(
2236 msg.contains("is not an asset lock transaction"),
2237 "unexpected error message: {msg}"
2238 );
2239 }
2240 other => panic!("expected CorruptedCodeExecution, got {other:?}"),
2241 }
2242 }
2243
2244 #[test]
2245 fn test_security_level_requirement_for_transfer() {
2246 let st = sample_transfer_st();
2248 let levels = st
2249 .security_level_requirement(Purpose::TRANSFER)
2250 .expect("transfer state transition should return a requirement");
2251 assert_eq!(levels, vec![SecurityLevel::CRITICAL]);
2252 }
2253
2254 #[test]
2255 fn test_purpose_requirement_for_transfer() {
2256 let st = sample_transfer_st();
2257 let purposes = st
2258 .purpose_requirement()
2259 .expect("transfer state transition should have a purpose");
2260 assert_eq!(purposes, vec![Purpose::TRANSFER]);
2261 }
2262
2263 #[test]
2264 fn test_optional_asset_lock_proof_none_for_transfer() {
2265 let st = sample_transfer_st();
2266 assert!(st.optional_asset_lock_proof().is_none());
2267 }
2268
2269 #[test]
2274 fn test_from_outer_enum_into_state_transition() {
2275 let outer: IdentityCreditTransferTransition =
2276 IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0::default());
2277 let st: StateTransition = outer.into();
2278 assert!(matches!(st, StateTransition::IdentityCreditTransfer(_)));
2279 }
2280
2281 #[test]
2282 fn test_from_masternode_vote_outer_into_state_transition() {
2283 let outer: MasternodeVoteTransition =
2284 MasternodeVoteTransition::V0(MasternodeVoteTransitionV0::default());
2285 let st: StateTransition = outer.into();
2286 assert!(matches!(st, StateTransition::MasternodeVote(_)));
2287 }
2288
2289 #[test]
2295 fn test_state_transition_platform_serialize_roundtrip() {
2296 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2297 let original = sample_transfer_st();
2298 let bytes =
2299 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2300 let restored =
2301 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2302 assert_eq!(original, restored);
2303 }
2304
2305 #[test]
2306 fn test_deserialize_from_bytes_in_version_succeeds_for_latest() {
2307 use crate::serialization::PlatformSerializable;
2308 let original = sample_transfer_st();
2309 let bytes =
2310 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2311 let restored =
2312 StateTransition::deserialize_from_bytes_in_version(&bytes, PlatformVersion::latest())
2313 .expect("deserialize_from_bytes_in_version should succeed");
2314 assert_eq!(original, restored);
2315 }
2316
2317 #[test]
2318 fn test_transaction_id_is_deterministic() {
2319 let st = sample_transfer_st();
2320 let a = st.transaction_id().expect("hash should succeed");
2321 let b = st.transaction_id().expect("hash should succeed");
2322 assert_eq!(a, b);
2323 assert_eq!(a.len(), 32);
2324 }
2325
2326 #[test]
2327 fn test_transaction_id_changes_on_signature_change() {
2328 let mut st = sample_transfer_st();
2329 let before = st.transaction_id().expect("hash should succeed");
2330 st.set_signature(BinaryData::new(vec![0xbb; 65]));
2331 let after = st.transaction_id().expect("hash should succeed");
2332 assert_ne!(before, after);
2334 }
2335
2336 #[test]
2337 fn test_clone_preserves_inner_state() {
2338 let st = sample_transfer_st();
2339 let cloned = st.clone();
2340 assert_eq!(st, cloned);
2341 }
2342
2343 use crate::data_contract::serialized_version::DataContractInSerializationFormat;
2354 use crate::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0;
2355 use crate::state_transition::batch_transition::document_base_transition::DocumentBaseTransition;
2356 use crate::state_transition::batch_transition::document_delete_transition::{
2357 DocumentDeleteTransition, DocumentDeleteTransitionV0,
2358 };
2359 use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionV0};
2360 use crate::state_transition::data_contract_create_transition::{
2361 DataContractCreateTransition, DataContractCreateTransitionV0,
2362 };
2363 use crate::state_transition::data_contract_update_transition::{
2364 DataContractUpdateTransition, DataContractUpdateTransitionV0,
2365 };
2366 use crate::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0;
2367 use crate::state_transition::identity_create_transition::IdentityCreateTransition;
2368 use crate::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0;
2369 use crate::state_transition::identity_topup_transition::IdentityTopUpTransition;
2370 use crate::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0;
2371 use crate::state_transition::identity_update_transition::IdentityUpdateTransition;
2372 use crate::state_transition::shielded_transfer_transition::v0::ShieldedTransferTransitionV0;
2373 use crate::state_transition::shielded_transfer_transition::ShieldedTransferTransition;
2374 use crate::state_transition::shielded_withdrawal_transition::v0::ShieldedWithdrawalTransitionV0;
2375 use crate::state_transition::shielded_withdrawal_transition::ShieldedWithdrawalTransition;
2376 use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
2377 use crate::state_transition::unshield_transition::UnshieldTransition;
2378
2379 fn sample_data_contract_in_serialization_format() -> DataContractInSerializationFormat {
2382 use crate::data_contract::config::v0::DataContractConfigV0;
2383 use crate::data_contract::config::DataContractConfig;
2384 use crate::data_contract::v1::DataContractV1;
2385 use crate::data_contract::DataContract;
2386 use platform_version::TryIntoPlatformVersioned;
2387 use std::collections::BTreeMap;
2388
2389 let contract = DataContract::V1(DataContractV1 {
2390 id: Identifier::from([9u8; 32]),
2391 version: 1,
2392 owner_id: Identifier::from([7u8; 32]),
2393 document_types: BTreeMap::new(),
2394 config: DataContractConfig::V0(DataContractConfigV0 {
2395 can_be_deleted: false,
2396 readonly: false,
2397 keeps_history: false,
2398 documents_keep_history_contract_default: false,
2399 documents_mutable_contract_default: false,
2400 documents_can_be_deleted_contract_default: false,
2401 requires_identity_encryption_bounded_key: None,
2402 requires_identity_decryption_bounded_key: None,
2403 }),
2404 schema_defs: None,
2405 created_at: None,
2406 updated_at: None,
2407 created_at_block_height: None,
2408 updated_at_block_height: None,
2409 created_at_epoch: None,
2410 updated_at_epoch: None,
2411 groups: BTreeMap::new(),
2412 tokens: BTreeMap::new(),
2413 keywords: Vec::new(),
2414 description: None,
2415 });
2416
2417 contract
2418 .try_into_platform_versioned(PlatformVersion::latest())
2419 .expect("expected to serialize a trivial contract")
2420 }
2421
2422 fn sample_data_contract_create_st() -> StateTransition {
2423 StateTransition::DataContractCreate(DataContractCreateTransition::V0(
2424 DataContractCreateTransitionV0 {
2425 data_contract: sample_data_contract_in_serialization_format(),
2426 identity_nonce: 1,
2427 user_fee_increase: 5,
2428 signature_public_key_id: 2,
2429 signature: BinaryData::new(vec![0xAB; 65]),
2430 },
2431 ))
2432 }
2433
2434 fn sample_data_contract_update_st() -> StateTransition {
2435 StateTransition::DataContractUpdate(DataContractUpdateTransition::V0(
2436 DataContractUpdateTransitionV0 {
2437 identity_contract_nonce: 4,
2438 data_contract: sample_data_contract_in_serialization_format(),
2439 user_fee_increase: 9,
2440 signature_public_key_id: 6,
2441 signature: BinaryData::new(vec![0xCD; 65]),
2442 },
2443 ))
2444 }
2445
2446 fn sample_batch_st_with_delete() -> StateTransition {
2447 let base = DocumentBaseTransition::V0(DocumentBaseTransitionV0 {
2448 id: Identifier::from([1u8; 32]),
2449 identity_contract_nonce: 3,
2450 document_type_name: "preorder".to_string(),
2451 data_contract_id: Identifier::from([2u8; 32]),
2452 });
2453 let delete =
2454 DocumentTransition::Delete(DocumentDeleteTransition::V0(DocumentDeleteTransitionV0 {
2455 base,
2456 }));
2457 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2458 owner_id: Identifier::from([8u8; 32]),
2459 transitions: vec![delete],
2460 user_fee_increase: 2,
2461 signature_public_key_id: 7,
2462 signature: BinaryData::new(vec![0xEE; 65]),
2463 }))
2464 }
2465
2466 fn sample_batch_st_empty() -> StateTransition {
2467 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2468 owner_id: Identifier::from([1u8; 32]),
2469 transitions: vec![],
2470 user_fee_increase: 0,
2471 signature_public_key_id: 0,
2472 signature: BinaryData::new(vec![]),
2473 }))
2474 }
2475
2476 fn sample_identity_create_st() -> StateTransition {
2477 StateTransition::IdentityCreate(IdentityCreateTransition::V0(IdentityCreateTransitionV0 {
2478 identity_id: Identifier::from([3u8; 32]),
2479 ..Default::default()
2480 }))
2481 }
2482
2483 fn sample_identity_top_up_st() -> StateTransition {
2484 StateTransition::IdentityTopUp(IdentityTopUpTransition::V0(IdentityTopUpTransitionV0 {
2485 identity_id: Identifier::from([4u8; 32]),
2486 ..Default::default()
2487 }))
2488 }
2489
2490 fn sample_identity_update_st() -> StateTransition {
2491 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
2492 identity_id: Identifier::from([5u8; 32]),
2493 revision: 1,
2494 nonce: 2,
2495 add_public_keys: vec![],
2496 disable_public_keys: vec![],
2497 user_fee_increase: 11,
2498 signature_public_key_id: 33,
2499 signature: BinaryData::new(vec![0xFF; 65]),
2500 }))
2501 }
2502
2503 fn sample_unshield_st() -> StateTransition {
2504 StateTransition::Unshield(UnshieldTransition::V0(UnshieldTransitionV0 {
2505 output_address: Default::default(),
2506 actions: vec![],
2507 unshielding_amount: 0,
2508 anchor: [0u8; 32],
2509 proof: vec![],
2510 binding_signature: [0u8; 64],
2511 }))
2512 }
2513
2514 fn sample_shielded_transfer_st() -> StateTransition {
2515 StateTransition::ShieldedTransfer(ShieldedTransferTransition::V0(
2516 ShieldedTransferTransitionV0 {
2517 actions: vec![],
2518 value_balance: 0,
2519 anchor: [0u8; 32],
2520 proof: vec![],
2521 binding_signature: [0u8; 64],
2522 },
2523 ))
2524 }
2525
2526 fn sample_shielded_withdrawal_st() -> StateTransition {
2527 use crate::identity::core_script::CoreScript;
2528 use crate::withdrawal::Pooling;
2529 StateTransition::ShieldedWithdrawal(ShieldedWithdrawalTransition::V0(
2530 ShieldedWithdrawalTransitionV0 {
2531 actions: vec![],
2532 unshielding_amount: 0,
2533 anchor: [0u8; 32],
2534 proof: vec![],
2535 binding_signature: [0u8; 64],
2536 core_fee_per_byte: 1,
2537 pooling: Pooling::Never,
2538 output_script: CoreScript::from_bytes(vec![]),
2539 },
2540 ))
2541 }
2542
2543 #[test]
2546 fn test_name_for_newly_covered_variants() {
2547 assert_eq!(
2548 sample_data_contract_create_st().name(),
2549 "DataContractCreate"
2550 );
2551 assert_eq!(
2552 sample_data_contract_update_st().name(),
2553 "DataContractUpdate"
2554 );
2555 assert_eq!(sample_identity_create_st().name(), "IdentityCreate");
2556 assert_eq!(sample_identity_top_up_st().name(), "IdentityTopUp");
2557 assert_eq!(sample_identity_update_st().name(), "IdentityUpdate");
2558 assert_eq!(sample_unshield_st().name(), "Unshield");
2559 assert_eq!(sample_shielded_transfer_st().name(), "ShieldedTransfer");
2560 assert_eq!(sample_shielded_withdrawal_st().name(), "ShieldedWithdrawal");
2561
2562 let batch_name = sample_batch_st_with_delete().name();
2565 assert_eq!(batch_name, "DocumentsBatch([Delete])");
2566
2567 let empty_name = sample_batch_st_empty().name();
2569 assert_eq!(empty_name, "DocumentsBatch([])");
2570 }
2571
2572 #[test]
2574 fn test_state_transition_type_for_newly_covered_variants() {
2575 assert_eq!(
2576 sample_data_contract_create_st().state_transition_type(),
2577 StateTransitionType::DataContractCreate
2578 );
2579 assert_eq!(
2580 sample_data_contract_update_st().state_transition_type(),
2581 StateTransitionType::DataContractUpdate
2582 );
2583 assert_eq!(
2584 sample_batch_st_with_delete().state_transition_type(),
2585 StateTransitionType::Batch
2586 );
2587 assert_eq!(
2588 sample_identity_create_st().state_transition_type(),
2589 StateTransitionType::IdentityCreate
2590 );
2591 assert_eq!(
2592 sample_identity_top_up_st().state_transition_type(),
2593 StateTransitionType::IdentityTopUp
2594 );
2595 assert_eq!(
2596 sample_identity_update_st().state_transition_type(),
2597 StateTransitionType::IdentityUpdate
2598 );
2599 assert_eq!(
2600 sample_unshield_st().state_transition_type(),
2601 StateTransitionType::Unshield
2602 );
2603 assert_eq!(
2604 sample_shielded_transfer_st().state_transition_type(),
2605 StateTransitionType::ShieldedTransfer
2606 );
2607 assert_eq!(
2608 sample_shielded_withdrawal_st().state_transition_type(),
2609 StateTransitionType::ShieldedWithdrawal
2610 );
2611 }
2612
2613 #[test]
2618 fn test_active_version_range_contract_and_shielded_branches() {
2619 let contract_v1_range = 9..=LATEST_VERSION;
2622 assert_eq!(
2623 sample_data_contract_create_st().active_version_range(),
2624 contract_v1_range
2625 );
2626 let contract_v1_range = 9..=LATEST_VERSION;
2627 assert_eq!(
2628 sample_data_contract_update_st().active_version_range(),
2629 contract_v1_range
2630 );
2631 assert_eq!(
2633 sample_batch_st_with_delete().active_version_range(),
2634 ALL_VERSIONS
2635 );
2636 assert_eq!(
2638 sample_identity_create_st().active_version_range(),
2639 ALL_VERSIONS
2640 );
2641 assert_eq!(
2642 sample_identity_top_up_st().active_version_range(),
2643 ALL_VERSIONS
2644 );
2645 assert_eq!(
2646 sample_identity_update_st().active_version_range(),
2647 ALL_VERSIONS
2648 );
2649 let shielded_range = 12..=LATEST_VERSION;
2651 assert_eq!(
2652 sample_shielded_transfer_st().active_version_range(),
2653 shielded_range.clone()
2654 );
2655 assert_eq!(
2656 sample_unshield_st().active_version_range(),
2657 shielded_range.clone()
2658 );
2659 assert_eq!(
2660 sample_shielded_withdrawal_st().active_version_range(),
2661 shielded_range
2662 );
2663 }
2664
2665 #[test]
2668 fn test_is_identity_signed_false_for_identity_create_topup_and_shielded() {
2669 assert!(!sample_identity_create_st().is_identity_signed());
2670 assert!(!sample_identity_top_up_st().is_identity_signed());
2671 assert!(!sample_unshield_st().is_identity_signed());
2672 assert!(!sample_shielded_transfer_st().is_identity_signed());
2673 assert!(!sample_shielded_withdrawal_st().is_identity_signed());
2674 }
2675
2676 #[test]
2680 fn test_signature_accessor_for_other_variants() {
2681 assert_eq!(
2683 sample_data_contract_create_st().signature().unwrap().len(),
2684 65
2685 );
2686 assert_eq!(
2687 sample_data_contract_update_st().signature().unwrap().len(),
2688 65
2689 );
2690 assert_eq!(sample_batch_st_with_delete().signature().unwrap().len(), 65);
2691 assert_eq!(sample_identity_update_st().signature().unwrap().len(), 65);
2692
2693 assert!(sample_unshield_st().signature().is_none());
2695 assert!(sample_shielded_transfer_st().signature().is_none());
2696 assert!(sample_shielded_withdrawal_st().signature().is_none());
2697 }
2698
2699 #[test]
2701 fn test_owner_id_accessor_for_other_variants() {
2702 assert_eq!(
2703 sample_data_contract_create_st().owner_id(),
2704 Some(Identifier::from([7u8; 32]))
2705 );
2706 assert_eq!(
2707 sample_data_contract_update_st().owner_id(),
2708 Some(Identifier::from([7u8; 32]))
2709 );
2710 assert_eq!(
2711 sample_batch_st_with_delete().owner_id(),
2712 Some(Identifier::from([8u8; 32]))
2713 );
2714 assert_eq!(
2715 sample_identity_update_st().owner_id(),
2716 Some(Identifier::from([5u8; 32]))
2717 );
2718 assert!(sample_unshield_st().owner_id().is_none());
2720 assert!(sample_shielded_transfer_st().owner_id().is_none());
2721 assert!(sample_shielded_withdrawal_st().owner_id().is_none());
2722 }
2723
2724 #[test]
2727 fn test_user_fee_increase_for_newly_covered_variants() {
2728 assert_eq!(sample_data_contract_create_st().user_fee_increase(), 5);
2729 assert_eq!(sample_data_contract_update_st().user_fee_increase(), 9);
2730 assert_eq!(sample_batch_st_with_delete().user_fee_increase(), 2);
2731 assert_eq!(sample_identity_update_st().user_fee_increase(), 11);
2732 assert_eq!(sample_shielded_transfer_st().user_fee_increase(), 0);
2734 assert_eq!(sample_shielded_withdrawal_st().user_fee_increase(), 0);
2735 assert_eq!(sample_unshield_st().user_fee_increase(), 0);
2736 }
2737
2738 #[test]
2741 fn test_set_user_fee_increase_for_newly_covered_variants() {
2742 let mut st = sample_data_contract_create_st();
2743 st.set_user_fee_increase(42);
2744 assert_eq!(st.user_fee_increase(), 42);
2745
2746 let mut st = sample_data_contract_update_st();
2747 st.set_user_fee_increase(13);
2748 assert_eq!(st.user_fee_increase(), 13);
2749
2750 let mut st = sample_batch_st_with_delete();
2751 st.set_user_fee_increase(101);
2752 assert_eq!(st.user_fee_increase(), 101);
2753
2754 let mut st = sample_identity_update_st();
2755 st.set_user_fee_increase(77);
2756 assert_eq!(st.user_fee_increase(), 77);
2757
2758 let mut shielded = sample_shielded_transfer_st();
2760 shielded.set_user_fee_increase(99);
2761 assert_eq!(shielded.user_fee_increase(), 0);
2762
2763 let mut withdrawal = sample_shielded_withdrawal_st();
2764 withdrawal.set_user_fee_increase(99);
2765 assert_eq!(withdrawal.user_fee_increase(), 0);
2766
2767 let mut unshield = sample_unshield_st();
2768 unshield.set_user_fee_increase(99);
2769 assert_eq!(unshield.user_fee_increase(), 0);
2770 }
2771
2772 #[test]
2776 fn test_set_signature_false_for_shielded_and_identity_create_topup() {
2777 let mut st = sample_unshield_st();
2779 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2780 let mut st = sample_shielded_transfer_st();
2781 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2782 let mut st = sample_shielded_withdrawal_st();
2783 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2784 }
2785
2786 #[test]
2787 fn test_set_signature_true_for_newly_covered_variants() {
2788 let mut st = sample_data_contract_create_st();
2789 assert!(st.set_signature(BinaryData::new(vec![0x11; 65])));
2790 assert_eq!(st.signature().unwrap().as_slice(), &[0x11; 65]);
2791
2792 let mut st = sample_data_contract_update_st();
2793 assert!(st.set_signature(BinaryData::new(vec![0x22; 65])));
2794 assert_eq!(st.signature().unwrap().as_slice(), &[0x22; 65]);
2795
2796 let mut st = sample_batch_st_with_delete();
2797 assert!(st.set_signature(BinaryData::new(vec![0x33; 65])));
2798 assert_eq!(st.signature().unwrap().as_slice(), &[0x33; 65]);
2799
2800 let mut st = sample_identity_update_st();
2801 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
2802 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
2803 }
2804
2805 #[test]
2808 fn test_signature_public_key_id_returns_none_for_non_signed() {
2809 assert!(sample_identity_create_st()
2812 .signature_public_key_id()
2813 .is_none());
2814 assert!(sample_identity_top_up_st()
2815 .signature_public_key_id()
2816 .is_none());
2817 assert!(sample_unshield_st().signature_public_key_id().is_none());
2818 assert!(sample_shielded_transfer_st()
2819 .signature_public_key_id()
2820 .is_none());
2821 assert!(sample_shielded_withdrawal_st()
2822 .signature_public_key_id()
2823 .is_none());
2824 }
2825
2826 #[test]
2827 fn test_signature_public_key_id_for_signed_variants() {
2828 assert_eq!(
2829 sample_data_contract_create_st().signature_public_key_id(),
2830 Some(2)
2831 );
2832 assert_eq!(
2833 sample_data_contract_update_st().signature_public_key_id(),
2834 Some(6)
2835 );
2836 assert_eq!(
2837 sample_batch_st_with_delete().signature_public_key_id(),
2838 Some(7)
2839 );
2840 assert_eq!(
2841 sample_identity_update_st().signature_public_key_id(),
2842 Some(33)
2843 );
2844 }
2845
2846 #[test]
2849 fn test_set_signature_public_key_id_noop_for_non_signed() {
2850 let mut st = sample_identity_create_st();
2853 st.set_signature_public_key_id(100);
2854 assert_eq!(st.signature_public_key_id(), None);
2855
2856 let mut st = sample_identity_top_up_st();
2857 st.set_signature_public_key_id(100);
2858 assert_eq!(st.signature_public_key_id(), None);
2859
2860 let mut st = sample_unshield_st();
2861 st.set_signature_public_key_id(100);
2862 assert_eq!(st.signature_public_key_id(), None);
2863 }
2864
2865 #[test]
2866 fn test_set_signature_public_key_id_updates_for_signed_variants() {
2867 let mut st = sample_data_contract_create_st();
2868 st.set_signature_public_key_id(42);
2869 assert_eq!(st.signature_public_key_id(), Some(42));
2870
2871 let mut st = sample_batch_st_with_delete();
2872 st.set_signature_public_key_id(43);
2873 assert_eq!(st.signature_public_key_id(), Some(43));
2874
2875 let mut st = sample_identity_update_st();
2876 st.set_signature_public_key_id(44);
2877 assert_eq!(st.signature_public_key_id(), Some(44));
2878 }
2879
2880 #[test]
2883 fn test_required_number_of_private_keys_various_variants() {
2884 assert_eq!(
2885 sample_data_contract_create_st().required_number_of_private_keys(),
2886 1
2887 );
2888 assert_eq!(
2889 sample_data_contract_update_st().required_number_of_private_keys(),
2890 1
2891 );
2892 assert_eq!(
2893 sample_batch_st_with_delete().required_number_of_private_keys(),
2894 1
2895 );
2896 assert_eq!(
2897 sample_identity_update_st().required_number_of_private_keys(),
2898 1
2899 );
2900 assert_eq!(
2901 sample_identity_create_st().required_number_of_private_keys(),
2902 1
2903 );
2904 assert_eq!(
2906 sample_shielded_transfer_st().required_number_of_private_keys(),
2907 0
2908 );
2909 assert_eq!(
2910 sample_shielded_withdrawal_st().required_number_of_private_keys(),
2911 0
2912 );
2913 assert_eq!(sample_unshield_st().required_number_of_private_keys(), 0);
2914 }
2915
2916 #[test]
2919 fn test_inputs_none_for_many_variants() {
2920 assert!(sample_data_contract_create_st().inputs().is_none());
2921 assert!(sample_data_contract_update_st().inputs().is_none());
2922 assert!(sample_batch_st_with_delete().inputs().is_none());
2923 assert!(sample_identity_create_st().inputs().is_none());
2924 assert!(sample_identity_top_up_st().inputs().is_none());
2925 assert!(sample_identity_update_st().inputs().is_none());
2926 assert!(sample_unshield_st().inputs().is_none());
2928 assert!(sample_shielded_transfer_st().inputs().is_none());
2929 assert!(sample_shielded_withdrawal_st().inputs().is_none());
2930 }
2931
2932 #[test]
2937 fn test_optional_asset_lock_proof_returns_none_for_wildcard_arms() {
2938 assert!(sample_data_contract_create_st()
2939 .optional_asset_lock_proof()
2940 .is_none());
2941 assert!(sample_data_contract_update_st()
2942 .optional_asset_lock_proof()
2943 .is_none());
2944 assert!(sample_batch_st_with_delete()
2945 .optional_asset_lock_proof()
2946 .is_none());
2947 assert!(sample_identity_update_st()
2948 .optional_asset_lock_proof()
2949 .is_none());
2950 assert!(sample_unshield_st().optional_asset_lock_proof().is_none());
2951 assert!(sample_shielded_transfer_st()
2952 .optional_asset_lock_proof()
2953 .is_none());
2954 assert!(sample_shielded_withdrawal_st()
2955 .optional_asset_lock_proof()
2956 .is_none());
2957 }
2958
2959 #[test]
2963 fn test_required_asset_lock_balance_errors_for_other_non_asset_lock_variants() {
2964 let platform_version = PlatformVersion::latest();
2965
2966 let cases: Vec<(&str, StateTransition)> = vec![
2967 ("DataContractCreate", sample_data_contract_create_st()),
2968 ("DataContractUpdate", sample_data_contract_update_st()),
2969 ("Batch", sample_batch_st_with_delete()),
2970 ("IdentityUpdate", sample_identity_update_st()),
2971 ("MasternodeVote", sample_masternode_vote_st()),
2972 ("Unshield", sample_unshield_st()),
2973 ("ShieldedTransfer", sample_shielded_transfer_st()),
2974 ("ShieldedWithdrawal", sample_shielded_withdrawal_st()),
2975 ];
2976
2977 for (label, st) in cases {
2978 let err = st
2979 .required_asset_lock_balance_for_processing_start(platform_version)
2980 .expect_err(&format!("expected error for {label}"));
2981 match err {
2982 ProtocolError::CorruptedCodeExecution(msg) => {
2983 assert!(
2984 msg.contains("is not an asset lock transaction"),
2985 "unexpected error for {label}: {msg}"
2986 );
2987 }
2988 other => panic!("expected CorruptedCodeExecution for {label}, got {other:?}"),
2989 }
2990 }
2991 }
2992
2993 #[test]
2999 fn test_unique_identifiers_non_empty_for_other_variants() {
3000 for st in [
3001 sample_data_contract_create_st(),
3002 sample_data_contract_update_st(),
3003 sample_batch_st_with_delete(),
3004 sample_identity_create_st(),
3005 sample_identity_top_up_st(),
3006 sample_identity_update_st(),
3007 ] {
3008 let ids = st.unique_identifiers();
3009 assert!(!ids.is_empty(), "unique_identifiers should not be empty");
3010 }
3011 }
3012
3013 #[test]
3017 fn test_security_level_requirement_returns_none_for_non_signed_variants() {
3018 let purpose = Purpose::AUTHENTICATION;
3019 assert!(sample_identity_create_st()
3020 .security_level_requirement(purpose)
3021 .is_none());
3022 assert!(sample_identity_top_up_st()
3023 .security_level_requirement(purpose)
3024 .is_none());
3025 assert!(sample_unshield_st()
3026 .security_level_requirement(purpose)
3027 .is_none());
3028 assert!(sample_shielded_transfer_st()
3029 .security_level_requirement(purpose)
3030 .is_none());
3031 assert!(sample_shielded_withdrawal_st()
3032 .security_level_requirement(purpose)
3033 .is_none());
3034 }
3035
3036 #[test]
3037 fn test_purpose_requirement_returns_none_for_non_signed_variants() {
3038 assert!(sample_identity_create_st().purpose_requirement().is_none());
3039 assert!(sample_identity_top_up_st().purpose_requirement().is_none());
3040 assert!(sample_unshield_st().purpose_requirement().is_none());
3041 assert!(sample_shielded_transfer_st()
3042 .purpose_requirement()
3043 .is_none());
3044 assert!(sample_shielded_withdrawal_st()
3045 .purpose_requirement()
3046 .is_none());
3047 }
3048
3049 #[test]
3051 fn test_from_outer_data_contract_create_into_state_transition() {
3052 let outer: DataContractCreateTransition =
3053 DataContractCreateTransition::V0(DataContractCreateTransitionV0 {
3054 data_contract: sample_data_contract_in_serialization_format(),
3055 identity_nonce: 1,
3056 user_fee_increase: 0,
3057 signature_public_key_id: 0,
3058 signature: Default::default(),
3059 });
3060 let st: StateTransition = outer.into();
3061 assert!(matches!(st, StateTransition::DataContractCreate(_)));
3062 }
3063
3064 #[test]
3065 fn test_from_outer_data_contract_update_into_state_transition() {
3066 let outer: DataContractUpdateTransition =
3067 DataContractUpdateTransition::V0(DataContractUpdateTransitionV0 {
3068 identity_contract_nonce: 2,
3069 data_contract: sample_data_contract_in_serialization_format(),
3070 user_fee_increase: 0,
3071 signature_public_key_id: 0,
3072 signature: Default::default(),
3073 });
3074 let st: StateTransition = outer.into();
3075 assert!(matches!(st, StateTransition::DataContractUpdate(_)));
3076 }
3077
3078 #[test]
3079 fn test_from_outer_batch_into_state_transition() {
3080 let outer: BatchTransition = BatchTransition::V0(BatchTransitionV0::default());
3081 let st: StateTransition = outer.into();
3082 assert!(matches!(st, StateTransition::Batch(_)));
3083 }
3084
3085 #[test]
3086 fn test_from_outer_identity_create_into_state_transition() {
3087 let outer: IdentityCreateTransition =
3088 IdentityCreateTransition::V0(IdentityCreateTransitionV0::default());
3089 let st: StateTransition = outer.into();
3090 assert!(matches!(st, StateTransition::IdentityCreate(_)));
3091 }
3092
3093 #[test]
3094 fn test_from_outer_identity_update_into_state_transition() {
3095 let outer: IdentityUpdateTransition =
3096 IdentityUpdateTransition::V0(IdentityUpdateTransitionV0::default());
3097 let st: StateTransition = outer.into();
3098 assert!(matches!(st, StateTransition::IdentityUpdate(_)));
3099 }
3100
3101 #[test]
3104 fn test_transaction_id_and_clone_for_identity_update() {
3105 let st = sample_identity_update_st();
3106 let id_a = st.transaction_id().expect("hash should succeed");
3107 let cloned = st.clone();
3108 let id_b = cloned.transaction_id().expect("hash should succeed");
3109 assert_eq!(id_a, id_b);
3110 assert_eq!(id_a.len(), 32);
3111 }
3112
3113 #[test]
3114 fn test_transaction_id_and_clone_for_data_contract_create() {
3115 let st = sample_data_contract_create_st();
3116 let id_a = st.transaction_id().expect("hash should succeed");
3117 let cloned = st.clone();
3118 let id_b = cloned.transaction_id().expect("hash should succeed");
3119 assert_eq!(id_a, id_b);
3120 assert_eq!(id_a.len(), 32);
3121 }
3122
3123 #[test]
3125 fn test_serialize_roundtrip_identity_update() {
3126 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3127 let original = sample_identity_update_st();
3128 let bytes =
3129 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3130 let restored =
3131 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
3132 assert_eq!(original, restored);
3133 }
3134
3135 #[test]
3136 fn test_serialize_roundtrip_data_contract_update() {
3137 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3138 let original = sample_data_contract_update_st();
3139 let bytes =
3140 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3141 let restored =
3142 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
3143 assert_eq!(original, restored);
3144 }
3145
3146 #[test]
3147 fn test_serialize_roundtrip_batch_empty() {
3148 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3149 let original = sample_batch_st_empty();
3150 let bytes =
3151 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3152 let restored =
3153 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
3154 assert_eq!(original, restored);
3155 }
3156
3157 #[cfg(all(feature = "state-transitions", feature = "validation"))]
3164 #[test]
3165 fn test_deserialize_from_bytes_in_version_returns_not_active_error() {
3166 use crate::serialization::PlatformSerializable;
3167
3168 let original = sample_shielded_transfer_st();
3170 let bytes =
3171 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
3172
3173 let low_version = PlatformVersion::get(1).expect("platform version 1 exists");
3177 assert!(
3178 low_version.protocol_version < 12,
3179 "expected sub-12 version for this test, got {}",
3180 low_version.protocol_version
3181 );
3182
3183 let err = StateTransition::deserialize_from_bytes_in_version(&bytes, low_version)
3184 .expect_err("expected StateTransitionIsNotActiveError for sub-12 protocol");
3185 match err {
3186 ProtocolError::StateTransitionError(
3187 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
3188 state_transition_type,
3189 active_version_range,
3190 current_protocol_version,
3191 },
3192 ) => {
3193 assert_eq!(state_transition_type, "ShieldedTransfer");
3194 assert_eq!(current_protocol_version, low_version.protocol_version);
3195 assert!(active_version_range.start() >= &12);
3196 }
3197 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
3198 }
3199 }
3200
3201 use crate::state_transition::identity_credit_transfer_to_addresses_transition::v0::IdentityCreditTransferToAddressesTransitionV0;
3214 use crate::state_transition::identity_credit_transfer_to_addresses_transition::IdentityCreditTransferToAddressesTransition;
3215 use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1;
3216 use crate::withdrawal::Pooling as WithdrawalPooling;
3217
3218 fn sample_withdrawal_v1_st() -> StateTransition {
3219 let v1 = IdentityCreditWithdrawalTransitionV1 {
3220 identity_id: Identifier::from([12u8; 32]),
3221 amount: 777,
3222 core_fee_per_byte: 2,
3223 pooling: WithdrawalPooling::Standard,
3224 output_script: None,
3225 nonce: 9,
3226 user_fee_increase: 4,
3227 signature_public_key_id: 21,
3228 signature: BinaryData::new(vec![0x12; 65]),
3229 };
3230 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(v1))
3231 }
3232
3233 fn sample_credit_transfer_to_addresses_st() -> StateTransition {
3234 let v0 = IdentityCreditTransferToAddressesTransitionV0 {
3235 identity_id: Identifier::from([13u8; 32]),
3236 ..Default::default()
3237 };
3238 StateTransition::IdentityCreditTransferToAddresses(
3239 IdentityCreditTransferToAddressesTransition::V0(v0),
3240 )
3241 }
3242
3243 fn sample_address_credit_withdrawal_st() -> StateTransition {
3244 use crate::state_transition::address_credit_withdrawal_transition::v0::AddressCreditWithdrawalTransitionV0;
3245 StateTransition::AddressCreditWithdrawal(AddressCreditWithdrawalTransition::V0(
3246 AddressCreditWithdrawalTransitionV0::default(),
3247 ))
3248 }
3249
3250 fn sample_shield_from_asset_lock_st() -> StateTransition {
3251 use crate::state_transition::shield_from_asset_lock_transition::v0::ShieldFromAssetLockTransitionV0;
3252 StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V0(
3253 ShieldFromAssetLockTransitionV0 {
3254 asset_lock_proof: Default::default(),
3255 actions: vec![],
3256 value_balance: 100,
3257 anchor: [0u8; 32],
3258 proof: vec![],
3259 binding_signature: [0u8; 64],
3260 surplus_output: None,
3261 signature: BinaryData::new(vec![0x55; 65]),
3262 },
3263 ))
3264 }
3265
3266 #[test]
3269 fn test_withdrawal_v1_name_and_type() {
3270 let st = sample_withdrawal_v1_st();
3271 assert_eq!(st.name(), "IdentityCreditWithdrawal");
3272 assert_eq!(
3273 st.state_transition_type(),
3274 StateTransitionType::IdentityCreditWithdrawal
3275 );
3276 }
3277
3278 #[test]
3279 fn test_withdrawal_v1_is_identity_signed_true() {
3280 assert!(sample_withdrawal_v1_st().is_identity_signed());
3281 }
3282
3283 #[test]
3284 fn test_withdrawal_v1_signature_and_owner_and_key_id() {
3285 let st = sample_withdrawal_v1_st();
3286 let sig = st.signature().expect("V1 withdrawal has a signature");
3288 assert_eq!(sig.as_slice(), &[0x12; 65]);
3289 assert_eq!(st.owner_id(), Some(Identifier::from([12u8; 32])));
3291 assert_eq!(st.signature_public_key_id(), Some(21));
3292 assert_eq!(st.user_fee_increase(), 4);
3293 }
3294
3295 #[test]
3296 fn test_withdrawal_v1_set_signature_and_fee_and_key_id() {
3297 let mut st = sample_withdrawal_v1_st();
3298 assert!(st.set_signature(BinaryData::new(vec![0x99; 65])));
3299 assert_eq!(st.signature().unwrap().as_slice(), &[0x99; 65]);
3300
3301 st.set_user_fee_increase(33);
3302 assert_eq!(st.user_fee_increase(), 33);
3303
3304 st.set_signature_public_key_id(64);
3305 assert_eq!(st.signature_public_key_id(), Some(64));
3306 }
3307
3308 #[test]
3309 fn test_withdrawal_v1_serialize_roundtrip_via_state_transition() {
3310 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3311 let original = sample_withdrawal_v1_st();
3312 let bytes = PlatformSerializable::serialize_to_bytes(&original).expect("serialize ok");
3313 let restored = StateTransition::deserialize_from_bytes(&bytes).expect("deserialize ok");
3314 assert_eq!(original, restored);
3315 match restored {
3318 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(
3319 _,
3320 )) => {}
3321 other => panic!("expected V1 inner variant, got: {:?}", other),
3322 }
3323 }
3324
3325 #[test]
3326 fn test_withdrawal_v1_transaction_id_differs_from_v0() {
3327 let v0 = sample_withdrawal_st();
3329 let v1 = sample_withdrawal_v1_st();
3330 let id_v0 = v0.transaction_id().expect("v0 hash");
3331 let id_v1 = v1.transaction_id().expect("v1 hash");
3332 assert_ne!(id_v0, id_v1);
3333 }
3334
3335 #[test]
3336 fn test_withdrawal_v1_required_asset_lock_balance_errors() {
3337 let err = sample_withdrawal_v1_st()
3338 .required_asset_lock_balance_for_processing_start(PlatformVersion::latest())
3339 .expect_err("withdrawal is not an asset lock ST");
3340 matches!(err, ProtocolError::CorruptedCodeExecution(_));
3341 }
3342
3343 #[test]
3346 fn test_credit_transfer_to_addresses_name_and_type() {
3347 let st = sample_credit_transfer_to_addresses_st();
3348 assert_eq!(st.name(), "IdentityCreditTransferToAddresses");
3349 assert_eq!(
3350 st.state_transition_type(),
3351 StateTransitionType::IdentityCreditTransferToAddresses
3352 );
3353 }
3354
3355 #[test]
3356 fn test_credit_transfer_to_addresses_signature_some_and_owner_some() {
3357 let st = sample_credit_transfer_to_addresses_st();
3358 assert!(st.signature().is_some(), "has a signature field");
3359 assert_eq!(st.owner_id(), Some(Identifier::from([13u8; 32])));
3360 }
3361
3362 #[test]
3363 fn test_credit_transfer_to_addresses_is_identity_signed_true() {
3364 assert!(sample_credit_transfer_to_addresses_st().is_identity_signed());
3366 }
3367
3368 #[test]
3369 fn test_credit_transfer_to_addresses_inputs_none_active_range_11_latest() {
3370 let st = sample_credit_transfer_to_addresses_st();
3371 assert!(st.inputs().is_none());
3372 let range = st.active_version_range();
3374 assert_eq!(*range.start(), 11);
3375 assert_eq!(*range.end(), LATEST_VERSION);
3376 }
3377
3378 #[test]
3379 fn test_credit_transfer_to_addresses_set_signature_returns_true() {
3380 let mut st = sample_credit_transfer_to_addresses_st();
3381 let ok = st.set_signature(BinaryData::new(vec![0x77; 65]));
3382 assert!(ok);
3383 assert_eq!(st.signature().unwrap().as_slice(), &[0x77; 65]);
3384 }
3385
3386 #[test]
3387 fn test_credit_transfer_to_addresses_from_outer_enum() {
3388 let outer = IdentityCreditTransferToAddressesTransition::V0(
3389 IdentityCreditTransferToAddressesTransitionV0::default(),
3390 );
3391 let st: StateTransition = outer.into();
3392 assert!(matches!(
3393 st,
3394 StateTransition::IdentityCreditTransferToAddresses(_)
3395 ));
3396 }
3397
3398 #[test]
3399 fn test_credit_transfer_to_addresses_user_fee_increase_setter() {
3400 let mut st = sample_credit_transfer_to_addresses_st();
3401 st.set_user_fee_increase(55);
3402 assert_eq!(st.user_fee_increase(), 55);
3403 }
3404
3405 #[test]
3408 fn test_address_credit_withdrawal_name_type_and_accessors() {
3409 let st = sample_address_credit_withdrawal_st();
3410 assert_eq!(st.name(), "AddressCreditWithdrawal");
3411 assert_eq!(
3412 st.state_transition_type(),
3413 StateTransitionType::AddressCreditWithdrawal
3414 );
3415 assert!(st.signature().is_none());
3417 assert!(st.owner_id().is_none());
3419 assert!(st.inputs().is_some());
3421 }
3422
3423 #[test]
3424 fn test_address_credit_withdrawal_set_signature_returns_false() {
3425 let mut st = sample_address_credit_withdrawal_st();
3426 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3427 }
3428
3429 #[test]
3430 fn test_address_credit_withdrawal_is_identity_signed_true() {
3431 assert!(sample_address_credit_withdrawal_st().is_identity_signed());
3434 }
3435
3436 #[test]
3437 fn test_address_credit_withdrawal_active_range_is_11_latest() {
3438 let range = sample_address_credit_withdrawal_st().active_version_range();
3439 assert_eq!(*range.start(), 11);
3440 assert_eq!(*range.end(), LATEST_VERSION);
3441 }
3442
3443 #[test]
3446 fn test_shield_from_asset_lock_name_type_and_accessors() {
3447 let st = sample_shield_from_asset_lock_st();
3448 assert_eq!(st.name(), "ShieldFromAssetLock");
3449 assert_eq!(
3450 st.state_transition_type(),
3451 StateTransitionType::ShieldFromAssetLock
3452 );
3453 let sig = st
3455 .signature()
3456 .expect("shield-from-asset-lock has signature");
3457 assert_eq!(sig.as_slice(), &[0x55; 65]);
3458 assert!(st.owner_id().is_none());
3460 }
3461
3462 #[test]
3463 fn test_shield_from_asset_lock_is_not_identity_signed() {
3464 assert!(!sample_shield_from_asset_lock_st().is_identity_signed());
3465 }
3466
3467 #[test]
3468 fn test_shield_from_asset_lock_optional_asset_lock_proof_some() {
3469 let st = sample_shield_from_asset_lock_st();
3473 assert!(st.optional_asset_lock_proof().is_some());
3474 }
3475
3476 #[test]
3477 fn test_shield_from_asset_lock_user_fee_increase_is_zero_and_setter_noop() {
3478 let mut st = sample_shield_from_asset_lock_st();
3479 assert_eq!(st.user_fee_increase(), 0);
3480 st.set_user_fee_increase(123);
3481 assert_eq!(st.user_fee_increase(), 0);
3483 }
3484
3485 #[test]
3486 fn test_shield_from_asset_lock_set_signature_returns_true() {
3487 let mut st = sample_shield_from_asset_lock_st();
3488 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
3489 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
3490 }
3491
3492 #[test]
3493 fn test_shield_from_asset_lock_required_asset_lock_balance_succeeds() {
3494 let st = sample_shield_from_asset_lock_st();
3497 let result = st.required_asset_lock_balance_for_processing_start(PlatformVersion::latest());
3498 assert!(
3499 result.is_ok(),
3500 "ShieldFromAssetLock should return Ok, got {:?}",
3501 result
3502 );
3503 }
3504
3505 #[test]
3506 fn test_shield_from_asset_lock_active_range_12_latest() {
3507 let range = sample_shield_from_asset_lock_st().active_version_range();
3508 assert_eq!(*range.start(), 12);
3509 assert_eq!(*range.end(), LATEST_VERSION);
3510 }
3511
3512 #[test]
3516 fn test_batch_with_token_transfer_name_contains_token_transfer() {
3517 use crate::state_transition::batch_transition::batched_transition::token_transition::TokenTransition as TT;
3518 use crate::state_transition::batch_transition::batched_transition::BatchedTransition;
3519 use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0;
3520 use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition;
3521 use crate::state_transition::batch_transition::token_transfer_transition::v0::TokenTransferTransitionV0;
3522 use crate::state_transition::batch_transition::token_transfer_transition::TokenTransferTransition;
3523 use crate::state_transition::batch_transition::BatchTransitionV1;
3524
3525 let base = TokenBaseTransition::V0(TokenBaseTransitionV0 {
3526 identity_contract_nonce: 1,
3527 token_contract_position: 0,
3528 data_contract_id: Identifier::from([1u8; 32]),
3529 token_id: Identifier::from([2u8; 32]),
3530 using_group_info: None,
3531 });
3532 let token_transfer = TokenTransferTransition::V0(TokenTransferTransitionV0 {
3533 base,
3534 amount: 100,
3535 recipient_id: Identifier::from([3u8; 32]),
3536 public_note: None,
3537 shared_encrypted_note: None,
3538 private_encrypted_note: None,
3539 });
3540
3541 let batch = BatchTransition::V1(BatchTransitionV1 {
3543 owner_id: Identifier::from([9u8; 32]),
3544 transitions: vec![BatchedTransition::Token(TT::Transfer(token_transfer))],
3545 user_fee_increase: 0,
3546 signature_public_key_id: 0,
3547 signature: BinaryData::new(vec![0u8; 65]),
3548 });
3549 let st = StateTransition::Batch(batch);
3550
3551 assert_eq!(st.name(), "DocumentsBatch([TokenTransfer])");
3552 }
3553
3554 #[test]
3561 fn test_transaction_id_length_32_for_new_variants() {
3562 for st in [
3563 sample_withdrawal_v1_st(),
3564 sample_credit_transfer_to_addresses_st(),
3565 sample_shield_from_asset_lock_st(),
3566 ] {
3567 let id = st.transaction_id().expect("hash");
3568 assert_eq!(id.len(), 32);
3569 }
3570 }
3571
3572 #[test]
3578 fn test_clone_eq_for_new_variants() {
3579 let cases = [
3580 sample_withdrawal_v1_st(),
3581 sample_credit_transfer_to_addresses_st(),
3582 sample_address_credit_withdrawal_st(),
3583 sample_shield_from_asset_lock_st(),
3584 ];
3585 for st in cases {
3586 let cloned = st.clone();
3587 assert_eq!(st, cloned, "clone must be equal for {}", st.name());
3588 }
3589 }
3590
3591 #[test]
3597 fn test_unique_identifiers_for_address_and_shielded_variants() {
3598 for st in [
3603 sample_address_credit_withdrawal_st(),
3604 sample_shield_from_asset_lock_st(),
3605 sample_credit_transfer_to_addresses_st(),
3606 sample_withdrawal_v1_st(),
3607 ] {
3608 let _ids = st.unique_identifiers();
3612 }
3613 assert!(!sample_withdrawal_v1_st().unique_identifiers().is_empty());
3615 assert!(!sample_credit_transfer_to_addresses_st()
3616 .unique_identifiers()
3617 .is_empty());
3618 }
3619
3620 #[cfg(all(
3632 feature = "state-transition-signing",
3633 feature = "core_key_wallet",
3634 feature = "bls-signatures"
3635 ))]
3636 #[tokio::test]
3637 async fn sign_with_core_signer_matches_sign_by_private_key_byte_for_byte() {
3638 use async_trait::async_trait;
3639 use dashcore::secp256k1::{
3640 ecdsa, rand::rngs::OsRng, Message, PublicKey, Secp256k1, SecretKey,
3641 };
3642 use key_wallet::bip32::{DerivationPath, ExtendedPubKey};
3643 use key_wallet::signer::{ExtendedPubKeySigner, Signer as KwSigner, SignerMethod};
3644
3645 #[derive(Debug)]
3650 struct FixedKeySigner {
3651 secret: SecretKey,
3652 public: PublicKey,
3653 }
3654
3655 #[async_trait]
3656 impl KwSigner for FixedKeySigner {
3657 type Error = String;
3658
3659 fn supported_methods(&self) -> &[SignerMethod] {
3660 &[SignerMethod::Digest]
3661 }
3662
3663 async fn sign_ecdsa(
3664 &self,
3665 _path: &DerivationPath,
3666 sighash: [u8; 32],
3667 ) -> Result<(ecdsa::Signature, PublicKey), Self::Error> {
3668 let secp = Secp256k1::new();
3669 let msg = Message::from_digest(sighash);
3670 let sig = secp.sign_ecdsa(&msg, &self.secret);
3671 Ok((sig, self.public))
3672 }
3673
3674 async fn public_key(&self, _path: &DerivationPath) -> Result<PublicKey, Self::Error> {
3675 Ok(self.public)
3676 }
3677 }
3678
3679 #[async_trait]
3680 impl ExtendedPubKeySigner for FixedKeySigner {
3681 async fn extended_public_key(
3682 &self,
3683 _path: &DerivationPath,
3684 ) -> Result<ExtendedPubKey, Self::Error> {
3685 Err("FixedKeySigner does not derive extended public keys".to_string())
3686 }
3687 }
3688
3689 let secp = Secp256k1::new();
3694 let (secret_key, public_key) = secp.generate_keypair(&mut OsRng);
3695 let private_key_bytes = secret_key.secret_bytes();
3696
3697 let signer = FixedKeySigner {
3698 secret: secret_key,
3699 public: public_key,
3700 };
3701 let path = DerivationPath::default();
3702
3703 let mut st_legacy = sample_transfer_st();
3707 let mut st_signer = sample_transfer_st();
3708
3709 assert_eq!(
3711 st_legacy.signable_bytes().expect("legacy signable_bytes"),
3712 st_signer.signable_bytes().expect("signer signable_bytes"),
3713 "signable_bytes pre-image must match across copies"
3714 );
3715
3716 let bls = crate::bls::native_bls::NativeBlsModule;
3721 st_legacy
3722 .sign_by_private_key(&private_key_bytes, KeyType::ECDSA_HASH160, &bls)
3723 .expect("sign_by_private_key");
3724
3725 st_signer
3728 .sign_with_core_signer(&path, &signer)
3729 .await
3730 .expect("sign_with_core_signer");
3731
3732 let sig_legacy = st_legacy.signature().expect("legacy signature set");
3733 let sig_signer = st_signer.signature().expect("signer signature set");
3734
3735 assert_eq!(
3736 sig_legacy.as_slice().len(),
3737 65,
3738 "legacy ECDSA signature must be 65 bytes (recoverable compact)"
3739 );
3740 assert_eq!(
3741 sig_signer.as_slice().len(),
3742 65,
3743 "signer ECDSA signature must be 65 bytes (recoverable compact)"
3744 );
3745 assert_eq!(
3746 sig_legacy.as_slice(),
3747 sig_signer.as_slice(),
3748 "sign_with_core_signer must produce byte-identical output to sign_by_private_key"
3749 );
3750 }
3751}