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 max_value_depth = platform_version
801 .system_limits
802 .max_document_value_depth
803 .map(usize::from);
804 let state_transition =
805 platform_value::with_value_decode_depth_limit(max_value_depth, || {
806 StateTransition::deserialize_from_bytes(bytes)
807 })?;
808 #[cfg(all(feature = "state-transitions", feature = "validation"))]
809 {
810 let active_version_range = state_transition.active_version_range();
811
812 if active_version_range.contains(&platform_version.protocol_version)
815 || platform_version.protocol_version > 268435456
816 {
817 Ok(state_transition)
818 } else {
819 Err(ProtocolError::StateTransitionError(
820 StateTransitionIsNotActiveError {
821 state_transition_type: state_transition.name(),
822 active_version_range,
823 current_protocol_version: platform_version.protocol_version,
824 },
825 ))
826 }
827 }
828 #[cfg(not(all(feature = "state-transitions", feature = "validation")))]
829 Ok(state_transition)
830 }
831
832 pub fn active_version_range(&self) -> RangeInclusive<ProtocolVersion> {
833 match self {
834 StateTransition::DataContractCreate(data_contract_create_transition) => {
835 match data_contract_create_transition.data_contract() {
836 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
837 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
838 }
839 }
840 StateTransition::DataContractUpdate(data_contract_update_transition) => {
841 match data_contract_update_transition.data_contract() {
842 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
843 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
844 }
845 }
846 StateTransition::Batch(batch_transition) => match batch_transition {
847 BatchTransition::V0(_) => ALL_VERSIONS,
848 BatchTransition::V1(_) => 9..=LATEST_VERSION,
849 },
850 StateTransition::IdentityCreate(_)
851 | StateTransition::IdentityTopUp(_)
852 | StateTransition::IdentityCreditWithdrawal(_)
853 | StateTransition::IdentityUpdate(_)
854 | StateTransition::IdentityCreditTransfer(_)
855 | StateTransition::MasternodeVote(_) => ALL_VERSIONS,
856 StateTransition::IdentityCreditTransferToAddresses(_)
857 | StateTransition::IdentityCreateFromAddresses(_)
858 | StateTransition::IdentityTopUpFromAddresses(_)
859 | StateTransition::AddressFundsTransfer(_)
860 | StateTransition::AddressFundingFromAssetLock(_)
861 | StateTransition::AddressCreditWithdrawal(_) => 11..=LATEST_VERSION,
862 StateTransition::Shield(_)
863 | StateTransition::ShieldedTransfer(_)
864 | StateTransition::Unshield(_)
865 | StateTransition::ShieldFromAssetLock(_)
866 | StateTransition::ShieldedWithdrawal(_)
867 | StateTransition::IdentityCreateFromShieldedPool(_) => 12..=LATEST_VERSION,
868 }
869 }
870
871 pub fn is_identity_signed(&self) -> bool {
872 !matches!(
873 self,
874 StateTransition::IdentityCreate(_)
875 | StateTransition::IdentityTopUp(_)
876 | StateTransition::Shield(_)
877 | StateTransition::ShieldedTransfer(_)
878 | StateTransition::Unshield(_)
879 | StateTransition::ShieldFromAssetLock(_)
880 | StateTransition::ShieldedWithdrawal(_)
881 | StateTransition::IdentityCreateFromShieldedPool(_)
882 )
883 }
884
885 pub fn required_asset_lock_balance_for_processing_start(
886 &self,
887 platform_version: &PlatformVersion,
888 ) -> Result<Credits, ProtocolError> {
889 match self {
890 StateTransition::IdentityCreate(st) => {
891 st.calculate_min_required_fee(platform_version)
892 }
893 StateTransition::IdentityTopUp(st) => {
894 st.calculate_min_required_fee(platform_version)
895 }
896 StateTransition::AddressFundingFromAssetLock(st) => {
897 st.calculate_min_required_fee(platform_version)
898 }
899 StateTransition::ShieldFromAssetLock(st) => {
900 st.calculate_min_required_fee(platform_version)
901 }
902 st => Err(ProtocolError::CorruptedCodeExecution(format!("{} is not an asset lock transaction, but we are calling required_asset_lock_balance_for_processing_start", st.name()))),
903 }
904 }
905
906 fn hash(&self, skip_signature: bool) -> Result<Vec<u8>, ProtocolError> {
907 if skip_signature {
908 Ok(hash_double_to_vec(self.signable_bytes()?))
909 } else {
910 Ok(hash_double_to_vec(
911 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
912 ))
913 }
914 }
915
916 pub fn name(&self) -> String {
918 match self {
919 Self::DataContractCreate(_) => "DataContractCreate".to_string(),
920 Self::DataContractUpdate(_) => "DataContractUpdate".to_string(),
921 Self::Batch(batch_transition) => {
922 let mut document_transition_types = vec![];
923 for transition in batch_transition.transitions_iter() {
924 let document_transition_name = match transition {
925 BatchedTransitionRef::Document(DocumentTransition::Create(_)) => "Create",
926 BatchedTransitionRef::Document(DocumentTransition::Replace(_)) => "Replace",
927 BatchedTransitionRef::Document(DocumentTransition::Delete(_)) => "Delete",
928 BatchedTransitionRef::Document(DocumentTransition::Transfer(_)) => {
929 "Transfer"
930 }
931 BatchedTransitionRef::Document(DocumentTransition::UpdatePrice(_)) => {
932 "UpdatePrice"
933 }
934 BatchedTransitionRef::Document(DocumentTransition::Purchase(_)) => {
935 "Purchase"
936 }
937 BatchedTransitionRef::Document(DocumentTransition::IndexOnlyDelete(_)) => {
938 "IndexOnlyDelete"
939 }
940 BatchedTransitionRef::Token(TokenTransition::Transfer(_)) => {
941 "TokenTransfer"
942 }
943 BatchedTransitionRef::Token(TokenTransition::Mint(_)) => "TokenMint",
944 BatchedTransitionRef::Token(TokenTransition::Burn(_)) => "TokenBurn",
945 BatchedTransitionRef::Token(TokenTransition::Freeze(_)) => "TokenFreeze",
946 BatchedTransitionRef::Token(TokenTransition::Unfreeze(_)) => {
947 "TokenUnfreeze"
948 }
949 BatchedTransitionRef::Token(TokenTransition::DestroyFrozenFunds(_)) => {
950 "TokenDestroyFrozenFunds"
951 }
952 BatchedTransitionRef::Token(TokenTransition::EmergencyAction(_)) => {
953 "TokenEmergencyAction"
954 }
955 BatchedTransitionRef::Token(TokenTransition::ConfigUpdate(_)) => {
956 "TokenConfigUpdate"
957 }
958 BatchedTransitionRef::Token(TokenTransition::Claim(_)) => "TokenClaim",
959 BatchedTransitionRef::Token(TokenTransition::DirectPurchase(_)) => {
960 "TokenDirectPurchase"
961 }
962 BatchedTransitionRef::Token(
963 TokenTransition::SetPriceForDirectPurchase(_),
964 ) => "SetPriceForDirectPurchase",
965 };
966 document_transition_types.push(document_transition_name);
967 }
968 format!("DocumentsBatch([{}])", document_transition_types.join(", "))
969 }
970 Self::IdentityCreate(_) => "IdentityCreate".to_string(),
971 Self::IdentityTopUp(_) => "IdentityTopUp".to_string(),
972 Self::IdentityCreditWithdrawal(_) => "IdentityCreditWithdrawal".to_string(),
973 Self::IdentityUpdate(_) => "IdentityUpdate".to_string(),
974 Self::IdentityCreditTransfer(_) => "IdentityCreditTransfer".to_string(),
975 Self::MasternodeVote(_) => "MasternodeVote".to_string(),
976 Self::IdentityCreditTransferToAddresses(_) => {
977 "IdentityCreditTransferToAddresses".to_string()
978 }
979 Self::IdentityCreateFromAddresses(_) => "IdentityCreateFromAddresses".to_string(),
980 Self::IdentityTopUpFromAddresses(_) => "IdentityTopUpFromAddresses".to_string(),
981 Self::AddressFundsTransfer(_) => "AddressFundsTransfer".to_string(),
982 Self::AddressFundingFromAssetLock(_) => "AddressFundingFromAssetLock".to_string(),
983 Self::AddressCreditWithdrawal(_) => "AddressCreditWithdrawal".to_string(),
984 Self::Shield(_) => "Shield".to_string(),
985 Self::ShieldedTransfer(_) => "ShieldedTransfer".to_string(),
986 Self::Unshield(_) => "Unshield".to_string(),
987 Self::ShieldFromAssetLock(_) => "ShieldFromAssetLock".to_string(),
988 Self::ShieldedWithdrawal(_) => "ShieldedWithdrawal".to_string(),
989 Self::IdentityCreateFromShieldedPool(_) => "IdentityCreateFromShieldedPool".to_string(),
990 }
991 }
992
993 pub fn signature(&self) -> Option<&BinaryData> {
995 match self {
996 StateTransition::DataContractCreate(st) => Some(st.signature()),
997 StateTransition::DataContractUpdate(st) => Some(st.signature()),
998 StateTransition::Batch(st) => Some(st.signature()),
999 StateTransition::IdentityCreate(st) => Some(st.signature()),
1000 StateTransition::IdentityTopUp(st) => Some(st.signature()),
1001 StateTransition::IdentityCreditWithdrawal(st) => Some(st.signature()),
1002 StateTransition::IdentityUpdate(st) => Some(st.signature()),
1003 StateTransition::IdentityCreditTransfer(st) => Some(st.signature()),
1004 StateTransition::MasternodeVote(st) => Some(st.signature()),
1005 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.signature()),
1006 StateTransition::IdentityCreateFromAddresses(_) => None,
1007 StateTransition::IdentityTopUpFromAddresses(_) => None,
1008 StateTransition::AddressFundsTransfer(_) => None,
1009 StateTransition::AddressFundingFromAssetLock(st) => Some(st.signature()),
1010 StateTransition::AddressCreditWithdrawal(_) => None,
1011 StateTransition::Shield(_) => None,
1012 StateTransition::ShieldedTransfer(_) => None,
1013 StateTransition::Unshield(_) => None,
1014 StateTransition::ShieldFromAssetLock(st) => Some(st.signature()),
1015 StateTransition::ShieldedWithdrawal(_) => None,
1016 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1017 }
1018 }
1019
1020 pub fn required_number_of_private_keys(&self) -> u16 {
1022 match self {
1023 StateTransition::IdentityCreateFromAddresses(st) => st.inputs().len() as u16,
1024 StateTransition::IdentityTopUpFromAddresses(st) => st.inputs().len() as u16,
1025 StateTransition::AddressFundsTransfer(st) => st.inputs().len() as u16,
1026 StateTransition::AddressCreditWithdrawal(st) => st.inputs().len() as u16,
1027 StateTransition::Shield(st) => st.inputs().len() as u16,
1028 StateTransition::ShieldedTransfer(_) => 0,
1029 StateTransition::Unshield(_) => 0,
1030 StateTransition::ShieldFromAssetLock(_) => 0,
1031 StateTransition::ShieldedWithdrawal(_) => 0,
1032 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1033 _ => 1,
1034 }
1035 }
1036
1037 pub fn user_fee_increase(&self) -> UserFeeIncrease {
1039 match self {
1040 StateTransition::DataContractCreate(st) => st.user_fee_increase(),
1041 StateTransition::DataContractUpdate(st) => st.user_fee_increase(),
1042 StateTransition::Batch(st) => st.user_fee_increase(),
1043 StateTransition::IdentityCreate(st) => st.user_fee_increase(),
1044 StateTransition::IdentityTopUp(st) => st.user_fee_increase(),
1045 StateTransition::IdentityCreditWithdrawal(st) => st.user_fee_increase(),
1046 StateTransition::IdentityUpdate(st) => st.user_fee_increase(),
1047 StateTransition::IdentityCreditTransfer(st) => st.user_fee_increase(),
1048 StateTransition::IdentityCreditTransferToAddresses(st) => st.user_fee_increase(),
1049 StateTransition::IdentityCreateFromAddresses(st) => st.user_fee_increase(),
1050 StateTransition::IdentityTopUpFromAddresses(st) => st.user_fee_increase(),
1051 StateTransition::AddressFundsTransfer(st) => st.user_fee_increase(),
1052 StateTransition::AddressFundingFromAssetLock(st) => st.user_fee_increase(),
1053 StateTransition::AddressCreditWithdrawal(st) => st.user_fee_increase(),
1054 StateTransition::Shield(st) => st.user_fee_increase(),
1055 StateTransition::ShieldFromAssetLock(_) => 0,
1057 StateTransition::MasternodeVote(_) => 0,
1058 StateTransition::ShieldedTransfer(_) => 0,
1059 StateTransition::Unshield(_) => 0,
1060 StateTransition::ShieldedWithdrawal(_) => 0,
1061 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1062 }
1063 }
1064
1065 fn calculate_estimated_fee(
1078 &self,
1079 platform_version: &PlatformVersion,
1080 ) -> Result<Credits, ProtocolError> {
1081 call_method!(self, calculate_min_required_fee, platform_version)
1082 }
1083
1084 pub fn transaction_id(&self) -> Result<[u8; 32], ProtocolError> {
1086 Ok(hash_single(
1087 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
1088 ))
1089 }
1090
1091 pub fn signature_public_key_id(&self) -> Option<KeyID> {
1093 call_getter_method_identity_signed!(self, signature_public_key_id)
1094 }
1095
1096 pub fn security_level_requirement(&self, purpose: Purpose) -> Option<Vec<SecurityLevel>> {
1098 call_getter_method_identity_signed!(self, security_level_requirement, purpose)
1099 }
1100
1101 pub fn purpose_requirement(&self) -> Option<Vec<Purpose>> {
1103 call_getter_method_identity_signed!(self, purpose_requirement)
1104 }
1105
1106 pub fn owner_id(&self) -> Option<Identifier> {
1108 match self {
1109 StateTransition::DataContractCreate(st) => Some(st.owner_id()),
1110 StateTransition::DataContractUpdate(st) => Some(st.owner_id()),
1111 StateTransition::Batch(st) => Some(st.owner_id()),
1112 StateTransition::IdentityCreate(st) => Some(st.owner_id()),
1113 StateTransition::IdentityTopUp(st) => Some(st.owner_id()),
1114 StateTransition::IdentityCreditWithdrawal(st) => Some(st.owner_id()),
1115 StateTransition::IdentityUpdate(st) => Some(st.owner_id()),
1116 StateTransition::IdentityCreditTransfer(st) => Some(st.owner_id()),
1117 StateTransition::MasternodeVote(st) => Some(st.owner_id()),
1118 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.owner_id()),
1119 StateTransition::IdentityCreateFromAddresses(_) => None,
1120 StateTransition::IdentityTopUpFromAddresses(_) => None,
1121 StateTransition::AddressFundsTransfer(_) => None,
1122 StateTransition::AddressFundingFromAssetLock(_) => None,
1123 StateTransition::AddressCreditWithdrawal(_) => None,
1124 StateTransition::Shield(_) => None,
1125 StateTransition::ShieldedTransfer(_) => None,
1126 StateTransition::Unshield(_) => None,
1127 StateTransition::ShieldFromAssetLock(_) => None,
1128 StateTransition::ShieldedWithdrawal(_) => None,
1129 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1130 }
1131 }
1132
1133 pub fn inputs(&self) -> Option<&BTreeMap<PlatformAddress, (AddressNonce, Credits)>> {
1135 match self {
1136 StateTransition::DataContractCreate(_)
1137 | StateTransition::DataContractUpdate(_)
1138 | StateTransition::Batch(_)
1139 | StateTransition::IdentityCreate(_)
1140 | StateTransition::IdentityTopUp(_)
1141 | StateTransition::IdentityCreditWithdrawal(_)
1142 | StateTransition::IdentityUpdate(_)
1143 | StateTransition::IdentityCreditTransfer(_)
1144 | StateTransition::MasternodeVote(_)
1145 | StateTransition::IdentityCreditTransferToAddresses(_) => None,
1146 StateTransition::IdentityCreateFromAddresses(st) => Some(st.inputs()),
1147 StateTransition::IdentityTopUpFromAddresses(st) => Some(st.inputs()),
1148 StateTransition::AddressFundsTransfer(st) => Some(st.inputs()),
1149 StateTransition::AddressFundingFromAssetLock(st) => Some(st.inputs()),
1150 StateTransition::AddressCreditWithdrawal(st) => Some(st.inputs()),
1151 StateTransition::Shield(st) => Some(st.inputs()),
1152 StateTransition::ShieldedTransfer(_) => None,
1153 StateTransition::Unshield(_) => None,
1154 StateTransition::ShieldFromAssetLock(_) => None,
1155 StateTransition::ShieldedWithdrawal(_) => None,
1156 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1157 }
1158 }
1159
1160 pub fn state_transition_type(&self) -> StateTransitionType {
1162 call_method!(self, state_transition_type)
1163 }
1164
1165 pub fn unique_identifiers(&self) -> Vec<String> {
1167 call_method!(self, unique_identifiers)
1168 }
1169
1170 pub fn set_signature(&mut self, signature: BinaryData) -> bool {
1172 match self {
1173 StateTransition::DataContractCreate(st) => {
1174 st.set_signature(signature);
1175 true
1176 }
1177 StateTransition::DataContractUpdate(st) => {
1178 st.set_signature(signature);
1179 true
1180 }
1181 StateTransition::Batch(st) => {
1182 st.set_signature(signature);
1183 true
1184 }
1185 StateTransition::IdentityCreate(st) => {
1186 st.set_signature(signature);
1187 true
1188 }
1189 StateTransition::IdentityTopUp(st) => {
1190 st.set_signature(signature);
1191 true
1192 }
1193 StateTransition::IdentityCreditWithdrawal(st) => {
1194 st.set_signature(signature);
1195 true
1196 }
1197 StateTransition::IdentityUpdate(st) => {
1198 st.set_signature(signature);
1199 true
1200 }
1201 StateTransition::IdentityCreditTransfer(st) => {
1202 st.set_signature(signature);
1203 true
1204 }
1205 StateTransition::MasternodeVote(st) => {
1206 st.set_signature(signature);
1207 true
1208 }
1209 StateTransition::IdentityCreditTransferToAddresses(st) => {
1210 st.set_signature(signature);
1211 true
1212 }
1213 StateTransition::IdentityCreateFromAddresses(_)
1214 | StateTransition::IdentityTopUpFromAddresses(_)
1215 | StateTransition::AddressFundsTransfer(_)
1216 | StateTransition::Shield(_)
1217 | StateTransition::ShieldedTransfer(_)
1218 | StateTransition::Unshield(_)
1219 | StateTransition::ShieldedWithdrawal(_)
1220 | StateTransition::IdentityCreateFromShieldedPool(_) => false,
1221 StateTransition::AddressFundingFromAssetLock(st) => {
1222 st.set_signature(signature);
1223 true
1224 }
1225 StateTransition::ShieldFromAssetLock(st) => {
1226 st.set_signature(signature);
1227 true
1228 }
1229 StateTransition::AddressCreditWithdrawal(_) => false,
1230 }
1231 }
1232
1233 pub fn set_user_fee_increase(&mut self, user_fee_increase: UserFeeIncrease) {
1235 match self {
1236 StateTransition::DataContractCreate(st) => st.set_user_fee_increase(user_fee_increase),
1237 StateTransition::DataContractUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1238 StateTransition::Batch(st) => st.set_user_fee_increase(user_fee_increase),
1239 StateTransition::IdentityCreate(st) => st.set_user_fee_increase(user_fee_increase),
1240 StateTransition::IdentityTopUp(st) => st.set_user_fee_increase(user_fee_increase),
1241 StateTransition::IdentityCreditWithdrawal(st) => {
1242 st.set_user_fee_increase(user_fee_increase)
1243 }
1244 StateTransition::IdentityUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1245 StateTransition::IdentityCreditTransfer(st) => {
1246 st.set_user_fee_increase(user_fee_increase)
1247 }
1248 StateTransition::IdentityCreditTransferToAddresses(st) => {
1249 st.set_user_fee_increase(user_fee_increase)
1250 }
1251 StateTransition::IdentityCreateFromAddresses(st) => {
1252 st.set_user_fee_increase(user_fee_increase)
1253 }
1254 StateTransition::IdentityTopUpFromAddresses(st) => {
1255 st.set_user_fee_increase(user_fee_increase)
1256 }
1257 StateTransition::AddressFundsTransfer(st) => {
1258 st.set_user_fee_increase(user_fee_increase)
1259 }
1260 StateTransition::AddressFundingFromAssetLock(st) => {
1261 st.set_user_fee_increase(user_fee_increase)
1262 }
1263 StateTransition::AddressCreditWithdrawal(st) => {
1264 st.set_user_fee_increase(user_fee_increase)
1265 }
1266 StateTransition::Shield(st) => st.set_user_fee_increase(user_fee_increase),
1267 StateTransition::ShieldFromAssetLock(_) => {}
1269 StateTransition::MasternodeVote(_) => {}
1270 StateTransition::ShieldedTransfer(_) => {}
1271 StateTransition::Unshield(_) => {}
1272 StateTransition::ShieldedWithdrawal(_) => {}
1273 StateTransition::IdentityCreateFromShieldedPool(_) => {}
1274 }
1275 }
1276
1277 pub fn set_signature_public_key_id(&mut self, public_key_id: KeyID) {
1279 call_method_identity_signed!(self, set_signature_public_key_id, public_key_id)
1280 }
1281
1282 #[cfg(feature = "state-transition-signing")]
1283 pub async fn sign_external<S: Signer<IdentityPublicKey>>(
1284 &mut self,
1285 identity_public_key: &IdentityPublicKey,
1286 signer: &S,
1287 get_data_contract_security_level_requirement: Option<
1288 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1289 >,
1290 ) -> Result<(), ProtocolError> {
1291 self.sign_external_with_options(
1292 identity_public_key,
1293 signer,
1294 get_data_contract_security_level_requirement,
1295 StateTransitionSigningOptions::default(),
1296 )
1297 .await
1298 }
1299
1300 #[cfg(feature = "state-transition-signing")]
1301 pub async fn sign_external_with_options<S: Signer<IdentityPublicKey>>(
1302 &mut self,
1303 identity_public_key: &IdentityPublicKey,
1304 signer: &S,
1305 get_data_contract_security_level_requirement: Option<
1306 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1307 >,
1308 options: StateTransitionSigningOptions,
1309 ) -> Result<(), ProtocolError> {
1310 match self {
1311 StateTransition::DataContractCreate(st) => {
1312 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1313 st.verify_public_key_is_enabled(identity_public_key)?;
1314 }
1315 StateTransition::DataContractUpdate(st) => {
1316 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1317 st.verify_public_key_is_enabled(identity_public_key)?;
1318 }
1319 StateTransition::Batch(st) => {
1320 let allow_token_transfer_keys = st.transitions_len() == 1
1321 && (st
1322 .first_transition()
1323 .expect("expected first transition with len 1")
1324 .as_transition_token_claim()
1325 .is_some()
1326 || st
1327 .first_transition()
1328 .expect("expected first transition with len 1")
1329 .as_transition_token_transfer()
1330 .is_some());
1331 let allowed_key_purposes = if allow_token_transfer_keys {
1332 vec![Purpose::AUTHENTICATION, Purpose::TRANSFER]
1333 } else {
1334 vec![Purpose::AUTHENTICATION]
1335 };
1336 if !options.allow_signing_with_any_purpose
1337 && !allowed_key_purposes.contains(&identity_public_key.purpose())
1338 {
1339 return Err(ProtocolError::WrongPublicKeyPurposeError(
1340 WrongPublicKeyPurposeError::new(
1341 identity_public_key.purpose(),
1342 allowed_key_purposes,
1343 ),
1344 ));
1345 }
1346 if !options.allow_signing_with_any_security_level {
1347 let security_level_requirement = st.combined_security_level_requirement(
1348 get_data_contract_security_level_requirement,
1349 )?;
1350 if !security_level_requirement.contains(&identity_public_key.security_level()) {
1351 return Err(ProtocolError::InvalidSignaturePublicKeySecurityLevelError(
1352 InvalidSignaturePublicKeySecurityLevelError::new(
1353 identity_public_key.security_level(),
1354 security_level_requirement,
1355 ),
1356 ));
1357 }
1358 }
1359 st.verify_public_key_is_enabled(identity_public_key)?;
1360 }
1361 StateTransition::IdentityCreditWithdrawal(st) => {
1362 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1363 st.verify_public_key_is_enabled(identity_public_key)?;
1364 }
1365 StateTransition::IdentityUpdate(st) => {
1366 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1367 st.verify_public_key_is_enabled(identity_public_key)?;
1368 }
1369 StateTransition::IdentityCreditTransfer(st) => {
1370 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1371 st.verify_public_key_is_enabled(identity_public_key)?;
1372 }
1373 StateTransition::IdentityCreate(_) => {
1374 return Err(ProtocolError::CorruptedCodeExecution(
1375 "identity create can not be called for identity signing".to_string(),
1376 ))
1377 }
1378 StateTransition::IdentityTopUp(_) => {
1379 return Err(ProtocolError::CorruptedCodeExecution(
1380 "identity top up can not be called for identity signing".to_string(),
1381 ))
1382 }
1383 StateTransition::MasternodeVote(st) => {
1384 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1385 st.verify_public_key_is_enabled(identity_public_key)?;
1386 }
1387 StateTransition::IdentityCreditTransferToAddresses(st) => {
1388 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1389 st.verify_public_key_is_enabled(identity_public_key)?;
1390 }
1391 StateTransition::IdentityCreateFromAddresses(_) => {
1392 return Err(ProtocolError::CorruptedCodeExecution(
1393 "identity create from addresses can not be called for identity signing"
1394 .to_string(),
1395 ))
1396 }
1397 StateTransition::IdentityTopUpFromAddresses(_) => {
1398 return Err(ProtocolError::CorruptedCodeExecution(
1399 "identity top up from addresses can not be called for identity signing"
1400 .to_string(),
1401 ))
1402 }
1403 StateTransition::AddressFundsTransfer(_) => {
1404 return Err(ProtocolError::CorruptedCodeExecution(
1405 "address funds transfer transition can not be called for identity signing"
1406 .to_string(),
1407 ))
1408 }
1409 StateTransition::AddressFundingFromAssetLock(_) => {
1410 return Err(ProtocolError::CorruptedCodeExecution(
1411 "address funding from asset lock transition can not be called for identity signing"
1412 .to_string(),
1413 ))
1414 }
1415 StateTransition::AddressCreditWithdrawal(_) => {
1416 return Err(ProtocolError::CorruptedCodeExecution(
1417 "address credit withdrawal transition can not be called for identity signing"
1418 .to_string(),
1419 ))
1420 }
1421 StateTransition::Shield(_) => {
1422 return Err(ProtocolError::CorruptedCodeExecution(
1423 "shield transition can not be called for identity signing".to_string(),
1424 ))
1425 }
1426 StateTransition::ShieldedTransfer(_) => {
1427 return Err(ProtocolError::CorruptedCodeExecution(
1428 "shielded transfer transition can not be called for identity signing"
1429 .to_string(),
1430 ))
1431 }
1432 StateTransition::Unshield(_) => {
1433 return Err(ProtocolError::CorruptedCodeExecution(
1434 "unshield transition can not be called for identity signing".to_string(),
1435 ))
1436 }
1437 StateTransition::ShieldFromAssetLock(_) => {
1438 return Err(ProtocolError::CorruptedCodeExecution(
1439 "shield from asset lock transition can not be called for identity signing"
1440 .to_string(),
1441 ))
1442 }
1443 StateTransition::ShieldedWithdrawal(_) => {
1444 return Err(ProtocolError::CorruptedCodeExecution(
1445 "shielded withdrawal transition can not be called for identity signing"
1446 .to_string(),
1447 ))
1448 }
1449 StateTransition::IdentityCreateFromShieldedPool(_) => {
1450 return Err(ProtocolError::CorruptedCodeExecution(
1451 "identity create from shielded pool transition can not be called for identity signing"
1452 .to_string(),
1453 ))
1454 }
1455 }
1456 let data = self.signable_bytes()?;
1457 self.set_signature(signer.sign(identity_public_key, data.as_slice()).await?);
1458 self.set_signature_public_key_id(identity_public_key.id());
1459 Ok(())
1460 }
1461
1462 #[cfg(feature = "state-transition-signing")]
1463 pub fn sign(
1464 &mut self,
1465 identity_public_key: &IdentityPublicKey,
1466 private_key: &[u8],
1467 bls: &impl BlsModule,
1468 ) -> Result<(), ProtocolError> {
1469 self.sign_with_options(
1470 identity_public_key,
1471 private_key,
1472 bls,
1473 StateTransitionSigningOptions::default(),
1474 )
1475 }
1476
1477 #[cfg(feature = "state-transition-signing")]
1478 pub fn sign_with_options(
1479 &mut self,
1480 identity_public_key: &IdentityPublicKey,
1481 private_key: &[u8],
1482 bls: &impl BlsModule,
1483 options: StateTransitionSigningOptions,
1484 ) -> Result<(), ProtocolError> {
1485 call_errorable_method_identity_signed!(
1486 self,
1487 verify_public_key_level_and_purpose,
1488 identity_public_key,
1489 options
1490 )?;
1491 call_errorable_method_identity_signed!(
1492 self,
1493 verify_public_key_is_enabled,
1494 identity_public_key
1495 )?;
1496
1497 match identity_public_key.key_type() {
1498 KeyType::ECDSA_SECP256K1 => {
1499 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1500
1501 if public_key_compressed.as_slice() != identity_public_key.data().as_slice() {
1506 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1507 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1508 ));
1509 }
1510
1511 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1512 }
1513 KeyType::ECDSA_HASH160 => {
1514 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1515 let pub_key_hash = ripemd160_sha256(&public_key_compressed);
1516
1517 if identity_public_key.data().as_slice() != pub_key_hash {
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 KeyType::BLS12_381 => {
1525 let public_key = bls.private_key_to_public_key(private_key)?;
1526
1527 if public_key != identity_public_key.data().as_slice() {
1528 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1529 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1530 ));
1531 }
1532 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1533 }
1534
1535 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1539 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1540 InvalidIdentityPublicKeyTypeError::new(identity_public_key.key_type()),
1541 ))
1542 }
1543 }?;
1544
1545 self.set_signature_public_key_id(identity_public_key.id());
1546
1547 Ok(())
1548 }
1549
1550 #[cfg(feature = "state-transition-signing")]
1551 pub fn sign_by_private_key(
1553 &mut self,
1554 private_key: &[u8],
1555 key_type: KeyType,
1556 bls: &impl BlsModule,
1557 ) -> Result<(), ProtocolError> {
1558 let data = self.signable_bytes()?;
1559 match key_type {
1560 KeyType::BLS12_381 => {
1561 if !self.set_signature(bls.sign(&data, private_key)?.into()) {
1562 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1563 needed: self.required_number_of_private_keys(),
1564 using: 1,
1565 msg: "failed to set BLS signature",
1566 });
1567 }
1568 }
1569
1570 KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => {
1572 let signature = signer::sign(&data, private_key)?;
1573 if !self.set_signature(signature.to_vec().into()) {
1574 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1575 needed: self.required_number_of_private_keys(),
1576 using: 1,
1577 msg: "failed to set ECDSA signature",
1578 });
1579 };
1580 }
1581
1582 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1586 return Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1587 InvalidIdentityPublicKeyTypeError::new(key_type),
1588 ))
1589 }
1590 };
1591 Ok(())
1592 }
1593
1594 #[cfg(all(feature = "state-transition-signing", feature = "core_key_wallet"))]
1672 pub async fn sign_with_core_signer<S: ::key_wallet::signer::Signer>(
1673 &mut self,
1674 path: &::key_wallet::bip32::DerivationPath,
1675 signer: &S,
1676 ) -> Result<(), ProtocolError> {
1677 use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1678 use dashcore::secp256k1::{Message, Secp256k1};
1679 use dashcore::signer::{double_sha, CompactSignature};
1680
1681 let data = self.signable_bytes()?;
1682 let data_hash = double_sha(&data);
1685 let digest: [u8; 32] = data_hash.as_slice().try_into().map_err(|_| {
1686 ProtocolError::Generic("double_sha did not return 32 bytes".to_string())
1687 })?;
1688
1689 let (signature, public_key) = signer
1690 .sign_ecdsa(path, digest)
1691 .await
1692 .map_err(|e| ProtocolError::ExternalSignerError(format!("signer failed: {}", e)))?;
1693
1694 let compact_64 = signature.serialize_compact();
1703 let secp = Secp256k1::new();
1704 let msg = Message::from_digest(digest);
1705
1706 let mut found: Option<RecoverableSignature> = None;
1707 for id in 0..4i32 {
1708 let recid = match RecoveryId::try_from(id) {
1709 Ok(r) => r,
1710 Err(_) => continue,
1711 };
1712 let candidate = match RecoverableSignature::from_compact(&compact_64, recid) {
1713 Ok(s) => s,
1714 Err(_) => continue,
1715 };
1716 if let Ok(recovered) = secp.recover_ecdsa(&msg, &candidate) {
1717 if recovered == public_key {
1718 found = Some(candidate);
1719 break;
1720 }
1721 }
1722 }
1723 let recoverable = found.ok_or_else(|| {
1724 ProtocolError::ExternalSignerError(
1730 "signer returned a signature whose recovery id does not match the returned public key".to_string(),
1731 )
1732 })?;
1733
1734 let compact_65 = recoverable.to_compact_signature(true);
1739
1740 if !self.set_signature(compact_65.to_vec().into()) {
1741 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1742 needed: self.required_number_of_private_keys(),
1743 using: 1,
1744 msg: "failed to set ECDSA signature",
1745 });
1746 }
1747 Ok(())
1748 }
1749
1750 #[cfg(feature = "state-transition-validation")]
1751 fn verify_by_raw_public_key<T: BlsModule>(
1752 &self,
1753 public_key: &[u8],
1754 public_key_type: KeyType,
1755 bls: &T,
1756 ) -> Result<(), ProtocolError> {
1757 match public_key_type {
1758 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key),
1759 KeyType::ECDSA_HASH160 => {
1760 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key)
1761 }
1762 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key, bls),
1763 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1764 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1765 InvalidIdentityPublicKeyTypeError::new(public_key_type),
1766 ))
1767 }
1768 }
1769 }
1770
1771 #[cfg(feature = "state-transition-validation")]
1772 pub fn verify_identity_signed_signature(
1773 &self,
1774 public_key: &IdentityPublicKey,
1775 bls: &impl BlsModule,
1776 ) -> Result<(), ProtocolError> {
1777 if public_key.disabled_at().is_some() {
1779 return Err(ProtocolError::PublicKeyIsDisabledError(
1780 PublicKeyIsDisabledError::new(public_key.id()),
1781 ));
1782 }
1783
1784 let Some(signature) = self.signature() else {
1785 return Err(ProtocolError::CorruptedCodeExecution("verifying identity signature for a state transition that doesn't use identity signatures".to_string()));
1786 };
1787 if signature.is_empty() {
1788 return Err(ProtocolError::StateTransitionIsNotSignedError(
1789 StateTransitionIsNotSignedError::new(self.clone()),
1790 ));
1791 }
1792
1793 if self.signature_public_key_id() != Some(public_key.id()) {
1794 return Err(ProtocolError::PublicKeyMismatchError(
1795 PublicKeyMismatchError::new(public_key.clone()),
1796 ));
1797 }
1798
1799 let public_key_bytes = public_key.data().as_slice();
1800 match public_key.key_type() {
1801 KeyType::ECDSA_HASH160 => {
1802 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key_bytes)
1803 }
1804
1805 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key_bytes),
1806
1807 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key_bytes, bls),
1808
1809 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => Ok(()),
1811 }
1812 }
1813
1814 #[cfg(feature = "state-transition-validation")]
1815 fn verify_ecdsa_hash_160_signature_by_public_key_hash(
1816 &self,
1817 public_key_hash: &[u8],
1818 ) -> Result<(), ProtocolError> {
1819 let Some(signature) = self.signature() else {
1820 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1821 needed: self.required_number_of_private_keys(),
1822 using: 1,
1823 msg: "This state transition type should a single signature",
1824 });
1825 };
1826 if signature.is_empty() {
1827 return Err(ProtocolError::StateTransitionIsNotSignedError(
1828 StateTransitionIsNotSignedError::new(self.clone()),
1829 ));
1830 }
1831 let data = self.signable_bytes()?;
1832 let data_hash = double_sha(data);
1833 signer::verify_hash_signature(&data_hash, signature.as_slice(), public_key_hash).map_err(
1834 |e| {
1835 ProtocolError::from(ConsensusError::SignatureError(
1836 SignatureError::InvalidStateTransitionSignatureError(
1837 InvalidStateTransitionSignatureError::new(e.to_string()),
1838 ),
1839 ))
1840 },
1841 )
1842 }
1843
1844 #[cfg(feature = "state-transition-validation")]
1845 fn verify_ecdsa_signature_by_public_key(&self, public_key: &[u8]) -> Result<(), ProtocolError> {
1847 let Some(signature) = self.signature() else {
1848 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1849 needed: self.required_number_of_private_keys(),
1850 using: 1,
1851 msg: "This state transition type should a single signature",
1852 });
1853 };
1854 if signature.is_empty() {
1855 return Err(ProtocolError::StateTransitionIsNotSignedError(
1856 StateTransitionIsNotSignedError::new(self.clone()),
1857 ));
1858 }
1859 let data = self.signable_bytes()?;
1860 signer::verify_data_signature(&data, signature.as_slice(), public_key).map_err(|e| {
1861 ProtocolError::from(ConsensusError::SignatureError(
1864 SignatureError::InvalidStateTransitionSignatureError(
1865 InvalidStateTransitionSignatureError::new(e.to_string()),
1866 ),
1867 ))
1868 })
1869 }
1870
1871 #[cfg(feature = "state-transition-validation")]
1872 fn verify_bls_signature_by_public_key<T: BlsModule>(
1874 &self,
1875 public_key: &[u8],
1876 bls: &T,
1877 ) -> Result<(), ProtocolError> {
1878 let Some(signature) = self.signature() else {
1879 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1880 needed: self.required_number_of_private_keys(),
1881 using: 1,
1882 msg: "This state transition type should a single signature",
1883 });
1884 };
1885 if signature.is_empty() {
1886 return Err(ProtocolError::StateTransitionIsNotSignedError(
1887 StateTransitionIsNotSignedError::new(self.clone()),
1888 ));
1889 }
1890
1891 let data = self.signable_bytes()?;
1892
1893 bls.verify_signature(signature.as_slice(), &data, public_key)
1894 .map(|_| ())
1895 .map_err(|e| {
1896 ProtocolError::from(ConsensusError::SignatureError(
1898 SignatureError::InvalidStateTransitionSignatureError(
1899 InvalidStateTransitionSignatureError::new(e.to_string()),
1900 ),
1901 ))
1902 })
1903 }
1904}
1905
1906#[cfg(feature = "state-transition-validation")]
1907impl StateTransitionStructureValidation for StateTransition {
1908 fn validate_structure(
1909 &self,
1910 platform_version: &PlatformVersion,
1911 ) -> crate::validation::SimpleConsensusValidationResult {
1912 match self {
1913 StateTransition::DataContractCreate(_)
1914 | StateTransition::DataContractUpdate(_)
1915 | StateTransition::Batch(_)
1916 | StateTransition::IdentityCreate(_)
1917 | StateTransition::IdentityTopUp(_)
1918 | StateTransition::IdentityCreditWithdrawal(_)
1919 | StateTransition::IdentityUpdate(_)
1920 | StateTransition::IdentityCreditTransfer(_)
1921 | StateTransition::MasternodeVote(_) => {
1922 crate::validation::SimpleConsensusValidationResult::new_with_error(
1923 UnsupportedFeatureError::new(
1924 "structure validation for identity-based state transitions".to_string(),
1925 platform_version.protocol_version,
1926 )
1927 .into(),
1928 )
1929 }
1930 StateTransition::IdentityCreditTransferToAddresses(transition) => {
1931 transition.validate_structure(platform_version)
1932 }
1933 StateTransition::IdentityCreateFromAddresses(transition) => {
1934 transition.validate_structure(platform_version)
1935 }
1936 StateTransition::IdentityTopUpFromAddresses(transition) => {
1937 transition.validate_structure(platform_version)
1938 }
1939 StateTransition::AddressFundsTransfer(transition) => {
1940 transition.validate_structure(platform_version)
1941 }
1942 StateTransition::AddressFundingFromAssetLock(transition) => {
1943 transition.validate_structure(platform_version)
1944 }
1945 StateTransition::AddressCreditWithdrawal(transition) => {
1946 transition.validate_structure(platform_version)
1947 }
1948 StateTransition::Shield(transition) => transition.validate_structure(platform_version),
1949 StateTransition::ShieldedTransfer(transition) => {
1950 transition.validate_structure(platform_version)
1951 }
1952 StateTransition::Unshield(transition) => {
1953 transition.validate_structure(platform_version)
1954 }
1955 StateTransition::ShieldFromAssetLock(transition) => {
1956 transition.validate_structure(platform_version)
1957 }
1958 StateTransition::ShieldedWithdrawal(transition) => {
1959 transition.validate_structure(platform_version)
1960 }
1961 StateTransition::IdentityCreateFromShieldedPool(transition) => {
1962 transition.validate_structure(platform_version)
1963 }
1964 }
1965 }
1966}
1967
1968#[cfg(test)]
1969mod tests {
1970 use super::*;
1971
1972 #[test]
1977 fn test_signing_options_default() {
1978 let opts = StateTransitionSigningOptions::default();
1979 assert!(!opts.allow_signing_with_any_security_level);
1980 assert!(!opts.allow_signing_with_any_purpose);
1981 }
1982
1983 #[test]
1984 fn test_signing_options_equality() {
1985 let a = StateTransitionSigningOptions {
1986 allow_signing_with_any_security_level: true,
1987 allow_signing_with_any_purpose: false,
1988 };
1989 let b = StateTransitionSigningOptions {
1990 allow_signing_with_any_security_level: true,
1991 allow_signing_with_any_purpose: false,
1992 };
1993 assert_eq!(a, b);
1994 }
1995
1996 #[test]
1997 fn test_signing_options_inequality() {
1998 let a = StateTransitionSigningOptions {
1999 allow_signing_with_any_security_level: true,
2000 allow_signing_with_any_purpose: false,
2001 };
2002 let b = StateTransitionSigningOptions {
2003 allow_signing_with_any_security_level: false,
2004 allow_signing_with_any_purpose: false,
2005 };
2006 assert_ne!(a, b);
2007 }
2008
2009 #[test]
2010 #[allow(clippy::clone_on_copy)]
2011 fn test_signing_options_clone() {
2012 let original = StateTransitionSigningOptions {
2013 allow_signing_with_any_security_level: true,
2014 allow_signing_with_any_purpose: true,
2015 };
2016 let cloned = original.clone();
2017 assert_eq!(original, cloned);
2018 }
2019
2020 #[test]
2021 fn test_signing_options_copy() {
2022 let original = StateTransitionSigningOptions {
2023 allow_signing_with_any_security_level: true,
2024 allow_signing_with_any_purpose: false,
2025 };
2026 let copied = original;
2027 assert_eq!(original, copied);
2028 }
2029
2030 #[test]
2031 fn test_signing_options_debug() {
2032 let opts = StateTransitionSigningOptions::default();
2033 let debug_str = format!("{:?}", opts);
2034 assert!(debug_str.contains("StateTransitionSigningOptions"));
2035 assert!(debug_str.contains("allow_signing_with_any_security_level"));
2036 assert!(debug_str.contains("allow_signing_with_any_purpose"));
2037 }
2038
2039 use crate::identity::core_script::CoreScript;
2049 use crate::identity::{Purpose, SecurityLevel};
2050 use crate::prelude::Identifier;
2051 use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0;
2052 use crate::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition;
2053 use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0;
2054 use crate::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition;
2055 use crate::state_transition::masternode_vote_transition::v0::MasternodeVoteTransitionV0;
2056 use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
2057 use crate::withdrawal::Pooling;
2058
2059 fn sample_transfer_st() -> StateTransition {
2060 let v0 = IdentityCreditTransferTransitionV0 {
2061 identity_id: Identifier::from([1u8; 32]),
2062 recipient_id: Identifier::from([2u8; 32]),
2063 amount: 1_000,
2064 nonce: 7,
2065 user_fee_increase: 3,
2066 signature_public_key_id: 11,
2067 signature: BinaryData::new(vec![0u8; 65]),
2068 };
2069 StateTransition::IdentityCreditTransfer(IdentityCreditTransferTransition::V0(v0))
2070 }
2071
2072 fn sample_masternode_vote_st() -> StateTransition {
2073 let v0 = MasternodeVoteTransitionV0 {
2074 pro_tx_hash: Identifier::from([3u8; 32]),
2075 voter_identity_id: Identifier::from([4u8; 32]),
2076 vote: Default::default(),
2077 nonce: 2,
2078 signature_public_key_id: 5,
2079 signature: BinaryData::new(vec![9u8; 10]),
2080 };
2081 StateTransition::MasternodeVote(MasternodeVoteTransition::V0(v0))
2082 }
2083
2084 fn sample_withdrawal_st() -> StateTransition {
2085 let v0 = IdentityCreditWithdrawalTransitionV0 {
2086 identity_id: Identifier::from([5u8; 32]),
2087 amount: 42,
2088 core_fee_per_byte: 1,
2089 pooling: Pooling::Never,
2090 output_script: CoreScript::from_bytes(vec![0x76, 0xa9]),
2091 nonce: 4,
2092 user_fee_increase: 1,
2093 signature_public_key_id: 3,
2094 signature: BinaryData::new(vec![8u8; 65]),
2095 };
2096 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V0(v0))
2097 }
2098
2099 #[test]
2100 fn test_name_returns_variant_names() {
2101 assert_eq!(sample_transfer_st().name(), "IdentityCreditTransfer");
2102 assert_eq!(sample_masternode_vote_st().name(), "MasternodeVote");
2103 assert_eq!(sample_withdrawal_st().name(), "IdentityCreditWithdrawal");
2104 }
2105
2106 #[test]
2107 fn test_state_transition_type_matches_variant() {
2108 assert_eq!(
2109 sample_transfer_st().state_transition_type(),
2110 StateTransitionType::IdentityCreditTransfer
2111 );
2112 assert_eq!(
2113 sample_masternode_vote_st().state_transition_type(),
2114 StateTransitionType::MasternodeVote
2115 );
2116 assert_eq!(
2117 sample_withdrawal_st().state_transition_type(),
2118 StateTransitionType::IdentityCreditWithdrawal
2119 );
2120 }
2121
2122 #[test]
2123 fn test_is_identity_signed_excludes_asset_lock_and_shielded() {
2124 assert!(sample_transfer_st().is_identity_signed());
2125 assert!(sample_masternode_vote_st().is_identity_signed());
2126 assert!(sample_withdrawal_st().is_identity_signed());
2127 }
2128
2129 #[test]
2130 fn test_signature_accessor() {
2131 let st = sample_transfer_st();
2132 let sig = st.signature().expect("transfer should expose signature");
2133 assert_eq!(sig.len(), 65);
2134
2135 let st = sample_masternode_vote_st();
2136 let sig = st.signature().expect("masternode vote has signature");
2137 assert_eq!(sig.as_slice(), &[9u8; 10]);
2138 }
2139
2140 #[test]
2141 fn test_owner_id_accessor() {
2142 let transfer = sample_transfer_st();
2143 assert_eq!(transfer.owner_id(), Some(Identifier::from([1u8; 32])));
2144
2145 let vote = sample_masternode_vote_st();
2146 assert_eq!(vote.owner_id(), Some(Identifier::from([4u8; 32])));
2147
2148 let withdraw = sample_withdrawal_st();
2149 assert_eq!(withdraw.owner_id(), Some(Identifier::from([5u8; 32])));
2150 }
2151
2152 #[test]
2153 fn test_signature_public_key_id_accessor() {
2154 assert_eq!(sample_transfer_st().signature_public_key_id(), Some(11));
2155 assert_eq!(
2156 sample_masternode_vote_st().signature_public_key_id(),
2157 Some(5)
2158 );
2159 assert_eq!(sample_withdrawal_st().signature_public_key_id(), Some(3));
2160 }
2161
2162 #[test]
2163 fn test_user_fee_increase_for_various_variants() {
2164 assert_eq!(sample_transfer_st().user_fee_increase(), 3);
2166 assert_eq!(sample_masternode_vote_st().user_fee_increase(), 0);
2168 assert_eq!(sample_withdrawal_st().user_fee_increase(), 1);
2170 }
2171
2172 #[test]
2173 fn test_set_signature_returns_true_for_supported() {
2174 let mut st = sample_transfer_st();
2175 let ok = st.set_signature(BinaryData::new(vec![0xaa; 65]));
2176 assert!(ok);
2177 assert_eq!(st.signature().unwrap().as_slice(), &[0xaa; 65]);
2178 }
2179
2180 #[test]
2181 fn test_set_user_fee_increase_updates_value() {
2182 let mut st = sample_transfer_st();
2183 st.set_user_fee_increase(42);
2184 assert_eq!(st.user_fee_increase(), 42);
2185
2186 let mut vote = sample_masternode_vote_st();
2188 vote.set_user_fee_increase(99);
2189 assert_eq!(vote.user_fee_increase(), 0);
2190 }
2191
2192 #[test]
2193 fn test_set_signature_public_key_id() {
2194 let mut st = sample_transfer_st();
2195 st.set_signature_public_key_id(1234);
2196 assert_eq!(st.signature_public_key_id(), Some(1234));
2197 }
2198
2199 #[test]
2200 fn test_required_number_of_private_keys_default() {
2201 assert_eq!(sample_transfer_st().required_number_of_private_keys(), 1);
2203 assert_eq!(
2204 sample_masternode_vote_st().required_number_of_private_keys(),
2205 1
2206 );
2207 assert_eq!(sample_withdrawal_st().required_number_of_private_keys(), 1);
2208 }
2209
2210 #[test]
2211 fn test_inputs_none_for_legacy_variants() {
2212 assert!(sample_transfer_st().inputs().is_none());
2214 assert!(sample_masternode_vote_st().inputs().is_none());
2215 assert!(sample_withdrawal_st().inputs().is_none());
2216 }
2217
2218 #[test]
2219 fn test_active_version_range_legacy_transitions() {
2220 assert_eq!(sample_transfer_st().active_version_range(), ALL_VERSIONS);
2222 assert_eq!(
2223 sample_masternode_vote_st().active_version_range(),
2224 ALL_VERSIONS
2225 );
2226 assert_eq!(sample_withdrawal_st().active_version_range(), ALL_VERSIONS);
2227 }
2228
2229 #[test]
2230 fn test_unique_identifiers_non_empty() {
2231 let ids = sample_transfer_st().unique_identifiers();
2232 assert_eq!(ids.len(), 1);
2233 assert!(!ids[0].is_empty());
2234 }
2235
2236 #[test]
2237 fn test_required_asset_lock_balance_rejects_non_asset_lock() {
2238 let platform_version = PlatformVersion::latest();
2239 let st = sample_transfer_st();
2240 let err = st
2241 .required_asset_lock_balance_for_processing_start(platform_version)
2242 .expect_err("credit transfer is not an asset lock state transition");
2243 match err {
2244 ProtocolError::CorruptedCodeExecution(msg) => {
2245 assert!(
2246 msg.contains("is not an asset lock transaction"),
2247 "unexpected error message: {msg}"
2248 );
2249 }
2250 other => panic!("expected CorruptedCodeExecution, got {other:?}"),
2251 }
2252 }
2253
2254 #[test]
2255 fn test_security_level_requirement_for_transfer() {
2256 let st = sample_transfer_st();
2258 let levels = st
2259 .security_level_requirement(Purpose::TRANSFER)
2260 .expect("transfer state transition should return a requirement");
2261 assert_eq!(levels, vec![SecurityLevel::CRITICAL]);
2262 }
2263
2264 #[test]
2265 fn test_purpose_requirement_for_transfer() {
2266 let st = sample_transfer_st();
2267 let purposes = st
2268 .purpose_requirement()
2269 .expect("transfer state transition should have a purpose");
2270 assert_eq!(purposes, vec![Purpose::TRANSFER]);
2271 }
2272
2273 #[test]
2274 fn test_optional_asset_lock_proof_none_for_transfer() {
2275 let st = sample_transfer_st();
2276 assert!(st.optional_asset_lock_proof().is_none());
2277 }
2278
2279 #[test]
2284 fn test_from_outer_enum_into_state_transition() {
2285 let outer: IdentityCreditTransferTransition =
2286 IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0::default());
2287 let st: StateTransition = outer.into();
2288 assert!(matches!(st, StateTransition::IdentityCreditTransfer(_)));
2289 }
2290
2291 #[test]
2292 fn test_from_masternode_vote_outer_into_state_transition() {
2293 let outer: MasternodeVoteTransition =
2294 MasternodeVoteTransition::V0(MasternodeVoteTransitionV0::default());
2295 let st: StateTransition = outer.into();
2296 assert!(matches!(st, StateTransition::MasternodeVote(_)));
2297 }
2298
2299 #[test]
2305 fn test_state_transition_platform_serialize_roundtrip() {
2306 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
2307 let original = sample_transfer_st();
2308 let bytes =
2309 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2310 let restored =
2311 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
2312 assert_eq!(original, restored);
2313 }
2314
2315 #[test]
2316 fn test_deserialize_from_bytes_in_version_succeeds_for_latest() {
2317 use crate::serialization::PlatformSerializable;
2318 let original = sample_transfer_st();
2319 let bytes =
2320 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2321 let restored =
2322 StateTransition::deserialize_from_bytes_in_version(&bytes, PlatformVersion::latest())
2323 .expect("deserialize_from_bytes_in_version should succeed");
2324 assert_eq!(original, restored);
2325 }
2326
2327 #[test]
2328 fn test_transaction_id_is_deterministic() {
2329 let st = sample_transfer_st();
2330 let a = st.transaction_id().expect("hash should succeed");
2331 let b = st.transaction_id().expect("hash should succeed");
2332 assert_eq!(a, b);
2333 assert_eq!(a.len(), 32);
2334 }
2335
2336 #[test]
2337 fn test_transaction_id_changes_on_signature_change() {
2338 let mut st = sample_transfer_st();
2339 let before = st.transaction_id().expect("hash should succeed");
2340 st.set_signature(BinaryData::new(vec![0xbb; 65]));
2341 let after = st.transaction_id().expect("hash should succeed");
2342 assert_ne!(before, after);
2344 }
2345
2346 #[test]
2347 fn test_clone_preserves_inner_state() {
2348 let st = sample_transfer_st();
2349 let cloned = st.clone();
2350 assert_eq!(st, cloned);
2351 }
2352
2353 use crate::data_contract::serialized_version::DataContractInSerializationFormat;
2364 use crate::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0;
2365 use crate::state_transition::batch_transition::document_base_transition::DocumentBaseTransition;
2366 use crate::state_transition::batch_transition::document_delete_transition::{
2367 DocumentDeleteTransition, DocumentDeleteTransitionV0,
2368 };
2369 use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionV0};
2370 use crate::state_transition::data_contract_create_transition::{
2371 DataContractCreateTransition, DataContractCreateTransitionV0,
2372 };
2373 use crate::state_transition::data_contract_update_transition::{
2374 DataContractUpdateTransition, DataContractUpdateTransitionV0,
2375 };
2376 use crate::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0;
2377 use crate::state_transition::identity_create_transition::IdentityCreateTransition;
2378 use crate::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0;
2379 use crate::state_transition::identity_topup_transition::IdentityTopUpTransition;
2380 use crate::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0;
2381 use crate::state_transition::identity_update_transition::IdentityUpdateTransition;
2382 use crate::state_transition::shielded_transfer_transition::v0::ShieldedTransferTransitionV0;
2383 use crate::state_transition::shielded_transfer_transition::ShieldedTransferTransition;
2384 use crate::state_transition::shielded_withdrawal_transition::v0::ShieldedWithdrawalTransitionV0;
2385 use crate::state_transition::shielded_withdrawal_transition::ShieldedWithdrawalTransition;
2386 use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
2387 use crate::state_transition::unshield_transition::UnshieldTransition;
2388
2389 fn sample_data_contract_in_serialization_format() -> DataContractInSerializationFormat {
2392 use crate::data_contract::config::v0::DataContractConfigV0;
2393 use crate::data_contract::config::DataContractConfig;
2394 use crate::data_contract::v1::DataContractV1;
2395 use crate::data_contract::DataContract;
2396 use platform_version::TryIntoPlatformVersioned;
2397 use std::collections::BTreeMap;
2398
2399 let contract = DataContract::V1(DataContractV1 {
2400 id: Identifier::from([9u8; 32]),
2401 version: 1,
2402 owner_id: Identifier::from([7u8; 32]),
2403 document_types: BTreeMap::new(),
2404 config: DataContractConfig::V0(DataContractConfigV0 {
2405 can_be_deleted: false,
2406 readonly: false,
2407 keeps_history: false,
2408 documents_keep_history_contract_default: false,
2409 documents_mutable_contract_default: false,
2410 documents_can_be_deleted_contract_default: false,
2411 requires_identity_encryption_bounded_key: None,
2412 requires_identity_decryption_bounded_key: None,
2413 }),
2414 schema_defs: None,
2415 created_at: None,
2416 updated_at: None,
2417 created_at_block_height: None,
2418 updated_at_block_height: None,
2419 created_at_epoch: None,
2420 updated_at_epoch: None,
2421 groups: BTreeMap::new(),
2422 tokens: BTreeMap::new(),
2423 keywords: Vec::new(),
2424 description: None,
2425 });
2426
2427 contract
2428 .try_into_platform_versioned(PlatformVersion::latest())
2429 .expect("expected to serialize a trivial contract")
2430 }
2431
2432 fn sample_data_contract_create_st() -> StateTransition {
2433 StateTransition::DataContractCreate(DataContractCreateTransition::V0(
2434 DataContractCreateTransitionV0 {
2435 data_contract: sample_data_contract_in_serialization_format(),
2436 identity_nonce: 1,
2437 user_fee_increase: 5,
2438 signature_public_key_id: 2,
2439 signature: BinaryData::new(vec![0xAB; 65]),
2440 },
2441 ))
2442 }
2443
2444 fn sample_data_contract_update_st() -> StateTransition {
2445 StateTransition::DataContractUpdate(DataContractUpdateTransition::V0(
2446 DataContractUpdateTransitionV0 {
2447 identity_contract_nonce: 4,
2448 data_contract: sample_data_contract_in_serialization_format(),
2449 user_fee_increase: 9,
2450 signature_public_key_id: 6,
2451 signature: BinaryData::new(vec![0xCD; 65]),
2452 },
2453 ))
2454 }
2455
2456 fn sample_batch_st_with_delete() -> StateTransition {
2457 let base = DocumentBaseTransition::V0(DocumentBaseTransitionV0 {
2458 id: Identifier::from([1u8; 32]),
2459 identity_contract_nonce: 3,
2460 document_type_name: "preorder".to_string(),
2461 data_contract_id: Identifier::from([2u8; 32]),
2462 });
2463 let delete =
2464 DocumentTransition::Delete(DocumentDeleteTransition::V0(DocumentDeleteTransitionV0 {
2465 base,
2466 }));
2467 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2468 owner_id: Identifier::from([8u8; 32]),
2469 transitions: vec![delete],
2470 user_fee_increase: 2,
2471 signature_public_key_id: 7,
2472 signature: BinaryData::new(vec![0xEE; 65]),
2473 }))
2474 }
2475
2476 fn sample_batch_st_empty() -> StateTransition {
2477 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2478 owner_id: Identifier::from([1u8; 32]),
2479 transitions: vec![],
2480 user_fee_increase: 0,
2481 signature_public_key_id: 0,
2482 signature: BinaryData::new(vec![]),
2483 }))
2484 }
2485
2486 fn sample_identity_create_st() -> StateTransition {
2487 StateTransition::IdentityCreate(IdentityCreateTransition::V0(IdentityCreateTransitionV0 {
2488 identity_id: Identifier::from([3u8; 32]),
2489 ..Default::default()
2490 }))
2491 }
2492
2493 fn sample_identity_top_up_st() -> StateTransition {
2494 StateTransition::IdentityTopUp(IdentityTopUpTransition::V0(IdentityTopUpTransitionV0 {
2495 identity_id: Identifier::from([4u8; 32]),
2496 ..Default::default()
2497 }))
2498 }
2499
2500 fn sample_identity_update_st() -> StateTransition {
2501 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
2502 identity_id: Identifier::from([5u8; 32]),
2503 revision: 1,
2504 nonce: 2,
2505 add_public_keys: vec![],
2506 disable_public_keys: vec![],
2507 user_fee_increase: 11,
2508 signature_public_key_id: 33,
2509 signature: BinaryData::new(vec![0xFF; 65]),
2510 }))
2511 }
2512
2513 fn sample_unshield_st() -> StateTransition {
2514 StateTransition::Unshield(UnshieldTransition::V0(UnshieldTransitionV0 {
2515 output_address: Default::default(),
2516 actions: vec![],
2517 unshielding_amount: 0,
2518 anchor: [0u8; 32],
2519 proof: vec![],
2520 binding_signature: [0u8; 64],
2521 }))
2522 }
2523
2524 fn sample_shielded_transfer_st() -> StateTransition {
2525 StateTransition::ShieldedTransfer(ShieldedTransferTransition::V0(
2526 ShieldedTransferTransitionV0 {
2527 actions: vec![],
2528 value_balance: 0,
2529 anchor: [0u8; 32],
2530 proof: vec![],
2531 binding_signature: [0u8; 64],
2532 },
2533 ))
2534 }
2535
2536 fn sample_shielded_withdrawal_st() -> StateTransition {
2537 use crate::identity::core_script::CoreScript;
2538 use crate::withdrawal::Pooling;
2539 StateTransition::ShieldedWithdrawal(ShieldedWithdrawalTransition::V0(
2540 ShieldedWithdrawalTransitionV0 {
2541 actions: vec![],
2542 unshielding_amount: 0,
2543 anchor: [0u8; 32],
2544 proof: vec![],
2545 binding_signature: [0u8; 64],
2546 core_fee_per_byte: 1,
2547 pooling: Pooling::Never,
2548 output_script: CoreScript::from_bytes(vec![]),
2549 },
2550 ))
2551 }
2552
2553 #[test]
2556 fn test_name_for_newly_covered_variants() {
2557 assert_eq!(
2558 sample_data_contract_create_st().name(),
2559 "DataContractCreate"
2560 );
2561 assert_eq!(
2562 sample_data_contract_update_st().name(),
2563 "DataContractUpdate"
2564 );
2565 assert_eq!(sample_identity_create_st().name(), "IdentityCreate");
2566 assert_eq!(sample_identity_top_up_st().name(), "IdentityTopUp");
2567 assert_eq!(sample_identity_update_st().name(), "IdentityUpdate");
2568 assert_eq!(sample_unshield_st().name(), "Unshield");
2569 assert_eq!(sample_shielded_transfer_st().name(), "ShieldedTransfer");
2570 assert_eq!(sample_shielded_withdrawal_st().name(), "ShieldedWithdrawal");
2571
2572 let batch_name = sample_batch_st_with_delete().name();
2575 assert_eq!(batch_name, "DocumentsBatch([Delete])");
2576
2577 let empty_name = sample_batch_st_empty().name();
2579 assert_eq!(empty_name, "DocumentsBatch([])");
2580 }
2581
2582 #[test]
2584 fn test_state_transition_type_for_newly_covered_variants() {
2585 assert_eq!(
2586 sample_data_contract_create_st().state_transition_type(),
2587 StateTransitionType::DataContractCreate
2588 );
2589 assert_eq!(
2590 sample_data_contract_update_st().state_transition_type(),
2591 StateTransitionType::DataContractUpdate
2592 );
2593 assert_eq!(
2594 sample_batch_st_with_delete().state_transition_type(),
2595 StateTransitionType::Batch
2596 );
2597 assert_eq!(
2598 sample_identity_create_st().state_transition_type(),
2599 StateTransitionType::IdentityCreate
2600 );
2601 assert_eq!(
2602 sample_identity_top_up_st().state_transition_type(),
2603 StateTransitionType::IdentityTopUp
2604 );
2605 assert_eq!(
2606 sample_identity_update_st().state_transition_type(),
2607 StateTransitionType::IdentityUpdate
2608 );
2609 assert_eq!(
2610 sample_unshield_st().state_transition_type(),
2611 StateTransitionType::Unshield
2612 );
2613 assert_eq!(
2614 sample_shielded_transfer_st().state_transition_type(),
2615 StateTransitionType::ShieldedTransfer
2616 );
2617 assert_eq!(
2618 sample_shielded_withdrawal_st().state_transition_type(),
2619 StateTransitionType::ShieldedWithdrawal
2620 );
2621 }
2622
2623 #[test]
2628 fn test_active_version_range_contract_and_shielded_branches() {
2629 let contract_v1_range = 9..=LATEST_VERSION;
2632 assert_eq!(
2633 sample_data_contract_create_st().active_version_range(),
2634 contract_v1_range
2635 );
2636 let contract_v1_range = 9..=LATEST_VERSION;
2637 assert_eq!(
2638 sample_data_contract_update_st().active_version_range(),
2639 contract_v1_range
2640 );
2641 assert_eq!(
2643 sample_batch_st_with_delete().active_version_range(),
2644 ALL_VERSIONS
2645 );
2646 assert_eq!(
2648 sample_identity_create_st().active_version_range(),
2649 ALL_VERSIONS
2650 );
2651 assert_eq!(
2652 sample_identity_top_up_st().active_version_range(),
2653 ALL_VERSIONS
2654 );
2655 assert_eq!(
2656 sample_identity_update_st().active_version_range(),
2657 ALL_VERSIONS
2658 );
2659 let shielded_range = 12..=LATEST_VERSION;
2661 assert_eq!(
2662 sample_shielded_transfer_st().active_version_range(),
2663 shielded_range.clone()
2664 );
2665 assert_eq!(
2666 sample_unshield_st().active_version_range(),
2667 shielded_range.clone()
2668 );
2669 assert_eq!(
2670 sample_shielded_withdrawal_st().active_version_range(),
2671 shielded_range
2672 );
2673 }
2674
2675 #[test]
2678 fn test_is_identity_signed_false_for_identity_create_topup_and_shielded() {
2679 assert!(!sample_identity_create_st().is_identity_signed());
2680 assert!(!sample_identity_top_up_st().is_identity_signed());
2681 assert!(!sample_unshield_st().is_identity_signed());
2682 assert!(!sample_shielded_transfer_st().is_identity_signed());
2683 assert!(!sample_shielded_withdrawal_st().is_identity_signed());
2684 }
2685
2686 #[test]
2690 fn test_signature_accessor_for_other_variants() {
2691 assert_eq!(
2693 sample_data_contract_create_st().signature().unwrap().len(),
2694 65
2695 );
2696 assert_eq!(
2697 sample_data_contract_update_st().signature().unwrap().len(),
2698 65
2699 );
2700 assert_eq!(sample_batch_st_with_delete().signature().unwrap().len(), 65);
2701 assert_eq!(sample_identity_update_st().signature().unwrap().len(), 65);
2702
2703 assert!(sample_unshield_st().signature().is_none());
2705 assert!(sample_shielded_transfer_st().signature().is_none());
2706 assert!(sample_shielded_withdrawal_st().signature().is_none());
2707 }
2708
2709 #[test]
2711 fn test_owner_id_accessor_for_other_variants() {
2712 assert_eq!(
2713 sample_data_contract_create_st().owner_id(),
2714 Some(Identifier::from([7u8; 32]))
2715 );
2716 assert_eq!(
2717 sample_data_contract_update_st().owner_id(),
2718 Some(Identifier::from([7u8; 32]))
2719 );
2720 assert_eq!(
2721 sample_batch_st_with_delete().owner_id(),
2722 Some(Identifier::from([8u8; 32]))
2723 );
2724 assert_eq!(
2725 sample_identity_update_st().owner_id(),
2726 Some(Identifier::from([5u8; 32]))
2727 );
2728 assert!(sample_unshield_st().owner_id().is_none());
2730 assert!(sample_shielded_transfer_st().owner_id().is_none());
2731 assert!(sample_shielded_withdrawal_st().owner_id().is_none());
2732 }
2733
2734 #[test]
2737 fn test_user_fee_increase_for_newly_covered_variants() {
2738 assert_eq!(sample_data_contract_create_st().user_fee_increase(), 5);
2739 assert_eq!(sample_data_contract_update_st().user_fee_increase(), 9);
2740 assert_eq!(sample_batch_st_with_delete().user_fee_increase(), 2);
2741 assert_eq!(sample_identity_update_st().user_fee_increase(), 11);
2742 assert_eq!(sample_shielded_transfer_st().user_fee_increase(), 0);
2744 assert_eq!(sample_shielded_withdrawal_st().user_fee_increase(), 0);
2745 assert_eq!(sample_unshield_st().user_fee_increase(), 0);
2746 }
2747
2748 #[test]
2751 fn test_set_user_fee_increase_for_newly_covered_variants() {
2752 let mut st = sample_data_contract_create_st();
2753 st.set_user_fee_increase(42);
2754 assert_eq!(st.user_fee_increase(), 42);
2755
2756 let mut st = sample_data_contract_update_st();
2757 st.set_user_fee_increase(13);
2758 assert_eq!(st.user_fee_increase(), 13);
2759
2760 let mut st = sample_batch_st_with_delete();
2761 st.set_user_fee_increase(101);
2762 assert_eq!(st.user_fee_increase(), 101);
2763
2764 let mut st = sample_identity_update_st();
2765 st.set_user_fee_increase(77);
2766 assert_eq!(st.user_fee_increase(), 77);
2767
2768 let mut shielded = sample_shielded_transfer_st();
2770 shielded.set_user_fee_increase(99);
2771 assert_eq!(shielded.user_fee_increase(), 0);
2772
2773 let mut withdrawal = sample_shielded_withdrawal_st();
2774 withdrawal.set_user_fee_increase(99);
2775 assert_eq!(withdrawal.user_fee_increase(), 0);
2776
2777 let mut unshield = sample_unshield_st();
2778 unshield.set_user_fee_increase(99);
2779 assert_eq!(unshield.user_fee_increase(), 0);
2780 }
2781
2782 #[test]
2786 fn test_set_signature_false_for_shielded_and_identity_create_topup() {
2787 let mut st = sample_unshield_st();
2789 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2790 let mut st = sample_shielded_transfer_st();
2791 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2792 let mut st = sample_shielded_withdrawal_st();
2793 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
2794 }
2795
2796 #[test]
2797 fn test_set_signature_true_for_newly_covered_variants() {
2798 let mut st = sample_data_contract_create_st();
2799 assert!(st.set_signature(BinaryData::new(vec![0x11; 65])));
2800 assert_eq!(st.signature().unwrap().as_slice(), &[0x11; 65]);
2801
2802 let mut st = sample_data_contract_update_st();
2803 assert!(st.set_signature(BinaryData::new(vec![0x22; 65])));
2804 assert_eq!(st.signature().unwrap().as_slice(), &[0x22; 65]);
2805
2806 let mut st = sample_batch_st_with_delete();
2807 assert!(st.set_signature(BinaryData::new(vec![0x33; 65])));
2808 assert_eq!(st.signature().unwrap().as_slice(), &[0x33; 65]);
2809
2810 let mut st = sample_identity_update_st();
2811 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
2812 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
2813 }
2814
2815 #[test]
2818 fn test_signature_public_key_id_returns_none_for_non_signed() {
2819 assert!(sample_identity_create_st()
2822 .signature_public_key_id()
2823 .is_none());
2824 assert!(sample_identity_top_up_st()
2825 .signature_public_key_id()
2826 .is_none());
2827 assert!(sample_unshield_st().signature_public_key_id().is_none());
2828 assert!(sample_shielded_transfer_st()
2829 .signature_public_key_id()
2830 .is_none());
2831 assert!(sample_shielded_withdrawal_st()
2832 .signature_public_key_id()
2833 .is_none());
2834 }
2835
2836 #[test]
2837 fn test_signature_public_key_id_for_signed_variants() {
2838 assert_eq!(
2839 sample_data_contract_create_st().signature_public_key_id(),
2840 Some(2)
2841 );
2842 assert_eq!(
2843 sample_data_contract_update_st().signature_public_key_id(),
2844 Some(6)
2845 );
2846 assert_eq!(
2847 sample_batch_st_with_delete().signature_public_key_id(),
2848 Some(7)
2849 );
2850 assert_eq!(
2851 sample_identity_update_st().signature_public_key_id(),
2852 Some(33)
2853 );
2854 }
2855
2856 #[test]
2859 fn test_set_signature_public_key_id_noop_for_non_signed() {
2860 let mut st = sample_identity_create_st();
2863 st.set_signature_public_key_id(100);
2864 assert_eq!(st.signature_public_key_id(), None);
2865
2866 let mut st = sample_identity_top_up_st();
2867 st.set_signature_public_key_id(100);
2868 assert_eq!(st.signature_public_key_id(), None);
2869
2870 let mut st = sample_unshield_st();
2871 st.set_signature_public_key_id(100);
2872 assert_eq!(st.signature_public_key_id(), None);
2873 }
2874
2875 #[test]
2876 fn test_set_signature_public_key_id_updates_for_signed_variants() {
2877 let mut st = sample_data_contract_create_st();
2878 st.set_signature_public_key_id(42);
2879 assert_eq!(st.signature_public_key_id(), Some(42));
2880
2881 let mut st = sample_batch_st_with_delete();
2882 st.set_signature_public_key_id(43);
2883 assert_eq!(st.signature_public_key_id(), Some(43));
2884
2885 let mut st = sample_identity_update_st();
2886 st.set_signature_public_key_id(44);
2887 assert_eq!(st.signature_public_key_id(), Some(44));
2888 }
2889
2890 #[test]
2893 fn test_required_number_of_private_keys_various_variants() {
2894 assert_eq!(
2895 sample_data_contract_create_st().required_number_of_private_keys(),
2896 1
2897 );
2898 assert_eq!(
2899 sample_data_contract_update_st().required_number_of_private_keys(),
2900 1
2901 );
2902 assert_eq!(
2903 sample_batch_st_with_delete().required_number_of_private_keys(),
2904 1
2905 );
2906 assert_eq!(
2907 sample_identity_update_st().required_number_of_private_keys(),
2908 1
2909 );
2910 assert_eq!(
2911 sample_identity_create_st().required_number_of_private_keys(),
2912 1
2913 );
2914 assert_eq!(
2916 sample_shielded_transfer_st().required_number_of_private_keys(),
2917 0
2918 );
2919 assert_eq!(
2920 sample_shielded_withdrawal_st().required_number_of_private_keys(),
2921 0
2922 );
2923 assert_eq!(sample_unshield_st().required_number_of_private_keys(), 0);
2924 }
2925
2926 #[test]
2929 fn test_inputs_none_for_many_variants() {
2930 assert!(sample_data_contract_create_st().inputs().is_none());
2931 assert!(sample_data_contract_update_st().inputs().is_none());
2932 assert!(sample_batch_st_with_delete().inputs().is_none());
2933 assert!(sample_identity_create_st().inputs().is_none());
2934 assert!(sample_identity_top_up_st().inputs().is_none());
2935 assert!(sample_identity_update_st().inputs().is_none());
2936 assert!(sample_unshield_st().inputs().is_none());
2938 assert!(sample_shielded_transfer_st().inputs().is_none());
2939 assert!(sample_shielded_withdrawal_st().inputs().is_none());
2940 }
2941
2942 #[test]
2947 fn test_optional_asset_lock_proof_returns_none_for_wildcard_arms() {
2948 assert!(sample_data_contract_create_st()
2949 .optional_asset_lock_proof()
2950 .is_none());
2951 assert!(sample_data_contract_update_st()
2952 .optional_asset_lock_proof()
2953 .is_none());
2954 assert!(sample_batch_st_with_delete()
2955 .optional_asset_lock_proof()
2956 .is_none());
2957 assert!(sample_identity_update_st()
2958 .optional_asset_lock_proof()
2959 .is_none());
2960 assert!(sample_unshield_st().optional_asset_lock_proof().is_none());
2961 assert!(sample_shielded_transfer_st()
2962 .optional_asset_lock_proof()
2963 .is_none());
2964 assert!(sample_shielded_withdrawal_st()
2965 .optional_asset_lock_proof()
2966 .is_none());
2967 }
2968
2969 #[test]
2973 fn test_required_asset_lock_balance_errors_for_other_non_asset_lock_variants() {
2974 let platform_version = PlatformVersion::latest();
2975
2976 let cases: Vec<(&str, StateTransition)> = vec![
2977 ("DataContractCreate", sample_data_contract_create_st()),
2978 ("DataContractUpdate", sample_data_contract_update_st()),
2979 ("Batch", sample_batch_st_with_delete()),
2980 ("IdentityUpdate", sample_identity_update_st()),
2981 ("MasternodeVote", sample_masternode_vote_st()),
2982 ("Unshield", sample_unshield_st()),
2983 ("ShieldedTransfer", sample_shielded_transfer_st()),
2984 ("ShieldedWithdrawal", sample_shielded_withdrawal_st()),
2985 ];
2986
2987 for (label, st) in cases {
2988 let err = st
2989 .required_asset_lock_balance_for_processing_start(platform_version)
2990 .expect_err(&format!("expected error for {label}"));
2991 match err {
2992 ProtocolError::CorruptedCodeExecution(msg) => {
2993 assert!(
2994 msg.contains("is not an asset lock transaction"),
2995 "unexpected error for {label}: {msg}"
2996 );
2997 }
2998 other => panic!("expected CorruptedCodeExecution for {label}, got {other:?}"),
2999 }
3000 }
3001 }
3002
3003 #[test]
3009 fn test_unique_identifiers_non_empty_for_other_variants() {
3010 for st in [
3011 sample_data_contract_create_st(),
3012 sample_data_contract_update_st(),
3013 sample_batch_st_with_delete(),
3014 sample_identity_create_st(),
3015 sample_identity_top_up_st(),
3016 sample_identity_update_st(),
3017 ] {
3018 let ids = st.unique_identifiers();
3019 assert!(!ids.is_empty(), "unique_identifiers should not be empty");
3020 }
3021 }
3022
3023 #[test]
3027 fn test_security_level_requirement_returns_none_for_non_signed_variants() {
3028 let purpose = Purpose::AUTHENTICATION;
3029 assert!(sample_identity_create_st()
3030 .security_level_requirement(purpose)
3031 .is_none());
3032 assert!(sample_identity_top_up_st()
3033 .security_level_requirement(purpose)
3034 .is_none());
3035 assert!(sample_unshield_st()
3036 .security_level_requirement(purpose)
3037 .is_none());
3038 assert!(sample_shielded_transfer_st()
3039 .security_level_requirement(purpose)
3040 .is_none());
3041 assert!(sample_shielded_withdrawal_st()
3042 .security_level_requirement(purpose)
3043 .is_none());
3044 }
3045
3046 #[test]
3047 fn test_purpose_requirement_returns_none_for_non_signed_variants() {
3048 assert!(sample_identity_create_st().purpose_requirement().is_none());
3049 assert!(sample_identity_top_up_st().purpose_requirement().is_none());
3050 assert!(sample_unshield_st().purpose_requirement().is_none());
3051 assert!(sample_shielded_transfer_st()
3052 .purpose_requirement()
3053 .is_none());
3054 assert!(sample_shielded_withdrawal_st()
3055 .purpose_requirement()
3056 .is_none());
3057 }
3058
3059 #[test]
3061 fn test_from_outer_data_contract_create_into_state_transition() {
3062 let outer: DataContractCreateTransition =
3063 DataContractCreateTransition::V0(DataContractCreateTransitionV0 {
3064 data_contract: sample_data_contract_in_serialization_format(),
3065 identity_nonce: 1,
3066 user_fee_increase: 0,
3067 signature_public_key_id: 0,
3068 signature: Default::default(),
3069 });
3070 let st: StateTransition = outer.into();
3071 assert!(matches!(st, StateTransition::DataContractCreate(_)));
3072 }
3073
3074 #[test]
3075 fn test_from_outer_data_contract_update_into_state_transition() {
3076 let outer: DataContractUpdateTransition =
3077 DataContractUpdateTransition::V0(DataContractUpdateTransitionV0 {
3078 identity_contract_nonce: 2,
3079 data_contract: sample_data_contract_in_serialization_format(),
3080 user_fee_increase: 0,
3081 signature_public_key_id: 0,
3082 signature: Default::default(),
3083 });
3084 let st: StateTransition = outer.into();
3085 assert!(matches!(st, StateTransition::DataContractUpdate(_)));
3086 }
3087
3088 #[test]
3089 fn test_from_outer_batch_into_state_transition() {
3090 let outer: BatchTransition = BatchTransition::V0(BatchTransitionV0::default());
3091 let st: StateTransition = outer.into();
3092 assert!(matches!(st, StateTransition::Batch(_)));
3093 }
3094
3095 #[test]
3096 fn test_from_outer_identity_create_into_state_transition() {
3097 let outer: IdentityCreateTransition =
3098 IdentityCreateTransition::V0(IdentityCreateTransitionV0::default());
3099 let st: StateTransition = outer.into();
3100 assert!(matches!(st, StateTransition::IdentityCreate(_)));
3101 }
3102
3103 #[test]
3104 fn test_from_outer_identity_update_into_state_transition() {
3105 let outer: IdentityUpdateTransition =
3106 IdentityUpdateTransition::V0(IdentityUpdateTransitionV0::default());
3107 let st: StateTransition = outer.into();
3108 assert!(matches!(st, StateTransition::IdentityUpdate(_)));
3109 }
3110
3111 #[test]
3114 fn test_transaction_id_and_clone_for_identity_update() {
3115 let st = sample_identity_update_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]
3124 fn test_transaction_id_and_clone_for_data_contract_create() {
3125 let st = sample_data_contract_create_st();
3126 let id_a = st.transaction_id().expect("hash should succeed");
3127 let cloned = st.clone();
3128 let id_b = cloned.transaction_id().expect("hash should succeed");
3129 assert_eq!(id_a, id_b);
3130 assert_eq!(id_a.len(), 32);
3131 }
3132
3133 #[test]
3135 fn test_serialize_roundtrip_identity_update() {
3136 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3137 let original = sample_identity_update_st();
3138 let bytes =
3139 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3140 let restored =
3141 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
3142 assert_eq!(original, restored);
3143 }
3144
3145 #[test]
3146 fn test_serialize_roundtrip_data_contract_update() {
3147 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3148 let original = sample_data_contract_update_st();
3149 let bytes =
3150 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3151 let restored =
3152 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
3153 assert_eq!(original, restored);
3154 }
3155
3156 #[test]
3157 fn test_serialize_roundtrip_batch_empty() {
3158 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3159 let original = sample_batch_st_empty();
3160 let bytes =
3161 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3162 let restored =
3163 StateTransition::deserialize_from_bytes(&bytes).expect("deserialize should succeed");
3164 assert_eq!(original, restored);
3165 }
3166
3167 #[cfg(all(feature = "state-transitions", feature = "validation"))]
3174 #[test]
3175 fn test_deserialize_from_bytes_in_version_returns_not_active_error() {
3176 use crate::serialization::PlatformSerializable;
3177
3178 let original = sample_shielded_transfer_st();
3180 let bytes =
3181 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
3182
3183 let low_version = PlatformVersion::get(1).expect("platform version 1 exists");
3187 assert!(
3188 low_version.protocol_version < 12,
3189 "expected sub-12 version for this test, got {}",
3190 low_version.protocol_version
3191 );
3192
3193 let err = StateTransition::deserialize_from_bytes_in_version(&bytes, low_version)
3194 .expect_err("expected StateTransitionIsNotActiveError for sub-12 protocol");
3195 match err {
3196 ProtocolError::StateTransitionError(
3197 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
3198 state_transition_type,
3199 active_version_range,
3200 current_protocol_version,
3201 },
3202 ) => {
3203 assert_eq!(state_transition_type, "ShieldedTransfer");
3204 assert_eq!(current_protocol_version, low_version.protocol_version);
3205 assert!(active_version_range.start() >= &12);
3206 }
3207 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
3208 }
3209 }
3210
3211 use crate::state_transition::identity_credit_transfer_to_addresses_transition::v0::IdentityCreditTransferToAddressesTransitionV0;
3224 use crate::state_transition::identity_credit_transfer_to_addresses_transition::IdentityCreditTransferToAddressesTransition;
3225 use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1;
3226 use crate::withdrawal::Pooling as WithdrawalPooling;
3227
3228 fn sample_withdrawal_v1_st() -> StateTransition {
3229 let v1 = IdentityCreditWithdrawalTransitionV1 {
3230 identity_id: Identifier::from([12u8; 32]),
3231 amount: 777,
3232 core_fee_per_byte: 2,
3233 pooling: WithdrawalPooling::Standard,
3234 output_script: None,
3235 nonce: 9,
3236 user_fee_increase: 4,
3237 signature_public_key_id: 21,
3238 signature: BinaryData::new(vec![0x12; 65]),
3239 };
3240 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(v1))
3241 }
3242
3243 fn sample_credit_transfer_to_addresses_st() -> StateTransition {
3244 let v0 = IdentityCreditTransferToAddressesTransitionV0 {
3245 identity_id: Identifier::from([13u8; 32]),
3246 ..Default::default()
3247 };
3248 StateTransition::IdentityCreditTransferToAddresses(
3249 IdentityCreditTransferToAddressesTransition::V0(v0),
3250 )
3251 }
3252
3253 fn sample_address_credit_withdrawal_st() -> StateTransition {
3254 use crate::state_transition::address_credit_withdrawal_transition::v0::AddressCreditWithdrawalTransitionV0;
3255 StateTransition::AddressCreditWithdrawal(AddressCreditWithdrawalTransition::V0(
3256 AddressCreditWithdrawalTransitionV0::default(),
3257 ))
3258 }
3259
3260 fn sample_shield_from_asset_lock_st() -> StateTransition {
3261 use crate::state_transition::shield_from_asset_lock_transition::v0::ShieldFromAssetLockTransitionV0;
3262 StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V0(
3263 ShieldFromAssetLockTransitionV0 {
3264 asset_lock_proof: Default::default(),
3265 actions: vec![],
3266 value_balance: 100,
3267 anchor: [0u8; 32],
3268 proof: vec![],
3269 binding_signature: [0u8; 64],
3270 surplus_output: None,
3271 signature: BinaryData::new(vec![0x55; 65]),
3272 },
3273 ))
3274 }
3275
3276 #[test]
3279 fn test_withdrawal_v1_name_and_type() {
3280 let st = sample_withdrawal_v1_st();
3281 assert_eq!(st.name(), "IdentityCreditWithdrawal");
3282 assert_eq!(
3283 st.state_transition_type(),
3284 StateTransitionType::IdentityCreditWithdrawal
3285 );
3286 }
3287
3288 #[test]
3289 fn test_withdrawal_v1_is_identity_signed_true() {
3290 assert!(sample_withdrawal_v1_st().is_identity_signed());
3291 }
3292
3293 #[test]
3294 fn test_withdrawal_v1_signature_and_owner_and_key_id() {
3295 let st = sample_withdrawal_v1_st();
3296 let sig = st.signature().expect("V1 withdrawal has a signature");
3298 assert_eq!(sig.as_slice(), &[0x12; 65]);
3299 assert_eq!(st.owner_id(), Some(Identifier::from([12u8; 32])));
3301 assert_eq!(st.signature_public_key_id(), Some(21));
3302 assert_eq!(st.user_fee_increase(), 4);
3303 }
3304
3305 #[test]
3306 fn test_withdrawal_v1_set_signature_and_fee_and_key_id() {
3307 let mut st = sample_withdrawal_v1_st();
3308 assert!(st.set_signature(BinaryData::new(vec![0x99; 65])));
3309 assert_eq!(st.signature().unwrap().as_slice(), &[0x99; 65]);
3310
3311 st.set_user_fee_increase(33);
3312 assert_eq!(st.user_fee_increase(), 33);
3313
3314 st.set_signature_public_key_id(64);
3315 assert_eq!(st.signature_public_key_id(), Some(64));
3316 }
3317
3318 #[test]
3319 fn test_withdrawal_v1_serialize_roundtrip_via_state_transition() {
3320 use crate::serialization::{PlatformDeserializable, PlatformSerializable};
3321 let original = sample_withdrawal_v1_st();
3322 let bytes = PlatformSerializable::serialize_to_bytes(&original).expect("serialize ok");
3323 let restored = StateTransition::deserialize_from_bytes(&bytes).expect("deserialize ok");
3324 assert_eq!(original, restored);
3325 match restored {
3328 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(
3329 _,
3330 )) => {}
3331 other => panic!("expected V1 inner variant, got: {:?}", other),
3332 }
3333 }
3334
3335 #[test]
3336 fn test_withdrawal_v1_transaction_id_differs_from_v0() {
3337 let v0 = sample_withdrawal_st();
3339 let v1 = sample_withdrawal_v1_st();
3340 let id_v0 = v0.transaction_id().expect("v0 hash");
3341 let id_v1 = v1.transaction_id().expect("v1 hash");
3342 assert_ne!(id_v0, id_v1);
3343 }
3344
3345 #[test]
3346 fn test_withdrawal_v1_required_asset_lock_balance_errors() {
3347 let err = sample_withdrawal_v1_st()
3348 .required_asset_lock_balance_for_processing_start(PlatformVersion::latest())
3349 .expect_err("withdrawal is not an asset lock ST");
3350 matches!(err, ProtocolError::CorruptedCodeExecution(_));
3351 }
3352
3353 #[test]
3356 fn test_credit_transfer_to_addresses_name_and_type() {
3357 let st = sample_credit_transfer_to_addresses_st();
3358 assert_eq!(st.name(), "IdentityCreditTransferToAddresses");
3359 assert_eq!(
3360 st.state_transition_type(),
3361 StateTransitionType::IdentityCreditTransferToAddresses
3362 );
3363 }
3364
3365 #[test]
3366 fn test_credit_transfer_to_addresses_signature_some_and_owner_some() {
3367 let st = sample_credit_transfer_to_addresses_st();
3368 assert!(st.signature().is_some(), "has a signature field");
3369 assert_eq!(st.owner_id(), Some(Identifier::from([13u8; 32])));
3370 }
3371
3372 #[test]
3373 fn test_credit_transfer_to_addresses_is_identity_signed_true() {
3374 assert!(sample_credit_transfer_to_addresses_st().is_identity_signed());
3376 }
3377
3378 #[test]
3379 fn test_credit_transfer_to_addresses_inputs_none_active_range_11_latest() {
3380 let st = sample_credit_transfer_to_addresses_st();
3381 assert!(st.inputs().is_none());
3382 let range = st.active_version_range();
3384 assert_eq!(*range.start(), 11);
3385 assert_eq!(*range.end(), LATEST_VERSION);
3386 }
3387
3388 #[test]
3389 fn test_credit_transfer_to_addresses_set_signature_returns_true() {
3390 let mut st = sample_credit_transfer_to_addresses_st();
3391 let ok = st.set_signature(BinaryData::new(vec![0x77; 65]));
3392 assert!(ok);
3393 assert_eq!(st.signature().unwrap().as_slice(), &[0x77; 65]);
3394 }
3395
3396 #[test]
3397 fn test_credit_transfer_to_addresses_from_outer_enum() {
3398 let outer = IdentityCreditTransferToAddressesTransition::V0(
3399 IdentityCreditTransferToAddressesTransitionV0::default(),
3400 );
3401 let st: StateTransition = outer.into();
3402 assert!(matches!(
3403 st,
3404 StateTransition::IdentityCreditTransferToAddresses(_)
3405 ));
3406 }
3407
3408 #[test]
3409 fn test_credit_transfer_to_addresses_user_fee_increase_setter() {
3410 let mut st = sample_credit_transfer_to_addresses_st();
3411 st.set_user_fee_increase(55);
3412 assert_eq!(st.user_fee_increase(), 55);
3413 }
3414
3415 #[test]
3418 fn test_address_credit_withdrawal_name_type_and_accessors() {
3419 let st = sample_address_credit_withdrawal_st();
3420 assert_eq!(st.name(), "AddressCreditWithdrawal");
3421 assert_eq!(
3422 st.state_transition_type(),
3423 StateTransitionType::AddressCreditWithdrawal
3424 );
3425 assert!(st.signature().is_none());
3427 assert!(st.owner_id().is_none());
3429 assert!(st.inputs().is_some());
3431 }
3432
3433 #[test]
3434 fn test_address_credit_withdrawal_set_signature_returns_false() {
3435 let mut st = sample_address_credit_withdrawal_st();
3436 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3437 }
3438
3439 #[test]
3440 fn test_address_credit_withdrawal_is_identity_signed_true() {
3441 assert!(sample_address_credit_withdrawal_st().is_identity_signed());
3444 }
3445
3446 #[test]
3447 fn test_address_credit_withdrawal_active_range_is_11_latest() {
3448 let range = sample_address_credit_withdrawal_st().active_version_range();
3449 assert_eq!(*range.start(), 11);
3450 assert_eq!(*range.end(), LATEST_VERSION);
3451 }
3452
3453 #[test]
3456 fn test_shield_from_asset_lock_name_type_and_accessors() {
3457 let st = sample_shield_from_asset_lock_st();
3458 assert_eq!(st.name(), "ShieldFromAssetLock");
3459 assert_eq!(
3460 st.state_transition_type(),
3461 StateTransitionType::ShieldFromAssetLock
3462 );
3463 let sig = st
3465 .signature()
3466 .expect("shield-from-asset-lock has signature");
3467 assert_eq!(sig.as_slice(), &[0x55; 65]);
3468 assert!(st.owner_id().is_none());
3470 }
3471
3472 #[test]
3473 fn test_shield_from_asset_lock_is_not_identity_signed() {
3474 assert!(!sample_shield_from_asset_lock_st().is_identity_signed());
3475 }
3476
3477 #[test]
3478 fn test_shield_from_asset_lock_optional_asset_lock_proof_some() {
3479 let st = sample_shield_from_asset_lock_st();
3483 assert!(st.optional_asset_lock_proof().is_some());
3484 }
3485
3486 #[test]
3487 fn test_shield_from_asset_lock_user_fee_increase_is_zero_and_setter_noop() {
3488 let mut st = sample_shield_from_asset_lock_st();
3489 assert_eq!(st.user_fee_increase(), 0);
3490 st.set_user_fee_increase(123);
3491 assert_eq!(st.user_fee_increase(), 0);
3493 }
3494
3495 #[test]
3496 fn test_shield_from_asset_lock_set_signature_returns_true() {
3497 let mut st = sample_shield_from_asset_lock_st();
3498 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
3499 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
3500 }
3501
3502 #[test]
3503 fn test_shield_from_asset_lock_required_asset_lock_balance_succeeds() {
3504 let st = sample_shield_from_asset_lock_st();
3507 let result = st.required_asset_lock_balance_for_processing_start(PlatformVersion::latest());
3508 assert!(
3509 result.is_ok(),
3510 "ShieldFromAssetLock should return Ok, got {:?}",
3511 result
3512 );
3513 }
3514
3515 #[test]
3516 fn test_shield_from_asset_lock_active_range_12_latest() {
3517 let range = sample_shield_from_asset_lock_st().active_version_range();
3518 assert_eq!(*range.start(), 12);
3519 assert_eq!(*range.end(), LATEST_VERSION);
3520 }
3521
3522 #[test]
3526 fn test_batch_with_token_transfer_name_contains_token_transfer() {
3527 use crate::state_transition::batch_transition::batched_transition::token_transition::TokenTransition as TT;
3528 use crate::state_transition::batch_transition::batched_transition::BatchedTransition;
3529 use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0;
3530 use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition;
3531 use crate::state_transition::batch_transition::token_transfer_transition::v0::TokenTransferTransitionV0;
3532 use crate::state_transition::batch_transition::token_transfer_transition::TokenTransferTransition;
3533 use crate::state_transition::batch_transition::BatchTransitionV1;
3534
3535 let base = TokenBaseTransition::V0(TokenBaseTransitionV0 {
3536 identity_contract_nonce: 1,
3537 token_contract_position: 0,
3538 data_contract_id: Identifier::from([1u8; 32]),
3539 token_id: Identifier::from([2u8; 32]),
3540 using_group_info: None,
3541 });
3542 let token_transfer = TokenTransferTransition::V0(TokenTransferTransitionV0 {
3543 base,
3544 amount: 100,
3545 recipient_id: Identifier::from([3u8; 32]),
3546 public_note: None,
3547 shared_encrypted_note: None,
3548 private_encrypted_note: None,
3549 });
3550
3551 let batch = BatchTransition::V1(BatchTransitionV1 {
3553 owner_id: Identifier::from([9u8; 32]),
3554 transitions: vec![BatchedTransition::Token(TT::Transfer(token_transfer))],
3555 user_fee_increase: 0,
3556 signature_public_key_id: 0,
3557 signature: BinaryData::new(vec![0u8; 65]),
3558 });
3559 let st = StateTransition::Batch(batch);
3560
3561 assert_eq!(st.name(), "DocumentsBatch([TokenTransfer])");
3562 }
3563
3564 #[test]
3571 fn test_transaction_id_length_32_for_new_variants() {
3572 for st in [
3573 sample_withdrawal_v1_st(),
3574 sample_credit_transfer_to_addresses_st(),
3575 sample_shield_from_asset_lock_st(),
3576 ] {
3577 let id = st.transaction_id().expect("hash");
3578 assert_eq!(id.len(), 32);
3579 }
3580 }
3581
3582 #[test]
3588 fn test_clone_eq_for_new_variants() {
3589 let cases = [
3590 sample_withdrawal_v1_st(),
3591 sample_credit_transfer_to_addresses_st(),
3592 sample_address_credit_withdrawal_st(),
3593 sample_shield_from_asset_lock_st(),
3594 ];
3595 for st in cases {
3596 let cloned = st.clone();
3597 assert_eq!(st, cloned, "clone must be equal for {}", st.name());
3598 }
3599 }
3600
3601 #[test]
3607 fn test_unique_identifiers_for_address_and_shielded_variants() {
3608 for st in [
3613 sample_address_credit_withdrawal_st(),
3614 sample_shield_from_asset_lock_st(),
3615 sample_credit_transfer_to_addresses_st(),
3616 sample_withdrawal_v1_st(),
3617 ] {
3618 let _ids = st.unique_identifiers();
3622 }
3623 assert!(!sample_withdrawal_v1_st().unique_identifiers().is_empty());
3625 assert!(!sample_credit_transfer_to_addresses_st()
3626 .unique_identifiers()
3627 .is_empty());
3628 }
3629
3630 #[cfg(all(
3642 feature = "state-transition-signing",
3643 feature = "core_key_wallet",
3644 feature = "bls-signatures"
3645 ))]
3646 #[tokio::test]
3647 async fn sign_with_core_signer_matches_sign_by_private_key_byte_for_byte() {
3648 use async_trait::async_trait;
3649 use dashcore::secp256k1::{
3650 ecdsa, rand::rngs::OsRng, Message, PublicKey, Secp256k1, SecretKey,
3651 };
3652 use key_wallet::bip32::{DerivationPath, ExtendedPubKey};
3653 use key_wallet::signer::{ExtendedPubKeySigner, Signer as KwSigner, SignerMethod};
3654
3655 #[derive(Debug)]
3660 struct FixedKeySigner {
3661 secret: SecretKey,
3662 public: PublicKey,
3663 }
3664
3665 #[async_trait]
3666 impl KwSigner for FixedKeySigner {
3667 type Error = String;
3668
3669 fn supported_methods(&self) -> &[SignerMethod] {
3670 &[SignerMethod::Digest]
3671 }
3672
3673 async fn sign_ecdsa(
3674 &self,
3675 _path: &DerivationPath,
3676 sighash: [u8; 32],
3677 ) -> Result<(ecdsa::Signature, PublicKey), Self::Error> {
3678 let secp = Secp256k1::new();
3679 let msg = Message::from_digest(sighash);
3680 let sig = secp.sign_ecdsa(&msg, &self.secret);
3681 Ok((sig, self.public))
3682 }
3683
3684 async fn public_key(&self, _path: &DerivationPath) -> Result<PublicKey, Self::Error> {
3685 Ok(self.public)
3686 }
3687 }
3688
3689 #[async_trait]
3690 impl ExtendedPubKeySigner for FixedKeySigner {
3691 async fn extended_public_key(
3692 &self,
3693 _path: &DerivationPath,
3694 ) -> Result<ExtendedPubKey, Self::Error> {
3695 Err("FixedKeySigner does not derive extended public keys".to_string())
3696 }
3697 }
3698
3699 let secp = Secp256k1::new();
3704 let (secret_key, public_key) = secp.generate_keypair(&mut OsRng);
3705 let private_key_bytes = secret_key.secret_bytes();
3706
3707 let signer = FixedKeySigner {
3708 secret: secret_key,
3709 public: public_key,
3710 };
3711 let path = DerivationPath::default();
3712
3713 let mut st_legacy = sample_transfer_st();
3717 let mut st_signer = sample_transfer_st();
3718
3719 assert_eq!(
3721 st_legacy.signable_bytes().expect("legacy signable_bytes"),
3722 st_signer.signable_bytes().expect("signer signable_bytes"),
3723 "signable_bytes pre-image must match across copies"
3724 );
3725
3726 let bls = crate::bls::native_bls::NativeBlsModule;
3731 st_legacy
3732 .sign_by_private_key(&private_key_bytes, KeyType::ECDSA_HASH160, &bls)
3733 .expect("sign_by_private_key");
3734
3735 st_signer
3738 .sign_with_core_signer(&path, &signer)
3739 .await
3740 .expect("sign_with_core_signer");
3741
3742 let sig_legacy = st_legacy.signature().expect("legacy signature set");
3743 let sig_signer = st_signer.signature().expect("signer signature set");
3744
3745 assert_eq!(
3746 sig_legacy.as_slice().len(),
3747 65,
3748 "legacy ECDSA signature must be 65 bytes (recoverable compact)"
3749 );
3750 assert_eq!(
3751 sig_signer.as_slice().len(),
3752 65,
3753 "signer ECDSA signature must be 65 bytes (recoverable compact)"
3754 );
3755 assert_eq!(
3756 sig_legacy.as_slice(),
3757 sig_signer.as_slice(),
3758 "sign_with_core_signer must produce byte-identical output to sign_by_private_key"
3759 );
3760 }
3761}