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, DecodeUntrusted, 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::{
20 PlatformDeserializeTrusted, PlatformDeserializeUntrusted, PlatformSerialize, PlatformSignable,
21};
22use platform_version::version::{PlatformVersion, ProtocolVersion, ALL_VERSIONS, LATEST_VERSION};
23
24#[cfg(any(
25 feature = "state-transition-signing",
26 feature = "state-transition-validation"
27))]
28use crate::BlsModule;
29use crate::ProtocolError;
30
31mod state_transition_types;
32
33pub mod state_transition_factory;
34
35pub mod errors;
36#[cfg(feature = "state-transition-signing")]
37use crate::util::hash::ripemd160_sha256;
38use crate::util::hash::{hash_double_to_vec, hash_single};
39
40pub mod proof_result;
41mod serialization;
42pub mod state_transitions;
43mod traits;
44
45#[cfg(feature = "state-transition-validation")]
48use crate::consensus::basic::UnsupportedFeatureError;
49#[cfg(feature = "state-transition-signing")]
50use crate::consensus::signature::InvalidSignaturePublicKeySecurityLevelError;
51#[cfg(feature = "state-transition-signing")]
52use crate::consensus::signature::{
53 ContractBoundedKeyNonBatchError, ContractBoundedKeyOutOfBoundsError,
54};
55#[cfg(feature = "state-transition-validation")]
56use crate::consensus::signature::{
57 InvalidStateTransitionSignatureError, PublicKeyIsDisabledError, SignatureError,
58};
59#[cfg(feature = "state-transition-validation")]
60use crate::consensus::ConsensusError;
61pub use traits::*;
62
63use crate::address_funds::PlatformAddress;
64use crate::data_contract::serialized_version::DataContractInSerializationFormat;
65use crate::fee::Credits;
66#[cfg(any(
67 feature = "state-transition-signing",
68 feature = "state-transition-validation"
69))]
70use crate::identity::identity_public_key::accessors::v0::IdentityPublicKeyGettersV0;
71#[cfg(feature = "state-transition-signing")]
72use crate::identity::identity_public_key::contract_bounds::BatchedTransitionBoundsCheck;
73#[cfg(feature = "state-transition-signing")]
74use crate::identity::signer::Signer;
75use crate::identity::state_transition::OptionallyAssetLockProved;
76use crate::identity::Purpose;
77#[cfg(any(
78 feature = "state-transition-signing",
79 feature = "state-transition-validation"
80))]
81use crate::identity::{IdentityPublicKey, KeyType};
82use crate::identity::{KeyID, SecurityLevel};
83use crate::prelude::{AddressNonce, AssetLockProof, UserFeeIncrease};
84use crate::serialization::{PlatformDeserializableUntrusted, Signable};
85use crate::state_transition::address_credit_withdrawal_transition::{
86 AddressCreditWithdrawalTransition, AddressCreditWithdrawalTransitionSignable,
87};
88use crate::state_transition::address_funding_from_asset_lock_transition::{
89 AddressFundingFromAssetLockTransition, AddressFundingFromAssetLockTransitionSignable,
90};
91use crate::state_transition::address_funds_transfer_transition::{
92 AddressFundsTransferTransition, AddressFundsTransferTransitionSignable,
93};
94use crate::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0;
95use crate::state_transition::batch_transition::batched_transition::BatchedTransitionRef;
96#[cfg(feature = "state-transition-signing")]
97use crate::state_transition::batch_transition::resolvers::v0::BatchTransitionResolversV0;
98use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionSignable};
99use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0;
100use crate::state_transition::data_contract_create_transition::{
101 DataContractCreateTransition, DataContractCreateTransitionSignable,
102};
103use crate::state_transition::data_contract_update_transition::accessors::DataContractUpdateTransitionAccessorsV0;
104use crate::state_transition::data_contract_update_transition::{
105 DataContractUpdateTransition, DataContractUpdateTransitionSignable,
106};
107#[cfg(feature = "state-transition-signing")]
108use crate::state_transition::errors::InvalidSignaturePublicKeyError;
109#[cfg(all(feature = "state-transitions", feature = "validation"))]
110use crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError;
111#[cfg(feature = "state-transition-signing")]
112use crate::state_transition::errors::WrongPublicKeyPurposeError;
113#[cfg(feature = "state-transition-validation")]
114use crate::state_transition::errors::{
115 InvalidIdentityPublicKeyTypeError, PublicKeyMismatchError, StateTransitionIsNotSignedError,
116};
117use crate::state_transition::identity_create_from_addresses_transition::accessors::IdentityCreateFromAddressesTransitionAccessorsV0;
118use crate::state_transition::identity_create_from_addresses_transition::{
119 IdentityCreateFromAddressesTransition, IdentityCreateFromAddressesTransitionSignable,
120};
121use crate::state_transition::identity_create_from_shielded_pool_transition::accessors::IdentityCreateFromShieldedPoolTransitionAccessorsV0;
122use crate::state_transition::identity_create_from_shielded_pool_transition::{
123 IdentityCreateFromShieldedPoolTransition, IdentityCreateFromShieldedPoolTransitionSignable,
124};
125use crate::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0;
126use crate::state_transition::identity_create_transition::{
127 IdentityCreateTransition, IdentityCreateTransitionSignable,
128};
129use crate::state_transition::identity_credit_transfer_to_addresses_transition::{
130 IdentityCreditTransferToAddressesTransition,
131 IdentityCreditTransferToAddressesTransitionSignable,
132};
133use crate::state_transition::identity_credit_transfer_transition::{
134 IdentityCreditTransferTransition, IdentityCreditTransferTransitionSignable,
135};
136use crate::state_transition::identity_credit_withdrawal_transition::{
137 IdentityCreditWithdrawalTransition, IdentityCreditWithdrawalTransitionSignable,
138};
139use crate::state_transition::identity_key_limits_update_transition::{
140 IdentityKeyLimitsUpdateTransition, IdentityKeyLimitsUpdateTransitionSignable,
141};
142use crate::state_transition::identity_top_up_from_shielded_pool_transition::{
143 IdentityTopUpFromShieldedPoolTransition, IdentityTopUpFromShieldedPoolTransitionSignable,
144};
145use crate::state_transition::identity_topup_from_addresses_transition::{
146 IdentityTopUpFromAddressesTransition, IdentityTopUpFromAddressesTransitionSignable,
147};
148use crate::state_transition::identity_topup_transition::{
149 IdentityTopUpTransition, IdentityTopUpTransitionSignable,
150};
151use crate::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0;
152use crate::state_transition::identity_update_transition::{
153 IdentityUpdateTransition, IdentityUpdateTransitionSignable,
154};
155use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
156use crate::state_transition::masternode_vote_transition::MasternodeVoteTransitionSignable;
157use crate::state_transition::public_key_in_creation::IdentityPublicKeyInCreation;
158use crate::state_transition::shield_from_asset_lock_transition::{
159 ShieldFromAssetLockTransition, ShieldFromAssetLockTransitionSignable,
160};
161use crate::state_transition::shield_from_identity_transition::{
162 ShieldFromIdentityTransition, ShieldFromIdentityTransitionSignable,
163};
164use crate::state_transition::shield_transition::{ShieldTransition, ShieldTransitionSignable};
165use crate::state_transition::shielded_transfer_transition::{
166 ShieldedTransferTransition, ShieldedTransferTransitionSignable,
167};
168use crate::state_transition::shielded_withdrawal_transition::{
169 ShieldedWithdrawalTransition, ShieldedWithdrawalTransitionSignable,
170};
171#[cfg(feature = "state-transition-signing")]
172use crate::state_transition::state_transitions::document::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0;
173use crate::state_transition::unshield_transition::{
174 UnshieldTransition, UnshieldTransitionSignable,
175};
176use state_transitions::document::batch_transition::batched_transition::token_transition::TokenTransition;
177pub use state_transitions::*;
178
179pub type GetDataContractSecurityLevelRequirementFn =
180 fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>;
181
182macro_rules! call_method {
183 ($state_transition:expr, $method:ident, $args:tt ) => {
184 match $state_transition {
185 StateTransition::DataContractCreate(st) => st.$method($args),
186 StateTransition::DataContractUpdate(st) => st.$method($args),
187 StateTransition::Batch(st) => st.$method($args),
188 StateTransition::IdentityCreate(st) => st.$method($args),
189 StateTransition::IdentityTopUp(st) => st.$method($args),
190 StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
191 StateTransition::IdentityUpdate(st) => st.$method($args),
192 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method($args),
193 StateTransition::IdentityCreditTransfer(st) => st.$method($args),
194 StateTransition::MasternodeVote(st) => st.$method($args),
195 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
196 StateTransition::IdentityCreateFromAddresses(st) => st.$method($args),
197 StateTransition::IdentityTopUpFromAddresses(st) => st.$method($args),
198 StateTransition::AddressFundsTransfer(st) => st.$method($args),
199 StateTransition::AddressFundingFromAssetLock(st) => st.$method($args),
200 StateTransition::AddressCreditWithdrawal(st) => st.$method($args),
201 StateTransition::Shield(st) => st.$method($args),
202 StateTransition::ShieldedTransfer(st) => st.$method($args),
203 StateTransition::Unshield(st) => st.$method($args),
204 StateTransition::IdentityTopUpFromShieldedPool(st) => st.$method($args),
205 StateTransition::ShieldFromAssetLock(st) => st.$method($args),
206 StateTransition::ShieldedWithdrawal(st) => st.$method($args),
207 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method($args),
208 StateTransition::ShieldFromIdentity(st) => st.$method($args),
209 }
210 };
211 ($state_transition:expr, $method:ident ) => {
212 match $state_transition {
213 StateTransition::DataContractCreate(st) => st.$method(),
214 StateTransition::DataContractUpdate(st) => st.$method(),
215 StateTransition::Batch(st) => st.$method(),
216 StateTransition::IdentityCreate(st) => st.$method(),
217 StateTransition::IdentityTopUp(st) => st.$method(),
218 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
219 StateTransition::IdentityUpdate(st) => st.$method(),
220 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method(),
221 StateTransition::IdentityCreditTransfer(st) => st.$method(),
222 StateTransition::MasternodeVote(st) => st.$method(),
223 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
224 StateTransition::IdentityCreateFromAddresses(st) => st.$method(),
225 StateTransition::IdentityTopUpFromAddresses(st) => st.$method(),
226 StateTransition::AddressFundsTransfer(st) => st.$method(),
227 StateTransition::AddressFundingFromAssetLock(st) => st.$method(),
228 StateTransition::AddressCreditWithdrawal(st) => st.$method(),
229 StateTransition::Shield(st) => st.$method(),
230 StateTransition::ShieldedTransfer(st) => st.$method(),
231 StateTransition::Unshield(st) => st.$method(),
232 StateTransition::IdentityTopUpFromShieldedPool(st) => st.$method(),
233 StateTransition::ShieldFromAssetLock(st) => st.$method(),
234 StateTransition::ShieldedWithdrawal(st) => st.$method(),
235 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method(),
236 StateTransition::ShieldFromIdentity(st) => st.$method(),
237 }
238 };
239}
240
241macro_rules! call_getter_method_identity_signed {
242 ($state_transition:expr, $method:ident, $args:tt ) => {
243 match $state_transition {
244 StateTransition::DataContractCreate(st) => Some(st.$method($args)),
245 StateTransition::DataContractUpdate(st) => Some(st.$method($args)),
246 StateTransition::Batch(st) => Some(st.$method($args)),
247 StateTransition::IdentityCreate(_) => None,
248 StateTransition::IdentityTopUp(_) => None,
249 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method($args)),
250 StateTransition::IdentityUpdate(st) => Some(st.$method($args)),
251 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.$method($args)),
252 StateTransition::IdentityCreditTransfer(st) => Some(st.$method($args)),
253 StateTransition::MasternodeVote(st) => Some(st.$method($args)),
254 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method($args)),
255 StateTransition::IdentityCreateFromAddresses(_) => None,
256 StateTransition::IdentityTopUpFromAddresses(_) => None,
257 StateTransition::AddressFundsTransfer(_) => None,
258 StateTransition::AddressFundingFromAssetLock(_) => None,
259 StateTransition::AddressCreditWithdrawal(_) => None,
260 StateTransition::Shield(_) => None,
261 StateTransition::ShieldedTransfer(_) => None,
262 StateTransition::Unshield(_) => None,
263 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
264 StateTransition::ShieldFromAssetLock(_) => None,
265 StateTransition::ShieldedWithdrawal(_) => None,
266 StateTransition::IdentityCreateFromShieldedPool(_) => None,
267 StateTransition::ShieldFromIdentity(st) => Some(st.$method($args)),
268 }
269 };
270 ($state_transition:expr, $method:ident ) => {
271 match $state_transition {
272 StateTransition::DataContractCreate(st) => Some(st.$method()),
273 StateTransition::DataContractUpdate(st) => Some(st.$method()),
274 StateTransition::Batch(st) => Some(st.$method()),
275 StateTransition::IdentityCreate(_) => None,
276 StateTransition::IdentityTopUp(_) => None,
277 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method()),
278 StateTransition::IdentityUpdate(st) => Some(st.$method()),
279 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.$method()),
280 StateTransition::IdentityCreditTransfer(st) => Some(st.$method()),
281 StateTransition::MasternodeVote(st) => Some(st.$method()),
282 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method()),
283 StateTransition::IdentityCreateFromAddresses(_) => None,
284 StateTransition::IdentityTopUpFromAddresses(_) => None,
285 StateTransition::AddressFundsTransfer(_) => None,
286 StateTransition::AddressFundingFromAssetLock(_) => None,
287 StateTransition::AddressCreditWithdrawal(_) => None,
288 StateTransition::Shield(_) => None,
289 StateTransition::ShieldedTransfer(_) => None,
290 StateTransition::Unshield(_) => None,
291 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
292 StateTransition::ShieldFromAssetLock(_) => None,
293 StateTransition::ShieldedWithdrawal(_) => None,
294 StateTransition::IdentityCreateFromShieldedPool(_) => None,
295 StateTransition::ShieldFromIdentity(st) => Some(st.$method()),
296 }
297 };
298}
299
300macro_rules! call_method_identity_signed {
301 ($state_transition:expr, $method:ident, $args:tt ) => {
302 match $state_transition {
303 StateTransition::DataContractCreate(st) => st.$method($args),
304 StateTransition::DataContractUpdate(st) => st.$method($args),
305 StateTransition::Batch(st) => st.$method($args),
306 StateTransition::IdentityCreate(_st) => {}
307 StateTransition::IdentityTopUp(_st) => {}
308 StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
309 StateTransition::IdentityUpdate(st) => st.$method($args),
310 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method($args),
311 StateTransition::IdentityCreditTransfer(st) => st.$method($args),
312 StateTransition::MasternodeVote(st) => st.$method($args),
313 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
314 StateTransition::IdentityCreateFromAddresses(_) => {}
315 StateTransition::IdentityTopUpFromAddresses(_) => {}
316 StateTransition::AddressFundsTransfer(_) => {}
317 StateTransition::AddressFundingFromAssetLock(_) => {}
318 StateTransition::AddressCreditWithdrawal(_) => {}
319 StateTransition::Shield(_) => {}
320 StateTransition::ShieldedTransfer(_) => {}
321 StateTransition::Unshield(_) => {}
322 StateTransition::IdentityTopUpFromShieldedPool(_) => {}
323 StateTransition::ShieldFromAssetLock(_) => {}
324 StateTransition::ShieldedWithdrawal(_) => {}
325 StateTransition::IdentityCreateFromShieldedPool(_) => {}
326 StateTransition::ShieldFromIdentity(st) => st.$method($args),
327 }
328 };
329 ($state_transition:expr, $method:ident ) => {
330 match $state_transition {
331 StateTransition::DataContractCreate(st) => st.$method(),
332 StateTransition::DataContractUpdate(st) => st.$method(),
333 StateTransition::Batch(st) => st.$method(),
334 StateTransition::IdentityCreate(st) => {}
335 StateTransition::IdentityTopUp(st) => {}
336 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
337 StateTransition::IdentityUpdate(st) => st.$method(),
338 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method(),
339 StateTransition::IdentityCreditTransfer(st) => st.$method(),
340 StateTransition::MasternodeVote(st) => st.$method(),
341 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
342 StateTransition::IdentityCreateFromAddresses(_) => {}
343 StateTransition::IdentityTopUpFromAddresses(_) => {}
344 StateTransition::AddressFundsTransfer(_) => {}
345 StateTransition::AddressFundingFromAssetLock(_) => {}
346 StateTransition::AddressCreditWithdrawal(_) => {}
347 StateTransition::Shield(_) => {}
348 StateTransition::ShieldedTransfer(_) => {}
349 StateTransition::Unshield(_) => {}
350 StateTransition::IdentityTopUpFromShieldedPool(_) => {}
351 StateTransition::ShieldFromAssetLock(_) => {}
352 StateTransition::ShieldedWithdrawal(_) => {}
353 StateTransition::IdentityCreateFromShieldedPool(_) => {}
354 StateTransition::ShieldFromIdentity(st) => st.$method(),
355 }
356 };
357}
358
359#[cfg(feature = "state-transition-signing")]
360macro_rules! call_errorable_method_identity_signed {
361 ($state_transition:expr, $method:ident, $( $arg:expr ),* ) => {
362 match $state_transition {
363 StateTransition::DataContractCreate(st) => st.$method($( $arg ),*),
364 StateTransition::DataContractUpdate(st) => st.$method($( $arg ),*),
365 StateTransition::Batch(st) => st.$method($( $arg ),*),
366 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
367 "identity create can not be called for identity signing".to_string(),
368 )),
369 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
370 "identity top up can not be called for identity signing".to_string(),
371 )),
372 StateTransition::IdentityCreditWithdrawal(st) => st.$method($( $arg ),*),
373 StateTransition::IdentityUpdate(st) => st.$method($( $arg ),*),
374 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method($( $arg ),*),
375 StateTransition::IdentityCreditTransfer(st) => st.$method($( $arg ),*),
376 StateTransition::MasternodeVote(st) => st.$method($( $arg ),*),
377 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($( $arg ),*),
378 StateTransition::IdentityCreateFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
379 "identity create from addresses can not be called for identity signing".to_string(),
380 )),
381 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
382 "identity top up from addresses can not be called for identity signing".to_string(),
383 )),
384 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
385 "address funds transfer can not be called for identity signing".to_string(),
386 )),
387 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
388 "address funding from asset lock can not be called for identity signing".to_string(),
389 )),
390 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
391 "address credit withdrawal can not be called for identity signing".to_string(),
392 )),
393 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
394 "shield transition can not be called for identity signing".to_string(),
395 )),
396 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
397 "shielded transfer transition can not be called for identity signing".to_string(),
398 )),
399 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
400 "unshield transition can not be called for identity signing".to_string(),
401 )),
402 StateTransition::IdentityTopUpFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
403 "identity top up from shielded pool transition can not be called for identity signing".to_string(),
404 )),
405 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
406 "shield from asset lock transition can not be called for identity signing".to_string(),
407 )),
408 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
409 "shielded withdrawal transition can not be called for identity signing".to_string(),
410 )),
411 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
412 "identity create from shielded pool transition can not be called for identity signing".to_string(),
413 )),
414 StateTransition::ShieldFromIdentity(st) => st.$method($( $arg ),*),
415 }
416 };
417 ($state_transition:expr, $method:ident) => {
418 match $state_transition {
419 StateTransition::DataContractCreate(st) => st.$method(),
420 StateTransition::DataContractUpdate(st) => st.$method(),
421 StateTransition::Batch(st) => st.$method(),
422 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
423 "identity create can not be called for identity signing".to_string(),
424 )),
425 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
426 "identity top up can not be called for identity signing".to_string(),
427 )),
428 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
429 StateTransition::IdentityUpdate(st) => st.$method(),
430 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method(),
431 StateTransition::IdentityCreditTransfer(st) => st.$method(),
432 StateTransition::MasternodeVote(st) => st.$method(),
433 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
434 StateTransition::IdentityCreateFromAddresses(st) => Err(ProtocolError::CorruptedCodeExecution(
435 "identity create from addresses can not be called for identity signing".to_string(),
436 )),
437 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
438 "identity top up from addresses can not be called for identity signing".to_string(),
439 )),
440 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
441 "address funds transfer can not be called for identity signing".to_string(),
442 )),
443 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
444 "address funding from asset lock can not be called for identity signing".to_string(),
445 )),
446 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
447 "address credit withdrawal can not be called for identity signing".to_string(),
448 )),
449 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
450 "shield transition can not be called for identity signing".to_string(),
451 )),
452 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
453 "shielded transfer transition can not be called for identity signing".to_string(),
454 )),
455 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
456 "unshield transition can not be called for identity signing".to_string(),
457 )),
458 StateTransition::IdentityTopUpFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
459 "identity top up from shielded pool transition can not be called for identity signing".to_string(),
460 )),
461 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
462 "shield from asset lock transition can not be called for identity signing".to_string(),
463 )),
464 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
465 "shielded withdrawal transition can not be called for identity signing".to_string(),
466 )),
467 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
468 "identity create from shielded pool transition can not be called for identity signing".to_string(),
469 )),
470 StateTransition::ShieldFromIdentity(st) => st.$method(),
471 }
472 };
473}
474
475#[derive(
476 Debug,
477 Clone,
478 Encode,
479 Decode,
480 PlatformSerialize,
481 PlatformDeserializeTrusted,
482 PlatformDeserializeUntrusted,
483 PlatformSignable,
484 From,
485 PartialEq,
486 DecodeUntrusted,
487)]
488#[cfg_attr(
508 feature = "serde-conversion",
509 derive(Serialize, Deserialize),
510 serde(tag = "$type", rename_all = "camelCase")
511)]
512#[platform_serialize(unversioned)] #[platform_serialize(limit = 100000)]
514pub enum StateTransition {
515 DataContractCreate(DataContractCreateTransition),
516 DataContractUpdate(DataContractUpdateTransition),
517 Batch(BatchTransition),
518 IdentityCreate(IdentityCreateTransition),
519 IdentityTopUp(IdentityTopUpTransition),
520 IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition),
521 IdentityUpdate(IdentityUpdateTransition),
522 IdentityCreditTransfer(IdentityCreditTransferTransition),
523 MasternodeVote(MasternodeVoteTransition),
524 IdentityCreditTransferToAddresses(IdentityCreditTransferToAddressesTransition),
525 IdentityCreateFromAddresses(IdentityCreateFromAddressesTransition),
526 IdentityTopUpFromAddresses(IdentityTopUpFromAddressesTransition),
527 AddressFundsTransfer(AddressFundsTransferTransition),
528 AddressFundingFromAssetLock(AddressFundingFromAssetLockTransition),
529 AddressCreditWithdrawal(AddressCreditWithdrawalTransition),
530 Shield(ShieldTransition),
531 ShieldedTransfer(ShieldedTransferTransition),
532 Unshield(UnshieldTransition),
533 ShieldFromAssetLock(ShieldFromAssetLockTransition),
534 ShieldedWithdrawal(ShieldedWithdrawalTransition),
535 IdentityCreateFromShieldedPool(IdentityCreateFromShieldedPoolTransition),
536 ShieldFromIdentity(ShieldFromIdentityTransition),
537 IdentityTopUpFromShieldedPool(IdentityTopUpFromShieldedPoolTransition),
538 IdentityKeyLimitsUpdate(IdentityKeyLimitsUpdateTransition),
539}
540
541#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
542impl crate::serialization::JsonConvertible for StateTransition {}
543
544#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
545impl crate::serialization::ValueConvertible for StateTransition {}
546
547#[cfg(all(
548 test,
549 feature = "json-conversion",
550 feature = "value-conversion",
551 feature = "serde-conversion"
552))]
553mod json_convertible_tests {
554 use super::*;
555
556 fn assert_umbrella_round_trip_inner(
575 original: StateTransition,
576 expected_type_tag: &str,
577 lossy_json_int_variants: bool,
578 ) {
579 use crate::serialization::{JsonConvertible, ValueConvertible};
580
581 let json = original.to_json().expect("to_json");
583 assert_eq!(
584 json["$type"], expected_type_tag,
585 "json type tag for {expected_type_tag}",
586 );
587 let recovered = StateTransition::from_json(json).expect("from_json round-trip");
588 assert_eq!(
589 std::mem::discriminant(&original),
590 std::mem::discriminant(&recovered),
591 "json round-trip variant for {expected_type_tag}",
592 );
593 if lossy_json_int_variants {
594 use crate::tests::utils::normalize_integer_variants_for_json_round_trip;
595 let mut original_canon = original.to_object().expect("to_object");
596 let mut recovered_canon = recovered.to_object().expect("to_object");
597 normalize_integer_variants_for_json_round_trip(&mut original_canon);
598 normalize_integer_variants_for_json_round_trip(&mut recovered_canon);
599 assert_eq!(
600 original_canon, recovered_canon,
601 "json round-trip equality (modulo int-variant) for {expected_type_tag}",
602 );
603 } else {
604 assert_eq!(
605 original, recovered,
606 "json round-trip equality for {expected_type_tag}"
607 );
608 }
609
610 let value = original.to_object().expect("to_object");
612 let map = value.as_map().expect("Value::Map");
613 let tag = map
614 .iter()
615 .find(|(k, _)| k.as_text() == Some("$type"))
616 .map(|(_, v)| v)
617 .unwrap_or_else(|| panic!("type tag missing for {expected_type_tag}"));
618 assert_eq!(
619 *tag,
620 platform_value::Value::Text(expected_type_tag.to_string()),
621 "value type tag for {expected_type_tag}",
622 );
623 let recovered = StateTransition::from_object(value).expect("from_object round-trip");
624 assert_eq!(
625 std::mem::discriminant(&original),
626 std::mem::discriminant(&recovered),
627 "value round-trip variant for {expected_type_tag}",
628 );
629 assert_eq!(
630 original, recovered,
631 "value round-trip equality for {expected_type_tag}"
632 );
633 }
634
635 fn assert_umbrella_round_trip(original: StateTransition, expected_type_tag: &str) {
636 assert_umbrella_round_trip_inner(original, expected_type_tag, false);
637 }
638
639 fn assert_umbrella_round_trip_lossy_json_int_variants(
646 original: StateTransition,
647 expected_type_tag: &str,
648 ) {
649 assert_umbrella_round_trip_inner(original, expected_type_tag, true);
650 }
651
652 #[test]
658 fn umbrella_data_contract_create() {
659 let inner = crate::state_transition::data_contract_create_transition::json_convertible_tests::fixture();
660 assert_umbrella_round_trip_lossy_json_int_variants(
661 StateTransition::DataContractCreate(inner),
662 "dataContractCreate",
663 );
664 }
665
666 #[test]
667 fn umbrella_data_contract_update() {
668 let inner = crate::state_transition::data_contract_update_transition::json_convertible_tests::fixture();
669 assert_umbrella_round_trip_lossy_json_int_variants(
670 StateTransition::DataContractUpdate(inner),
671 "dataContractUpdate",
672 );
673 }
674
675 #[test]
676 fn umbrella_batch() {
677 let inner = crate::state_transition::batch_transition::json_convertible_tests::fixture();
678 assert_umbrella_round_trip(StateTransition::Batch(inner), "batch");
679 }
680
681 #[test]
682 fn umbrella_identity_create() {
683 let inner =
684 crate::state_transition::identity_create_transition::json_convertible_tests::fixture();
685 assert_umbrella_round_trip(StateTransition::IdentityCreate(inner), "identityCreate");
686 }
687
688 #[test]
689 fn umbrella_identity_top_up() {
690 let inner =
691 crate::state_transition::identity_topup_transition::json_convertible_tests::fixture();
692 assert_umbrella_round_trip(StateTransition::IdentityTopUp(inner), "identityTopUp");
693 }
694
695 #[test]
696 fn umbrella_identity_credit_withdrawal() {
697 let inner = crate::state_transition::identity_credit_withdrawal_transition::json_convertible_tests::fixture();
698 assert_umbrella_round_trip(
699 StateTransition::IdentityCreditWithdrawal(inner),
700 "identityCreditWithdrawal",
701 );
702 }
703
704 #[test]
705 fn umbrella_identity_update() {
706 let inner =
707 crate::state_transition::identity_update_transition::json_convertible_tests::fixture();
708 assert_umbrella_round_trip(StateTransition::IdentityUpdate(inner), "identityUpdate");
709 }
710
711 #[test]
712 fn umbrella_identity_key_limits_update() {
713 let inner = crate::state_transition::identity_key_limits_update_transition::json_convertible_tests::fixture();
714 assert_umbrella_round_trip(
715 StateTransition::IdentityKeyLimitsUpdate(inner),
716 "identityKeyLimitsUpdate",
717 );
718 }
719
720 #[test]
721 fn umbrella_identity_credit_transfer() {
722 let inner = crate::state_transition::identity_credit_transfer_transition::json_convertible_tests::fixture();
723 assert_umbrella_round_trip(
724 StateTransition::IdentityCreditTransfer(inner),
725 "identityCreditTransfer",
726 );
727 }
728
729 #[test]
730 fn umbrella_masternode_vote() {
731 let inner =
732 crate::state_transition::masternode_vote_transition::json_convertible_tests::fixture();
733 assert_umbrella_round_trip(StateTransition::MasternodeVote(inner), "masternodeVote");
734 }
735
736 #[test]
737 fn umbrella_identity_credit_transfer_to_addresses() {
738 let inner = crate::state_transition::identity_credit_transfer_to_addresses_transition::json_convertible_tests::fixture();
739 assert_umbrella_round_trip(
740 StateTransition::IdentityCreditTransferToAddresses(inner),
741 "identityCreditTransferToAddresses",
742 );
743 }
744
745 #[test]
746 fn umbrella_identity_create_from_addresses() {
747 let inner = crate::state_transition::identity_create_from_addresses_transition::json_convertible_tests::fixture();
748 assert_umbrella_round_trip(
749 StateTransition::IdentityCreateFromAddresses(inner),
750 "identityCreateFromAddresses",
751 );
752 }
753
754 #[test]
755 fn umbrella_identity_top_up_from_addresses() {
756 let inner = crate::state_transition::identity_topup_from_addresses_transition::json_convertible_tests::fixture();
757 assert_umbrella_round_trip(
758 StateTransition::IdentityTopUpFromAddresses(inner),
759 "identityTopUpFromAddresses",
760 );
761 }
762
763 #[test]
764 fn umbrella_address_funds_transfer() {
765 let inner = crate::state_transition::address_funds_transfer_transition::json_convertible_tests::fixture();
766 assert_umbrella_round_trip(
767 StateTransition::AddressFundsTransfer(inner),
768 "addressFundsTransfer",
769 );
770 }
771
772 #[test]
773 fn umbrella_address_funding_from_asset_lock() {
774 let inner = crate::state_transition::address_funding_from_asset_lock_transition::json_convertible_tests::fixture();
775 assert_umbrella_round_trip(
776 StateTransition::AddressFundingFromAssetLock(inner),
777 "addressFundingFromAssetLock",
778 );
779 }
780
781 #[test]
782 fn umbrella_address_credit_withdrawal() {
783 let inner = crate::state_transition::address_credit_withdrawal_transition::json_convertible_tests::fixture();
784 assert_umbrella_round_trip(
785 StateTransition::AddressCreditWithdrawal(inner),
786 "addressCreditWithdrawal",
787 );
788 }
789
790 #[test]
791 fn umbrella_shield() {
792 let inner = crate::state_transition::shield_transition::json_convertible_tests::fixture();
793 assert_umbrella_round_trip(StateTransition::Shield(inner), "shield");
794 }
795
796 #[test]
797 fn umbrella_shielded_transfer() {
798 let inner =
799 crate::state_transition::shielded_transfer_transition::json_convertible_tests::fixture(
800 );
801 assert_umbrella_round_trip(StateTransition::ShieldedTransfer(inner), "shieldedTransfer");
802 }
803
804 #[test]
805 fn umbrella_unshield() {
806 let inner = crate::state_transition::unshield_transition::json_convertible_tests::fixture();
807 assert_umbrella_round_trip(StateTransition::Unshield(inner), "unshield");
808 }
809
810 #[test]
811 fn umbrella_shield_from_asset_lock() {
812 let inner = crate::state_transition::shield_from_asset_lock_transition::json_convertible_tests::fixture();
813 assert_umbrella_round_trip(
814 StateTransition::ShieldFromAssetLock(inner),
815 "shieldFromAssetLock",
816 );
817 }
818
819 #[test]
820 fn umbrella_shielded_withdrawal() {
821 let inner = crate::state_transition::shielded_withdrawal_transition::json_convertible_tests::fixture();
822 assert_umbrella_round_trip(
823 StateTransition::ShieldedWithdrawal(inner),
824 "shieldedWithdrawal",
825 );
826 }
827
828 #[test]
829 fn umbrella_identity_create_from_shielded_pool() {
830 let inner = crate::state_transition::identity_create_from_shielded_pool_transition::json_convertible_tests::fixture();
831 assert_umbrella_round_trip(
832 StateTransition::IdentityCreateFromShieldedPool(inner),
833 "identityCreateFromShieldedPool",
834 );
835 }
836
837 #[test]
838 fn umbrella_identity_top_up_from_shielded_pool() {
839 let inner = crate::state_transition::identity_top_up_from_shielded_pool_transition::json_convertible_tests::fixture();
840 assert_umbrella_round_trip(
841 StateTransition::IdentityTopUpFromShieldedPool(inner),
842 "identityTopUpFromShieldedPool",
843 );
844 }
845
846 #[test]
847 fn umbrella_shield_from_identity() {
848 let inner = crate::state_transition::shield_from_identity_transition::json_convertible_tests::fixture();
849 assert_umbrella_round_trip(
850 StateTransition::ShieldFromIdentity(inner),
851 "shieldFromIdentity",
852 );
853 }
854}
855
856impl OptionallyAssetLockProved for StateTransition {
857 fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> {
858 match self {
859 StateTransition::IdentityCreate(st) => st.optional_asset_lock_proof(),
860 StateTransition::IdentityTopUp(st) => st.optional_asset_lock_proof(),
861 StateTransition::ShieldFromAssetLock(st) => st.optional_asset_lock_proof(),
862 _ => None,
863 }
864 }
865}
866
867#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
869pub struct StateTransitionSigningOptions {
870 pub allow_signing_with_any_security_level: bool,
872 pub allow_signing_with_any_purpose: bool,
874}
875
876fn active_version_range_for_keys_in_creation(
880 keys: &[IdentityPublicKeyInCreation],
881 otherwise: RangeInclusive<ProtocolVersion>,
882) -> RangeInclusive<ProtocolVersion> {
883 if IdentityPublicKeyInCreation::first_bound_to_a_contract_group(keys).is_some()
884 || IdentityPublicKeyInCreation::first_in_version_1_format(keys).is_some()
885 {
886 14..=LATEST_VERSION
887 } else {
888 otherwise
889 }
890}
891
892impl StateTransition {
893 #[allow(unused_variables)]
894 pub fn deserialize_from_bytes_untrusted_in_version(
895 bytes: &[u8],
896 platform_version: &PlatformVersion,
897 ) -> Result<Self, ProtocolError> {
898 let max_value_depth = platform_version
899 .system_limits
900 .max_document_value_depth
901 .map(usize::from);
902 let state_transition =
903 platform_value::with_value_decode_depth_limit(max_value_depth, || {
904 StateTransition::deserialize_from_bytes_untrusted(bytes)
905 })?;
906 #[cfg(all(feature = "state-transitions", feature = "validation"))]
907 {
908 let active_version_range = state_transition.active_version_range();
909
910 if active_version_range.contains(&platform_version.protocol_version)
913 || platform_version.protocol_version > 268435456
914 {
915 Ok(state_transition)
916 } else {
917 Err(ProtocolError::StateTransitionError(
918 StateTransitionIsNotActiveError {
919 state_transition_type: state_transition.name(),
920 active_version_range,
921 current_protocol_version: platform_version.protocol_version,
922 },
923 ))
924 }
925 }
926 #[cfg(not(all(feature = "state-transitions", feature = "validation")))]
927 Ok(state_transition)
928 }
929
930 pub fn active_version_range(&self) -> RangeInclusive<ProtocolVersion> {
931 match self {
932 StateTransition::DataContractCreate(data_contract_create_transition) => {
933 match data_contract_create_transition {
934 DataContractCreateTransition::V1(_) => 14..=LATEST_VERSION,
938 DataContractCreateTransition::V0(_) => {
939 match data_contract_create_transition.data_contract() {
940 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
941 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
942 }
943 }
944 }
945 }
946 StateTransition::DataContractUpdate(data_contract_update_transition) => {
947 match data_contract_update_transition.data_contract() {
948 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
949 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
950 }
951 }
952 StateTransition::Batch(batch_transition) => match batch_transition {
953 BatchTransition::V0(_) => ALL_VERSIONS,
954 BatchTransition::V1(_) => 9..=LATEST_VERSION,
955 },
956 StateTransition::IdentityCreate(st) => {
960 active_version_range_for_keys_in_creation(st.public_keys(), ALL_VERSIONS)
961 }
962 StateTransition::IdentityUpdate(st) => {
963 active_version_range_for_keys_in_creation(st.public_keys_to_add(), ALL_VERSIONS)
964 }
965 StateTransition::IdentityCreateFromAddresses(st) => {
966 active_version_range_for_keys_in_creation(st.public_keys(), 11..=LATEST_VERSION)
967 }
968 StateTransition::IdentityCreateFromShieldedPool(st) => {
969 active_version_range_for_keys_in_creation(st.public_keys(), 12..=LATEST_VERSION)
970 }
971 StateTransition::IdentityTopUp(_)
972 | StateTransition::IdentityCreditWithdrawal(_)
973 | StateTransition::IdentityCreditTransfer(_)
974 | StateTransition::MasternodeVote(_) => ALL_VERSIONS,
975 StateTransition::IdentityCreditTransferToAddresses(_)
976 | StateTransition::IdentityTopUpFromAddresses(_)
977 | StateTransition::AddressFundsTransfer(_)
978 | StateTransition::AddressFundingFromAssetLock(_)
979 | StateTransition::AddressCreditWithdrawal(_) => 11..=LATEST_VERSION,
980 StateTransition::Shield(_)
981 | StateTransition::ShieldedTransfer(_)
982 | StateTransition::Unshield(_)
983 | StateTransition::ShieldFromAssetLock(_)
984 | StateTransition::ShieldedWithdrawal(_) => 12..=LATEST_VERSION,
985 StateTransition::ShieldFromIdentity(_)
986 | StateTransition::IdentityTopUpFromShieldedPool(_)
987 | StateTransition::IdentityKeyLimitsUpdate(_) => 14..=LATEST_VERSION,
988 }
989 }
990
991 pub fn is_identity_signed(&self) -> bool {
992 !matches!(
993 self,
994 StateTransition::IdentityCreate(_)
995 | StateTransition::IdentityTopUp(_)
996 | StateTransition::Shield(_)
997 | StateTransition::ShieldedTransfer(_)
998 | StateTransition::Unshield(_)
999 | StateTransition::ShieldFromAssetLock(_)
1000 | StateTransition::ShieldedWithdrawal(_)
1001 | StateTransition::IdentityCreateFromShieldedPool(_)
1002 | StateTransition::IdentityTopUpFromShieldedPool(_)
1003 )
1004 }
1005
1006 pub fn required_asset_lock_balance_for_processing_start(
1007 &self,
1008 platform_version: &PlatformVersion,
1009 ) -> Result<Credits, ProtocolError> {
1010 match self {
1011 StateTransition::IdentityCreate(st) => {
1012 st.calculate_min_required_fee(platform_version)
1013 }
1014 StateTransition::IdentityTopUp(st) => {
1015 st.calculate_min_required_fee(platform_version)
1016 }
1017 StateTransition::AddressFundingFromAssetLock(st) => {
1018 st.calculate_min_required_fee(platform_version)
1019 }
1020 StateTransition::ShieldFromAssetLock(st) => {
1021 st.calculate_min_required_fee(platform_version)
1022 }
1023 st => Err(ProtocolError::CorruptedCodeExecution(format!("{} is not an asset lock transaction, but we are calling required_asset_lock_balance_for_processing_start", st.name()))),
1024 }
1025 }
1026
1027 fn hash(&self, skip_signature: bool) -> Result<Vec<u8>, ProtocolError> {
1028 if skip_signature {
1029 Ok(hash_double_to_vec(self.signable_bytes()?))
1030 } else {
1031 Ok(hash_double_to_vec(
1032 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
1033 ))
1034 }
1035 }
1036
1037 pub fn name(&self) -> String {
1039 match self {
1040 Self::DataContractCreate(_) => "DataContractCreate".to_string(),
1041 Self::DataContractUpdate(_) => "DataContractUpdate".to_string(),
1042 Self::Batch(batch_transition) => {
1043 let mut document_transition_types = vec![];
1044 for transition in batch_transition.transitions_iter() {
1045 let document_transition_name = match transition {
1046 BatchedTransitionRef::Document(DocumentTransition::Create(_)) => "Create",
1047 BatchedTransitionRef::Document(DocumentTransition::Replace(_)) => "Replace",
1048 BatchedTransitionRef::Document(DocumentTransition::Delete(_)) => "Delete",
1049 BatchedTransitionRef::Document(DocumentTransition::Transfer(_)) => {
1050 "Transfer"
1051 }
1052 BatchedTransitionRef::Document(DocumentTransition::UpdatePrice(_)) => {
1053 "UpdatePrice"
1054 }
1055 BatchedTransitionRef::Document(DocumentTransition::Purchase(_)) => {
1056 "Purchase"
1057 }
1058 BatchedTransitionRef::Document(DocumentTransition::IndexOnlyDelete(_)) => {
1059 "IndexOnlyDelete"
1060 }
1061 BatchedTransitionRef::Token(TokenTransition::Transfer(_)) => {
1062 "TokenTransfer"
1063 }
1064 BatchedTransitionRef::Token(TokenTransition::Mint(_)) => "TokenMint",
1065 BatchedTransitionRef::Token(TokenTransition::Burn(_)) => "TokenBurn",
1066 BatchedTransitionRef::Token(TokenTransition::Freeze(_)) => "TokenFreeze",
1067 BatchedTransitionRef::Token(TokenTransition::Unfreeze(_)) => {
1068 "TokenUnfreeze"
1069 }
1070 BatchedTransitionRef::Token(TokenTransition::DestroyFrozenFunds(_)) => {
1071 "TokenDestroyFrozenFunds"
1072 }
1073 BatchedTransitionRef::Token(TokenTransition::EmergencyAction(_)) => {
1074 "TokenEmergencyAction"
1075 }
1076 BatchedTransitionRef::Token(TokenTransition::ConfigUpdate(_)) => {
1077 "TokenConfigUpdate"
1078 }
1079 BatchedTransitionRef::Token(TokenTransition::Claim(_)) => "TokenClaim",
1080 BatchedTransitionRef::Token(TokenTransition::DirectPurchase(_)) => {
1081 "TokenDirectPurchase"
1082 }
1083 BatchedTransitionRef::Token(
1084 TokenTransition::SetPriceForDirectPurchase(_),
1085 ) => "SetPriceForDirectPurchase",
1086 };
1087 document_transition_types.push(document_transition_name);
1088 }
1089 format!("DocumentsBatch([{}])", document_transition_types.join(", "))
1090 }
1091 Self::IdentityCreate(_) => "IdentityCreate".to_string(),
1092 Self::IdentityTopUp(_) => "IdentityTopUp".to_string(),
1093 Self::IdentityCreditWithdrawal(_) => "IdentityCreditWithdrawal".to_string(),
1094 Self::IdentityUpdate(_) => "IdentityUpdate".to_string(),
1095 Self::IdentityKeyLimitsUpdate(_) => "IdentityKeyLimitsUpdate".to_string(),
1096 Self::IdentityCreditTransfer(_) => "IdentityCreditTransfer".to_string(),
1097 Self::MasternodeVote(_) => "MasternodeVote".to_string(),
1098 Self::IdentityCreditTransferToAddresses(_) => {
1099 "IdentityCreditTransferToAddresses".to_string()
1100 }
1101 Self::IdentityCreateFromAddresses(_) => "IdentityCreateFromAddresses".to_string(),
1102 Self::IdentityTopUpFromAddresses(_) => "IdentityTopUpFromAddresses".to_string(),
1103 Self::AddressFundsTransfer(_) => "AddressFundsTransfer".to_string(),
1104 Self::AddressFundingFromAssetLock(_) => "AddressFundingFromAssetLock".to_string(),
1105 Self::AddressCreditWithdrawal(_) => "AddressCreditWithdrawal".to_string(),
1106 Self::Shield(_) => "Shield".to_string(),
1107 Self::ShieldedTransfer(_) => "ShieldedTransfer".to_string(),
1108 Self::Unshield(_) => "Unshield".to_string(),
1109 Self::IdentityTopUpFromShieldedPool(_) => "IdentityTopUpFromShieldedPool".to_string(),
1110 Self::ShieldFromAssetLock(_) => "ShieldFromAssetLock".to_string(),
1111 Self::ShieldedWithdrawal(_) => "ShieldedWithdrawal".to_string(),
1112 Self::IdentityCreateFromShieldedPool(_) => "IdentityCreateFromShieldedPool".to_string(),
1113 Self::ShieldFromIdentity(_) => "ShieldFromIdentity".to_string(),
1114 }
1115 }
1116
1117 pub fn signature(&self) -> Option<&BinaryData> {
1119 match self {
1120 StateTransition::DataContractCreate(st) => Some(st.signature()),
1121 StateTransition::DataContractUpdate(st) => Some(st.signature()),
1122 StateTransition::Batch(st) => Some(st.signature()),
1123 StateTransition::IdentityCreate(st) => Some(st.signature()),
1124 StateTransition::IdentityTopUp(st) => Some(st.signature()),
1125 StateTransition::IdentityCreditWithdrawal(st) => Some(st.signature()),
1126 StateTransition::IdentityUpdate(st) => Some(st.signature()),
1127 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.signature()),
1128 StateTransition::IdentityCreditTransfer(st) => Some(st.signature()),
1129 StateTransition::MasternodeVote(st) => Some(st.signature()),
1130 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.signature()),
1131 StateTransition::IdentityCreateFromAddresses(_) => None,
1132 StateTransition::IdentityTopUpFromAddresses(_) => None,
1133 StateTransition::AddressFundsTransfer(_) => None,
1134 StateTransition::AddressFundingFromAssetLock(st) => Some(st.signature()),
1135 StateTransition::AddressCreditWithdrawal(_) => None,
1136 StateTransition::Shield(_) => None,
1137 StateTransition::ShieldedTransfer(_) => None,
1138 StateTransition::Unshield(_) => None,
1139 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
1140 StateTransition::ShieldFromAssetLock(st) => Some(st.signature()),
1141 StateTransition::ShieldedWithdrawal(_) => None,
1142 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1143 StateTransition::ShieldFromIdentity(st) => Some(st.signature()),
1144 }
1145 }
1146
1147 pub fn required_number_of_private_keys(&self) -> u16 {
1149 match self {
1150 StateTransition::IdentityCreateFromAddresses(st) => st.inputs().len() as u16,
1151 StateTransition::IdentityTopUpFromAddresses(st) => st.inputs().len() as u16,
1152 StateTransition::AddressFundsTransfer(st) => st.inputs().len() as u16,
1153 StateTransition::AddressCreditWithdrawal(st) => st.inputs().len() as u16,
1154 StateTransition::Shield(st) => st.inputs().len() as u16,
1155 StateTransition::ShieldedTransfer(_) => 0,
1156 StateTransition::Unshield(_) => 0,
1157 StateTransition::IdentityTopUpFromShieldedPool(_) => 0,
1158 StateTransition::ShieldFromAssetLock(_) => 0,
1159 StateTransition::ShieldedWithdrawal(_) => 0,
1160 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1161 _ => 1,
1162 }
1163 }
1164
1165 pub fn user_fee_increase(&self) -> UserFeeIncrease {
1167 match self {
1168 StateTransition::DataContractCreate(st) => st.user_fee_increase(),
1169 StateTransition::DataContractUpdate(st) => st.user_fee_increase(),
1170 StateTransition::Batch(st) => st.user_fee_increase(),
1171 StateTransition::IdentityCreate(st) => st.user_fee_increase(),
1172 StateTransition::IdentityTopUp(st) => st.user_fee_increase(),
1173 StateTransition::IdentityCreditWithdrawal(st) => st.user_fee_increase(),
1174 StateTransition::IdentityUpdate(st) => st.user_fee_increase(),
1175 StateTransition::IdentityKeyLimitsUpdate(st) => st.user_fee_increase(),
1176 StateTransition::IdentityCreditTransfer(st) => st.user_fee_increase(),
1177 StateTransition::IdentityCreditTransferToAddresses(st) => st.user_fee_increase(),
1178 StateTransition::IdentityCreateFromAddresses(st) => st.user_fee_increase(),
1179 StateTransition::IdentityTopUpFromAddresses(st) => st.user_fee_increase(),
1180 StateTransition::AddressFundsTransfer(st) => st.user_fee_increase(),
1181 StateTransition::AddressFundingFromAssetLock(st) => st.user_fee_increase(),
1182 StateTransition::AddressCreditWithdrawal(st) => st.user_fee_increase(),
1183 StateTransition::Shield(st) => st.user_fee_increase(),
1184 StateTransition::ShieldFromAssetLock(_) => 0,
1186 StateTransition::MasternodeVote(_) => 0,
1187 StateTransition::ShieldedTransfer(_) => 0,
1188 StateTransition::Unshield(_) => 0,
1189 StateTransition::IdentityTopUpFromShieldedPool(_) => 0,
1190 StateTransition::ShieldedWithdrawal(_) => 0,
1191 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1192 StateTransition::ShieldFromIdentity(st) => st.user_fee_increase(),
1193 }
1194 }
1195
1196 fn calculate_estimated_fee(
1209 &self,
1210 platform_version: &PlatformVersion,
1211 ) -> Result<Credits, ProtocolError> {
1212 call_method!(self, calculate_min_required_fee, platform_version)
1213 }
1214
1215 pub fn transaction_id(&self) -> Result<[u8; 32], ProtocolError> {
1217 Ok(hash_single(
1218 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
1219 ))
1220 }
1221
1222 pub fn signature_public_key_id(&self) -> Option<KeyID> {
1224 call_getter_method_identity_signed!(self, signature_public_key_id)
1225 }
1226
1227 pub fn security_level_requirement(&self, purpose: Purpose) -> Option<Vec<SecurityLevel>> {
1229 call_getter_method_identity_signed!(self, security_level_requirement, purpose)
1230 }
1231
1232 pub fn purpose_requirement(&self) -> Option<Vec<Purpose>> {
1234 call_getter_method_identity_signed!(self, purpose_requirement)
1235 }
1236
1237 pub fn owner_id(&self) -> Option<Identifier> {
1239 match self {
1240 StateTransition::DataContractCreate(st) => Some(st.owner_id()),
1241 StateTransition::DataContractUpdate(st) => Some(st.owner_id()),
1242 StateTransition::Batch(st) => Some(st.owner_id()),
1243 StateTransition::IdentityCreate(st) => Some(st.owner_id()),
1244 StateTransition::IdentityTopUp(st) => Some(st.owner_id()),
1245 StateTransition::IdentityCreditWithdrawal(st) => Some(st.owner_id()),
1246 StateTransition::IdentityUpdate(st) => Some(st.owner_id()),
1247 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.owner_id()),
1248 StateTransition::IdentityCreditTransfer(st) => Some(st.owner_id()),
1249 StateTransition::MasternodeVote(st) => Some(st.owner_id()),
1250 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.owner_id()),
1251 StateTransition::IdentityCreateFromAddresses(_) => None,
1252 StateTransition::IdentityTopUpFromAddresses(_) => None,
1253 StateTransition::AddressFundsTransfer(_) => None,
1254 StateTransition::AddressFundingFromAssetLock(_) => None,
1255 StateTransition::AddressCreditWithdrawal(_) => None,
1256 StateTransition::Shield(_) => None,
1257 StateTransition::ShieldedTransfer(_) => None,
1258 StateTransition::Unshield(_) => None,
1259 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
1260 StateTransition::ShieldFromAssetLock(_) => None,
1261 StateTransition::ShieldedWithdrawal(_) => None,
1262 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1263 StateTransition::ShieldFromIdentity(st) => Some(st.owner_id()),
1264 }
1265 }
1266
1267 pub fn inputs(&self) -> Option<&BTreeMap<PlatformAddress, (AddressNonce, Credits)>> {
1269 match self {
1270 StateTransition::DataContractCreate(_)
1271 | StateTransition::DataContractUpdate(_)
1272 | StateTransition::Batch(_)
1273 | StateTransition::IdentityCreate(_)
1274 | StateTransition::IdentityTopUp(_)
1275 | StateTransition::IdentityCreditWithdrawal(_)
1276 | StateTransition::IdentityUpdate(_)
1277 | StateTransition::IdentityKeyLimitsUpdate(_)
1278 | StateTransition::IdentityCreditTransfer(_)
1279 | StateTransition::MasternodeVote(_)
1280 | StateTransition::IdentityCreditTransferToAddresses(_) => None,
1281 StateTransition::IdentityCreateFromAddresses(st) => Some(st.inputs()),
1282 StateTransition::IdentityTopUpFromAddresses(st) => Some(st.inputs()),
1283 StateTransition::AddressFundsTransfer(st) => Some(st.inputs()),
1284 StateTransition::AddressFundingFromAssetLock(st) => Some(st.inputs()),
1285 StateTransition::AddressCreditWithdrawal(st) => Some(st.inputs()),
1286 StateTransition::Shield(st) => Some(st.inputs()),
1287 StateTransition::ShieldedTransfer(_) => None,
1288 StateTransition::Unshield(_) => None,
1289 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
1290 StateTransition::ShieldFromAssetLock(_) => None,
1291 StateTransition::ShieldedWithdrawal(_) => None,
1292 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1293 StateTransition::ShieldFromIdentity(_) => None,
1294 }
1295 }
1296
1297 pub fn state_transition_type(&self) -> StateTransitionType {
1299 call_method!(self, state_transition_type)
1300 }
1301
1302 pub fn unique_identifiers(&self) -> Vec<String> {
1304 call_method!(self, unique_identifiers)
1305 }
1306
1307 pub fn set_signature(&mut self, signature: BinaryData) -> bool {
1309 match self {
1310 StateTransition::DataContractCreate(st) => {
1311 st.set_signature(signature);
1312 true
1313 }
1314 StateTransition::DataContractUpdate(st) => {
1315 st.set_signature(signature);
1316 true
1317 }
1318 StateTransition::Batch(st) => {
1319 st.set_signature(signature);
1320 true
1321 }
1322 StateTransition::IdentityCreate(st) => {
1323 st.set_signature(signature);
1324 true
1325 }
1326 StateTransition::IdentityTopUp(st) => {
1327 st.set_signature(signature);
1328 true
1329 }
1330 StateTransition::IdentityCreditWithdrawal(st) => {
1331 st.set_signature(signature);
1332 true
1333 }
1334 StateTransition::IdentityUpdate(st) => {
1335 st.set_signature(signature);
1336 true
1337 }
1338 StateTransition::IdentityKeyLimitsUpdate(st) => {
1339 st.set_signature(signature);
1340 true
1341 }
1342 StateTransition::IdentityCreditTransfer(st) => {
1343 st.set_signature(signature);
1344 true
1345 }
1346 StateTransition::MasternodeVote(st) => {
1347 st.set_signature(signature);
1348 true
1349 }
1350 StateTransition::IdentityCreditTransferToAddresses(st) => {
1351 st.set_signature(signature);
1352 true
1353 }
1354 StateTransition::IdentityCreateFromAddresses(_)
1355 | StateTransition::IdentityTopUpFromAddresses(_)
1356 | StateTransition::AddressFundsTransfer(_)
1357 | StateTransition::Shield(_)
1358 | StateTransition::ShieldedTransfer(_)
1359 | StateTransition::Unshield(_)
1360 | StateTransition::ShieldedWithdrawal(_)
1361 | StateTransition::IdentityCreateFromShieldedPool(_)
1362 | StateTransition::IdentityTopUpFromShieldedPool(_) => false,
1363 StateTransition::ShieldFromIdentity(st) => {
1364 st.set_signature(signature);
1365 true
1366 }
1367 StateTransition::AddressFundingFromAssetLock(st) => {
1368 st.set_signature(signature);
1369 true
1370 }
1371 StateTransition::ShieldFromAssetLock(st) => {
1372 st.set_signature(signature);
1373 true
1374 }
1375 StateTransition::AddressCreditWithdrawal(_) => false,
1376 }
1377 }
1378
1379 pub fn set_user_fee_increase(&mut self, user_fee_increase: UserFeeIncrease) {
1381 match self {
1382 StateTransition::DataContractCreate(st) => st.set_user_fee_increase(user_fee_increase),
1383 StateTransition::DataContractUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1384 StateTransition::Batch(st) => st.set_user_fee_increase(user_fee_increase),
1385 StateTransition::IdentityCreate(st) => st.set_user_fee_increase(user_fee_increase),
1386 StateTransition::IdentityTopUp(st) => st.set_user_fee_increase(user_fee_increase),
1387 StateTransition::IdentityCreditWithdrawal(st) => {
1388 st.set_user_fee_increase(user_fee_increase)
1389 }
1390 StateTransition::IdentityUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1391 StateTransition::IdentityKeyLimitsUpdate(st) => {
1392 st.set_user_fee_increase(user_fee_increase)
1393 }
1394 StateTransition::IdentityCreditTransfer(st) => {
1395 st.set_user_fee_increase(user_fee_increase)
1396 }
1397 StateTransition::IdentityCreditTransferToAddresses(st) => {
1398 st.set_user_fee_increase(user_fee_increase)
1399 }
1400 StateTransition::IdentityCreateFromAddresses(st) => {
1401 st.set_user_fee_increase(user_fee_increase)
1402 }
1403 StateTransition::IdentityTopUpFromAddresses(st) => {
1404 st.set_user_fee_increase(user_fee_increase)
1405 }
1406 StateTransition::AddressFundsTransfer(st) => {
1407 st.set_user_fee_increase(user_fee_increase)
1408 }
1409 StateTransition::AddressFundingFromAssetLock(st) => {
1410 st.set_user_fee_increase(user_fee_increase)
1411 }
1412 StateTransition::AddressCreditWithdrawal(st) => {
1413 st.set_user_fee_increase(user_fee_increase)
1414 }
1415 StateTransition::Shield(st) => st.set_user_fee_increase(user_fee_increase),
1416 StateTransition::ShieldFromAssetLock(_) => {}
1418 StateTransition::MasternodeVote(_) => {}
1419 StateTransition::ShieldedTransfer(_) => {}
1420 StateTransition::Unshield(_) => {}
1421 StateTransition::IdentityTopUpFromShieldedPool(_) => {}
1422 StateTransition::ShieldedWithdrawal(_) => {}
1423 StateTransition::IdentityCreateFromShieldedPool(_) => {}
1424 StateTransition::ShieldFromIdentity(st) => st.set_user_fee_increase(user_fee_increase),
1425 }
1426 }
1427
1428 pub fn set_signature_public_key_id(&mut self, public_key_id: KeyID) {
1430 call_method_identity_signed!(self, set_signature_public_key_id, public_key_id)
1431 }
1432
1433 #[cfg(feature = "state-transition-signing")]
1434 pub async fn sign_external<S: Signer<IdentityPublicKey>>(
1435 &mut self,
1436 identity_public_key: &IdentityPublicKey,
1437 signer: &S,
1438 get_data_contract_security_level_requirement: Option<
1439 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1440 >,
1441 ) -> Result<(), ProtocolError> {
1442 self.sign_external_with_options(
1443 identity_public_key,
1444 signer,
1445 get_data_contract_security_level_requirement,
1446 StateTransitionSigningOptions::default(),
1447 )
1448 .await
1449 }
1450
1451 #[cfg(feature = "state-transition-signing")]
1452 pub async fn sign_external_with_options<S: Signer<IdentityPublicKey>>(
1453 &mut self,
1454 identity_public_key: &IdentityPublicKey,
1455 signer: &S,
1456 get_data_contract_security_level_requirement: Option<
1457 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1458 >,
1459 options: StateTransitionSigningOptions,
1460 ) -> Result<(), ProtocolError> {
1461 self.verify_identity_key_bounds(identity_public_key)?;
1462 match self {
1463 StateTransition::DataContractCreate(st) => {
1464 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1465 st.verify_public_key_is_enabled(identity_public_key)?;
1466 }
1467 StateTransition::DataContractUpdate(st) => {
1468 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1469 st.verify_public_key_is_enabled(identity_public_key)?;
1470 }
1471 StateTransition::Batch(st) => {
1472 let allow_token_transfer_keys = st.transitions_len() == 1
1473 && (st
1474 .first_transition()
1475 .expect("expected first transition with len 1")
1476 .as_transition_token_claim()
1477 .is_some()
1478 || st
1479 .first_transition()
1480 .expect("expected first transition with len 1")
1481 .as_transition_token_transfer()
1482 .is_some());
1483 let allowed_key_purposes = if allow_token_transfer_keys {
1484 vec![Purpose::AUTHENTICATION, Purpose::TRANSFER]
1485 } else {
1486 vec![Purpose::AUTHENTICATION]
1487 };
1488 if !options.allow_signing_with_any_purpose
1489 && !allowed_key_purposes.contains(&identity_public_key.purpose())
1490 {
1491 return Err(ProtocolError::WrongPublicKeyPurposeError(
1492 WrongPublicKeyPurposeError::new(
1493 identity_public_key.purpose(),
1494 allowed_key_purposes,
1495 ),
1496 ));
1497 }
1498 if !options.allow_signing_with_any_security_level {
1499 let security_level_requirement = st.combined_security_level_requirement(
1500 get_data_contract_security_level_requirement,
1501 )?;
1502 if !security_level_requirement.contains(&identity_public_key.security_level()) {
1503 return Err(ProtocolError::InvalidSignaturePublicKeySecurityLevelError(
1504 InvalidSignaturePublicKeySecurityLevelError::new(
1505 identity_public_key.security_level(),
1506 security_level_requirement,
1507 ),
1508 ));
1509 }
1510 }
1511 st.verify_public_key_is_enabled(identity_public_key)?;
1512 }
1513 StateTransition::IdentityCreditWithdrawal(st) => {
1514 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1515 st.verify_public_key_is_enabled(identity_public_key)?;
1516 }
1517 StateTransition::IdentityUpdate(st) => {
1518 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1519 st.verify_public_key_is_enabled(identity_public_key)?;
1520 }
1521 StateTransition::IdentityKeyLimitsUpdate(st) => {
1522 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1523 st.verify_public_key_is_enabled(identity_public_key)?;
1524 }
1525 StateTransition::IdentityCreditTransfer(st) => {
1526 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1527 st.verify_public_key_is_enabled(identity_public_key)?;
1528 }
1529 StateTransition::IdentityCreate(_) => {
1530 return Err(ProtocolError::CorruptedCodeExecution(
1531 "identity create can not be called for identity signing".to_string(),
1532 ))
1533 }
1534 StateTransition::IdentityTopUp(_) => {
1535 return Err(ProtocolError::CorruptedCodeExecution(
1536 "identity top up can not be called for identity signing".to_string(),
1537 ))
1538 }
1539 StateTransition::MasternodeVote(st) => {
1540 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1541 st.verify_public_key_is_enabled(identity_public_key)?;
1542 }
1543 StateTransition::IdentityCreditTransferToAddresses(st) => {
1544 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1545 st.verify_public_key_is_enabled(identity_public_key)?;
1546 }
1547 StateTransition::IdentityCreateFromAddresses(_) => {
1548 return Err(ProtocolError::CorruptedCodeExecution(
1549 "identity create from addresses can not be called for identity signing"
1550 .to_string(),
1551 ))
1552 }
1553 StateTransition::IdentityTopUpFromAddresses(_) => {
1554 return Err(ProtocolError::CorruptedCodeExecution(
1555 "identity top up from addresses can not be called for identity signing"
1556 .to_string(),
1557 ))
1558 }
1559 StateTransition::AddressFundsTransfer(_) => {
1560 return Err(ProtocolError::CorruptedCodeExecution(
1561 "address funds transfer transition can not be called for identity signing"
1562 .to_string(),
1563 ))
1564 }
1565 StateTransition::AddressFundingFromAssetLock(_) => {
1566 return Err(ProtocolError::CorruptedCodeExecution(
1567 "address funding from asset lock transition can not be called for identity signing"
1568 .to_string(),
1569 ))
1570 }
1571 StateTransition::AddressCreditWithdrawal(_) => {
1572 return Err(ProtocolError::CorruptedCodeExecution(
1573 "address credit withdrawal transition can not be called for identity signing"
1574 .to_string(),
1575 ))
1576 }
1577 StateTransition::Shield(_) => {
1578 return Err(ProtocolError::CorruptedCodeExecution(
1579 "shield transition can not be called for identity signing".to_string(),
1580 ))
1581 }
1582 StateTransition::ShieldedTransfer(_) => {
1583 return Err(ProtocolError::CorruptedCodeExecution(
1584 "shielded transfer transition can not be called for identity signing"
1585 .to_string(),
1586 ))
1587 }
1588 StateTransition::Unshield(_) => {
1589 return Err(ProtocolError::CorruptedCodeExecution(
1590 "unshield transition can not be called for identity signing".to_string(),
1591 ))
1592 }
1593 StateTransition::IdentityTopUpFromShieldedPool(_) => {
1594 return Err(ProtocolError::CorruptedCodeExecution(
1595 "identity top up from shielded pool transition can not be called for identity signing".to_string(),
1596 ))
1597 }
1598 StateTransition::ShieldFromAssetLock(_) => {
1599 return Err(ProtocolError::CorruptedCodeExecution(
1600 "shield from asset lock transition can not be called for identity signing"
1601 .to_string(),
1602 ))
1603 }
1604 StateTransition::ShieldedWithdrawal(_) => {
1605 return Err(ProtocolError::CorruptedCodeExecution(
1606 "shielded withdrawal transition can not be called for identity signing"
1607 .to_string(),
1608 ))
1609 }
1610 StateTransition::IdentityCreateFromShieldedPool(_) => {
1611 return Err(ProtocolError::CorruptedCodeExecution(
1612 "identity create from shielded pool transition can not be called for identity signing"
1613 .to_string(),
1614 ))
1615 }
1616 StateTransition::ShieldFromIdentity(st) => {
1617 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1618 st.verify_public_key_is_enabled(identity_public_key)?;
1619 }
1620 }
1621 let data = self.signable_bytes()?;
1622 self.set_signature(signer.sign(identity_public_key, data.as_slice()).await?);
1623 self.set_signature_public_key_id(identity_public_key.id());
1624 Ok(())
1625 }
1626
1627 #[cfg(feature = "state-transition-signing")]
1632 fn verify_identity_key_bounds(
1633 &self,
1634 identity_public_key: &IdentityPublicKey,
1635 ) -> Result<(), ProtocolError> {
1636 if identity_public_key.purpose() != Purpose::AUTHENTICATION {
1637 return Ok(());
1638 }
1639 let Some(bounds) = identity_public_key.contract_bounds() else {
1640 return Ok(());
1641 };
1642 match self {
1643 StateTransition::Batch(batch) => {
1644 use crate::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0;
1645 if batch.transitions_iter().all(|member| {
1646 bounds.check_batched_transition(member) != BatchedTransitionBoundsCheck::Denied
1647 }) {
1648 Ok(())
1649 } else {
1650 Err(ProtocolError::ConsensusError(Box::new(
1651 ContractBoundedKeyOutOfBoundsError::new(identity_public_key.id()).into(),
1652 )))
1653 }
1654 }
1655 _ => Err(ProtocolError::ConsensusError(Box::new(
1656 ContractBoundedKeyNonBatchError::new(identity_public_key.id()).into(),
1657 ))),
1658 }
1659 }
1660
1661 #[cfg(feature = "state-transition-signing")]
1662 pub fn sign(
1663 &mut self,
1664 identity_public_key: &IdentityPublicKey,
1665 private_key: &[u8],
1666 bls: &impl BlsModule,
1667 ) -> Result<(), ProtocolError> {
1668 self.sign_with_options(
1669 identity_public_key,
1670 private_key,
1671 bls,
1672 StateTransitionSigningOptions::default(),
1673 )
1674 }
1675
1676 #[cfg(feature = "state-transition-signing")]
1677 pub fn sign_with_options(
1678 &mut self,
1679 identity_public_key: &IdentityPublicKey,
1680 private_key: &[u8],
1681 bls: &impl BlsModule,
1682 options: StateTransitionSigningOptions,
1683 ) -> Result<(), ProtocolError> {
1684 self.verify_identity_key_bounds(identity_public_key)?;
1685 call_errorable_method_identity_signed!(
1686 self,
1687 verify_public_key_level_and_purpose,
1688 identity_public_key,
1689 options
1690 )?;
1691 call_errorable_method_identity_signed!(
1692 self,
1693 verify_public_key_is_enabled,
1694 identity_public_key
1695 )?;
1696
1697 match identity_public_key.key_type() {
1698 KeyType::ECDSA_SECP256K1 => {
1699 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1700
1701 if public_key_compressed.as_slice() != identity_public_key.data().as_slice() {
1706 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1707 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1708 ));
1709 }
1710
1711 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1712 }
1713 KeyType::ECDSA_HASH160 => {
1714 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
1715 let pub_key_hash = ripemd160_sha256(&public_key_compressed);
1716
1717 if identity_public_key.data().as_slice() != pub_key_hash {
1718 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1719 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1720 ));
1721 }
1722 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1723 }
1724 KeyType::BLS12_381 => {
1725 let public_key = bls.private_key_to_public_key(private_key)?;
1726
1727 if public_key != identity_public_key.data().as_slice() {
1728 return Err(ProtocolError::InvalidSignaturePublicKeyError(
1729 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
1730 ));
1731 }
1732 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
1733 }
1734
1735 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1739 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1740 InvalidIdentityPublicKeyTypeError::new(identity_public_key.key_type()),
1741 ))
1742 }
1743 }?;
1744
1745 self.set_signature_public_key_id(identity_public_key.id());
1746
1747 Ok(())
1748 }
1749
1750 #[cfg(feature = "state-transition-signing")]
1751 pub fn sign_by_private_key(
1753 &mut self,
1754 private_key: &[u8],
1755 key_type: KeyType,
1756 bls: &impl BlsModule,
1757 ) -> Result<(), ProtocolError> {
1758 let data = self.signable_bytes()?;
1759 match key_type {
1760 KeyType::BLS12_381 => {
1761 if !self.set_signature(bls.sign(&data, private_key)?.into()) {
1762 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1763 needed: self.required_number_of_private_keys(),
1764 using: 1,
1765 msg: "failed to set BLS signature",
1766 });
1767 }
1768 }
1769
1770 KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => {
1772 let signature = signer::sign(&data, private_key)?;
1773 if !self.set_signature(signature.to_vec().into()) {
1774 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1775 needed: self.required_number_of_private_keys(),
1776 using: 1,
1777 msg: "failed to set ECDSA signature",
1778 });
1779 };
1780 }
1781
1782 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1786 return Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1787 InvalidIdentityPublicKeyTypeError::new(key_type),
1788 ))
1789 }
1790 };
1791 Ok(())
1792 }
1793
1794 #[cfg(all(feature = "state-transition-signing", feature = "core_key_wallet"))]
1872 pub async fn sign_with_core_signer<S: ::key_wallet::signer::Signer>(
1873 &mut self,
1874 path: &::key_wallet::bip32::DerivationPath,
1875 signer: &S,
1876 ) -> Result<(), ProtocolError> {
1877 use dashcore::secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
1878 use dashcore::secp256k1::{Message, Secp256k1};
1879 use dashcore::signer::{double_sha, CompactSignature};
1880
1881 let data = self.signable_bytes()?;
1882 let data_hash = double_sha(&data);
1885 let digest: [u8; 32] = data_hash.as_slice().try_into().map_err(|_| {
1886 ProtocolError::Generic("double_sha did not return 32 bytes".to_string())
1887 })?;
1888
1889 let (signature, public_key) = signer
1890 .sign_ecdsa(path, digest)
1891 .await
1892 .map_err(|e| ProtocolError::ExternalSignerError(format!("signer failed: {}", e)))?;
1893
1894 let compact_64 = signature.serialize_compact();
1903 let secp = Secp256k1::new();
1904 let msg = Message::from_digest(digest);
1905
1906 let mut found: Option<RecoverableSignature> = None;
1907 for id in 0..4i32 {
1908 let recid = match RecoveryId::try_from(id) {
1909 Ok(r) => r,
1910 Err(_) => continue,
1911 };
1912 let candidate = match RecoverableSignature::from_compact(&compact_64, recid) {
1913 Ok(s) => s,
1914 Err(_) => continue,
1915 };
1916 if let Ok(recovered) = secp.recover_ecdsa(&msg, &candidate) {
1917 if recovered == public_key {
1918 found = Some(candidate);
1919 break;
1920 }
1921 }
1922 }
1923 let recoverable = found.ok_or_else(|| {
1924 ProtocolError::ExternalSignerError(
1930 "signer returned a signature whose recovery id does not match the returned public key".to_string(),
1931 )
1932 })?;
1933
1934 let compact_65 = recoverable.to_compact_signature(true);
1939
1940 if !self.set_signature(compact_65.to_vec().into()) {
1941 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
1942 needed: self.required_number_of_private_keys(),
1943 using: 1,
1944 msg: "failed to set ECDSA signature",
1945 });
1946 }
1947 Ok(())
1948 }
1949
1950 #[cfg(feature = "state-transition-validation")]
1951 fn verify_by_raw_public_key<T: BlsModule>(
1952 &self,
1953 public_key: &[u8],
1954 public_key_type: KeyType,
1955 bls: &T,
1956 ) -> Result<(), ProtocolError> {
1957 match public_key_type {
1958 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key),
1959 KeyType::ECDSA_HASH160 => {
1960 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key)
1961 }
1962 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key, bls),
1963 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
1964 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
1965 InvalidIdentityPublicKeyTypeError::new(public_key_type),
1966 ))
1967 }
1968 }
1969 }
1970
1971 #[cfg(feature = "state-transition-validation")]
1972 pub fn verify_identity_signed_signature(
1973 &self,
1974 public_key: &IdentityPublicKey,
1975 bls: &impl BlsModule,
1976 ) -> Result<(), ProtocolError> {
1977 if public_key.disabled_at().is_some() {
1979 return Err(ProtocolError::PublicKeyIsDisabledError(
1980 PublicKeyIsDisabledError::new(public_key.id()),
1981 ));
1982 }
1983
1984 let Some(signature) = self.signature() else {
1985 return Err(ProtocolError::CorruptedCodeExecution("verifying identity signature for a state transition that doesn't use identity signatures".to_string()));
1986 };
1987 if signature.is_empty() {
1988 return Err(ProtocolError::StateTransitionIsNotSignedError(
1989 StateTransitionIsNotSignedError::new(self.clone()),
1990 ));
1991 }
1992
1993 if self.signature_public_key_id() != Some(public_key.id()) {
1994 return Err(ProtocolError::PublicKeyMismatchError(
1995 PublicKeyMismatchError::new(public_key.clone()),
1996 ));
1997 }
1998
1999 let public_key_bytes = public_key.data().as_slice();
2000 match public_key.key_type() {
2001 KeyType::ECDSA_HASH160 => {
2002 self.verify_ecdsa_hash_160_signature_by_public_key_hash(public_key_bytes)
2003 }
2004
2005 KeyType::ECDSA_SECP256K1 => self.verify_ecdsa_signature_by_public_key(public_key_bytes),
2006
2007 KeyType::BLS12_381 => self.verify_bls_signature_by_public_key(public_key_bytes, bls),
2008
2009 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => Ok(()),
2011 }
2012 }
2013
2014 #[cfg(feature = "state-transition-validation")]
2015 fn verify_ecdsa_hash_160_signature_by_public_key_hash(
2016 &self,
2017 public_key_hash: &[u8],
2018 ) -> Result<(), ProtocolError> {
2019 let Some(signature) = self.signature() else {
2020 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2021 needed: self.required_number_of_private_keys(),
2022 using: 1,
2023 msg: "This state transition type should a single signature",
2024 });
2025 };
2026 if signature.is_empty() {
2027 return Err(ProtocolError::StateTransitionIsNotSignedError(
2028 StateTransitionIsNotSignedError::new(self.clone()),
2029 ));
2030 }
2031 let data = self.signable_bytes()?;
2032 let data_hash = double_sha(data);
2033 signer::verify_hash_signature(&data_hash, signature.as_slice(), public_key_hash).map_err(
2034 |e| {
2035 ProtocolError::from(ConsensusError::SignatureError(
2036 SignatureError::InvalidStateTransitionSignatureError(
2037 InvalidStateTransitionSignatureError::new(e.to_string()),
2038 ),
2039 ))
2040 },
2041 )
2042 }
2043
2044 #[cfg(feature = "state-transition-validation")]
2045 fn verify_ecdsa_signature_by_public_key(&self, public_key: &[u8]) -> Result<(), ProtocolError> {
2047 let Some(signature) = self.signature() else {
2048 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2049 needed: self.required_number_of_private_keys(),
2050 using: 1,
2051 msg: "This state transition type should a single signature",
2052 });
2053 };
2054 if signature.is_empty() {
2055 return Err(ProtocolError::StateTransitionIsNotSignedError(
2056 StateTransitionIsNotSignedError::new(self.clone()),
2057 ));
2058 }
2059 let data = self.signable_bytes()?;
2060 signer::verify_data_signature(&data, signature.as_slice(), public_key).map_err(|e| {
2061 ProtocolError::from(ConsensusError::SignatureError(
2064 SignatureError::InvalidStateTransitionSignatureError(
2065 InvalidStateTransitionSignatureError::new(e.to_string()),
2066 ),
2067 ))
2068 })
2069 }
2070
2071 #[cfg(feature = "state-transition-validation")]
2072 fn verify_bls_signature_by_public_key<T: BlsModule>(
2074 &self,
2075 public_key: &[u8],
2076 bls: &T,
2077 ) -> Result<(), ProtocolError> {
2078 let Some(signature) = self.signature() else {
2079 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2080 needed: self.required_number_of_private_keys(),
2081 using: 1,
2082 msg: "This state transition type should a single signature",
2083 });
2084 };
2085 if signature.is_empty() {
2086 return Err(ProtocolError::StateTransitionIsNotSignedError(
2087 StateTransitionIsNotSignedError::new(self.clone()),
2088 ));
2089 }
2090
2091 let data = self.signable_bytes()?;
2092
2093 bls.verify_signature(signature.as_slice(), &data, public_key)
2094 .map(|_| ())
2095 .map_err(|e| {
2096 ProtocolError::from(ConsensusError::SignatureError(
2098 SignatureError::InvalidStateTransitionSignatureError(
2099 InvalidStateTransitionSignatureError::new(e.to_string()),
2100 ),
2101 ))
2102 })
2103 }
2104}
2105
2106#[cfg(feature = "state-transition-validation")]
2107impl StateTransitionStructureValidation for StateTransition {
2108 fn validate_structure(
2109 &self,
2110 platform_version: &PlatformVersion,
2111 ) -> crate::validation::SimpleConsensusValidationResult {
2112 match self {
2113 StateTransition::DataContractCreate(_)
2114 | StateTransition::DataContractUpdate(_)
2115 | StateTransition::Batch(_)
2116 | StateTransition::IdentityCreate(_)
2117 | StateTransition::IdentityTopUp(_)
2118 | StateTransition::IdentityCreditWithdrawal(_)
2119 | StateTransition::IdentityUpdate(_)
2120 | StateTransition::IdentityKeyLimitsUpdate(_)
2121 | StateTransition::IdentityCreditTransfer(_)
2122 | StateTransition::MasternodeVote(_) => {
2123 crate::validation::SimpleConsensusValidationResult::new_with_error(
2124 UnsupportedFeatureError::new(
2125 "structure validation for identity-based state transitions".to_string(),
2126 platform_version.protocol_version,
2127 )
2128 .into(),
2129 )
2130 }
2131 StateTransition::IdentityCreditTransferToAddresses(transition) => {
2132 transition.validate_structure(platform_version)
2133 }
2134 StateTransition::IdentityCreateFromAddresses(transition) => {
2135 transition.validate_structure(platform_version)
2136 }
2137 StateTransition::IdentityTopUpFromAddresses(transition) => {
2138 transition.validate_structure(platform_version)
2139 }
2140 StateTransition::AddressFundsTransfer(transition) => {
2141 transition.validate_structure(platform_version)
2142 }
2143 StateTransition::AddressFundingFromAssetLock(transition) => {
2144 transition.validate_structure(platform_version)
2145 }
2146 StateTransition::AddressCreditWithdrawal(transition) => {
2147 transition.validate_structure(platform_version)
2148 }
2149 StateTransition::Shield(transition) => transition.validate_structure(platform_version),
2150 StateTransition::ShieldedTransfer(transition) => {
2151 transition.validate_structure(platform_version)
2152 }
2153 StateTransition::Unshield(transition) => {
2154 transition.validate_structure(platform_version)
2155 }
2156 StateTransition::IdentityTopUpFromShieldedPool(transition) => {
2157 transition.validate_structure(platform_version)
2158 }
2159 StateTransition::ShieldFromAssetLock(transition) => {
2160 transition.validate_structure(platform_version)
2161 }
2162 StateTransition::ShieldedWithdrawal(transition) => {
2163 transition.validate_structure(platform_version)
2164 }
2165 StateTransition::IdentityCreateFromShieldedPool(transition) => {
2166 transition.validate_structure(platform_version)
2167 }
2168 StateTransition::ShieldFromIdentity(transition) => {
2169 transition.validate_structure(platform_version)
2170 }
2171 }
2172 }
2173}
2174
2175#[cfg(test)]
2176mod tests {
2177 use super::*;
2178
2179 #[test]
2184 fn test_signing_options_default() {
2185 let opts = StateTransitionSigningOptions::default();
2186 assert!(!opts.allow_signing_with_any_security_level);
2187 assert!(!opts.allow_signing_with_any_purpose);
2188 }
2189
2190 #[test]
2191 fn test_signing_options_equality() {
2192 let a = StateTransitionSigningOptions {
2193 allow_signing_with_any_security_level: true,
2194 allow_signing_with_any_purpose: false,
2195 };
2196 let b = StateTransitionSigningOptions {
2197 allow_signing_with_any_security_level: true,
2198 allow_signing_with_any_purpose: false,
2199 };
2200 assert_eq!(a, b);
2201 }
2202
2203 #[test]
2204 fn test_signing_options_inequality() {
2205 let a = StateTransitionSigningOptions {
2206 allow_signing_with_any_security_level: true,
2207 allow_signing_with_any_purpose: false,
2208 };
2209 let b = StateTransitionSigningOptions {
2210 allow_signing_with_any_security_level: false,
2211 allow_signing_with_any_purpose: false,
2212 };
2213 assert_ne!(a, b);
2214 }
2215
2216 #[test]
2217 #[allow(clippy::clone_on_copy)]
2218 fn test_signing_options_clone() {
2219 let original = StateTransitionSigningOptions {
2220 allow_signing_with_any_security_level: true,
2221 allow_signing_with_any_purpose: true,
2222 };
2223 let cloned = original.clone();
2224 assert_eq!(original, cloned);
2225 }
2226
2227 #[test]
2228 fn test_signing_options_copy() {
2229 let original = StateTransitionSigningOptions {
2230 allow_signing_with_any_security_level: true,
2231 allow_signing_with_any_purpose: false,
2232 };
2233 let copied = original;
2234 assert_eq!(original, copied);
2235 }
2236
2237 #[test]
2238 fn test_signing_options_debug() {
2239 let opts = StateTransitionSigningOptions::default();
2240 let debug_str = format!("{:?}", opts);
2241 assert!(debug_str.contains("StateTransitionSigningOptions"));
2242 assert!(debug_str.contains("allow_signing_with_any_security_level"));
2243 assert!(debug_str.contains("allow_signing_with_any_purpose"));
2244 }
2245
2246 use crate::identity::core_script::CoreScript;
2256 use crate::identity::{Purpose, SecurityLevel};
2257 use crate::prelude::Identifier;
2258 use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0;
2259 use crate::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition;
2260 use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0;
2261 use crate::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition;
2262 use crate::state_transition::masternode_vote_transition::v0::MasternodeVoteTransitionV0;
2263 use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
2264 use crate::withdrawal::Pooling;
2265
2266 fn sample_transfer_st() -> StateTransition {
2267 let v0 = IdentityCreditTransferTransitionV0 {
2268 identity_id: Identifier::from([1u8; 32]),
2269 recipient_id: Identifier::from([2u8; 32]),
2270 amount: 1_000,
2271 nonce: 7,
2272 user_fee_increase: 3,
2273 signature_public_key_id: 11,
2274 signature: BinaryData::new(vec![0u8; 65]),
2275 };
2276 StateTransition::IdentityCreditTransfer(IdentityCreditTransferTransition::V0(v0))
2277 }
2278
2279 fn sample_masternode_vote_st() -> StateTransition {
2280 let v0 = MasternodeVoteTransitionV0 {
2281 pro_tx_hash: Identifier::from([3u8; 32]),
2282 voter_identity_id: Identifier::from([4u8; 32]),
2283 vote: Default::default(),
2284 nonce: 2,
2285 signature_public_key_id: 5,
2286 signature: BinaryData::new(vec![9u8; 10]),
2287 };
2288 StateTransition::MasternodeVote(MasternodeVoteTransition::V0(v0))
2289 }
2290
2291 fn sample_withdrawal_st() -> StateTransition {
2292 let v0 = IdentityCreditWithdrawalTransitionV0 {
2293 identity_id: Identifier::from([5u8; 32]),
2294 amount: 42,
2295 core_fee_per_byte: 1,
2296 pooling: Pooling::Never,
2297 output_script: CoreScript::from_bytes(vec![0x76, 0xa9]),
2298 nonce: 4,
2299 user_fee_increase: 1,
2300 signature_public_key_id: 3,
2301 signature: BinaryData::new(vec![8u8; 65]),
2302 };
2303 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V0(v0))
2304 }
2305
2306 #[test]
2307 fn test_name_returns_variant_names() {
2308 assert_eq!(sample_transfer_st().name(), "IdentityCreditTransfer");
2309 assert_eq!(sample_masternode_vote_st().name(), "MasternodeVote");
2310 assert_eq!(sample_withdrawal_st().name(), "IdentityCreditWithdrawal");
2311 }
2312
2313 #[test]
2314 fn test_state_transition_type_matches_variant() {
2315 assert_eq!(
2316 sample_transfer_st().state_transition_type(),
2317 StateTransitionType::IdentityCreditTransfer
2318 );
2319 assert_eq!(
2320 sample_masternode_vote_st().state_transition_type(),
2321 StateTransitionType::MasternodeVote
2322 );
2323 assert_eq!(
2324 sample_withdrawal_st().state_transition_type(),
2325 StateTransitionType::IdentityCreditWithdrawal
2326 );
2327 }
2328
2329 #[test]
2330 fn test_is_identity_signed_excludes_asset_lock_and_shielded() {
2331 assert!(sample_transfer_st().is_identity_signed());
2332 assert!(sample_masternode_vote_st().is_identity_signed());
2333 assert!(sample_withdrawal_st().is_identity_signed());
2334 }
2335
2336 #[test]
2337 fn test_signature_accessor() {
2338 let st = sample_transfer_st();
2339 let sig = st.signature().expect("transfer should expose signature");
2340 assert_eq!(sig.len(), 65);
2341
2342 let st = sample_masternode_vote_st();
2343 let sig = st.signature().expect("masternode vote has signature");
2344 assert_eq!(sig.as_slice(), &[9u8; 10]);
2345 }
2346
2347 #[test]
2348 fn test_owner_id_accessor() {
2349 let transfer = sample_transfer_st();
2350 assert_eq!(transfer.owner_id(), Some(Identifier::from([1u8; 32])));
2351
2352 let vote = sample_masternode_vote_st();
2353 assert_eq!(vote.owner_id(), Some(Identifier::from([4u8; 32])));
2354
2355 let withdraw = sample_withdrawal_st();
2356 assert_eq!(withdraw.owner_id(), Some(Identifier::from([5u8; 32])));
2357 }
2358
2359 #[test]
2360 fn test_signature_public_key_id_accessor() {
2361 assert_eq!(sample_transfer_st().signature_public_key_id(), Some(11));
2362 assert_eq!(
2363 sample_masternode_vote_st().signature_public_key_id(),
2364 Some(5)
2365 );
2366 assert_eq!(sample_withdrawal_st().signature_public_key_id(), Some(3));
2367 }
2368
2369 #[test]
2370 fn test_user_fee_increase_for_various_variants() {
2371 assert_eq!(sample_transfer_st().user_fee_increase(), 3);
2373 assert_eq!(sample_masternode_vote_st().user_fee_increase(), 0);
2375 assert_eq!(sample_withdrawal_st().user_fee_increase(), 1);
2377 }
2378
2379 #[test]
2380 fn test_set_signature_returns_true_for_supported() {
2381 let mut st = sample_transfer_st();
2382 let ok = st.set_signature(BinaryData::new(vec![0xaa; 65]));
2383 assert!(ok);
2384 assert_eq!(st.signature().unwrap().as_slice(), &[0xaa; 65]);
2385 }
2386
2387 #[test]
2388 fn test_set_user_fee_increase_updates_value() {
2389 let mut st = sample_transfer_st();
2390 st.set_user_fee_increase(42);
2391 assert_eq!(st.user_fee_increase(), 42);
2392
2393 let mut vote = sample_masternode_vote_st();
2395 vote.set_user_fee_increase(99);
2396 assert_eq!(vote.user_fee_increase(), 0);
2397 }
2398
2399 #[test]
2400 fn test_set_signature_public_key_id() {
2401 let mut st = sample_transfer_st();
2402 st.set_signature_public_key_id(1234);
2403 assert_eq!(st.signature_public_key_id(), Some(1234));
2404 }
2405
2406 #[test]
2407 fn test_required_number_of_private_keys_default() {
2408 assert_eq!(sample_transfer_st().required_number_of_private_keys(), 1);
2410 assert_eq!(
2411 sample_masternode_vote_st().required_number_of_private_keys(),
2412 1
2413 );
2414 assert_eq!(sample_withdrawal_st().required_number_of_private_keys(), 1);
2415 }
2416
2417 #[test]
2418 fn test_inputs_none_for_legacy_variants() {
2419 assert!(sample_transfer_st().inputs().is_none());
2421 assert!(sample_masternode_vote_st().inputs().is_none());
2422 assert!(sample_withdrawal_st().inputs().is_none());
2423 }
2424
2425 #[test]
2426 fn test_active_version_range_legacy_transitions() {
2427 assert_eq!(sample_transfer_st().active_version_range(), ALL_VERSIONS);
2429 assert_eq!(
2430 sample_masternode_vote_st().active_version_range(),
2431 ALL_VERSIONS
2432 );
2433 assert_eq!(sample_withdrawal_st().active_version_range(), ALL_VERSIONS);
2434 }
2435
2436 #[test]
2437 fn test_unique_identifiers_non_empty() {
2438 let ids = sample_transfer_st().unique_identifiers();
2439 assert_eq!(ids.len(), 1);
2440 assert!(!ids[0].is_empty());
2441 }
2442
2443 #[test]
2444 fn test_required_asset_lock_balance_rejects_non_asset_lock() {
2445 let platform_version = PlatformVersion::latest();
2446 let st = sample_transfer_st();
2447 let err = st
2448 .required_asset_lock_balance_for_processing_start(platform_version)
2449 .expect_err("credit transfer is not an asset lock state transition");
2450 match err {
2451 ProtocolError::CorruptedCodeExecution(msg) => {
2452 assert!(
2453 msg.contains("is not an asset lock transaction"),
2454 "unexpected error message: {msg}"
2455 );
2456 }
2457 other => panic!("expected CorruptedCodeExecution, got {other:?}"),
2458 }
2459 }
2460
2461 #[test]
2462 fn test_security_level_requirement_for_transfer() {
2463 let st = sample_transfer_st();
2465 let levels = st
2466 .security_level_requirement(Purpose::TRANSFER)
2467 .expect("transfer state transition should return a requirement");
2468 assert_eq!(levels, vec![SecurityLevel::CRITICAL]);
2469 }
2470
2471 #[test]
2472 fn test_purpose_requirement_for_transfer() {
2473 let st = sample_transfer_st();
2474 let purposes = st
2475 .purpose_requirement()
2476 .expect("transfer state transition should have a purpose");
2477 assert_eq!(purposes, vec![Purpose::TRANSFER]);
2478 }
2479
2480 #[test]
2481 fn test_optional_asset_lock_proof_none_for_transfer() {
2482 let st = sample_transfer_st();
2483 assert!(st.optional_asset_lock_proof().is_none());
2484 }
2485
2486 #[test]
2491 fn test_from_outer_enum_into_state_transition() {
2492 let outer: IdentityCreditTransferTransition =
2493 IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0::default());
2494 let st: StateTransition = outer.into();
2495 assert!(matches!(st, StateTransition::IdentityCreditTransfer(_)));
2496 }
2497
2498 #[test]
2499 fn test_from_masternode_vote_outer_into_state_transition() {
2500 let outer: MasternodeVoteTransition =
2501 MasternodeVoteTransition::V0(MasternodeVoteTransitionV0::default());
2502 let st: StateTransition = outer.into();
2503 assert!(matches!(st, StateTransition::MasternodeVote(_)));
2504 }
2505
2506 #[test]
2512 fn test_state_transition_platform_serialize_roundtrip() {
2513 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
2514 let original = sample_transfer_st();
2515 let bytes =
2516 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2517 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
2518 .expect("deserialize should succeed");
2519 assert_eq!(original, restored);
2520 }
2521
2522 #[test]
2523 fn test_deserialize_from_bytes_in_version_succeeds_for_latest() {
2524 use crate::serialization::PlatformSerializable;
2525 let original = sample_transfer_st();
2526 let bytes =
2527 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2528 let restored = StateTransition::deserialize_from_bytes_untrusted_in_version(
2529 &bytes,
2530 PlatformVersion::latest(),
2531 )
2532 .expect("deserialize_from_bytes_in_version should succeed");
2533 assert_eq!(original, restored);
2534 }
2535
2536 #[test]
2537 fn test_transaction_id_is_deterministic() {
2538 let st = sample_transfer_st();
2539 let a = st.transaction_id().expect("hash should succeed");
2540 let b = st.transaction_id().expect("hash should succeed");
2541 assert_eq!(a, b);
2542 assert_eq!(a.len(), 32);
2543 }
2544
2545 #[test]
2546 fn test_transaction_id_changes_on_signature_change() {
2547 let mut st = sample_transfer_st();
2548 let before = st.transaction_id().expect("hash should succeed");
2549 st.set_signature(BinaryData::new(vec![0xbb; 65]));
2550 let after = st.transaction_id().expect("hash should succeed");
2551 assert_ne!(before, after);
2553 }
2554
2555 #[test]
2556 fn test_clone_preserves_inner_state() {
2557 let st = sample_transfer_st();
2558 let cloned = st.clone();
2559 assert_eq!(st, cloned);
2560 }
2561
2562 use crate::data_contract::serialized_version::DataContractInSerializationFormat;
2573 use crate::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0;
2574 use crate::state_transition::batch_transition::document_base_transition::DocumentBaseTransition;
2575 use crate::state_transition::batch_transition::document_delete_transition::{
2576 DocumentDeleteTransition, DocumentDeleteTransitionV0,
2577 };
2578 use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionV0};
2579 use crate::state_transition::data_contract_create_transition::{
2580 DataContractCreateTransition, DataContractCreateTransitionV0,
2581 DataContractCreateTransitionV1,
2582 };
2583 use crate::state_transition::data_contract_update_transition::{
2584 DataContractUpdateTransition, DataContractUpdateTransitionV0,
2585 };
2586 use crate::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0;
2587 use crate::state_transition::identity_create_transition::IdentityCreateTransition;
2588 use crate::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0;
2589 use crate::state_transition::identity_topup_transition::IdentityTopUpTransition;
2590 use crate::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0;
2591 use crate::state_transition::identity_update_transition::IdentityUpdateTransition;
2592 use crate::state_transition::shielded_transfer_transition::v0::ShieldedTransferTransitionV0;
2593 use crate::state_transition::shielded_transfer_transition::ShieldedTransferTransition;
2594 use crate::state_transition::shielded_withdrawal_transition::v0::ShieldedWithdrawalTransitionV0;
2595 use crate::state_transition::shielded_withdrawal_transition::ShieldedWithdrawalTransition;
2596 use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
2597 use crate::state_transition::unshield_transition::UnshieldTransition;
2598
2599 fn sample_data_contract_in_serialization_format() -> DataContractInSerializationFormat {
2602 use crate::data_contract::config::v0::DataContractConfigV0;
2603 use crate::data_contract::config::DataContractConfig;
2604 use crate::data_contract::v1::DataContractV1;
2605 use crate::data_contract::DataContract;
2606 use platform_version::TryIntoPlatformVersioned;
2607 use std::collections::BTreeMap;
2608
2609 let contract = DataContract::V1(DataContractV1 {
2610 id: Identifier::from([9u8; 32]),
2611 version: 1,
2612 owner_id: Identifier::from([7u8; 32]),
2613 document_types: BTreeMap::new(),
2614 config: DataContractConfig::V0(DataContractConfigV0 {
2615 can_be_deleted: false,
2616 readonly: false,
2617 keeps_history: false,
2618 documents_keep_history_contract_default: false,
2619 documents_mutable_contract_default: false,
2620 documents_can_be_deleted_contract_default: false,
2621 requires_identity_encryption_bounded_key: None,
2622 requires_identity_decryption_bounded_key: None,
2623 }),
2624 schema_defs: None,
2625 created_at: None,
2626 updated_at: None,
2627 created_at_block_height: None,
2628 updated_at_block_height: None,
2629 created_at_epoch: None,
2630 updated_at_epoch: None,
2631 groups: BTreeMap::new(),
2632 tokens: BTreeMap::new(),
2633 keywords: Vec::new(),
2634 description: None,
2635 });
2636
2637 contract
2638 .try_into_platform_versioned(PlatformVersion::latest())
2639 .expect("expected to serialize a trivial contract")
2640 }
2641
2642 fn sample_data_contract_create_st() -> StateTransition {
2643 StateTransition::DataContractCreate(DataContractCreateTransition::V0(
2644 DataContractCreateTransitionV0 {
2645 data_contract: sample_data_contract_in_serialization_format(),
2646 identity_nonce: 1,
2647 user_fee_increase: 5,
2648 signature_public_key_id: 2,
2649 signature: BinaryData::new(vec![0xAB; 65]),
2650 },
2651 ))
2652 }
2653
2654 fn sample_data_contract_create_v1_st() -> StateTransition {
2655 StateTransition::DataContractCreate(DataContractCreateTransition::V1(
2656 DataContractCreateTransitionV1 {
2657 data_contract: sample_data_contract_in_serialization_format(),
2658 identity_nonce: 1,
2659 contract_group: None,
2660 contract_group_memberships: vec![],
2661 user_fee_increase: 5,
2662 signature_public_key_id: 2,
2663 signature: BinaryData::new(vec![0xAB; 65]),
2664 },
2665 ))
2666 }
2667
2668 fn sample_data_contract_update_st() -> StateTransition {
2669 StateTransition::DataContractUpdate(DataContractUpdateTransition::V0(
2670 DataContractUpdateTransitionV0 {
2671 identity_contract_nonce: 4,
2672 data_contract: sample_data_contract_in_serialization_format(),
2673 user_fee_increase: 9,
2674 signature_public_key_id: 6,
2675 signature: BinaryData::new(vec![0xCD; 65]),
2676 },
2677 ))
2678 }
2679
2680 #[cfg(all(feature = "state-transition-signing", feature = "bls-signatures"))]
2681 #[test]
2682 fn should_enforce_contract_bounds_before_private_key_signing() {
2683 use crate::consensus::signature::{
2684 ContractBoundedKeyNonBatchError, ContractBoundedKeyOutOfBoundsError,
2685 };
2686 use crate::identity::contract_bounds::ContractBounds;
2687 use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
2688
2689 let private_key = [1; 32];
2690 let bls = crate::bls::native_bls::NativeBlsModule;
2691 let mut key = IdentityPublicKeyV0 {
2693 id: 7,
2694 purpose: Purpose::AUTHENTICATION,
2695 security_level: SecurityLevel::HIGH,
2696 key_type: KeyType::ECDSA_SECP256K1,
2697 data: get_compressed_public_ec_key(&private_key)
2698 .unwrap()
2699 .to_vec()
2700 .into(),
2701 contract_bounds: Some(ContractBounds::SingleContract {
2702 id: Identifier::from([2; 32]),
2703 }),
2704 ..Default::default()
2705 };
2706 sample_batch_st_with_delete()
2707 .sign(&key.clone().into(), &private_key, &bls)
2708 .expect("a batch inside the bounds must sign");
2709 key.contract_bounds = Some(ContractBounds::SingleContractDocumentType {
2710 id: Identifier::from([2; 32]),
2711 document_type_name: "preorder".to_string(),
2712 });
2713 sample_batch_st_with_delete()
2714 .sign(&key.clone().into(), &private_key, &bls)
2715 .expect("a batch of the bound document type must sign");
2716
2717 let err = sample_transfer_st()
2718 .sign(&key.clone().into(), &private_key, &bls)
2719 .unwrap_err();
2720 assert!(matches!(err, ProtocolError::ConsensusError(error)
2721 if *error == ContractBoundedKeyNonBatchError::new(key.id).into()));
2722
2723 for denied in [
2724 ContractBounds::SingleContract {
2725 id: Identifier::from([3; 32]),
2726 },
2727 ContractBounds::SingleContractDocumentType {
2728 id: Identifier::from([2; 32]),
2729 document_type_name: "other".to_string(),
2730 },
2731 ] {
2732 key.contract_bounds = Some(denied.clone());
2733 let mut transition = sample_batch_st_with_delete();
2734 let original = transition.clone();
2735 let err = transition
2736 .sign(&key.clone().into(), &private_key, &bls)
2737 .unwrap_err();
2738 assert!(
2739 matches!(err, ProtocolError::ConsensusError(error)
2740 if *error == ContractBoundedKeyOutOfBoundsError::new(key.id).into()),
2741 "{denied:?}"
2742 );
2743 assert_eq!(
2744 transition, original,
2745 "rejection must preserve the transition"
2746 );
2747 }
2748
2749 key.contract_bounds = None;
2750 sample_batch_st_with_delete()
2751 .sign(&key.into(), &private_key, &bls)
2752 .expect("unbounded keys must still sign");
2753 }
2754
2755 fn sample_batch_st_with_delete() -> StateTransition {
2756 let base = DocumentBaseTransition::V0(DocumentBaseTransitionV0 {
2757 id: Identifier::from([1u8; 32]),
2758 identity_contract_nonce: 3,
2759 document_type_name: "preorder".to_string(),
2760 data_contract_id: Identifier::from([2u8; 32]),
2761 });
2762 let delete =
2763 DocumentTransition::Delete(DocumentDeleteTransition::V0(DocumentDeleteTransitionV0 {
2764 base,
2765 }));
2766 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2767 owner_id: Identifier::from([8u8; 32]),
2768 transitions: vec![delete],
2769 user_fee_increase: 2,
2770 signature_public_key_id: 7,
2771 signature: BinaryData::new(vec![0xEE; 65]),
2772 }))
2773 }
2774
2775 fn sample_batch_st_empty() -> StateTransition {
2776 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
2777 owner_id: Identifier::from([1u8; 32]),
2778 transitions: vec![],
2779 user_fee_increase: 0,
2780 signature_public_key_id: 0,
2781 signature: BinaryData::new(vec![]),
2782 }))
2783 }
2784
2785 fn sample_identity_create_st() -> StateTransition {
2786 StateTransition::IdentityCreate(IdentityCreateTransition::V0(IdentityCreateTransitionV0 {
2787 identity_id: Identifier::from([3u8; 32]),
2788 ..Default::default()
2789 }))
2790 }
2791
2792 fn sample_identity_top_up_st() -> StateTransition {
2793 StateTransition::IdentityTopUp(IdentityTopUpTransition::V0(IdentityTopUpTransitionV0 {
2794 identity_id: Identifier::from([4u8; 32]),
2795 ..Default::default()
2796 }))
2797 }
2798
2799 fn sample_identity_update_st() -> StateTransition {
2800 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
2801 identity_id: Identifier::from([5u8; 32]),
2802 revision: 1,
2803 nonce: 2,
2804 add_public_keys: vec![],
2805 disable_public_keys: vec![],
2806 user_fee_increase: 11,
2807 signature_public_key_id: 33,
2808 signature: BinaryData::new(vec![0xFF; 65]),
2809 }))
2810 }
2811
2812 fn sample_unshield_st() -> StateTransition {
2813 StateTransition::Unshield(UnshieldTransition::V0(UnshieldTransitionV0 {
2814 output_address: Default::default(),
2815 actions: vec![],
2816 unshielding_amount: 0,
2817 anchor: [0u8; 32],
2818 proof: vec![],
2819 binding_signature: [0u8; 64],
2820 }))
2821 }
2822
2823 fn sample_shielded_transfer_st() -> StateTransition {
2824 StateTransition::ShieldedTransfer(ShieldedTransferTransition::V0(
2825 ShieldedTransferTransitionV0 {
2826 actions: vec![],
2827 value_balance: 0,
2828 anchor: [0u8; 32],
2829 proof: vec![],
2830 binding_signature: [0u8; 64],
2831 },
2832 ))
2833 }
2834
2835 fn sample_shielded_withdrawal_st() -> StateTransition {
2836 use crate::identity::core_script::CoreScript;
2837 use crate::withdrawal::Pooling;
2838 StateTransition::ShieldedWithdrawal(ShieldedWithdrawalTransition::V0(
2839 ShieldedWithdrawalTransitionV0 {
2840 actions: vec![],
2841 unshielding_amount: 0,
2842 anchor: [0u8; 32],
2843 proof: vec![],
2844 binding_signature: [0u8; 64],
2845 core_fee_per_byte: 1,
2846 pooling: Pooling::Never,
2847 output_script: CoreScript::from_bytes(vec![]),
2848 },
2849 ))
2850 }
2851
2852 #[test]
2855 fn test_name_for_newly_covered_variants() {
2856 assert_eq!(
2857 sample_data_contract_create_st().name(),
2858 "DataContractCreate"
2859 );
2860 assert_eq!(
2861 sample_data_contract_update_st().name(),
2862 "DataContractUpdate"
2863 );
2864 assert_eq!(sample_identity_create_st().name(), "IdentityCreate");
2865 assert_eq!(sample_identity_top_up_st().name(), "IdentityTopUp");
2866 assert_eq!(sample_identity_update_st().name(), "IdentityUpdate");
2867 assert_eq!(sample_unshield_st().name(), "Unshield");
2868 assert_eq!(sample_shielded_transfer_st().name(), "ShieldedTransfer");
2869 assert_eq!(sample_shielded_withdrawal_st().name(), "ShieldedWithdrawal");
2870
2871 let batch_name = sample_batch_st_with_delete().name();
2874 assert_eq!(batch_name, "DocumentsBatch([Delete])");
2875
2876 let empty_name = sample_batch_st_empty().name();
2878 assert_eq!(empty_name, "DocumentsBatch([])");
2879 }
2880
2881 #[test]
2883 fn test_state_transition_type_for_newly_covered_variants() {
2884 assert_eq!(
2885 sample_data_contract_create_st().state_transition_type(),
2886 StateTransitionType::DataContractCreate
2887 );
2888 assert_eq!(
2889 sample_data_contract_update_st().state_transition_type(),
2890 StateTransitionType::DataContractUpdate
2891 );
2892 assert_eq!(
2893 sample_batch_st_with_delete().state_transition_type(),
2894 StateTransitionType::Batch
2895 );
2896 assert_eq!(
2897 sample_identity_create_st().state_transition_type(),
2898 StateTransitionType::IdentityCreate
2899 );
2900 assert_eq!(
2901 sample_identity_top_up_st().state_transition_type(),
2902 StateTransitionType::IdentityTopUp
2903 );
2904 assert_eq!(
2905 sample_identity_update_st().state_transition_type(),
2906 StateTransitionType::IdentityUpdate
2907 );
2908 assert_eq!(
2909 sample_unshield_st().state_transition_type(),
2910 StateTransitionType::Unshield
2911 );
2912 assert_eq!(
2913 sample_shielded_transfer_st().state_transition_type(),
2914 StateTransitionType::ShieldedTransfer
2915 );
2916 assert_eq!(
2917 sample_shielded_withdrawal_st().state_transition_type(),
2918 StateTransitionType::ShieldedWithdrawal
2919 );
2920 }
2921
2922 #[test]
2927 fn test_active_version_range_contract_and_shielded_branches() {
2928 let contract_v1_range = 9..=LATEST_VERSION;
2931 assert_eq!(
2932 sample_data_contract_create_st().active_version_range(),
2933 contract_v1_range
2934 );
2935 let contract_v1_range = 9..=LATEST_VERSION;
2936 assert_eq!(
2937 sample_data_contract_update_st().active_version_range(),
2938 contract_v1_range
2939 );
2940 assert_eq!(
2942 sample_batch_st_with_delete().active_version_range(),
2943 ALL_VERSIONS
2944 );
2945 assert_eq!(
2947 sample_identity_create_st().active_version_range(),
2948 ALL_VERSIONS
2949 );
2950 assert_eq!(
2951 sample_identity_top_up_st().active_version_range(),
2952 ALL_VERSIONS
2953 );
2954 assert_eq!(
2955 sample_identity_update_st().active_version_range(),
2956 ALL_VERSIONS
2957 );
2958 let shielded_range = 12..=LATEST_VERSION;
2960 assert_eq!(
2961 sample_shielded_transfer_st().active_version_range(),
2962 shielded_range.clone()
2963 );
2964 assert_eq!(
2965 sample_unshield_st().active_version_range(),
2966 shielded_range.clone()
2967 );
2968 assert_eq!(
2969 sample_shielded_withdrawal_st().active_version_range(),
2970 shielded_range
2971 );
2972 }
2973
2974 fn sample_identity_update_st_adding(
2975 public_key: crate::state_transition::public_key_in_creation::IdentityPublicKeyInCreation,
2976 ) -> StateTransition {
2977 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
2978 identity_id: Identifier::from([5u8; 32]),
2979 revision: 1,
2980 nonce: 2,
2981 add_public_keys: vec![public_key],
2982 disable_public_keys: vec![],
2983 user_fee_increase: 0,
2984 signature_public_key_id: 0,
2985 signature: BinaryData::new(vec![0xFF; 65]),
2986 }))
2987 }
2988
2989 #[test]
2990 fn should_only_admit_a_version_1_public_key_in_creation_from_protocol_version_14() {
2991 use crate::serialization::PlatformSerializable;
2992 use crate::state_transition::public_key_in_creation::v0::IdentityPublicKeyInCreationV0;
2993 use crate::state_transition::public_key_in_creation::v1::IdentityPublicKeyInCreationV1;
2994
2995 let version_0_key = IdentityPublicKeyInCreationV0 {
2996 id: 1,
2997 data: BinaryData::new(vec![2; 33]),
2998 ..Default::default()
2999 };
3000 let limited_key = IdentityPublicKeyInCreationV1::from_v0_with_limits(
3001 version_0_key.clone(),
3002 Some(1_000),
3003 None,
3004 );
3005
3006 let plain = sample_identity_update_st_adding(version_0_key.into());
3007 assert_eq!(plain.active_version_range(), ALL_VERSIONS);
3008
3009 let limited = sample_identity_update_st_adding(limited_key.into());
3010 assert_eq!(limited.active_version_range(), 14..=LATEST_VERSION);
3011
3012 let bytes = limited.serialize_to_bytes().expect("expected to serialize");
3015 let version_13 = PlatformVersion::get(13).expect("expected protocol version 13");
3016 assert!(matches!(
3017 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_13),
3018 Err(ProtocolError::StateTransitionError(
3019 StateTransitionIsNotActiveError { .. }
3020 ))
3021 ));
3022 let version_14 = PlatformVersion::get(14).expect("expected protocol version 14");
3023 assert_eq!(
3024 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_14)
3025 .expect("expected protocol version 14 to decode a limited key"),
3026 limited
3027 );
3028 }
3029
3030 #[test]
3033 fn test_is_identity_signed_false_for_identity_create_topup_and_shielded() {
3034 assert!(!sample_identity_create_st().is_identity_signed());
3035 assert!(!sample_identity_top_up_st().is_identity_signed());
3036 assert!(!sample_unshield_st().is_identity_signed());
3037 assert!(!sample_shielded_transfer_st().is_identity_signed());
3038 assert!(!sample_shielded_withdrawal_st().is_identity_signed());
3039 }
3040
3041 #[test]
3045 fn test_signature_accessor_for_other_variants() {
3046 assert_eq!(
3048 sample_data_contract_create_st().signature().unwrap().len(),
3049 65
3050 );
3051 assert_eq!(
3052 sample_data_contract_update_st().signature().unwrap().len(),
3053 65
3054 );
3055 assert_eq!(sample_batch_st_with_delete().signature().unwrap().len(), 65);
3056 assert_eq!(sample_identity_update_st().signature().unwrap().len(), 65);
3057
3058 assert!(sample_unshield_st().signature().is_none());
3060 assert!(sample_shielded_transfer_st().signature().is_none());
3061 assert!(sample_shielded_withdrawal_st().signature().is_none());
3062 }
3063
3064 #[test]
3066 fn test_owner_id_accessor_for_other_variants() {
3067 assert_eq!(
3068 sample_data_contract_create_st().owner_id(),
3069 Some(Identifier::from([7u8; 32]))
3070 );
3071 assert_eq!(
3072 sample_data_contract_update_st().owner_id(),
3073 Some(Identifier::from([7u8; 32]))
3074 );
3075 assert_eq!(
3076 sample_batch_st_with_delete().owner_id(),
3077 Some(Identifier::from([8u8; 32]))
3078 );
3079 assert_eq!(
3080 sample_identity_update_st().owner_id(),
3081 Some(Identifier::from([5u8; 32]))
3082 );
3083 assert!(sample_unshield_st().owner_id().is_none());
3085 assert!(sample_shielded_transfer_st().owner_id().is_none());
3086 assert!(sample_shielded_withdrawal_st().owner_id().is_none());
3087 }
3088
3089 #[test]
3092 fn test_user_fee_increase_for_newly_covered_variants() {
3093 assert_eq!(sample_data_contract_create_st().user_fee_increase(), 5);
3094 assert_eq!(sample_data_contract_update_st().user_fee_increase(), 9);
3095 assert_eq!(sample_batch_st_with_delete().user_fee_increase(), 2);
3096 assert_eq!(sample_identity_update_st().user_fee_increase(), 11);
3097 assert_eq!(sample_shielded_transfer_st().user_fee_increase(), 0);
3099 assert_eq!(sample_shielded_withdrawal_st().user_fee_increase(), 0);
3100 assert_eq!(sample_unshield_st().user_fee_increase(), 0);
3101 }
3102
3103 #[test]
3106 fn test_set_user_fee_increase_for_newly_covered_variants() {
3107 let mut st = sample_data_contract_create_st();
3108 st.set_user_fee_increase(42);
3109 assert_eq!(st.user_fee_increase(), 42);
3110
3111 let mut st = sample_data_contract_update_st();
3112 st.set_user_fee_increase(13);
3113 assert_eq!(st.user_fee_increase(), 13);
3114
3115 let mut st = sample_batch_st_with_delete();
3116 st.set_user_fee_increase(101);
3117 assert_eq!(st.user_fee_increase(), 101);
3118
3119 let mut st = sample_identity_update_st();
3120 st.set_user_fee_increase(77);
3121 assert_eq!(st.user_fee_increase(), 77);
3122
3123 let mut shielded = sample_shielded_transfer_st();
3125 shielded.set_user_fee_increase(99);
3126 assert_eq!(shielded.user_fee_increase(), 0);
3127
3128 let mut withdrawal = sample_shielded_withdrawal_st();
3129 withdrawal.set_user_fee_increase(99);
3130 assert_eq!(withdrawal.user_fee_increase(), 0);
3131
3132 let mut unshield = sample_unshield_st();
3133 unshield.set_user_fee_increase(99);
3134 assert_eq!(unshield.user_fee_increase(), 0);
3135 }
3136
3137 #[test]
3141 fn test_set_signature_false_for_shielded_and_identity_create_topup() {
3142 let mut st = sample_unshield_st();
3144 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3145 let mut st = sample_shielded_transfer_st();
3146 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3147 let mut st = sample_shielded_withdrawal_st();
3148 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3149 }
3150
3151 #[test]
3152 fn test_set_signature_true_for_newly_covered_variants() {
3153 let mut st = sample_data_contract_create_st();
3154 assert!(st.set_signature(BinaryData::new(vec![0x11; 65])));
3155 assert_eq!(st.signature().unwrap().as_slice(), &[0x11; 65]);
3156
3157 let mut st = sample_data_contract_update_st();
3158 assert!(st.set_signature(BinaryData::new(vec![0x22; 65])));
3159 assert_eq!(st.signature().unwrap().as_slice(), &[0x22; 65]);
3160
3161 let mut st = sample_batch_st_with_delete();
3162 assert!(st.set_signature(BinaryData::new(vec![0x33; 65])));
3163 assert_eq!(st.signature().unwrap().as_slice(), &[0x33; 65]);
3164
3165 let mut st = sample_identity_update_st();
3166 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
3167 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
3168 }
3169
3170 #[test]
3173 fn test_signature_public_key_id_returns_none_for_non_signed() {
3174 assert!(sample_identity_create_st()
3177 .signature_public_key_id()
3178 .is_none());
3179 assert!(sample_identity_top_up_st()
3180 .signature_public_key_id()
3181 .is_none());
3182 assert!(sample_unshield_st().signature_public_key_id().is_none());
3183 assert!(sample_shielded_transfer_st()
3184 .signature_public_key_id()
3185 .is_none());
3186 assert!(sample_shielded_withdrawal_st()
3187 .signature_public_key_id()
3188 .is_none());
3189 }
3190
3191 #[test]
3192 fn test_signature_public_key_id_for_signed_variants() {
3193 assert_eq!(
3194 sample_data_contract_create_st().signature_public_key_id(),
3195 Some(2)
3196 );
3197 assert_eq!(
3198 sample_data_contract_update_st().signature_public_key_id(),
3199 Some(6)
3200 );
3201 assert_eq!(
3202 sample_batch_st_with_delete().signature_public_key_id(),
3203 Some(7)
3204 );
3205 assert_eq!(
3206 sample_identity_update_st().signature_public_key_id(),
3207 Some(33)
3208 );
3209 }
3210
3211 #[test]
3214 fn test_set_signature_public_key_id_noop_for_non_signed() {
3215 let mut st = sample_identity_create_st();
3218 st.set_signature_public_key_id(100);
3219 assert_eq!(st.signature_public_key_id(), None);
3220
3221 let mut st = sample_identity_top_up_st();
3222 st.set_signature_public_key_id(100);
3223 assert_eq!(st.signature_public_key_id(), None);
3224
3225 let mut st = sample_unshield_st();
3226 st.set_signature_public_key_id(100);
3227 assert_eq!(st.signature_public_key_id(), None);
3228 }
3229
3230 #[test]
3231 fn test_set_signature_public_key_id_updates_for_signed_variants() {
3232 let mut st = sample_data_contract_create_st();
3233 st.set_signature_public_key_id(42);
3234 assert_eq!(st.signature_public_key_id(), Some(42));
3235
3236 let mut st = sample_batch_st_with_delete();
3237 st.set_signature_public_key_id(43);
3238 assert_eq!(st.signature_public_key_id(), Some(43));
3239
3240 let mut st = sample_identity_update_st();
3241 st.set_signature_public_key_id(44);
3242 assert_eq!(st.signature_public_key_id(), Some(44));
3243 }
3244
3245 #[test]
3248 fn test_required_number_of_private_keys_various_variants() {
3249 assert_eq!(
3250 sample_data_contract_create_st().required_number_of_private_keys(),
3251 1
3252 );
3253 assert_eq!(
3254 sample_data_contract_update_st().required_number_of_private_keys(),
3255 1
3256 );
3257 assert_eq!(
3258 sample_batch_st_with_delete().required_number_of_private_keys(),
3259 1
3260 );
3261 assert_eq!(
3262 sample_identity_update_st().required_number_of_private_keys(),
3263 1
3264 );
3265 assert_eq!(
3266 sample_identity_create_st().required_number_of_private_keys(),
3267 1
3268 );
3269 assert_eq!(
3271 sample_shielded_transfer_st().required_number_of_private_keys(),
3272 0
3273 );
3274 assert_eq!(
3275 sample_shielded_withdrawal_st().required_number_of_private_keys(),
3276 0
3277 );
3278 assert_eq!(sample_unshield_st().required_number_of_private_keys(), 0);
3279 }
3280
3281 #[test]
3284 fn test_inputs_none_for_many_variants() {
3285 assert!(sample_data_contract_create_st().inputs().is_none());
3286 assert!(sample_data_contract_update_st().inputs().is_none());
3287 assert!(sample_batch_st_with_delete().inputs().is_none());
3288 assert!(sample_identity_create_st().inputs().is_none());
3289 assert!(sample_identity_top_up_st().inputs().is_none());
3290 assert!(sample_identity_update_st().inputs().is_none());
3291 assert!(sample_unshield_st().inputs().is_none());
3293 assert!(sample_shielded_transfer_st().inputs().is_none());
3294 assert!(sample_shielded_withdrawal_st().inputs().is_none());
3295 }
3296
3297 #[test]
3302 fn test_optional_asset_lock_proof_returns_none_for_wildcard_arms() {
3303 assert!(sample_data_contract_create_st()
3304 .optional_asset_lock_proof()
3305 .is_none());
3306 assert!(sample_data_contract_update_st()
3307 .optional_asset_lock_proof()
3308 .is_none());
3309 assert!(sample_batch_st_with_delete()
3310 .optional_asset_lock_proof()
3311 .is_none());
3312 assert!(sample_identity_update_st()
3313 .optional_asset_lock_proof()
3314 .is_none());
3315 assert!(sample_unshield_st().optional_asset_lock_proof().is_none());
3316 assert!(sample_shielded_transfer_st()
3317 .optional_asset_lock_proof()
3318 .is_none());
3319 assert!(sample_shielded_withdrawal_st()
3320 .optional_asset_lock_proof()
3321 .is_none());
3322 }
3323
3324 #[test]
3328 fn test_required_asset_lock_balance_errors_for_other_non_asset_lock_variants() {
3329 let platform_version = PlatformVersion::latest();
3330
3331 let cases: Vec<(&str, StateTransition)> = vec![
3332 ("DataContractCreate", sample_data_contract_create_st()),
3333 ("DataContractUpdate", sample_data_contract_update_st()),
3334 ("Batch", sample_batch_st_with_delete()),
3335 ("IdentityUpdate", sample_identity_update_st()),
3336 ("MasternodeVote", sample_masternode_vote_st()),
3337 ("Unshield", sample_unshield_st()),
3338 ("ShieldedTransfer", sample_shielded_transfer_st()),
3339 ("ShieldedWithdrawal", sample_shielded_withdrawal_st()),
3340 ];
3341
3342 for (label, st) in cases {
3343 let err = st
3344 .required_asset_lock_balance_for_processing_start(platform_version)
3345 .expect_err(&format!("expected error for {label}"));
3346 match err {
3347 ProtocolError::CorruptedCodeExecution(msg) => {
3348 assert!(
3349 msg.contains("is not an asset lock transaction"),
3350 "unexpected error for {label}: {msg}"
3351 );
3352 }
3353 other => panic!("expected CorruptedCodeExecution for {label}, got {other:?}"),
3354 }
3355 }
3356 }
3357
3358 #[test]
3364 fn test_unique_identifiers_non_empty_for_other_variants() {
3365 for st in [
3366 sample_data_contract_create_st(),
3367 sample_data_contract_update_st(),
3368 sample_batch_st_with_delete(),
3369 sample_identity_create_st(),
3370 sample_identity_top_up_st(),
3371 sample_identity_update_st(),
3372 ] {
3373 let ids = st.unique_identifiers();
3374 assert!(!ids.is_empty(), "unique_identifiers should not be empty");
3375 }
3376 }
3377
3378 #[test]
3382 fn test_security_level_requirement_returns_none_for_non_signed_variants() {
3383 let purpose = Purpose::AUTHENTICATION;
3384 assert!(sample_identity_create_st()
3385 .security_level_requirement(purpose)
3386 .is_none());
3387 assert!(sample_identity_top_up_st()
3388 .security_level_requirement(purpose)
3389 .is_none());
3390 assert!(sample_unshield_st()
3391 .security_level_requirement(purpose)
3392 .is_none());
3393 assert!(sample_shielded_transfer_st()
3394 .security_level_requirement(purpose)
3395 .is_none());
3396 assert!(sample_shielded_withdrawal_st()
3397 .security_level_requirement(purpose)
3398 .is_none());
3399 }
3400
3401 #[test]
3402 fn test_purpose_requirement_returns_none_for_non_signed_variants() {
3403 assert!(sample_identity_create_st().purpose_requirement().is_none());
3404 assert!(sample_identity_top_up_st().purpose_requirement().is_none());
3405 assert!(sample_unshield_st().purpose_requirement().is_none());
3406 assert!(sample_shielded_transfer_st()
3407 .purpose_requirement()
3408 .is_none());
3409 assert!(sample_shielded_withdrawal_st()
3410 .purpose_requirement()
3411 .is_none());
3412 }
3413
3414 #[test]
3416 fn test_from_outer_data_contract_create_into_state_transition() {
3417 let outer: DataContractCreateTransition =
3418 DataContractCreateTransition::V0(DataContractCreateTransitionV0 {
3419 data_contract: sample_data_contract_in_serialization_format(),
3420 identity_nonce: 1,
3421 user_fee_increase: 0,
3422 signature_public_key_id: 0,
3423 signature: Default::default(),
3424 });
3425 let st: StateTransition = outer.into();
3426 assert!(matches!(st, StateTransition::DataContractCreate(_)));
3427 }
3428
3429 #[test]
3430 fn test_from_outer_data_contract_update_into_state_transition() {
3431 let outer: DataContractUpdateTransition =
3432 DataContractUpdateTransition::V0(DataContractUpdateTransitionV0 {
3433 identity_contract_nonce: 2,
3434 data_contract: sample_data_contract_in_serialization_format(),
3435 user_fee_increase: 0,
3436 signature_public_key_id: 0,
3437 signature: Default::default(),
3438 });
3439 let st: StateTransition = outer.into();
3440 assert!(matches!(st, StateTransition::DataContractUpdate(_)));
3441 }
3442
3443 #[test]
3444 fn test_from_outer_batch_into_state_transition() {
3445 let outer: BatchTransition = BatchTransition::V0(BatchTransitionV0::default());
3446 let st: StateTransition = outer.into();
3447 assert!(matches!(st, StateTransition::Batch(_)));
3448 }
3449
3450 #[test]
3451 fn test_from_outer_identity_create_into_state_transition() {
3452 let outer: IdentityCreateTransition =
3453 IdentityCreateTransition::V0(IdentityCreateTransitionV0::default());
3454 let st: StateTransition = outer.into();
3455 assert!(matches!(st, StateTransition::IdentityCreate(_)));
3456 }
3457
3458 #[test]
3459 fn test_from_outer_identity_update_into_state_transition() {
3460 let outer: IdentityUpdateTransition =
3461 IdentityUpdateTransition::V0(IdentityUpdateTransitionV0::default());
3462 let st: StateTransition = outer.into();
3463 assert!(matches!(st, StateTransition::IdentityUpdate(_)));
3464 }
3465
3466 #[test]
3469 fn test_transaction_id_and_clone_for_identity_update() {
3470 let st = sample_identity_update_st();
3471 let id_a = st.transaction_id().expect("hash should succeed");
3472 let cloned = st.clone();
3473 let id_b = cloned.transaction_id().expect("hash should succeed");
3474 assert_eq!(id_a, id_b);
3475 assert_eq!(id_a.len(), 32);
3476 }
3477
3478 #[test]
3479 fn test_transaction_id_and_clone_for_data_contract_create() {
3480 let st = sample_data_contract_create_st();
3481 let id_a = st.transaction_id().expect("hash should succeed");
3482 let cloned = st.clone();
3483 let id_b = cloned.transaction_id().expect("hash should succeed");
3484 assert_eq!(id_a, id_b);
3485 assert_eq!(id_a.len(), 32);
3486 }
3487
3488 #[test]
3490 fn test_serialize_roundtrip_identity_update() {
3491 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
3492 let original = sample_identity_update_st();
3493 let bytes =
3494 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3495 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
3496 .expect("deserialize should succeed");
3497 assert_eq!(original, restored);
3498 }
3499
3500 #[test]
3501 fn test_serialize_roundtrip_data_contract_update() {
3502 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
3503 let original = sample_data_contract_update_st();
3504 let bytes =
3505 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3506 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
3507 .expect("deserialize should succeed");
3508 assert_eq!(original, restored);
3509 }
3510
3511 #[test]
3512 fn test_serialize_roundtrip_batch_empty() {
3513 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
3514 let original = sample_batch_st_empty();
3515 let bytes =
3516 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
3517 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
3518 .expect("deserialize should succeed");
3519 assert_eq!(original, restored);
3520 }
3521
3522 #[cfg(all(feature = "state-transitions", feature = "validation"))]
3529 #[test]
3530 fn test_deserialize_from_bytes_in_version_returns_not_active_error() {
3531 use crate::serialization::PlatformSerializable;
3532
3533 let original = sample_shielded_transfer_st();
3535 let bytes =
3536 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
3537
3538 let low_version = PlatformVersion::get(1).expect("platform version 1 exists");
3542 assert!(
3543 low_version.protocol_version < 12,
3544 "expected sub-12 version for this test, got {}",
3545 low_version.protocol_version
3546 );
3547
3548 let err = StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, low_version)
3549 .expect_err("expected StateTransitionIsNotActiveError for sub-12 protocol");
3550 match err {
3551 ProtocolError::StateTransitionError(
3552 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
3553 state_transition_type,
3554 active_version_range,
3555 current_protocol_version,
3556 },
3557 ) => {
3558 assert_eq!(state_transition_type, "ShieldedTransfer");
3559 assert_eq!(current_protocol_version, low_version.protocol_version);
3560 assert!(active_version_range.start() >= &12);
3561 }
3562 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
3563 }
3564 }
3565
3566 #[test]
3570 fn test_data_contract_create_v1_is_not_active_before_protocol_version_14() {
3571 use crate::serialization::PlatformSerializable;
3572
3573 let original = sample_data_contract_create_v1_st();
3574 assert_eq!(original.active_version_range(), 14..=LATEST_VERSION);
3575
3576 let bytes =
3577 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
3578
3579 let version_13 = PlatformVersion::get(13).expect("platform version 13 exists");
3580 let err = StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_13)
3581 .expect_err("expected StateTransitionIsNotActiveError at protocol version 13");
3582 match err {
3583 ProtocolError::StateTransitionError(
3584 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
3585 active_version_range,
3586 current_protocol_version,
3587 ..
3588 },
3589 ) => {
3590 assert_eq!(current_protocol_version, 13);
3591 assert_eq!(*active_version_range.start(), 14);
3592 }
3593 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
3594 }
3595
3596 let version_14 = PlatformVersion::get(14).expect("platform version 14 exists");
3597 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_14)
3598 .expect("a version 1 create is active at protocol version 14");
3599 }
3600
3601 #[test]
3602 fn should_gate_identity_transitions_carrying_a_contract_group_bound_key_to_protocol_version_14()
3603 {
3604 use crate::identity::contract_bounds::ContractBounds;
3605 use crate::identity::{KeyType, Purpose, SecurityLevel};
3606 use crate::serialization::PlatformSerializable;
3607 use crate::state_transition::errors::StateTransitionError;
3608 use crate::state_transition::public_key_in_creation::v0::IdentityPublicKeyInCreationV0;
3609 use crate::state_transition::public_key_in_creation::IdentityPublicKeyInCreation;
3610
3611 let key = |contract_bounds| {
3612 IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 {
3613 id: 1,
3614 key_type: KeyType::ECDSA_SECP256K1,
3615 purpose: Purpose::AUTHENTICATION,
3616 security_level: SecurityLevel::HIGH,
3617 contract_bounds,
3618 read_only: false,
3619 data: BinaryData::new(vec![2u8; 33]),
3620 signature: BinaryData::default(),
3621 })
3622 };
3623 let contract_bound = Some(ContractBounds::SingleContract {
3624 id: Identifier::from([9u8; 32]),
3625 });
3626 let group_bound = Some(ContractBounds::ContractGroup {
3627 id: Identifier::from([9u8; 32]),
3628 });
3629 let create = |bounds| {
3630 StateTransition::IdentityCreate(IdentityCreateTransition::V0(
3631 IdentityCreateTransitionV0 {
3632 identity_id: Identifier::from([3u8; 32]),
3633 public_keys: vec![key(bounds)],
3634 ..Default::default()
3635 },
3636 ))
3637 };
3638 let update = |bounds| {
3639 let StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(mut v0)) =
3640 sample_identity_update_st()
3641 else {
3642 panic!("expected a version 0 identity update");
3643 };
3644 v0.add_public_keys = vec![key(bounds)];
3645 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(v0))
3646 };
3647
3648 assert_eq!(
3649 create(contract_bound.clone()).active_version_range(),
3650 ALL_VERSIONS
3651 );
3652 assert_eq!(
3653 create(group_bound.clone()).active_version_range(),
3654 14..=LATEST_VERSION
3655 );
3656 assert_eq!(update(contract_bound).active_version_range(), ALL_VERSIONS);
3657 assert_eq!(
3658 update(group_bound.clone()).active_version_range(),
3659 14..=LATEST_VERSION
3660 );
3661
3662 let bytes = PlatformSerializable::serialize_to_bytes(&update(group_bound))
3663 .expect("serialize succeeds");
3664 let version_13 = PlatformVersion::get(13).expect("platform version 13 exists");
3665 let err = StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_13)
3666 .expect_err("a contract group bound key is not active at protocol version 13");
3667 assert!(
3668 matches!(
3669 err,
3670 ProtocolError::StateTransitionError(
3671 StateTransitionError::StateTransitionIsNotActiveError { .. }
3672 )
3673 ),
3674 "{err:?}"
3675 );
3676 let version_14 = PlatformVersion::get(14).expect("platform version 14 exists");
3677 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_14)
3678 .expect("a contract group bound key is active at protocol version 14");
3679 }
3680
3681 use crate::state_transition::identity_credit_transfer_to_addresses_transition::v0::IdentityCreditTransferToAddressesTransitionV0;
3694 use crate::state_transition::identity_credit_transfer_to_addresses_transition::IdentityCreditTransferToAddressesTransition;
3695 use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1;
3696 use crate::withdrawal::Pooling as WithdrawalPooling;
3697
3698 fn sample_withdrawal_v1_st() -> StateTransition {
3699 let v1 = IdentityCreditWithdrawalTransitionV1 {
3700 identity_id: Identifier::from([12u8; 32]),
3701 amount: 777,
3702 core_fee_per_byte: 2,
3703 pooling: WithdrawalPooling::Standard,
3704 output_script: None,
3705 nonce: 9,
3706 user_fee_increase: 4,
3707 signature_public_key_id: 21,
3708 signature: BinaryData::new(vec![0x12; 65]),
3709 };
3710 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(v1))
3711 }
3712
3713 fn sample_credit_transfer_to_addresses_st() -> StateTransition {
3714 let v0 = IdentityCreditTransferToAddressesTransitionV0 {
3715 identity_id: Identifier::from([13u8; 32]),
3716 ..Default::default()
3717 };
3718 StateTransition::IdentityCreditTransferToAddresses(
3719 IdentityCreditTransferToAddressesTransition::V0(v0),
3720 )
3721 }
3722
3723 fn sample_address_credit_withdrawal_st() -> StateTransition {
3724 use crate::state_transition::address_credit_withdrawal_transition::v0::AddressCreditWithdrawalTransitionV0;
3725 StateTransition::AddressCreditWithdrawal(AddressCreditWithdrawalTransition::V0(
3726 AddressCreditWithdrawalTransitionV0::default(),
3727 ))
3728 }
3729
3730 fn sample_shield_from_asset_lock_st() -> StateTransition {
3731 use crate::state_transition::shield_from_asset_lock_transition::v0::ShieldFromAssetLockTransitionV0;
3732 StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V0(
3733 ShieldFromAssetLockTransitionV0 {
3734 asset_lock_proof: Default::default(),
3735 actions: vec![],
3736 value_balance: 100,
3737 anchor: [0u8; 32],
3738 proof: vec![],
3739 binding_signature: [0u8; 64],
3740 surplus_output: None,
3741 signature: BinaryData::new(vec![0x55; 65]),
3742 },
3743 ))
3744 }
3745
3746 #[test]
3749 fn test_withdrawal_v1_name_and_type() {
3750 let st = sample_withdrawal_v1_st();
3751 assert_eq!(st.name(), "IdentityCreditWithdrawal");
3752 assert_eq!(
3753 st.state_transition_type(),
3754 StateTransitionType::IdentityCreditWithdrawal
3755 );
3756 }
3757
3758 #[test]
3759 fn test_withdrawal_v1_is_identity_signed_true() {
3760 assert!(sample_withdrawal_v1_st().is_identity_signed());
3761 }
3762
3763 #[test]
3764 fn test_withdrawal_v1_signature_and_owner_and_key_id() {
3765 let st = sample_withdrawal_v1_st();
3766 let sig = st.signature().expect("V1 withdrawal has a signature");
3768 assert_eq!(sig.as_slice(), &[0x12; 65]);
3769 assert_eq!(st.owner_id(), Some(Identifier::from([12u8; 32])));
3771 assert_eq!(st.signature_public_key_id(), Some(21));
3772 assert_eq!(st.user_fee_increase(), 4);
3773 }
3774
3775 #[test]
3776 fn test_withdrawal_v1_set_signature_and_fee_and_key_id() {
3777 let mut st = sample_withdrawal_v1_st();
3778 assert!(st.set_signature(BinaryData::new(vec![0x99; 65])));
3779 assert_eq!(st.signature().unwrap().as_slice(), &[0x99; 65]);
3780
3781 st.set_user_fee_increase(33);
3782 assert_eq!(st.user_fee_increase(), 33);
3783
3784 st.set_signature_public_key_id(64);
3785 assert_eq!(st.signature_public_key_id(), Some(64));
3786 }
3787
3788 #[test]
3789 fn test_withdrawal_v1_serialize_roundtrip_via_state_transition() {
3790 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
3791 let original = sample_withdrawal_v1_st();
3792 let bytes = PlatformSerializable::serialize_to_bytes(&original).expect("serialize ok");
3793 let restored =
3794 StateTransition::deserialize_from_bytes_untrusted(&bytes).expect("deserialize ok");
3795 assert_eq!(original, restored);
3796 match restored {
3799 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(
3800 _,
3801 )) => {}
3802 other => panic!("expected V1 inner variant, got: {:?}", other),
3803 }
3804 }
3805
3806 #[test]
3807 fn test_withdrawal_v1_transaction_id_differs_from_v0() {
3808 let v0 = sample_withdrawal_st();
3810 let v1 = sample_withdrawal_v1_st();
3811 let id_v0 = v0.transaction_id().expect("v0 hash");
3812 let id_v1 = v1.transaction_id().expect("v1 hash");
3813 assert_ne!(id_v0, id_v1);
3814 }
3815
3816 #[test]
3817 fn test_withdrawal_v1_required_asset_lock_balance_errors() {
3818 let err = sample_withdrawal_v1_st()
3819 .required_asset_lock_balance_for_processing_start(PlatformVersion::latest())
3820 .expect_err("withdrawal is not an asset lock ST");
3821 matches!(err, ProtocolError::CorruptedCodeExecution(_));
3822 }
3823
3824 #[test]
3827 fn test_credit_transfer_to_addresses_name_and_type() {
3828 let st = sample_credit_transfer_to_addresses_st();
3829 assert_eq!(st.name(), "IdentityCreditTransferToAddresses");
3830 assert_eq!(
3831 st.state_transition_type(),
3832 StateTransitionType::IdentityCreditTransferToAddresses
3833 );
3834 }
3835
3836 #[test]
3837 fn test_credit_transfer_to_addresses_signature_some_and_owner_some() {
3838 let st = sample_credit_transfer_to_addresses_st();
3839 assert!(st.signature().is_some(), "has a signature field");
3840 assert_eq!(st.owner_id(), Some(Identifier::from([13u8; 32])));
3841 }
3842
3843 #[test]
3844 fn test_credit_transfer_to_addresses_is_identity_signed_true() {
3845 assert!(sample_credit_transfer_to_addresses_st().is_identity_signed());
3847 }
3848
3849 #[test]
3850 fn test_credit_transfer_to_addresses_inputs_none_active_range_11_latest() {
3851 let st = sample_credit_transfer_to_addresses_st();
3852 assert!(st.inputs().is_none());
3853 let range = st.active_version_range();
3855 assert_eq!(*range.start(), 11);
3856 assert_eq!(*range.end(), LATEST_VERSION);
3857 }
3858
3859 #[test]
3860 fn test_credit_transfer_to_addresses_set_signature_returns_true() {
3861 let mut st = sample_credit_transfer_to_addresses_st();
3862 let ok = st.set_signature(BinaryData::new(vec![0x77; 65]));
3863 assert!(ok);
3864 assert_eq!(st.signature().unwrap().as_slice(), &[0x77; 65]);
3865 }
3866
3867 #[test]
3868 fn test_credit_transfer_to_addresses_from_outer_enum() {
3869 let outer = IdentityCreditTransferToAddressesTransition::V0(
3870 IdentityCreditTransferToAddressesTransitionV0::default(),
3871 );
3872 let st: StateTransition = outer.into();
3873 assert!(matches!(
3874 st,
3875 StateTransition::IdentityCreditTransferToAddresses(_)
3876 ));
3877 }
3878
3879 #[test]
3880 fn test_credit_transfer_to_addresses_user_fee_increase_setter() {
3881 let mut st = sample_credit_transfer_to_addresses_st();
3882 st.set_user_fee_increase(55);
3883 assert_eq!(st.user_fee_increase(), 55);
3884 }
3885
3886 #[test]
3889 fn test_address_credit_withdrawal_name_type_and_accessors() {
3890 let st = sample_address_credit_withdrawal_st();
3891 assert_eq!(st.name(), "AddressCreditWithdrawal");
3892 assert_eq!(
3893 st.state_transition_type(),
3894 StateTransitionType::AddressCreditWithdrawal
3895 );
3896 assert!(st.signature().is_none());
3898 assert!(st.owner_id().is_none());
3900 assert!(st.inputs().is_some());
3902 }
3903
3904 #[test]
3905 fn test_address_credit_withdrawal_set_signature_returns_false() {
3906 let mut st = sample_address_credit_withdrawal_st();
3907 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3908 }
3909
3910 #[test]
3911 fn test_address_credit_withdrawal_is_identity_signed_true() {
3912 assert!(sample_address_credit_withdrawal_st().is_identity_signed());
3915 }
3916
3917 #[test]
3918 fn test_address_credit_withdrawal_active_range_is_11_latest() {
3919 let range = sample_address_credit_withdrawal_st().active_version_range();
3920 assert_eq!(*range.start(), 11);
3921 assert_eq!(*range.end(), LATEST_VERSION);
3922 }
3923
3924 #[test]
3927 fn test_shield_from_asset_lock_name_type_and_accessors() {
3928 let st = sample_shield_from_asset_lock_st();
3929 assert_eq!(st.name(), "ShieldFromAssetLock");
3930 assert_eq!(
3931 st.state_transition_type(),
3932 StateTransitionType::ShieldFromAssetLock
3933 );
3934 let sig = st
3936 .signature()
3937 .expect("shield-from-asset-lock has signature");
3938 assert_eq!(sig.as_slice(), &[0x55; 65]);
3939 assert!(st.owner_id().is_none());
3941 }
3942
3943 #[test]
3944 fn test_shield_from_asset_lock_is_not_identity_signed() {
3945 assert!(!sample_shield_from_asset_lock_st().is_identity_signed());
3946 }
3947
3948 #[test]
3949 fn test_shield_from_asset_lock_optional_asset_lock_proof_some() {
3950 let st = sample_shield_from_asset_lock_st();
3954 assert!(st.optional_asset_lock_proof().is_some());
3955 }
3956
3957 #[test]
3958 fn test_shield_from_asset_lock_user_fee_increase_is_zero_and_setter_noop() {
3959 let mut st = sample_shield_from_asset_lock_st();
3960 assert_eq!(st.user_fee_increase(), 0);
3961 st.set_user_fee_increase(123);
3962 assert_eq!(st.user_fee_increase(), 0);
3964 }
3965
3966 #[test]
3967 fn test_shield_from_asset_lock_set_signature_returns_true() {
3968 let mut st = sample_shield_from_asset_lock_st();
3969 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
3970 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
3971 }
3972
3973 #[test]
3974 fn test_shield_from_asset_lock_required_asset_lock_balance_succeeds() {
3975 let st = sample_shield_from_asset_lock_st();
3978 let result = st.required_asset_lock_balance_for_processing_start(PlatformVersion::latest());
3979 assert!(
3980 result.is_ok(),
3981 "ShieldFromAssetLock should return Ok, got {:?}",
3982 result
3983 );
3984 }
3985
3986 #[test]
3987 fn test_shield_from_asset_lock_active_range_12_latest() {
3988 let range = sample_shield_from_asset_lock_st().active_version_range();
3989 assert_eq!(*range.start(), 12);
3990 assert_eq!(*range.end(), LATEST_VERSION);
3991 }
3992
3993 #[test]
3997 fn test_batch_with_token_transfer_name_contains_token_transfer() {
3998 use crate::state_transition::batch_transition::batched_transition::token_transition::TokenTransition as TT;
3999 use crate::state_transition::batch_transition::batched_transition::BatchedTransition;
4000 use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0;
4001 use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition;
4002 use crate::state_transition::batch_transition::token_transfer_transition::v0::TokenTransferTransitionV0;
4003 use crate::state_transition::batch_transition::token_transfer_transition::TokenTransferTransition;
4004 use crate::state_transition::batch_transition::BatchTransitionV1;
4005
4006 let base = TokenBaseTransition::V0(TokenBaseTransitionV0 {
4007 identity_contract_nonce: 1,
4008 token_contract_position: 0,
4009 data_contract_id: Identifier::from([1u8; 32]),
4010 token_id: Identifier::from([2u8; 32]),
4011 using_group_info: None,
4012 });
4013 let token_transfer = TokenTransferTransition::V0(TokenTransferTransitionV0 {
4014 base,
4015 amount: 100,
4016 recipient_id: Identifier::from([3u8; 32]),
4017 public_note: None,
4018 shared_encrypted_note: None,
4019 private_encrypted_note: None,
4020 });
4021
4022 let batch = BatchTransition::V1(BatchTransitionV1 {
4024 owner_id: Identifier::from([9u8; 32]),
4025 transitions: vec![BatchedTransition::Token(TT::Transfer(token_transfer))],
4026 user_fee_increase: 0,
4027 signature_public_key_id: 0,
4028 signature: BinaryData::new(vec![0u8; 65]),
4029 });
4030 let st = StateTransition::Batch(batch);
4031
4032 assert_eq!(st.name(), "DocumentsBatch([TokenTransfer])");
4033 }
4034
4035 #[test]
4042 fn test_transaction_id_length_32_for_new_variants() {
4043 for st in [
4044 sample_withdrawal_v1_st(),
4045 sample_credit_transfer_to_addresses_st(),
4046 sample_shield_from_asset_lock_st(),
4047 ] {
4048 let id = st.transaction_id().expect("hash");
4049 assert_eq!(id.len(), 32);
4050 }
4051 }
4052
4053 #[test]
4059 fn test_clone_eq_for_new_variants() {
4060 let cases = [
4061 sample_withdrawal_v1_st(),
4062 sample_credit_transfer_to_addresses_st(),
4063 sample_address_credit_withdrawal_st(),
4064 sample_shield_from_asset_lock_st(),
4065 ];
4066 for st in cases {
4067 let cloned = st.clone();
4068 assert_eq!(st, cloned, "clone must be equal for {}", st.name());
4069 }
4070 }
4071
4072 #[test]
4078 fn test_unique_identifiers_for_address_and_shielded_variants() {
4079 for st in [
4084 sample_address_credit_withdrawal_st(),
4085 sample_shield_from_asset_lock_st(),
4086 sample_credit_transfer_to_addresses_st(),
4087 sample_withdrawal_v1_st(),
4088 ] {
4089 let _ids = st.unique_identifiers();
4093 }
4094 assert!(!sample_withdrawal_v1_st().unique_identifiers().is_empty());
4096 assert!(!sample_credit_transfer_to_addresses_st()
4097 .unique_identifiers()
4098 .is_empty());
4099 }
4100
4101 #[cfg(all(
4113 feature = "state-transition-signing",
4114 feature = "core_key_wallet",
4115 feature = "bls-signatures"
4116 ))]
4117 #[tokio::test]
4118 async fn sign_with_core_signer_matches_sign_by_private_key_byte_for_byte() {
4119 use async_trait::async_trait;
4120 use dashcore::secp256k1::{
4121 ecdsa, rand::rngs::OsRng, Message, PublicKey, Secp256k1, SecretKey,
4122 };
4123 use key_wallet::bip32::{DerivationPath, ExtendedPubKey};
4124 use key_wallet::signer::{ExtendedPubKeySigner, Signer as KwSigner, SignerMethod};
4125
4126 #[derive(Debug)]
4131 struct FixedKeySigner {
4132 secret: SecretKey,
4133 public: PublicKey,
4134 }
4135
4136 #[async_trait]
4137 impl KwSigner for FixedKeySigner {
4138 type Error = String;
4139
4140 fn supported_methods(&self) -> &[SignerMethod] {
4141 &[SignerMethod::Digest]
4142 }
4143
4144 async fn sign_ecdsa(
4145 &self,
4146 _path: &DerivationPath,
4147 sighash: [u8; 32],
4148 ) -> Result<(ecdsa::Signature, PublicKey), Self::Error> {
4149 let secp = Secp256k1::new();
4150 let msg = Message::from_digest(sighash);
4151 let sig = secp.sign_ecdsa(&msg, &self.secret);
4152 Ok((sig, self.public))
4153 }
4154
4155 async fn public_key(&self, _path: &DerivationPath) -> Result<PublicKey, Self::Error> {
4156 Ok(self.public)
4157 }
4158 }
4159
4160 #[async_trait]
4161 impl ExtendedPubKeySigner for FixedKeySigner {
4162 async fn extended_public_key(
4163 &self,
4164 _path: &DerivationPath,
4165 ) -> Result<ExtendedPubKey, Self::Error> {
4166 Err("FixedKeySigner does not derive extended public keys".to_string())
4167 }
4168 }
4169
4170 let secp = Secp256k1::new();
4175 let (secret_key, public_key) = secp.generate_keypair(&mut OsRng);
4176 let private_key_bytes = secret_key.secret_bytes();
4177
4178 let signer = FixedKeySigner {
4179 secret: secret_key,
4180 public: public_key,
4181 };
4182 let path = DerivationPath::default();
4183
4184 let mut st_legacy = sample_transfer_st();
4188 let mut st_signer = sample_transfer_st();
4189
4190 assert_eq!(
4192 st_legacy.signable_bytes().expect("legacy signable_bytes"),
4193 st_signer.signable_bytes().expect("signer signable_bytes"),
4194 "signable_bytes pre-image must match across copies"
4195 );
4196
4197 let bls = crate::bls::native_bls::NativeBlsModule;
4202 st_legacy
4203 .sign_by_private_key(&private_key_bytes, KeyType::ECDSA_HASH160, &bls)
4204 .expect("sign_by_private_key");
4205
4206 st_signer
4209 .sign_with_core_signer(&path, &signer)
4210 .await
4211 .expect("sign_with_core_signer");
4212
4213 let sig_legacy = st_legacy.signature().expect("legacy signature set");
4214 let sig_signer = st_signer.signature().expect("signer signature set");
4215
4216 assert_eq!(
4217 sig_legacy.as_slice().len(),
4218 65,
4219 "legacy ECDSA signature must be 65 bytes (recoverable compact)"
4220 );
4221 assert_eq!(
4222 sig_signer.as_slice().len(),
4223 65,
4224 "signer ECDSA signature must be 65 bytes (recoverable compact)"
4225 );
4226 assert_eq!(
4227 sig_legacy.as_slice(),
4228 sig_signer.as_slice(),
4229 "sign_with_core_signer must produce byte-identical output to sign_by_private_key"
4230 );
4231 }
4232}