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::feature_initial_protocol_versions::YES_NO_VOTE_POLL_INITIAL_PROTOCOL_VERSION;
23use platform_version::version::{PlatformVersion, ProtocolVersion, ALL_VERSIONS, LATEST_VERSION};
24
25#[cfg(any(
26 feature = "state-transition-signing",
27 feature = "state-transition-validation"
28))]
29use crate::BlsModule;
30use crate::ProtocolError;
31
32mod state_transition_types;
33
34pub mod state_transition_factory;
35
36pub mod errors;
37#[cfg(feature = "state-transition-signing")]
38use crate::util::hash::ripemd160_sha256;
39use crate::util::hash::{hash_double_to_vec, hash_single};
40
41pub mod proof_result;
42mod serialization;
43pub mod state_transitions;
44mod traits;
45#[cfg(feature = "state-transition-validation")]
46mod verify_identity_signed_signature;
47
48use crate::consensus::basic::state_transition::StateTransitionMaxSizeExceededError;
51#[cfg(feature = "state-transition-validation")]
52use crate::consensus::basic::UnsupportedFeatureError;
53#[cfg(feature = "state-transition-signing")]
54use crate::consensus::signature::InvalidSignaturePublicKeySecurityLevelError;
55#[cfg(feature = "state-transition-signing")]
56use crate::consensus::signature::{
57 ContractBoundedKeyNonBatchError, ContractBoundedKeyOutOfBoundsError,
58};
59#[cfg(feature = "state-transition-validation")]
60use crate::consensus::signature::{InvalidStateTransitionSignatureError, SignatureError};
61#[cfg(feature = "state-transition-validation")]
62use crate::consensus::ConsensusError;
63pub use traits::*;
64
65use crate::address_funds::PlatformAddress;
66use crate::data_contract::config::DataContractConfig;
67use crate::data_contract::serialized_version::DataContractInSerializationFormat;
68use crate::fee::Credits;
69#[cfg(feature = "state-transition-signing")]
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(feature = "state-transition-signing")]
78use crate::identity::{IdentityPublicKey, KeyType};
79use crate::identity::{KeyID, SecurityLevel};
80use crate::prelude::{AddressNonce, AssetLockProof, UserFeeIncrease};
81#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
82use crate::serialization::JsonConvertible;
83#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
84use crate::serialization::ValueConvertible;
85use crate::serialization::{PlatformDeserializableUntrusted, Signable};
86use crate::state_transition::address_credit_withdrawal_transition::{
87 AddressCreditWithdrawalTransition, AddressCreditWithdrawalTransitionSignable,
88};
89use crate::state_transition::address_funding_from_asset_lock_transition::{
90 AddressFundingFromAssetLockTransition, AddressFundingFromAssetLockTransitionSignable,
91};
92use crate::state_transition::address_funds_transfer_transition::{
93 AddressFundsTransferTransition, AddressFundsTransferTransitionSignable,
94};
95use crate::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0;
96use crate::state_transition::batch_transition::batched_transition::document_transition::DocumentTransitionV0Methods;
97use crate::state_transition::batch_transition::batched_transition::BatchedTransitionRef;
98#[cfg(feature = "state-transition-signing")]
99use crate::state_transition::batch_transition::resolvers::v0::BatchTransitionResolversV0;
100use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionSignable};
101use crate::state_transition::contract_fee_claim_transition::{
102 ContractFeeClaimTransition, ContractFeeClaimTransitionSignable,
103};
104use crate::state_transition::contract_user_moderation_transition::{
105 ContractUserModerationTransition, ContractUserModerationTransitionSignable,
106};
107use crate::state_transition::data_contract_create_transition::accessors::DataContractCreateTransitionAccessorsV0;
108use crate::state_transition::data_contract_create_transition::{
109 DataContractCreateTransition, DataContractCreateTransitionSignable,
110};
111use crate::state_transition::data_contract_update_transition::accessors::DataContractUpdateTransitionAccessorsV0;
112use crate::state_transition::data_contract_update_transition::{
113 DataContractUpdateTransition, DataContractUpdateTransitionSignable,
114};
115#[cfg(feature = "state-transition-signing")]
116use crate::state_transition::errors::InvalidIdentityPublicKeyTypeError;
117#[cfg(feature = "state-transition-signing")]
118use crate::state_transition::errors::InvalidSignaturePublicKeyError;
119#[cfg(all(feature = "state-transitions", feature = "validation"))]
120use crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError;
121#[cfg(feature = "state-transition-validation")]
122use crate::state_transition::errors::StateTransitionIsNotSignedError;
123#[cfg(feature = "state-transition-signing")]
124use crate::state_transition::errors::WrongPublicKeyPurposeError;
125use crate::state_transition::identity_create_from_addresses_transition::accessors::IdentityCreateFromAddressesTransitionAccessorsV0;
126use crate::state_transition::identity_create_from_addresses_transition::{
127 IdentityCreateFromAddressesTransition, IdentityCreateFromAddressesTransitionSignable,
128};
129use crate::state_transition::identity_create_from_shielded_pool_transition::accessors::IdentityCreateFromShieldedPoolTransitionAccessorsV0;
130use crate::state_transition::identity_create_from_shielded_pool_transition::{
131 IdentityCreateFromShieldedPoolTransition, IdentityCreateFromShieldedPoolTransitionSignable,
132};
133use crate::state_transition::identity_create_transition::accessors::IdentityCreateTransitionAccessorsV0;
134use crate::state_transition::identity_create_transition::{
135 IdentityCreateTransition, IdentityCreateTransitionSignable,
136};
137use crate::state_transition::identity_credit_transfer_to_addresses_transition::{
138 IdentityCreditTransferToAddressesTransition,
139 IdentityCreditTransferToAddressesTransitionSignable,
140};
141use crate::state_transition::identity_credit_transfer_transition::{
142 IdentityCreditTransferTransition, IdentityCreditTransferTransitionSignable,
143};
144use crate::state_transition::identity_credit_withdrawal_transition::{
145 IdentityCreditWithdrawalTransition, IdentityCreditWithdrawalTransitionSignable,
146};
147use crate::state_transition::identity_key_limits_update_transition::{
148 IdentityKeyLimitsUpdateTransition, IdentityKeyLimitsUpdateTransitionSignable,
149};
150use crate::state_transition::identity_top_up_from_shielded_pool_transition::{
151 IdentityTopUpFromShieldedPoolTransition, IdentityTopUpFromShieldedPoolTransitionSignable,
152};
153use crate::state_transition::identity_topup_from_addresses_transition::{
154 IdentityTopUpFromAddressesTransition, IdentityTopUpFromAddressesTransitionSignable,
155};
156use crate::state_transition::identity_topup_transition::{
157 IdentityTopUpTransition, IdentityTopUpTransitionSignable,
158};
159use crate::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0;
160use crate::state_transition::identity_update_transition::{
161 IdentityUpdateTransition, IdentityUpdateTransitionSignable,
162};
163use crate::state_transition::masternode_vote_transition::accessors::MasternodeVoteTransitionAccessorsV0;
164use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
165use crate::state_transition::masternode_vote_transition::MasternodeVoteTransitionSignable;
166use crate::state_transition::public_key_in_creation::IdentityPublicKeyInCreation;
167use crate::state_transition::shield_from_asset_lock_transition::{
168 ShieldFromAssetLockTransition, ShieldFromAssetLockTransitionSignable,
169};
170use crate::state_transition::shield_from_identity_transition::{
171 ShieldFromIdentityTransition, ShieldFromIdentityTransitionSignable,
172};
173use crate::state_transition::shield_transition::{ShieldTransition, ShieldTransitionSignable};
174use crate::state_transition::shielded_transfer_transition::{
175 ShieldedTransferTransition, ShieldedTransferTransitionSignable,
176};
177use crate::state_transition::shielded_withdrawal_transition::{
178 ShieldedWithdrawalTransition, ShieldedWithdrawalTransitionSignable,
179};
180#[cfg(feature = "state-transition-signing")]
181use crate::state_transition::state_transitions::document::batch_transition::methods::v0::DocumentsBatchTransitionMethodsV0;
182use crate::state_transition::token_purchase_from_shielded_pool_transition::{
183 TokenPurchaseFromShieldedPoolTransition, TokenPurchaseFromShieldedPoolTransitionSignable,
184};
185use crate::state_transition::token_shielded_transfer_with_shielded_fee_transition::{
186 TokenShieldedTransferWithShieldedFeeTransition,
187 TokenShieldedTransferWithShieldedFeeTransitionSignable,
188};
189use crate::state_transition::token_unshield_with_shielded_fee_transition::{
190 TokenUnshieldWithShieldedFeeTransition, TokenUnshieldWithShieldedFeeTransitionSignable,
191};
192use crate::state_transition::unshield_transition::{
193 UnshieldTransition, UnshieldTransitionSignable,
194};
195use crate::voting::vote_polls::VotePoll;
196use crate::voting::votes::resource_vote::accessors::v0::ResourceVoteGettersV0;
197use crate::voting::votes::Vote;
198use state_transitions::document::batch_transition::batched_transition::token_transition::TokenTransition;
199pub use state_transitions::*;
200
201pub type GetDataContractSecurityLevelRequirementFn =
202 fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>;
203
204macro_rules! call_method {
205 ($state_transition:expr, $method:ident, $args:tt ) => {
206 match $state_transition {
207 StateTransition::DataContractCreate(st) => st.$method($args),
208 StateTransition::DataContractUpdate(st) => st.$method($args),
209 StateTransition::ContractUserModeration(st) => st.$method($args),
210 StateTransition::ContractFeeClaim(st) => st.$method($args),
211 StateTransition::Batch(st) => st.$method($args),
212 StateTransition::IdentityCreate(st) => st.$method($args),
213 StateTransition::IdentityTopUp(st) => st.$method($args),
214 StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
215 StateTransition::IdentityUpdate(st) => st.$method($args),
216 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method($args),
217 StateTransition::IdentityCreditTransfer(st) => st.$method($args),
218 StateTransition::MasternodeVote(st) => st.$method($args),
219 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
220 StateTransition::IdentityCreateFromAddresses(st) => st.$method($args),
221 StateTransition::IdentityTopUpFromAddresses(st) => st.$method($args),
222 StateTransition::AddressFundsTransfer(st) => st.$method($args),
223 StateTransition::AddressFundingFromAssetLock(st) => st.$method($args),
224 StateTransition::AddressCreditWithdrawal(st) => st.$method($args),
225 StateTransition::Shield(st) => st.$method($args),
226 StateTransition::ShieldedTransfer(st) => st.$method($args),
227 StateTransition::Unshield(st) => st.$method($args),
228 StateTransition::IdentityTopUpFromShieldedPool(st) => st.$method($args),
229 StateTransition::TokenShieldedTransferWithShieldedFee(st) => st.$method($args),
230 StateTransition::TokenUnshieldWithShieldedFee(st) => st.$method($args),
231 StateTransition::TokenPurchaseFromShieldedPool(st) => st.$method($args),
232 StateTransition::ShieldFromAssetLock(st) => st.$method($args),
233 StateTransition::ShieldedWithdrawal(st) => st.$method($args),
234 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method($args),
235 StateTransition::ShieldFromIdentity(st) => st.$method($args),
236 }
237 };
238 ($state_transition:expr, $method:ident ) => {
239 match $state_transition {
240 StateTransition::DataContractCreate(st) => st.$method(),
241 StateTransition::DataContractUpdate(st) => st.$method(),
242 StateTransition::ContractUserModeration(st) => st.$method(),
243 StateTransition::ContractFeeClaim(st) => st.$method(),
244 StateTransition::Batch(st) => st.$method(),
245 StateTransition::IdentityCreate(st) => st.$method(),
246 StateTransition::IdentityTopUp(st) => st.$method(),
247 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
248 StateTransition::IdentityUpdate(st) => st.$method(),
249 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method(),
250 StateTransition::IdentityCreditTransfer(st) => st.$method(),
251 StateTransition::MasternodeVote(st) => st.$method(),
252 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
253 StateTransition::IdentityCreateFromAddresses(st) => st.$method(),
254 StateTransition::IdentityTopUpFromAddresses(st) => st.$method(),
255 StateTransition::AddressFundsTransfer(st) => st.$method(),
256 StateTransition::AddressFundingFromAssetLock(st) => st.$method(),
257 StateTransition::AddressCreditWithdrawal(st) => st.$method(),
258 StateTransition::Shield(st) => st.$method(),
259 StateTransition::ShieldedTransfer(st) => st.$method(),
260 StateTransition::Unshield(st) => st.$method(),
261 StateTransition::IdentityTopUpFromShieldedPool(st) => st.$method(),
262 StateTransition::TokenShieldedTransferWithShieldedFee(st) => st.$method(),
263 StateTransition::TokenUnshieldWithShieldedFee(st) => st.$method(),
264 StateTransition::TokenPurchaseFromShieldedPool(st) => st.$method(),
265 StateTransition::ShieldFromAssetLock(st) => st.$method(),
266 StateTransition::ShieldedWithdrawal(st) => st.$method(),
267 StateTransition::IdentityCreateFromShieldedPool(st) => st.$method(),
268 StateTransition::ShieldFromIdentity(st) => st.$method(),
269 }
270 };
271}
272
273macro_rules! call_getter_method_identity_signed {
274 ($state_transition:expr, $method:ident, $args:tt ) => {
275 match $state_transition {
276 StateTransition::DataContractCreate(st) => Some(st.$method($args)),
277 StateTransition::DataContractUpdate(st) => Some(st.$method($args)),
278 StateTransition::ContractUserModeration(st) => Some(st.$method($args)),
279 StateTransition::ContractFeeClaim(st) => Some(st.$method($args)),
280 StateTransition::Batch(st) => Some(st.$method($args)),
281 StateTransition::IdentityCreate(_) => None,
282 StateTransition::IdentityTopUp(_) => None,
283 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method($args)),
284 StateTransition::IdentityUpdate(st) => Some(st.$method($args)),
285 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.$method($args)),
286 StateTransition::IdentityCreditTransfer(st) => Some(st.$method($args)),
287 StateTransition::MasternodeVote(st) => Some(st.$method($args)),
288 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method($args)),
289 StateTransition::IdentityCreateFromAddresses(_) => None,
290 StateTransition::IdentityTopUpFromAddresses(_) => None,
291 StateTransition::AddressFundsTransfer(_) => None,
292 StateTransition::AddressFundingFromAssetLock(_) => None,
293 StateTransition::AddressCreditWithdrawal(_) => None,
294 StateTransition::Shield(_) => None,
295 StateTransition::ShieldedTransfer(_) => None,
296 StateTransition::Unshield(_) => None,
297 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
298 StateTransition::TokenShieldedTransferWithShieldedFee(_) => None,
299 StateTransition::TokenUnshieldWithShieldedFee(_) => None,
300 StateTransition::TokenPurchaseFromShieldedPool(_) => None,
301 StateTransition::ShieldFromAssetLock(_) => None,
302 StateTransition::ShieldedWithdrawal(_) => None,
303 StateTransition::IdentityCreateFromShieldedPool(_) => None,
304 StateTransition::ShieldFromIdentity(st) => Some(st.$method($args)),
305 }
306 };
307 ($state_transition:expr, $method:ident ) => {
308 match $state_transition {
309 StateTransition::DataContractCreate(st) => Some(st.$method()),
310 StateTransition::DataContractUpdate(st) => Some(st.$method()),
311 StateTransition::ContractUserModeration(st) => Some(st.$method()),
312 StateTransition::ContractFeeClaim(st) => Some(st.$method()),
313 StateTransition::Batch(st) => Some(st.$method()),
314 StateTransition::IdentityCreate(_) => None,
315 StateTransition::IdentityTopUp(_) => None,
316 StateTransition::IdentityCreditWithdrawal(st) => Some(st.$method()),
317 StateTransition::IdentityUpdate(st) => Some(st.$method()),
318 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.$method()),
319 StateTransition::IdentityCreditTransfer(st) => Some(st.$method()),
320 StateTransition::MasternodeVote(st) => Some(st.$method()),
321 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.$method()),
322 StateTransition::IdentityCreateFromAddresses(_) => None,
323 StateTransition::IdentityTopUpFromAddresses(_) => None,
324 StateTransition::AddressFundsTransfer(_) => None,
325 StateTransition::AddressFundingFromAssetLock(_) => None,
326 StateTransition::AddressCreditWithdrawal(_) => None,
327 StateTransition::Shield(_) => None,
328 StateTransition::ShieldedTransfer(_) => None,
329 StateTransition::Unshield(_) => None,
330 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
331 StateTransition::TokenShieldedTransferWithShieldedFee(_) => None,
332 StateTransition::TokenUnshieldWithShieldedFee(_) => None,
333 StateTransition::TokenPurchaseFromShieldedPool(_) => None,
334 StateTransition::ShieldFromAssetLock(_) => None,
335 StateTransition::ShieldedWithdrawal(_) => None,
336 StateTransition::IdentityCreateFromShieldedPool(_) => None,
337 StateTransition::ShieldFromIdentity(st) => Some(st.$method()),
338 }
339 };
340}
341
342macro_rules! call_method_identity_signed {
343 ($state_transition:expr, $method:ident, $args:tt ) => {
344 match $state_transition {
345 StateTransition::DataContractCreate(st) => st.$method($args),
346 StateTransition::DataContractUpdate(st) => st.$method($args),
347 StateTransition::ContractUserModeration(st) => st.$method($args),
348 StateTransition::ContractFeeClaim(st) => st.$method($args),
349 StateTransition::Batch(st) => st.$method($args),
350 StateTransition::IdentityCreate(_st) => {}
351 StateTransition::IdentityTopUp(_st) => {}
352 StateTransition::IdentityCreditWithdrawal(st) => st.$method($args),
353 StateTransition::IdentityUpdate(st) => st.$method($args),
354 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method($args),
355 StateTransition::IdentityCreditTransfer(st) => st.$method($args),
356 StateTransition::MasternodeVote(st) => st.$method($args),
357 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($args),
358 StateTransition::IdentityCreateFromAddresses(_) => {}
359 StateTransition::IdentityTopUpFromAddresses(_) => {}
360 StateTransition::AddressFundsTransfer(_) => {}
361 StateTransition::AddressFundingFromAssetLock(_) => {}
362 StateTransition::AddressCreditWithdrawal(_) => {}
363 StateTransition::Shield(_) => {}
364 StateTransition::ShieldedTransfer(_) => {}
365 StateTransition::Unshield(_) => {}
366 StateTransition::IdentityTopUpFromShieldedPool(_) => {}
367 StateTransition::TokenShieldedTransferWithShieldedFee(_) => {}
368 StateTransition::TokenUnshieldWithShieldedFee(_) => {}
369 StateTransition::TokenPurchaseFromShieldedPool(_) => {}
370 StateTransition::ShieldFromAssetLock(_) => {}
371 StateTransition::ShieldedWithdrawal(_) => {}
372 StateTransition::IdentityCreateFromShieldedPool(_) => {}
373 StateTransition::ShieldFromIdentity(st) => st.$method($args),
374 }
375 };
376 ($state_transition:expr, $method:ident ) => {
377 match $state_transition {
378 StateTransition::DataContractCreate(st) => st.$method(),
379 StateTransition::DataContractUpdate(st) => st.$method(),
380 StateTransition::ContractUserModeration(st) => st.$method(),
381 StateTransition::ContractFeeClaim(st) => st.$method(),
382 StateTransition::Batch(st) => st.$method(),
383 StateTransition::IdentityCreate(st) => {}
384 StateTransition::IdentityTopUp(st) => {}
385 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
386 StateTransition::IdentityUpdate(st) => st.$method(),
387 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method(),
388 StateTransition::IdentityCreditTransfer(st) => st.$method(),
389 StateTransition::MasternodeVote(st) => st.$method(),
390 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
391 StateTransition::IdentityCreateFromAddresses(_) => {}
392 StateTransition::IdentityTopUpFromAddresses(_) => {}
393 StateTransition::AddressFundsTransfer(_) => {}
394 StateTransition::AddressFundingFromAssetLock(_) => {}
395 StateTransition::AddressCreditWithdrawal(_) => {}
396 StateTransition::Shield(_) => {}
397 StateTransition::ShieldedTransfer(_) => {}
398 StateTransition::Unshield(_) => {}
399 StateTransition::IdentityTopUpFromShieldedPool(_) => {}
400 StateTransition::TokenShieldedTransferWithShieldedFee(_) => {}
401 StateTransition::TokenUnshieldWithShieldedFee(_) => {}
402 StateTransition::TokenPurchaseFromShieldedPool(_) => {}
403 StateTransition::ShieldFromAssetLock(_) => {}
404 StateTransition::ShieldedWithdrawal(_) => {}
405 StateTransition::IdentityCreateFromShieldedPool(_) => {}
406 StateTransition::ShieldFromIdentity(st) => st.$method(),
407 }
408 };
409}
410
411#[cfg(feature = "state-transition-signing")]
412macro_rules! call_errorable_method_identity_signed {
413 ($state_transition:expr, $method:ident, $( $arg:expr ),* ) => {
414 match $state_transition {
415 StateTransition::DataContractCreate(st) => st.$method($( $arg ),*),
416 StateTransition::DataContractUpdate(st) => st.$method($( $arg ),*),
417 StateTransition::ContractUserModeration(st) => st.$method($( $arg ),*),
418 StateTransition::ContractFeeClaim(st) => st.$method($( $arg ),*),
419 StateTransition::Batch(st) => st.$method($( $arg ),*),
420 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
421 "identity create can not be called for identity signing".to_string(),
422 )),
423 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
424 "identity top up can not be called for identity signing".to_string(),
425 )),
426 StateTransition::IdentityCreditWithdrawal(st) => st.$method($( $arg ),*),
427 StateTransition::IdentityUpdate(st) => st.$method($( $arg ),*),
428 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method($( $arg ),*),
429 StateTransition::IdentityCreditTransfer(st) => st.$method($( $arg ),*),
430 StateTransition::MasternodeVote(st) => st.$method($( $arg ),*),
431 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method($( $arg ),*),
432 StateTransition::IdentityCreateFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
433 "identity create from addresses can not be called for identity signing".to_string(),
434 )),
435 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
436 "identity top up from addresses can not be called for identity signing".to_string(),
437 )),
438 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
439 "address funds transfer can not be called for identity signing".to_string(),
440 )),
441 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
442 "address funding from asset lock can not be called for identity signing".to_string(),
443 )),
444 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
445 "address credit withdrawal can not be called for identity signing".to_string(),
446 )),
447 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
448 "shield transition can not be called for identity signing".to_string(),
449 )),
450 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
451 "shielded transfer transition can not be called for identity signing".to_string(),
452 )),
453 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
454 "unshield transition can not be called for identity signing".to_string(),
455 )),
456 StateTransition::IdentityTopUpFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
457 "identity top up from shielded pool transition can not be called for identity signing".to_string(),
458 )),
459 StateTransition::TokenShieldedTransferWithShieldedFee(_) => Err(ProtocolError::CorruptedCodeExecution(
460 "token shielded transfer with shielded fee transition can not be called for identity signing".to_string(),
461 )),
462 StateTransition::TokenUnshieldWithShieldedFee(_) => Err(ProtocolError::CorruptedCodeExecution(
463 "token unshield with shielded fee transition can not be called for identity signing".to_string(),
464 )),
465 StateTransition::TokenPurchaseFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
466 "token purchase from shielded pool transition can not be called for identity signing".to_string(),
467 )),
468 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
469 "shield from asset lock transition can not be called for identity signing".to_string(),
470 )),
471 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
472 "shielded withdrawal transition can not be called for identity signing".to_string(),
473 )),
474 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
475 "identity create from shielded pool transition can not be called for identity signing".to_string(),
476 )),
477 StateTransition::ShieldFromIdentity(st) => st.$method($( $arg ),*),
478 }
479 };
480 ($state_transition:expr, $method:ident) => {
481 match $state_transition {
482 StateTransition::DataContractCreate(st) => st.$method(),
483 StateTransition::DataContractUpdate(st) => st.$method(),
484 StateTransition::ContractUserModeration(st) => st.$method(),
485 StateTransition::ContractFeeClaim(st) => st.$method(),
486 StateTransition::Batch(st) => st.$method(),
487 StateTransition::IdentityCreate(_) => Err(ProtocolError::CorruptedCodeExecution(
488 "identity create can not be called for identity signing".to_string(),
489 )),
490 StateTransition::IdentityTopUp(_) => Err(ProtocolError::CorruptedCodeExecution(
491 "identity top up can not be called for identity signing".to_string(),
492 )),
493 StateTransition::IdentityCreditWithdrawal(st) => st.$method(),
494 StateTransition::IdentityUpdate(st) => st.$method(),
495 StateTransition::IdentityKeyLimitsUpdate(st) => st.$method(),
496 StateTransition::IdentityCreditTransfer(st) => st.$method(),
497 StateTransition::MasternodeVote(st) => st.$method(),
498 StateTransition::IdentityCreditTransferToAddresses(st) => st.$method(),
499 StateTransition::IdentityCreateFromAddresses(st) => Err(ProtocolError::CorruptedCodeExecution(
500 "identity create from addresses can not be called for identity signing".to_string(),
501 )),
502 StateTransition::IdentityTopUpFromAddresses(_) => Err(ProtocolError::CorruptedCodeExecution(
503 "identity top up from addresses can not be called for identity signing".to_string(),
504 )),
505 StateTransition::AddressFundsTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
506 "address funds transfer can not be called for identity signing".to_string(),
507 )),
508 StateTransition::AddressFundingFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
509 "address funding from asset lock can not be called for identity signing".to_string(),
510 )),
511 StateTransition::AddressCreditWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
512 "address credit withdrawal can not be called for identity signing".to_string(),
513 )),
514 StateTransition::Shield(_) => Err(ProtocolError::CorruptedCodeExecution(
515 "shield transition can not be called for identity signing".to_string(),
516 )),
517 StateTransition::ShieldedTransfer(_) => Err(ProtocolError::CorruptedCodeExecution(
518 "shielded transfer transition can not be called for identity signing".to_string(),
519 )),
520 StateTransition::Unshield(_) => Err(ProtocolError::CorruptedCodeExecution(
521 "unshield transition can not be called for identity signing".to_string(),
522 )),
523 StateTransition::IdentityTopUpFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
524 "identity top up from shielded pool transition can not be called for identity signing".to_string(),
525 )),
526 StateTransition::TokenShieldedTransferWithShieldedFee(_) => Err(ProtocolError::CorruptedCodeExecution(
527 "token shielded transfer with shielded fee transition can not be called for identity signing".to_string(),
528 )),
529 StateTransition::TokenUnshieldWithShieldedFee(_) => Err(ProtocolError::CorruptedCodeExecution(
530 "token unshield with shielded fee transition can not be called for identity signing".to_string(),
531 )),
532 StateTransition::TokenPurchaseFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
533 "token purchase from shielded pool transition can not be called for identity signing".to_string(),
534 )),
535 StateTransition::ShieldFromAssetLock(_) => Err(ProtocolError::CorruptedCodeExecution(
536 "shield from asset lock transition can not be called for identity signing".to_string(),
537 )),
538 StateTransition::ShieldedWithdrawal(_) => Err(ProtocolError::CorruptedCodeExecution(
539 "shielded withdrawal transition can not be called for identity signing".to_string(),
540 )),
541 StateTransition::IdentityCreateFromShieldedPool(_) => Err(ProtocolError::CorruptedCodeExecution(
542 "identity create from shielded pool transition can not be called for identity signing".to_string(),
543 )),
544 StateTransition::ShieldFromIdentity(st) => st.$method(),
545 }
546 };
547}
548
549pub const STATE_TRANSITION_MAX_ENCODED_BYTES: usize = 100_000;
552
553pub fn validate_state_transition_size(
565 size_bytes: u64,
566 platform_version: &PlatformVersion,
567) -> Result<(), StateTransitionMaxSizeExceededError> {
568 let max_size_bytes = platform_version.system_limits.max_state_transition_size;
569 if size_bytes > max_size_bytes {
570 return Err(StateTransitionMaxSizeExceededError::new(
571 size_bytes,
572 max_size_bytes,
573 ));
574 }
575 Ok(())
576}
577
578#[derive(
579 Debug,
580 Clone,
581 Encode,
582 Decode,
583 PlatformSerialize,
584 PlatformDeserializeTrusted,
585 PlatformDeserializeUntrusted,
586 PlatformSignable,
587 From,
588 PartialEq,
589 DecodeUntrusted,
590)]
591#[cfg_attr(
611 feature = "serde-conversion",
612 derive(Serialize, Deserialize),
613 serde(tag = "$type", rename_all = "camelCase")
614)]
615#[platform_serialize(unversioned)] #[platform_serialize(limit = 100000)]
617pub enum StateTransition {
618 DataContractCreate(DataContractCreateTransition),
619 DataContractUpdate(DataContractUpdateTransition),
620 Batch(BatchTransition),
621 IdentityCreate(IdentityCreateTransition),
622 IdentityTopUp(IdentityTopUpTransition),
623 IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition),
624 IdentityUpdate(IdentityUpdateTransition),
625 IdentityCreditTransfer(IdentityCreditTransferTransition),
626 MasternodeVote(MasternodeVoteTransition),
627 IdentityCreditTransferToAddresses(IdentityCreditTransferToAddressesTransition),
628 IdentityCreateFromAddresses(IdentityCreateFromAddressesTransition),
629 IdentityTopUpFromAddresses(IdentityTopUpFromAddressesTransition),
630 AddressFundsTransfer(AddressFundsTransferTransition),
631 AddressFundingFromAssetLock(AddressFundingFromAssetLockTransition),
632 AddressCreditWithdrawal(AddressCreditWithdrawalTransition),
633 Shield(ShieldTransition),
634 ShieldedTransfer(ShieldedTransferTransition),
635 Unshield(UnshieldTransition),
636 ShieldFromAssetLock(ShieldFromAssetLockTransition),
637 ShieldedWithdrawal(ShieldedWithdrawalTransition),
638 IdentityCreateFromShieldedPool(IdentityCreateFromShieldedPoolTransition),
639 ShieldFromIdentity(ShieldFromIdentityTransition),
640 IdentityTopUpFromShieldedPool(IdentityTopUpFromShieldedPoolTransition),
641 IdentityKeyLimitsUpdate(IdentityKeyLimitsUpdateTransition),
642 ContractUserModeration(ContractUserModerationTransition),
643 ContractFeeClaim(ContractFeeClaimTransition),
644 TokenShieldedTransferWithShieldedFee(TokenShieldedTransferWithShieldedFeeTransition),
645 TokenUnshieldWithShieldedFee(TokenUnshieldWithShieldedFeeTransition),
646 TokenPurchaseFromShieldedPool(TokenPurchaseFromShieldedPoolTransition),
647}
648
649#[cfg(all(feature = "json-conversion", feature = "serde-conversion"))]
650impl JsonConvertible for StateTransition {}
651
652#[cfg(all(feature = "value-conversion", feature = "serde-conversion"))]
653impl ValueConvertible for StateTransition {}
654
655#[cfg(all(
656 test,
657 feature = "json-conversion",
658 feature = "value-conversion",
659 feature = "serde-conversion"
660))]
661mod json_convertible_tests {
662 use super::*;
663
664 fn assert_umbrella_round_trip_inner(
683 original: StateTransition,
684 expected_type_tag: &str,
685 lossy_json_int_variants: bool,
686 ) {
687 use crate::serialization::{JsonConvertible, ValueConvertible};
688
689 let json = original.to_json().expect("to_json");
691 assert_eq!(
692 json["$type"], expected_type_tag,
693 "json type tag for {expected_type_tag}",
694 );
695 let recovered = StateTransition::from_json(json).expect("from_json round-trip");
696 assert_eq!(
697 std::mem::discriminant(&original),
698 std::mem::discriminant(&recovered),
699 "json round-trip variant for {expected_type_tag}",
700 );
701 if lossy_json_int_variants {
702 use crate::tests::utils::normalize_integer_variants_for_json_round_trip;
703 let mut original_canon = original.to_object().expect("to_object");
704 let mut recovered_canon = recovered.to_object().expect("to_object");
705 normalize_integer_variants_for_json_round_trip(&mut original_canon);
706 normalize_integer_variants_for_json_round_trip(&mut recovered_canon);
707 assert_eq!(
708 original_canon, recovered_canon,
709 "json round-trip equality (modulo int-variant) for {expected_type_tag}",
710 );
711 } else {
712 assert_eq!(
713 original, recovered,
714 "json round-trip equality for {expected_type_tag}"
715 );
716 }
717
718 let value = original.to_object().expect("to_object");
720 let map = value.as_map().expect("Value::Map");
721 let tag = map
722 .iter()
723 .find(|(k, _)| k.as_text() == Some("$type"))
724 .map(|(_, v)| v)
725 .unwrap_or_else(|| panic!("type tag missing for {expected_type_tag}"));
726 assert_eq!(
727 *tag,
728 platform_value::Value::Text(expected_type_tag.to_string()),
729 "value type tag for {expected_type_tag}",
730 );
731 let recovered = StateTransition::from_object(value).expect("from_object round-trip");
732 assert_eq!(
733 std::mem::discriminant(&original),
734 std::mem::discriminant(&recovered),
735 "value round-trip variant for {expected_type_tag}",
736 );
737 assert_eq!(
738 original, recovered,
739 "value round-trip equality for {expected_type_tag}"
740 );
741 }
742
743 fn assert_umbrella_round_trip(original: StateTransition, expected_type_tag: &str) {
744 assert_umbrella_round_trip_inner(original, expected_type_tag, false);
745 }
746
747 fn assert_umbrella_round_trip_lossy_json_int_variants(
754 original: StateTransition,
755 expected_type_tag: &str,
756 ) {
757 assert_umbrella_round_trip_inner(original, expected_type_tag, true);
758 }
759
760 #[test]
766 fn umbrella_data_contract_create() {
767 let inner = crate::state_transition::data_contract_create_transition::json_convertible_tests::fixture();
768 assert_umbrella_round_trip_lossy_json_int_variants(
769 StateTransition::DataContractCreate(inner),
770 "dataContractCreate",
771 );
772 }
773
774 #[test]
775 fn umbrella_data_contract_update() {
776 let inner = crate::state_transition::data_contract_update_transition::json_convertible_tests::fixture();
777 assert_umbrella_round_trip_lossy_json_int_variants(
778 StateTransition::DataContractUpdate(inner),
779 "dataContractUpdate",
780 );
781 }
782
783 #[test]
784 fn umbrella_batch() {
785 let inner = crate::state_transition::batch_transition::json_convertible_tests::fixture();
786 assert_umbrella_round_trip(StateTransition::Batch(inner), "batch");
787 }
788
789 #[test]
790 fn umbrella_identity_create() {
791 let inner =
792 crate::state_transition::identity_create_transition::json_convertible_tests::fixture();
793 assert_umbrella_round_trip(StateTransition::IdentityCreate(inner), "identityCreate");
794 }
795
796 #[test]
797 fn umbrella_identity_top_up() {
798 let inner =
799 crate::state_transition::identity_topup_transition::json_convertible_tests::fixture();
800 assert_umbrella_round_trip(StateTransition::IdentityTopUp(inner), "identityTopUp");
801 }
802
803 #[test]
804 fn umbrella_identity_credit_withdrawal() {
805 let inner = crate::state_transition::identity_credit_withdrawal_transition::json_convertible_tests::fixture();
806 assert_umbrella_round_trip(
807 StateTransition::IdentityCreditWithdrawal(inner),
808 "identityCreditWithdrawal",
809 );
810 }
811
812 #[test]
813 fn umbrella_identity_update() {
814 let inner =
815 crate::state_transition::identity_update_transition::json_convertible_tests::fixture();
816 assert_umbrella_round_trip(StateTransition::IdentityUpdate(inner), "identityUpdate");
817 }
818
819 #[test]
820 fn umbrella_identity_key_limits_update() {
821 let inner = crate::state_transition::identity_key_limits_update_transition::json_convertible_tests::fixture();
822 assert_umbrella_round_trip(
823 StateTransition::IdentityKeyLimitsUpdate(inner),
824 "identityKeyLimitsUpdate",
825 );
826 }
827
828 #[test]
829 fn umbrella_round_trip_contract_user_moderation() {
830 let inner = crate::state_transition::contract_user_moderation_transition::json_convertible_tests::fixture();
831 assert_umbrella_round_trip(
832 StateTransition::ContractUserModeration(inner),
833 "contractUserModeration",
834 );
835 }
836
837 #[test]
838 fn umbrella_round_trip_contract_fee_claim() {
839 let inner =
840 crate::state_transition::contract_fee_claim_transition::json_convertible_tests::fixture(
841 );
842 assert_umbrella_round_trip(StateTransition::ContractFeeClaim(inner), "contractFeeClaim");
843 }
844
845 #[test]
846 fn umbrella_identity_credit_transfer() {
847 let inner = crate::state_transition::identity_credit_transfer_transition::json_convertible_tests::fixture();
848 assert_umbrella_round_trip(
849 StateTransition::IdentityCreditTransfer(inner),
850 "identityCreditTransfer",
851 );
852 }
853
854 #[test]
855 fn umbrella_masternode_vote() {
856 let inner =
857 crate::state_transition::masternode_vote_transition::json_convertible_tests::fixture();
858 assert_umbrella_round_trip(StateTransition::MasternodeVote(inner), "masternodeVote");
859 }
860
861 #[test]
862 fn umbrella_identity_credit_transfer_to_addresses() {
863 let inner = crate::state_transition::identity_credit_transfer_to_addresses_transition::json_convertible_tests::fixture();
864 assert_umbrella_round_trip(
865 StateTransition::IdentityCreditTransferToAddresses(inner),
866 "identityCreditTransferToAddresses",
867 );
868 }
869
870 #[test]
871 fn umbrella_identity_create_from_addresses() {
872 let inner = crate::state_transition::identity_create_from_addresses_transition::json_convertible_tests::fixture();
873 assert_umbrella_round_trip(
874 StateTransition::IdentityCreateFromAddresses(inner),
875 "identityCreateFromAddresses",
876 );
877 }
878
879 #[test]
880 fn umbrella_identity_top_up_from_addresses() {
881 let inner = crate::state_transition::identity_topup_from_addresses_transition::json_convertible_tests::fixture();
882 assert_umbrella_round_trip(
883 StateTransition::IdentityTopUpFromAddresses(inner),
884 "identityTopUpFromAddresses",
885 );
886 }
887
888 #[test]
889 fn umbrella_address_funds_transfer() {
890 let inner = crate::state_transition::address_funds_transfer_transition::json_convertible_tests::fixture();
891 assert_umbrella_round_trip(
892 StateTransition::AddressFundsTransfer(inner),
893 "addressFundsTransfer",
894 );
895 }
896
897 #[test]
898 fn umbrella_address_funding_from_asset_lock() {
899 let inner = crate::state_transition::address_funding_from_asset_lock_transition::json_convertible_tests::fixture();
900 assert_umbrella_round_trip(
901 StateTransition::AddressFundingFromAssetLock(inner),
902 "addressFundingFromAssetLock",
903 );
904 }
905
906 #[test]
907 fn umbrella_address_credit_withdrawal() {
908 let inner = crate::state_transition::address_credit_withdrawal_transition::json_convertible_tests::fixture();
909 assert_umbrella_round_trip(
910 StateTransition::AddressCreditWithdrawal(inner),
911 "addressCreditWithdrawal",
912 );
913 }
914
915 #[test]
916 fn umbrella_shield() {
917 let inner = crate::state_transition::shield_transition::json_convertible_tests::fixture();
918 assert_umbrella_round_trip(StateTransition::Shield(inner), "shield");
919 }
920
921 #[test]
922 fn umbrella_shielded_transfer() {
923 let inner =
924 crate::state_transition::shielded_transfer_transition::json_convertible_tests::fixture(
925 );
926 assert_umbrella_round_trip(StateTransition::ShieldedTransfer(inner), "shieldedTransfer");
927 }
928
929 #[test]
930 fn umbrella_unshield() {
931 let inner = crate::state_transition::unshield_transition::json_convertible_tests::fixture();
932 assert_umbrella_round_trip(StateTransition::Unshield(inner), "unshield");
933 }
934
935 #[test]
936 fn umbrella_shield_from_asset_lock() {
937 let inner = crate::state_transition::shield_from_asset_lock_transition::json_convertible_tests::fixture();
938 assert_umbrella_round_trip(
939 StateTransition::ShieldFromAssetLock(inner),
940 "shieldFromAssetLock",
941 );
942 }
943
944 #[test]
945 fn umbrella_shielded_withdrawal() {
946 let inner = crate::state_transition::shielded_withdrawal_transition::json_convertible_tests::fixture();
947 assert_umbrella_round_trip(
948 StateTransition::ShieldedWithdrawal(inner),
949 "shieldedWithdrawal",
950 );
951 }
952
953 #[test]
954 fn umbrella_identity_create_from_shielded_pool() {
955 let inner = crate::state_transition::identity_create_from_shielded_pool_transition::json_convertible_tests::fixture();
956 assert_umbrella_round_trip(
957 StateTransition::IdentityCreateFromShieldedPool(inner),
958 "identityCreateFromShieldedPool",
959 );
960 }
961
962 #[test]
963 fn umbrella_identity_top_up_from_shielded_pool() {
964 let inner = crate::state_transition::identity_top_up_from_shielded_pool_transition::json_convertible_tests::fixture();
965 assert_umbrella_round_trip(
966 StateTransition::IdentityTopUpFromShieldedPool(inner),
967 "identityTopUpFromShieldedPool",
968 );
969 }
970 #[test]
971 fn umbrella_token_shielded_transfer_with_shielded_fee() {
972 let inner = crate::state_transition::token_shielded_transfer_with_shielded_fee_transition::json_convertible_tests::fixture();
973 assert_umbrella_round_trip(
974 StateTransition::TokenShieldedTransferWithShieldedFee(inner),
975 "tokenShieldedTransferWithShieldedFee",
976 );
977 }
978 #[test]
979 fn umbrella_token_unshield_with_shielded_fee() {
980 let inner = crate::state_transition::token_unshield_with_shielded_fee_transition::json_convertible_tests::fixture();
981 assert_umbrella_round_trip(
982 StateTransition::TokenUnshieldWithShieldedFee(inner),
983 "tokenUnshieldWithShieldedFee",
984 );
985 }
986 #[test]
987 fn umbrella_token_purchase_from_shielded_pool() {
988 let inner = crate::state_transition::token_purchase_from_shielded_pool_transition::json_convertible_tests::fixture();
989 assert_umbrella_round_trip(
990 StateTransition::TokenPurchaseFromShieldedPool(inner),
991 "tokenPurchaseFromShieldedPool",
992 );
993 }
994
995 #[test]
996 fn umbrella_shield_from_identity() {
997 let inner = crate::state_transition::shield_from_identity_transition::json_convertible_tests::fixture();
998 assert_umbrella_round_trip(
999 StateTransition::ShieldFromIdentity(inner),
1000 "shieldFromIdentity",
1001 );
1002 }
1003
1004 #[test]
1007 fn every_kind_decodes_untagged_and_exactly() {
1008 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
1009
1010 let transitions = [
1011 StateTransition::DataContractCreate(crate::state_transition::data_contract_create_transition::json_convertible_tests::fixture()),
1012 StateTransition::DataContractUpdate(crate::state_transition::data_contract_update_transition::json_convertible_tests::fixture()),
1013 StateTransition::Batch(crate::state_transition::batch_transition::json_convertible_tests::fixture()),
1014 StateTransition::IdentityCreate(crate::state_transition::identity_create_transition::json_convertible_tests::fixture()),
1015 StateTransition::IdentityTopUp(crate::state_transition::identity_topup_transition::json_convertible_tests::fixture()),
1016 StateTransition::IdentityCreditWithdrawal(crate::state_transition::identity_credit_withdrawal_transition::json_convertible_tests::fixture()),
1017 StateTransition::IdentityUpdate(crate::state_transition::identity_update_transition::json_convertible_tests::fixture()),
1018 StateTransition::IdentityKeyLimitsUpdate(crate::state_transition::identity_key_limits_update_transition::json_convertible_tests::fixture()),
1019 StateTransition::ContractUserModeration(crate::state_transition::contract_user_moderation_transition::json_convertible_tests::fixture()),
1020 StateTransition::ContractFeeClaim(crate::state_transition::contract_fee_claim_transition::json_convertible_tests::fixture()),
1021 StateTransition::IdentityCreditTransfer(crate::state_transition::identity_credit_transfer_transition::json_convertible_tests::fixture()),
1022 StateTransition::MasternodeVote(crate::state_transition::masternode_vote_transition::json_convertible_tests::fixture()),
1023 StateTransition::IdentityCreditTransferToAddresses(crate::state_transition::identity_credit_transfer_to_addresses_transition::json_convertible_tests::fixture()),
1024 StateTransition::IdentityCreateFromAddresses(crate::state_transition::identity_create_from_addresses_transition::json_convertible_tests::fixture()),
1025 StateTransition::IdentityTopUpFromAddresses(crate::state_transition::identity_topup_from_addresses_transition::json_convertible_tests::fixture()),
1026 StateTransition::AddressFundsTransfer(crate::state_transition::address_funds_transfer_transition::json_convertible_tests::fixture()),
1027 StateTransition::AddressFundingFromAssetLock(crate::state_transition::address_funding_from_asset_lock_transition::json_convertible_tests::fixture()),
1028 StateTransition::AddressCreditWithdrawal(crate::state_transition::address_credit_withdrawal_transition::json_convertible_tests::fixture()),
1029 StateTransition::Shield(crate::state_transition::shield_transition::json_convertible_tests::fixture()),
1030 StateTransition::ShieldedTransfer(crate::state_transition::shielded_transfer_transition::json_convertible_tests::fixture()),
1031 StateTransition::Unshield(crate::state_transition::unshield_transition::json_convertible_tests::fixture()),
1032 StateTransition::ShieldFromAssetLock(crate::state_transition::shield_from_asset_lock_transition::json_convertible_tests::fixture()),
1033 StateTransition::ShieldedWithdrawal(crate::state_transition::shielded_withdrawal_transition::json_convertible_tests::fixture()),
1034 StateTransition::IdentityCreateFromShieldedPool(crate::state_transition::identity_create_from_shielded_pool_transition::json_convertible_tests::fixture()),
1035 StateTransition::IdentityTopUpFromShieldedPool(crate::state_transition::identity_top_up_from_shielded_pool_transition::json_convertible_tests::fixture()),
1036 StateTransition::ShieldFromIdentity(crate::state_transition::shield_from_identity_transition::json_convertible_tests::fixture()),
1037 ];
1038 for transition in transitions {
1039 let tagged = transition.serialize_to_bytes().expect("serializes");
1040 assert_eq!(
1041 StateTransition::deserialize_from_bytes_untrusted_exact(&tagged).expect("tagged"),
1042 transition
1043 );
1044 let untagged = &tagged[1..];
1046 assert_eq!(
1047 StateTransition::deserialize_untagged_untrusted_exact(
1048 transition.state_transition_type(),
1049 untagged
1050 )
1051 .unwrap_or_else(|e| panic!("{} untagged: {e}", transition.name())),
1052 transition
1053 );
1054 }
1055 }
1056}
1057
1058impl OptionallyAssetLockProved for StateTransition {
1059 fn optional_asset_lock_proof(&self) -> Option<&AssetLockProof> {
1060 match self {
1061 StateTransition::IdentityCreate(st) => st.optional_asset_lock_proof(),
1062 StateTransition::IdentityTopUp(st) => st.optional_asset_lock_proof(),
1063 StateTransition::ShieldFromAssetLock(st) => st.optional_asset_lock_proof(),
1064 _ => None,
1065 }
1066 }
1067}
1068
1069#[derive(Debug, Clone, Copy, Default, Eq, PartialEq)]
1071pub struct StateTransitionSigningOptions {
1072 pub allow_signing_with_any_security_level: bool,
1074 pub allow_signing_with_any_purpose: bool,
1076}
1077
1078fn active_version_range_for_keys_in_creation(
1082 keys: &[IdentityPublicKeyInCreation],
1083 otherwise: RangeInclusive<ProtocolVersion>,
1084) -> RangeInclusive<ProtocolVersion> {
1085 if IdentityPublicKeyInCreation::first_bound_to_a_contract_group(keys).is_some()
1086 || IdentityPublicKeyInCreation::first_in_version_1_format(keys).is_some()
1087 {
1088 14..=LATEST_VERSION
1089 } else {
1090 otherwise
1091 }
1092}
1093
1094fn active_version_range_for_contract(
1104 contract: &DataContractInSerializationFormat,
1105) -> RangeInclusive<ProtocolVersion> {
1106 match contract {
1107 DataContractInSerializationFormat::V2(_) => NO_VERSION,
1108 _ if matches!(contract.config(), DataContractConfig::V2(_)) => 14..=LATEST_VERSION,
1109 DataContractInSerializationFormat::V0(_) => ALL_VERSIONS,
1110 DataContractInSerializationFormat::V1(_) => 9..=LATEST_VERSION,
1111 }
1112}
1113
1114#[allow(clippy::reversed_empty_ranges)]
1117const NO_VERSION: RangeInclusive<ProtocolVersion> = 1..=0;
1118
1119fn batch_carries_a_version_2_document_base(batch_transition: &BatchTransition) -> bool {
1122 batch_transition
1123 .transitions_iter()
1124 .any(|transition| match transition {
1125 BatchedTransitionRef::Document(document_transition) => {
1126 document_transition.base().feature_version() >= 2
1127 }
1128 BatchedTransitionRef::Token(_) => false,
1129 })
1130}
1131
1132impl StateTransition {
1133 pub fn deserialize_from_bytes_untrusted_in_version(
1137 bytes: &[u8],
1138 platform_version: &PlatformVersion,
1139 ) -> Result<Self, ProtocolError> {
1140 Self::decode_untrusted_in_version(
1141 bytes,
1142 StateTransition::deserialize_from_bytes_untrusted,
1143 platform_version,
1144 )
1145 }
1146
1147 pub fn deserialize_from_bytes_untrusted_exact_in_version(
1151 bytes: &[u8],
1152 platform_version: &PlatformVersion,
1153 ) -> Result<Self, ProtocolError> {
1154 Self::decode_untrusted_in_version(
1155 bytes,
1156 StateTransition::deserialize_from_bytes_untrusted_exact,
1157 platform_version,
1158 )
1159 }
1160
1161 #[allow(unused_variables)]
1162 fn decode_untrusted_in_version(
1163 bytes: &[u8],
1164 decode: fn(&[u8]) -> Result<Self, ProtocolError>,
1165 platform_version: &PlatformVersion,
1166 ) -> Result<Self, ProtocolError> {
1167 let max_value_depth = platform_version
1168 .system_limits
1169 .max_document_value_depth
1170 .map(usize::from);
1171 let state_transition =
1172 platform_value::with_value_decode_depth_limit(max_value_depth, || decode(bytes))?;
1173 #[cfg(all(feature = "state-transitions", feature = "validation"))]
1174 {
1175 let active_version_range = state_transition.active_version_range();
1176
1177 if active_version_range.contains(&platform_version.protocol_version)
1180 || platform_version.protocol_version > 268435456
1181 {
1182 Ok(state_transition)
1183 } else {
1184 Err(ProtocolError::StateTransitionError(
1185 StateTransitionIsNotActiveError {
1186 state_transition_type: state_transition.name(),
1187 active_version_range,
1188 current_protocol_version: platform_version.protocol_version,
1189 },
1190 ))
1191 }
1192 }
1193 #[cfg(not(all(feature = "state-transitions", feature = "validation")))]
1194 Ok(state_transition)
1195 }
1196
1197 pub fn active_version_range(&self) -> RangeInclusive<ProtocolVersion> {
1198 match self {
1199 StateTransition::DataContractCreate(data_contract_create_transition) => {
1200 match data_contract_create_transition {
1201 DataContractCreateTransition::V1(_) => {
1207 let contract = active_version_range_for_contract(
1208 data_contract_create_transition.data_contract(),
1209 );
1210 (*contract.start()).max(14)..=(*contract.end()).min(LATEST_VERSION)
1211 }
1212 DataContractCreateTransition::V0(_) => active_version_range_for_contract(
1213 data_contract_create_transition.data_contract(),
1214 ),
1215 }
1216 }
1217 StateTransition::DataContractUpdate(data_contract_update_transition) => {
1218 active_version_range_for_contract(data_contract_update_transition.data_contract())
1219 }
1220 StateTransition::Batch(batch_transition) => {
1221 if batch_carries_a_version_2_document_base(batch_transition) {
1225 return 14..=LATEST_VERSION;
1226 }
1227 match batch_transition {
1228 BatchTransition::V0(_) => ALL_VERSIONS,
1229 BatchTransition::V1(_) => 9..=LATEST_VERSION,
1230 }
1231 }
1232 StateTransition::IdentityCreate(st) => {
1236 active_version_range_for_keys_in_creation(st.public_keys(), ALL_VERSIONS)
1237 }
1238 StateTransition::IdentityUpdate(st) => {
1239 active_version_range_for_keys_in_creation(st.public_keys_to_add(), ALL_VERSIONS)
1240 }
1241 StateTransition::IdentityCreateFromAddresses(st) => {
1242 active_version_range_for_keys_in_creation(st.public_keys(), 11..=LATEST_VERSION)
1243 }
1244 StateTransition::IdentityCreateFromShieldedPool(st) => {
1245 active_version_range_for_keys_in_creation(st.public_keys(), 12..=LATEST_VERSION)
1246 }
1247 StateTransition::IdentityTopUp(_)
1248 | StateTransition::IdentityCreditWithdrawal(_)
1249 | StateTransition::IdentityCreditTransfer(_) => ALL_VERSIONS,
1250 StateTransition::MasternodeVote(masternode_vote) => match masternode_vote.vote() {
1255 Vote::ResourceVote(resource_vote) => match resource_vote.vote_poll() {
1256 VotePoll::ContestedDocumentResourceVotePoll(_) => ALL_VERSIONS,
1257 VotePoll::YesNoVotePoll(_) => {
1258 YES_NO_VOTE_POLL_INITIAL_PROTOCOL_VERSION..=LATEST_VERSION
1259 }
1260 },
1261 Vote::YesNoVote(_) => YES_NO_VOTE_POLL_INITIAL_PROTOCOL_VERSION..=LATEST_VERSION,
1262 },
1263 StateTransition::IdentityCreditTransferToAddresses(_)
1264 | StateTransition::IdentityTopUpFromAddresses(_)
1265 | StateTransition::AddressFundsTransfer(_)
1266 | StateTransition::AddressFundingFromAssetLock(_)
1267 | StateTransition::AddressCreditWithdrawal(_) => 11..=LATEST_VERSION,
1268 StateTransition::Shield(_)
1269 | StateTransition::ShieldedTransfer(_)
1270 | StateTransition::Unshield(_)
1271 | StateTransition::ShieldedWithdrawal(_) => 12..=LATEST_VERSION,
1272 StateTransition::ShieldFromAssetLock(st) => match st {
1278 ShieldFromAssetLockTransition::V0(_) => 12..=13,
1279 ShieldFromAssetLockTransition::V1(_) => 14..=LATEST_VERSION,
1280 },
1281 StateTransition::ShieldFromIdentity(_)
1282 | StateTransition::IdentityTopUpFromShieldedPool(_)
1283 | StateTransition::IdentityKeyLimitsUpdate(_)
1284 | StateTransition::ContractUserModeration(_)
1285 | StateTransition::ContractFeeClaim(_)
1286 | StateTransition::TokenShieldedTransferWithShieldedFee(_)
1287 | StateTransition::TokenUnshieldWithShieldedFee(_)
1288 | StateTransition::TokenPurchaseFromShieldedPool(_) => 14..=LATEST_VERSION,
1289 }
1290 }
1291
1292 pub fn is_identity_signed(&self) -> bool {
1293 !matches!(
1294 self,
1295 StateTransition::IdentityCreate(_)
1296 | StateTransition::IdentityTopUp(_)
1297 | StateTransition::Shield(_)
1298 | StateTransition::ShieldedTransfer(_)
1299 | StateTransition::Unshield(_)
1300 | StateTransition::ShieldFromAssetLock(_)
1301 | StateTransition::ShieldedWithdrawal(_)
1302 | StateTransition::IdentityCreateFromShieldedPool(_)
1303 | StateTransition::IdentityTopUpFromShieldedPool(_)
1304 | StateTransition::TokenShieldedTransferWithShieldedFee(_)
1305 | StateTransition::TokenUnshieldWithShieldedFee(_)
1306 | StateTransition::TokenPurchaseFromShieldedPool(_)
1307 )
1308 }
1309
1310 pub fn required_asset_lock_balance_for_processing_start(
1311 &self,
1312 platform_version: &PlatformVersion,
1313 ) -> Result<Credits, ProtocolError> {
1314 match self {
1315 StateTransition::IdentityCreate(st) => {
1316 st.calculate_min_required_fee(platform_version)
1317 }
1318 StateTransition::IdentityTopUp(st) => {
1319 st.calculate_min_required_fee(platform_version)
1320 }
1321 StateTransition::AddressFundingFromAssetLock(st) => {
1322 st.calculate_min_required_fee(platform_version)
1323 }
1324 StateTransition::ShieldFromAssetLock(st) => {
1325 st.calculate_min_required_fee(platform_version)
1326 }
1327 st => Err(ProtocolError::CorruptedCodeExecution(format!("{} is not an asset lock transaction, but we are calling required_asset_lock_balance_for_processing_start", st.name()))),
1328 }
1329 }
1330
1331 fn hash(&self, skip_signature: bool) -> Result<Vec<u8>, ProtocolError> {
1332 if skip_signature {
1333 Ok(hash_double_to_vec(self.signable_bytes()?))
1334 } else {
1335 Ok(hash_double_to_vec(
1336 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
1337 ))
1338 }
1339 }
1340
1341 pub fn name(&self) -> String {
1343 match self {
1344 Self::DataContractCreate(_) => "DataContractCreate".to_string(),
1345 Self::DataContractUpdate(_) => "DataContractUpdate".to_string(),
1346 Self::ContractUserModeration(_) => "ContractUserModeration".to_string(),
1347 Self::ContractFeeClaim(_) => "ContractFeeClaim".to_string(),
1348 Self::Batch(batch_transition) => {
1349 let mut document_transition_types = vec![];
1350 for transition in batch_transition.transitions_iter() {
1351 let document_transition_name = match transition {
1352 BatchedTransitionRef::Document(DocumentTransition::Create(_)) => "Create",
1353 BatchedTransitionRef::Document(DocumentTransition::Replace(_)) => "Replace",
1354 BatchedTransitionRef::Document(DocumentTransition::Delete(_)) => "Delete",
1355 BatchedTransitionRef::Document(DocumentTransition::Transfer(_)) => {
1356 "Transfer"
1357 }
1358 BatchedTransitionRef::Document(DocumentTransition::UpdatePrice(_)) => {
1359 "UpdatePrice"
1360 }
1361 BatchedTransitionRef::Document(DocumentTransition::Purchase(_)) => {
1362 "Purchase"
1363 }
1364 BatchedTransitionRef::Document(DocumentTransition::IndexOnlyDelete(_)) => {
1365 "IndexOnlyDelete"
1366 }
1367 BatchedTransitionRef::Token(TokenTransition::Transfer(_)) => {
1368 "TokenTransfer"
1369 }
1370 BatchedTransitionRef::Token(TokenTransition::Mint(_)) => "TokenMint",
1371 BatchedTransitionRef::Token(TokenTransition::Burn(_)) => "TokenBurn",
1372 BatchedTransitionRef::Token(TokenTransition::Freeze(_)) => "TokenFreeze",
1373 BatchedTransitionRef::Token(TokenTransition::Unfreeze(_)) => {
1374 "TokenUnfreeze"
1375 }
1376 BatchedTransitionRef::Token(TokenTransition::DestroyFrozenFunds(_)) => {
1377 "TokenDestroyFrozenFunds"
1378 }
1379 BatchedTransitionRef::Token(TokenTransition::EmergencyAction(_)) => {
1380 "TokenEmergencyAction"
1381 }
1382 BatchedTransitionRef::Token(TokenTransition::ConfigUpdate(_)) => {
1383 "TokenConfigUpdate"
1384 }
1385 BatchedTransitionRef::Token(TokenTransition::Claim(_)) => "TokenClaim",
1386 BatchedTransitionRef::Token(TokenTransition::DirectPurchase(_)) => {
1387 "TokenDirectPurchase"
1388 }
1389 BatchedTransitionRef::Token(
1390 TokenTransition::SetPriceForDirectPurchase(_),
1391 ) => "SetPriceForDirectPurchase",
1392 BatchedTransitionRef::Token(TokenTransition::Shield(_)) => "TokenShield",
1393 BatchedTransitionRef::Token(TokenTransition::Unshield(_)) => {
1394 "TokenUnshield"
1395 }
1396 BatchedTransitionRef::Token(TokenTransition::ShieldedTransfer(_)) => {
1397 "TokenShieldedTransfer"
1398 }
1399 BatchedTransitionRef::Token(TokenTransition::MintToPool(_)) => {
1400 "TokenMintToPool"
1401 }
1402 BatchedTransitionRef::Token(TokenTransition::BurnFromPool(_)) => {
1403 "TokenBurnFromPool"
1404 }
1405 BatchedTransitionRef::Token(TokenTransition::ClaimToPool(_)) => {
1406 "TokenClaimToPool"
1407 }
1408 BatchedTransitionRef::Token(TokenTransition::DirectPurchaseToPool(_)) => {
1409 "TokenDirectPurchaseToPool"
1410 }
1411 };
1412 document_transition_types.push(document_transition_name);
1413 }
1414 format!("DocumentsBatch([{}])", document_transition_types.join(", "))
1415 }
1416 Self::IdentityCreate(_) => "IdentityCreate".to_string(),
1417 Self::IdentityTopUp(_) => "IdentityTopUp".to_string(),
1418 Self::IdentityCreditWithdrawal(_) => "IdentityCreditWithdrawal".to_string(),
1419 Self::IdentityUpdate(_) => "IdentityUpdate".to_string(),
1420 Self::IdentityKeyLimitsUpdate(_) => "IdentityKeyLimitsUpdate".to_string(),
1421 Self::IdentityCreditTransfer(_) => "IdentityCreditTransfer".to_string(),
1422 Self::MasternodeVote(_) => "MasternodeVote".to_string(),
1423 Self::IdentityCreditTransferToAddresses(_) => {
1424 "IdentityCreditTransferToAddresses".to_string()
1425 }
1426 Self::IdentityCreateFromAddresses(_) => "IdentityCreateFromAddresses".to_string(),
1427 Self::IdentityTopUpFromAddresses(_) => "IdentityTopUpFromAddresses".to_string(),
1428 Self::AddressFundsTransfer(_) => "AddressFundsTransfer".to_string(),
1429 Self::AddressFundingFromAssetLock(_) => "AddressFundingFromAssetLock".to_string(),
1430 Self::AddressCreditWithdrawal(_) => "AddressCreditWithdrawal".to_string(),
1431 Self::Shield(_) => "Shield".to_string(),
1432 Self::ShieldedTransfer(_) => "ShieldedTransfer".to_string(),
1433 Self::Unshield(_) => "Unshield".to_string(),
1434 Self::IdentityTopUpFromShieldedPool(_) => "IdentityTopUpFromShieldedPool".to_string(),
1435 Self::TokenShieldedTransferWithShieldedFee(_) => {
1436 "TokenShieldedTransferWithShieldedFee".to_string()
1437 }
1438 Self::TokenUnshieldWithShieldedFee(_) => "TokenUnshieldWithShieldedFee".to_string(),
1439 Self::TokenPurchaseFromShieldedPool(_) => "TokenPurchaseFromShieldedPool".to_string(),
1440 Self::ShieldFromAssetLock(_) => "ShieldFromAssetLock".to_string(),
1441 Self::ShieldedWithdrawal(_) => "ShieldedWithdrawal".to_string(),
1442 Self::IdentityCreateFromShieldedPool(_) => "IdentityCreateFromShieldedPool".to_string(),
1443 Self::ShieldFromIdentity(_) => "ShieldFromIdentity".to_string(),
1444 }
1445 }
1446
1447 pub fn signature(&self) -> Option<&BinaryData> {
1449 match self {
1450 StateTransition::DataContractCreate(st) => Some(st.signature()),
1451 StateTransition::DataContractUpdate(st) => Some(st.signature()),
1452 StateTransition::ContractUserModeration(st) => Some(st.signature()),
1453 StateTransition::ContractFeeClaim(st) => Some(st.signature()),
1454 StateTransition::Batch(st) => Some(st.signature()),
1455 StateTransition::IdentityCreate(st) => Some(st.signature()),
1456 StateTransition::IdentityTopUp(st) => Some(st.signature()),
1457 StateTransition::IdentityCreditWithdrawal(st) => Some(st.signature()),
1458 StateTransition::IdentityUpdate(st) => Some(st.signature()),
1459 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.signature()),
1460 StateTransition::IdentityCreditTransfer(st) => Some(st.signature()),
1461 StateTransition::MasternodeVote(st) => Some(st.signature()),
1462 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.signature()),
1463 StateTransition::IdentityCreateFromAddresses(_) => None,
1464 StateTransition::IdentityTopUpFromAddresses(_) => None,
1465 StateTransition::AddressFundsTransfer(_) => None,
1466 StateTransition::AddressFundingFromAssetLock(st) => Some(st.signature()),
1467 StateTransition::AddressCreditWithdrawal(_) => None,
1468 StateTransition::Shield(_) => None,
1469 StateTransition::ShieldedTransfer(_) => None,
1470 StateTransition::Unshield(_) => None,
1471 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
1472 StateTransition::TokenShieldedTransferWithShieldedFee(_) => None,
1473 StateTransition::TokenUnshieldWithShieldedFee(_) => None,
1474 StateTransition::TokenPurchaseFromShieldedPool(_) => None,
1475 StateTransition::ShieldFromAssetLock(st) => Some(st.signature()),
1476 StateTransition::ShieldedWithdrawal(_) => None,
1477 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1478 StateTransition::ShieldFromIdentity(st) => Some(st.signature()),
1479 }
1480 }
1481
1482 pub fn required_number_of_private_keys(&self) -> u16 {
1484 match self {
1485 StateTransition::IdentityCreateFromAddresses(st) => st.inputs().len() as u16,
1486 StateTransition::IdentityTopUpFromAddresses(st) => st.inputs().len() as u16,
1487 StateTransition::AddressFundsTransfer(st) => st.inputs().len() as u16,
1488 StateTransition::AddressCreditWithdrawal(st) => st.inputs().len() as u16,
1489 StateTransition::Shield(st) => st.inputs().len() as u16,
1490 StateTransition::ShieldedTransfer(_) => 0,
1491 StateTransition::Unshield(_) => 0,
1492 StateTransition::IdentityTopUpFromShieldedPool(_) => 0,
1493 StateTransition::TokenShieldedTransferWithShieldedFee(_) => 0,
1494 StateTransition::TokenUnshieldWithShieldedFee(_) => 0,
1495 StateTransition::TokenPurchaseFromShieldedPool(_) => 0,
1496 StateTransition::ShieldFromAssetLock(_) => 0,
1497 StateTransition::ShieldedWithdrawal(_) => 0,
1498 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1499 _ => 1,
1500 }
1501 }
1502
1503 pub fn user_fee_increase(&self) -> UserFeeIncrease {
1505 match self {
1506 StateTransition::DataContractCreate(st) => st.user_fee_increase(),
1507 StateTransition::DataContractUpdate(st) => st.user_fee_increase(),
1508 StateTransition::ContractUserModeration(st) => st.user_fee_increase(),
1509 StateTransition::ContractFeeClaim(st) => st.user_fee_increase(),
1510 StateTransition::Batch(st) => st.user_fee_increase(),
1511 StateTransition::IdentityCreate(st) => st.user_fee_increase(),
1512 StateTransition::IdentityTopUp(st) => st.user_fee_increase(),
1513 StateTransition::IdentityCreditWithdrawal(st) => st.user_fee_increase(),
1514 StateTransition::IdentityUpdate(st) => st.user_fee_increase(),
1515 StateTransition::IdentityKeyLimitsUpdate(st) => st.user_fee_increase(),
1516 StateTransition::IdentityCreditTransfer(st) => st.user_fee_increase(),
1517 StateTransition::IdentityCreditTransferToAddresses(st) => st.user_fee_increase(),
1518 StateTransition::IdentityCreateFromAddresses(st) => st.user_fee_increase(),
1519 StateTransition::IdentityTopUpFromAddresses(st) => st.user_fee_increase(),
1520 StateTransition::AddressFundsTransfer(st) => st.user_fee_increase(),
1521 StateTransition::AddressFundingFromAssetLock(st) => st.user_fee_increase(),
1522 StateTransition::AddressCreditWithdrawal(st) => st.user_fee_increase(),
1523 StateTransition::Shield(st) => st.user_fee_increase(),
1524 StateTransition::ShieldFromAssetLock(_) => 0,
1526 StateTransition::MasternodeVote(_) => 0,
1527 StateTransition::ShieldedTransfer(_) => 0,
1528 StateTransition::Unshield(_) => 0,
1529 StateTransition::IdentityTopUpFromShieldedPool(_) => 0,
1530 StateTransition::TokenShieldedTransferWithShieldedFee(_) => 0,
1531 StateTransition::TokenUnshieldWithShieldedFee(_) => 0,
1532 StateTransition::TokenPurchaseFromShieldedPool(_) => 0,
1533 StateTransition::ShieldedWithdrawal(_) => 0,
1534 StateTransition::IdentityCreateFromShieldedPool(_) => 0,
1535 StateTransition::ShieldFromIdentity(st) => st.user_fee_increase(),
1536 }
1537 }
1538
1539 fn calculate_estimated_fee(
1552 &self,
1553 platform_version: &PlatformVersion,
1554 ) -> Result<Credits, ProtocolError> {
1555 call_method!(self, calculate_min_required_fee, platform_version)
1556 }
1557
1558 pub fn transaction_id(&self) -> Result<[u8; 32], ProtocolError> {
1560 Ok(hash_single(
1561 crate::serialization::PlatformSerializable::serialize_to_bytes(self)?,
1562 ))
1563 }
1564
1565 pub fn signature_public_key_id(&self) -> Option<KeyID> {
1567 call_getter_method_identity_signed!(self, signature_public_key_id)
1568 }
1569
1570 pub fn security_level_requirement(&self, purpose: Purpose) -> Option<Vec<SecurityLevel>> {
1572 call_getter_method_identity_signed!(self, security_level_requirement, purpose)
1573 }
1574
1575 pub fn purpose_requirement(&self) -> Option<Vec<Purpose>> {
1577 call_getter_method_identity_signed!(self, purpose_requirement)
1578 }
1579
1580 pub fn owner_id(&self) -> Option<Identifier> {
1582 match self {
1583 StateTransition::DataContractCreate(st) => Some(st.owner_id()),
1584 StateTransition::DataContractUpdate(st) => Some(st.owner_id()),
1585 StateTransition::ContractUserModeration(st) => Some(st.owner_id()),
1586 StateTransition::ContractFeeClaim(st) => Some(st.owner_id()),
1587 StateTransition::Batch(st) => Some(st.owner_id()),
1588 StateTransition::IdentityCreate(st) => Some(st.owner_id()),
1589 StateTransition::IdentityTopUp(st) => Some(st.owner_id()),
1590 StateTransition::IdentityCreditWithdrawal(st) => Some(st.owner_id()),
1591 StateTransition::IdentityUpdate(st) => Some(st.owner_id()),
1592 StateTransition::IdentityKeyLimitsUpdate(st) => Some(st.owner_id()),
1593 StateTransition::IdentityCreditTransfer(st) => Some(st.owner_id()),
1594 StateTransition::MasternodeVote(st) => Some(st.owner_id()),
1595 StateTransition::IdentityCreditTransferToAddresses(st) => Some(st.owner_id()),
1596 StateTransition::IdentityCreateFromAddresses(_) => None,
1597 StateTransition::IdentityTopUpFromAddresses(_) => None,
1598 StateTransition::AddressFundsTransfer(_) => None,
1599 StateTransition::AddressFundingFromAssetLock(_) => None,
1600 StateTransition::AddressCreditWithdrawal(_) => None,
1601 StateTransition::Shield(_) => None,
1602 StateTransition::ShieldedTransfer(_) => None,
1603 StateTransition::Unshield(_) => None,
1604 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
1605 StateTransition::TokenShieldedTransferWithShieldedFee(_) => None,
1606 StateTransition::TokenUnshieldWithShieldedFee(_) => None,
1607 StateTransition::TokenPurchaseFromShieldedPool(_) => None,
1608 StateTransition::ShieldFromAssetLock(_) => None,
1609 StateTransition::ShieldedWithdrawal(_) => None,
1610 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1611 StateTransition::ShieldFromIdentity(st) => Some(st.owner_id()),
1612 }
1613 }
1614
1615 pub fn inputs(&self) -> Option<&BTreeMap<PlatformAddress, (AddressNonce, Credits)>> {
1617 match self {
1618 StateTransition::DataContractCreate(_)
1619 | StateTransition::DataContractUpdate(_)
1620 | StateTransition::ContractUserModeration(_)
1621 | StateTransition::ContractFeeClaim(_)
1622 | StateTransition::Batch(_)
1623 | StateTransition::IdentityCreate(_)
1624 | StateTransition::IdentityTopUp(_)
1625 | StateTransition::IdentityCreditWithdrawal(_)
1626 | StateTransition::IdentityUpdate(_)
1627 | StateTransition::IdentityKeyLimitsUpdate(_)
1628 | StateTransition::IdentityCreditTransfer(_)
1629 | StateTransition::MasternodeVote(_)
1630 | StateTransition::IdentityCreditTransferToAddresses(_) => None,
1631 StateTransition::IdentityCreateFromAddresses(st) => Some(st.inputs()),
1632 StateTransition::IdentityTopUpFromAddresses(st) => Some(st.inputs()),
1633 StateTransition::AddressFundsTransfer(st) => Some(st.inputs()),
1634 StateTransition::AddressFundingFromAssetLock(st) => Some(st.inputs()),
1635 StateTransition::AddressCreditWithdrawal(st) => Some(st.inputs()),
1636 StateTransition::Shield(st) => Some(st.inputs()),
1637 StateTransition::ShieldedTransfer(_) => None,
1638 StateTransition::Unshield(_) => None,
1639 StateTransition::IdentityTopUpFromShieldedPool(_) => None,
1640 StateTransition::TokenShieldedTransferWithShieldedFee(_) => None,
1641 StateTransition::TokenUnshieldWithShieldedFee(_) => None,
1642 StateTransition::TokenPurchaseFromShieldedPool(_) => None,
1643 StateTransition::ShieldFromAssetLock(_) => None,
1644 StateTransition::ShieldedWithdrawal(_) => None,
1645 StateTransition::IdentityCreateFromShieldedPool(_) => None,
1646 StateTransition::ShieldFromIdentity(_) => None,
1647 }
1648 }
1649
1650 pub fn state_transition_type(&self) -> StateTransitionType {
1652 call_method!(self, state_transition_type)
1653 }
1654
1655 pub fn unique_identifiers(&self) -> Vec<String> {
1657 call_method!(self, unique_identifiers)
1658 }
1659
1660 pub fn set_signature(&mut self, signature: BinaryData) -> bool {
1662 match self {
1663 StateTransition::DataContractCreate(st) => {
1664 st.set_signature(signature);
1665 true
1666 }
1667 StateTransition::DataContractUpdate(st) => {
1668 st.set_signature(signature);
1669 true
1670 }
1671 StateTransition::ContractUserModeration(st) => {
1672 st.set_signature(signature);
1673 true
1674 }
1675 StateTransition::ContractFeeClaim(st) => {
1676 st.set_signature(signature);
1677 true
1678 }
1679 StateTransition::Batch(st) => {
1680 st.set_signature(signature);
1681 true
1682 }
1683 StateTransition::IdentityCreate(st) => {
1684 st.set_signature(signature);
1685 true
1686 }
1687 StateTransition::IdentityTopUp(st) => {
1688 st.set_signature(signature);
1689 true
1690 }
1691 StateTransition::IdentityCreditWithdrawal(st) => {
1692 st.set_signature(signature);
1693 true
1694 }
1695 StateTransition::IdentityUpdate(st) => {
1696 st.set_signature(signature);
1697 true
1698 }
1699 StateTransition::IdentityKeyLimitsUpdate(st) => {
1700 st.set_signature(signature);
1701 true
1702 }
1703 StateTransition::IdentityCreditTransfer(st) => {
1704 st.set_signature(signature);
1705 true
1706 }
1707 StateTransition::MasternodeVote(st) => {
1708 st.set_signature(signature);
1709 true
1710 }
1711 StateTransition::IdentityCreditTransferToAddresses(st) => {
1712 st.set_signature(signature);
1713 true
1714 }
1715 StateTransition::IdentityCreateFromAddresses(_)
1716 | StateTransition::IdentityTopUpFromAddresses(_)
1717 | StateTransition::AddressFundsTransfer(_)
1718 | StateTransition::Shield(_)
1719 | StateTransition::ShieldedTransfer(_)
1720 | StateTransition::Unshield(_)
1721 | StateTransition::ShieldedWithdrawal(_)
1722 | StateTransition::IdentityCreateFromShieldedPool(_)
1723 | StateTransition::TokenShieldedTransferWithShieldedFee(_)
1724 | StateTransition::TokenUnshieldWithShieldedFee(_)
1725 | StateTransition::TokenPurchaseFromShieldedPool(_)
1726 | StateTransition::IdentityTopUpFromShieldedPool(_) => false,
1727 StateTransition::ShieldFromIdentity(st) => {
1728 st.set_signature(signature);
1729 true
1730 }
1731 StateTransition::AddressFundingFromAssetLock(st) => {
1732 st.set_signature(signature);
1733 true
1734 }
1735 StateTransition::ShieldFromAssetLock(st) => {
1736 st.set_signature(signature);
1737 true
1738 }
1739 StateTransition::AddressCreditWithdrawal(_) => false,
1740 }
1741 }
1742
1743 pub fn set_user_fee_increase(&mut self, user_fee_increase: UserFeeIncrease) {
1745 match self {
1746 StateTransition::DataContractCreate(st) => st.set_user_fee_increase(user_fee_increase),
1747 StateTransition::DataContractUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1748 StateTransition::ContractUserModeration(st) => {
1749 st.set_user_fee_increase(user_fee_increase)
1750 }
1751 StateTransition::ContractFeeClaim(st) => st.set_user_fee_increase(user_fee_increase),
1752 StateTransition::Batch(st) => st.set_user_fee_increase(user_fee_increase),
1753 StateTransition::IdentityCreate(st) => st.set_user_fee_increase(user_fee_increase),
1754 StateTransition::IdentityTopUp(st) => st.set_user_fee_increase(user_fee_increase),
1755 StateTransition::IdentityCreditWithdrawal(st) => {
1756 st.set_user_fee_increase(user_fee_increase)
1757 }
1758 StateTransition::IdentityUpdate(st) => st.set_user_fee_increase(user_fee_increase),
1759 StateTransition::IdentityKeyLimitsUpdate(st) => {
1760 st.set_user_fee_increase(user_fee_increase)
1761 }
1762 StateTransition::IdentityCreditTransfer(st) => {
1763 st.set_user_fee_increase(user_fee_increase)
1764 }
1765 StateTransition::IdentityCreditTransferToAddresses(st) => {
1766 st.set_user_fee_increase(user_fee_increase)
1767 }
1768 StateTransition::IdentityCreateFromAddresses(st) => {
1769 st.set_user_fee_increase(user_fee_increase)
1770 }
1771 StateTransition::IdentityTopUpFromAddresses(st) => {
1772 st.set_user_fee_increase(user_fee_increase)
1773 }
1774 StateTransition::AddressFundsTransfer(st) => {
1775 st.set_user_fee_increase(user_fee_increase)
1776 }
1777 StateTransition::AddressFundingFromAssetLock(st) => {
1778 st.set_user_fee_increase(user_fee_increase)
1779 }
1780 StateTransition::AddressCreditWithdrawal(st) => {
1781 st.set_user_fee_increase(user_fee_increase)
1782 }
1783 StateTransition::Shield(st) => st.set_user_fee_increase(user_fee_increase),
1784 StateTransition::ShieldFromAssetLock(_) => {}
1786 StateTransition::MasternodeVote(_) => {}
1787 StateTransition::ShieldedTransfer(_) => {}
1788 StateTransition::Unshield(_) => {}
1789 StateTransition::IdentityTopUpFromShieldedPool(_) => {}
1790 StateTransition::TokenShieldedTransferWithShieldedFee(_) => {}
1791 StateTransition::TokenUnshieldWithShieldedFee(_) => {}
1792 StateTransition::TokenPurchaseFromShieldedPool(_) => {}
1793 StateTransition::ShieldedWithdrawal(_) => {}
1794 StateTransition::IdentityCreateFromShieldedPool(_) => {}
1795 StateTransition::ShieldFromIdentity(st) => st.set_user_fee_increase(user_fee_increase),
1796 }
1797 }
1798
1799 pub fn set_signature_public_key_id(&mut self, public_key_id: KeyID) {
1801 call_method_identity_signed!(self, set_signature_public_key_id, public_key_id)
1802 }
1803
1804 #[cfg(feature = "state-transition-signing")]
1805 pub async fn sign_external<S: Signer<IdentityPublicKey>>(
1806 &mut self,
1807 identity_public_key: &IdentityPublicKey,
1808 signer: &S,
1809 get_data_contract_security_level_requirement: Option<
1810 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1811 >,
1812 ) -> Result<(), ProtocolError> {
1813 self.sign_external_with_options(
1814 identity_public_key,
1815 signer,
1816 get_data_contract_security_level_requirement,
1817 StateTransitionSigningOptions::default(),
1818 )
1819 .await
1820 }
1821
1822 #[cfg(feature = "state-transition-signing")]
1823 pub async fn sign_external_with_options<S: Signer<IdentityPublicKey>>(
1824 &mut self,
1825 identity_public_key: &IdentityPublicKey,
1826 signer: &S,
1827 get_data_contract_security_level_requirement: Option<
1828 impl Fn(Identifier, String) -> Result<SecurityLevel, ProtocolError>,
1829 >,
1830 options: StateTransitionSigningOptions,
1831 ) -> Result<(), ProtocolError> {
1832 self.verify_identity_key_bounds(identity_public_key)?;
1833 match self {
1834 StateTransition::DataContractCreate(st) => {
1835 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1836 st.verify_public_key_is_enabled(identity_public_key)?;
1837 }
1838 StateTransition::DataContractUpdate(st) => {
1839 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1840 st.verify_public_key_is_enabled(identity_public_key)?;
1841 }
1842 StateTransition::ContractUserModeration(st) => {
1843 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1844 st.verify_public_key_is_enabled(identity_public_key)?;
1845 }
1846 StateTransition::ContractFeeClaim(st) => {
1847 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1848 st.verify_public_key_is_enabled(identity_public_key)?;
1849 }
1850 StateTransition::Batch(st) => {
1851 let allow_token_transfer_keys = st.transitions_len() == 1
1852 && (st
1853 .first_transition()
1854 .expect("expected first transition with len 1")
1855 .as_transition_token_claim()
1856 .is_some()
1857 || st
1858 .first_transition()
1859 .expect("expected first transition with len 1")
1860 .as_transition_token_transfer()
1861 .is_some());
1862 let allowed_key_purposes = if allow_token_transfer_keys {
1863 vec![Purpose::AUTHENTICATION, Purpose::TRANSFER]
1864 } else {
1865 vec![Purpose::AUTHENTICATION]
1866 };
1867 if !options.allow_signing_with_any_purpose
1868 && !allowed_key_purposes.contains(&identity_public_key.purpose())
1869 {
1870 return Err(ProtocolError::WrongPublicKeyPurposeError(
1871 WrongPublicKeyPurposeError::new(
1872 identity_public_key.purpose(),
1873 allowed_key_purposes,
1874 ),
1875 ));
1876 }
1877 if !options.allow_signing_with_any_security_level {
1878 let security_level_requirement = st.combined_security_level_requirement(
1879 get_data_contract_security_level_requirement,
1880 )?;
1881 if !security_level_requirement.contains(&identity_public_key.security_level()) {
1882 return Err(ProtocolError::InvalidSignaturePublicKeySecurityLevelError(
1883 InvalidSignaturePublicKeySecurityLevelError::new(
1884 identity_public_key.security_level(),
1885 security_level_requirement,
1886 ),
1887 ));
1888 }
1889 }
1890 st.verify_public_key_is_enabled(identity_public_key)?;
1891 }
1892 StateTransition::IdentityCreditWithdrawal(st) => {
1893 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1894 st.verify_public_key_is_enabled(identity_public_key)?;
1895 }
1896 StateTransition::IdentityUpdate(st) => {
1897 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1898 st.verify_public_key_is_enabled(identity_public_key)?;
1899 }
1900 StateTransition::IdentityKeyLimitsUpdate(st) => {
1901 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1902 st.verify_public_key_is_enabled(identity_public_key)?;
1903 }
1904 StateTransition::IdentityCreditTransfer(st) => {
1905 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1906 st.verify_public_key_is_enabled(identity_public_key)?;
1907 }
1908 StateTransition::IdentityCreate(_) => {
1909 return Err(ProtocolError::CorruptedCodeExecution(
1910 "identity create can not be called for identity signing".to_string(),
1911 ))
1912 }
1913 StateTransition::IdentityTopUp(_) => {
1914 return Err(ProtocolError::CorruptedCodeExecution(
1915 "identity top up can not be called for identity signing".to_string(),
1916 ))
1917 }
1918 StateTransition::MasternodeVote(st) => {
1919 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1920 st.verify_public_key_is_enabled(identity_public_key)?;
1921 }
1922 StateTransition::IdentityCreditTransferToAddresses(st) => {
1923 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
1924 st.verify_public_key_is_enabled(identity_public_key)?;
1925 }
1926 StateTransition::IdentityCreateFromAddresses(_) => {
1927 return Err(ProtocolError::CorruptedCodeExecution(
1928 "identity create from addresses can not be called for identity signing"
1929 .to_string(),
1930 ))
1931 }
1932 StateTransition::IdentityTopUpFromAddresses(_) => {
1933 return Err(ProtocolError::CorruptedCodeExecution(
1934 "identity top up from addresses can not be called for identity signing"
1935 .to_string(),
1936 ))
1937 }
1938 StateTransition::AddressFundsTransfer(_) => {
1939 return Err(ProtocolError::CorruptedCodeExecution(
1940 "address funds transfer transition can not be called for identity signing"
1941 .to_string(),
1942 ))
1943 }
1944 StateTransition::AddressFundingFromAssetLock(_) => {
1945 return Err(ProtocolError::CorruptedCodeExecution(
1946 "address funding from asset lock transition can not be called for identity signing"
1947 .to_string(),
1948 ))
1949 }
1950 StateTransition::AddressCreditWithdrawal(_) => {
1951 return Err(ProtocolError::CorruptedCodeExecution(
1952 "address credit withdrawal transition can not be called for identity signing"
1953 .to_string(),
1954 ))
1955 }
1956 StateTransition::Shield(_) => {
1957 return Err(ProtocolError::CorruptedCodeExecution(
1958 "shield transition can not be called for identity signing".to_string(),
1959 ))
1960 }
1961 StateTransition::ShieldedTransfer(_) => {
1962 return Err(ProtocolError::CorruptedCodeExecution(
1963 "shielded transfer transition can not be called for identity signing"
1964 .to_string(),
1965 ))
1966 }
1967 StateTransition::Unshield(_) => {
1968 return Err(ProtocolError::CorruptedCodeExecution(
1969 "unshield transition can not be called for identity signing".to_string(),
1970 ))
1971 }
1972 StateTransition::IdentityTopUpFromShieldedPool(_) => {
1973 return Err(ProtocolError::CorruptedCodeExecution(
1974 "identity top up from shielded pool transition can not be called for identity signing".to_string(),
1975 ))
1976 }
1977 StateTransition::TokenShieldedTransferWithShieldedFee(_) => {
1978 return Err(ProtocolError::CorruptedCodeExecution(
1979 "token shielded transfer with shielded fee transition can not be called for identity signing".to_string(),
1980 ))
1981 }
1982 StateTransition::TokenUnshieldWithShieldedFee(_) => {
1983 return Err(ProtocolError::CorruptedCodeExecution(
1984 "token unshield with shielded fee transition can not be called for identity signing".to_string(),
1985 ))
1986 }
1987 StateTransition::TokenPurchaseFromShieldedPool(_) => {
1988 return Err(ProtocolError::CorruptedCodeExecution(
1989 "token purchase from shielded pool transition can not be called for identity signing".to_string(),
1990 ))
1991 }
1992 StateTransition::ShieldFromAssetLock(_) => {
1993 return Err(ProtocolError::CorruptedCodeExecution(
1994 "shield from asset lock transition can not be called for identity signing"
1995 .to_string(),
1996 ))
1997 }
1998 StateTransition::ShieldedWithdrawal(_) => {
1999 return Err(ProtocolError::CorruptedCodeExecution(
2000 "shielded withdrawal transition can not be called for identity signing"
2001 .to_string(),
2002 ))
2003 }
2004 StateTransition::IdentityCreateFromShieldedPool(_) => {
2005 return Err(ProtocolError::CorruptedCodeExecution(
2006 "identity create from shielded pool transition can not be called for identity signing"
2007 .to_string(),
2008 ))
2009 }
2010 StateTransition::ShieldFromIdentity(st) => {
2011 st.verify_public_key_level_and_purpose(identity_public_key, options)?;
2012 st.verify_public_key_is_enabled(identity_public_key)?;
2013 }
2014 }
2015 let data = self.signable_bytes()?;
2016 self.set_signature(signer.sign(identity_public_key, data.as_slice()).await?);
2017 self.set_signature_public_key_id(identity_public_key.id());
2018 Ok(())
2019 }
2020
2021 #[cfg(feature = "state-transition-signing")]
2026 fn verify_identity_key_bounds(
2027 &self,
2028 identity_public_key: &IdentityPublicKey,
2029 ) -> Result<(), ProtocolError> {
2030 if identity_public_key.purpose() != Purpose::AUTHENTICATION {
2031 return Ok(());
2032 }
2033 let Some(bounds) = identity_public_key.contract_bounds() else {
2034 return Ok(());
2035 };
2036 match self {
2037 StateTransition::Batch(batch) => {
2038 use crate::state_transition::batch_transition::accessors::DocumentsBatchTransitionAccessorsV0;
2039 if batch.transitions_iter().all(|member| {
2040 bounds.check_batched_transition(member) != BatchedTransitionBoundsCheck::Denied
2041 }) {
2042 Ok(())
2043 } else {
2044 Err(ProtocolError::ConsensusError(Box::new(
2045 ContractBoundedKeyOutOfBoundsError::new(identity_public_key.id()).into(),
2046 )))
2047 }
2048 }
2049 _ => Err(ProtocolError::ConsensusError(Box::new(
2050 ContractBoundedKeyNonBatchError::new(identity_public_key.id()).into(),
2051 ))),
2052 }
2053 }
2054
2055 #[cfg(feature = "state-transition-signing")]
2056 pub fn sign(
2057 &mut self,
2058 identity_public_key: &IdentityPublicKey,
2059 private_key: &[u8],
2060 bls: &impl BlsModule,
2061 ) -> Result<(), ProtocolError> {
2062 self.sign_with_options(
2063 identity_public_key,
2064 private_key,
2065 bls,
2066 StateTransitionSigningOptions::default(),
2067 )
2068 }
2069
2070 #[cfg(feature = "state-transition-signing")]
2071 pub fn sign_with_options(
2072 &mut self,
2073 identity_public_key: &IdentityPublicKey,
2074 private_key: &[u8],
2075 bls: &impl BlsModule,
2076 options: StateTransitionSigningOptions,
2077 ) -> Result<(), ProtocolError> {
2078 self.verify_identity_key_bounds(identity_public_key)?;
2079 call_errorable_method_identity_signed!(
2080 self,
2081 verify_public_key_level_and_purpose,
2082 identity_public_key,
2083 options
2084 )?;
2085 call_errorable_method_identity_signed!(
2086 self,
2087 verify_public_key_is_enabled,
2088 identity_public_key
2089 )?;
2090
2091 match identity_public_key.key_type() {
2092 KeyType::ECDSA_SECP256K1 => {
2093 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
2094
2095 if public_key_compressed.as_slice() != identity_public_key.data().as_slice() {
2100 return Err(ProtocolError::InvalidSignaturePublicKeyError(
2101 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
2102 ));
2103 }
2104
2105 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
2106 }
2107 KeyType::ECDSA_HASH160 => {
2108 let public_key_compressed = get_compressed_public_ec_key(private_key)?;
2109 let pub_key_hash = ripemd160_sha256(&public_key_compressed);
2110
2111 if identity_public_key.data().as_slice() != pub_key_hash {
2112 return Err(ProtocolError::InvalidSignaturePublicKeyError(
2113 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
2114 ));
2115 }
2116 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
2117 }
2118 KeyType::BLS12_381 => {
2119 let public_key = bls.private_key_to_public_key(private_key)?;
2120
2121 if public_key != identity_public_key.data().as_slice() {
2122 return Err(ProtocolError::InvalidSignaturePublicKeyError(
2123 InvalidSignaturePublicKeyError::new(identity_public_key.data().to_vec()),
2124 ));
2125 }
2126 self.sign_by_private_key(private_key, identity_public_key.key_type(), bls)
2127 }
2128
2129 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
2133 Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
2134 InvalidIdentityPublicKeyTypeError::new(identity_public_key.key_type()),
2135 ))
2136 }
2137 }?;
2138
2139 self.set_signature_public_key_id(identity_public_key.id());
2140
2141 Ok(())
2142 }
2143
2144 #[cfg(feature = "state-transition-signing")]
2145 pub fn sign_by_private_key(
2147 &mut self,
2148 private_key: &[u8],
2149 key_type: KeyType,
2150 bls: &impl BlsModule,
2151 ) -> Result<(), ProtocolError> {
2152 let data = self.signable_bytes()?;
2153 match key_type {
2154 KeyType::BLS12_381 => {
2155 if !self.set_signature(bls.sign(&data, private_key)?.into()) {
2156 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2157 needed: self.required_number_of_private_keys(),
2158 using: 1,
2159 msg: "failed to set BLS signature",
2160 });
2161 }
2162 }
2163
2164 KeyType::ECDSA_SECP256K1 | KeyType::ECDSA_HASH160 => {
2166 let signature = signer::sign(&data, private_key)?;
2167 if !self.set_signature(signature.to_vec().into()) {
2168 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2169 needed: self.required_number_of_private_keys(),
2170 using: 1,
2171 msg: "failed to set ECDSA signature",
2172 });
2173 };
2174 }
2175
2176 KeyType::BIP13_SCRIPT_HASH | KeyType::EDDSA_25519_HASH160 => {
2180 return Err(ProtocolError::InvalidIdentityPublicKeyTypeError(
2181 InvalidIdentityPublicKeyTypeError::new(key_type),
2182 ))
2183 }
2184 };
2185 Ok(())
2186 }
2187
2188 #[cfg(all(feature = "state-transition-signing", feature = "core_key_wallet"))]
2266 pub async fn sign_with_core_signer<S: ::key_wallet::signer::Signer>(
2267 &mut self,
2268 path: &::key_wallet::bip32::DerivationPath,
2269 signer: &S,
2270 ) -> Result<(), ProtocolError> {
2271 use dashcore::crypto::ecdsa::EcdsaRecSignature;
2272 use dashcore::signer::double_sha;
2273 use secp256k1::ecdsa::{RecoverableSignature, RecoveryId};
2274 use secp256k1::Message;
2275
2276 let data = self.signable_bytes()?;
2277 let data_hash = double_sha(&data);
2280 let digest: [u8; 32] = data_hash.as_slice().try_into().map_err(|_| {
2281 ProtocolError::Generic("double_sha did not return 32 bytes".to_string())
2282 })?;
2283
2284 let (signature, public_key) = signer
2285 .sign_ecdsa(path, digest)
2286 .await
2287 .map_err(|e| ProtocolError::ExternalSignerError(format!("signer failed: {}", e)))?;
2288
2289 let compact_64 = signature.to_bytes();
2298 let msg = Message::from_digest(digest);
2299
2300 let mut found: Option<RecoverableSignature> = None;
2301 for id in 0..4i32 {
2302 let recid = match RecoveryId::try_from(id) {
2303 Ok(r) => r,
2304 Err(_) => continue,
2305 };
2306 let candidate = match RecoverableSignature::from_compact(&compact_64, recid) {
2307 Ok(s) => s,
2308 Err(_) => continue,
2309 };
2310 if let Ok(recovered) = candidate.recover_ecdsa(msg) {
2311 if recovered == public_key.into() {
2312 found = Some(candidate);
2313 break;
2314 }
2315 }
2316 }
2317 let recoverable = found.ok_or_else(|| {
2318 ProtocolError::ExternalSignerError(
2324 "signer returned a signature whose recovery id does not match the returned public key".to_string(),
2325 )
2326 })?;
2327
2328 let (recovery_id, _) = recoverable.serialize_compact();
2333 let compact_65 = EcdsaRecSignature::from_parts(
2334 recoverable.to_standard().into(),
2335 recovery_id.to_u8(),
2336 true,
2337 )
2338 .map_err(|e| ProtocolError::ExternalSignerError(e.to_string()))?
2339 .to_bytes();
2340
2341 if !self.set_signature(compact_65.to_vec().into()) {
2342 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2343 needed: self.required_number_of_private_keys(),
2344 using: 1,
2345 msg: "failed to set ECDSA signature",
2346 });
2347 }
2348 Ok(())
2349 }
2350
2351 #[cfg(feature = "state-transition-validation")]
2352 fn verify_ecdsa_hash_160_signature_by_public_key_hash(
2353 &self,
2354 public_key_hash: &[u8],
2355 ) -> Result<(), ProtocolError> {
2356 let Some(signature) = self.signature() else {
2357 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2358 needed: self.required_number_of_private_keys(),
2359 using: 1,
2360 msg: "This state transition type should a single signature",
2361 });
2362 };
2363 if signature.is_empty() {
2364 return Err(ProtocolError::StateTransitionIsNotSignedError(
2365 StateTransitionIsNotSignedError::new(self.clone()),
2366 ));
2367 }
2368 let data = self.signable_bytes()?;
2369 let data_hash = double_sha(data);
2370 signer::verify_hash_signature(&data_hash, signature.as_slice(), public_key_hash).map_err(
2371 |e| {
2372 ProtocolError::from(ConsensusError::SignatureError(
2373 SignatureError::InvalidStateTransitionSignatureError(
2374 InvalidStateTransitionSignatureError::new(e.to_string()),
2375 ),
2376 ))
2377 },
2378 )
2379 }
2380
2381 #[cfg(feature = "state-transition-validation")]
2382 fn verify_ecdsa_signature_by_public_key(&self, public_key: &[u8]) -> Result<(), ProtocolError> {
2384 let Some(signature) = self.signature() else {
2385 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2386 needed: self.required_number_of_private_keys(),
2387 using: 1,
2388 msg: "This state transition type should a single signature",
2389 });
2390 };
2391 if signature.is_empty() {
2392 return Err(ProtocolError::StateTransitionIsNotSignedError(
2393 StateTransitionIsNotSignedError::new(self.clone()),
2394 ));
2395 }
2396 let data = self.signable_bytes()?;
2397 signer::verify_data_signature(&data, signature.as_slice(), public_key).map_err(|e| {
2398 ProtocolError::from(ConsensusError::SignatureError(
2401 SignatureError::InvalidStateTransitionSignatureError(
2402 InvalidStateTransitionSignatureError::new(e.to_string()),
2403 ),
2404 ))
2405 })
2406 }
2407
2408 #[cfg(feature = "state-transition-validation")]
2409 fn verify_bls_signature_by_public_key<T: BlsModule>(
2412 &self,
2413 public_key: &[u8],
2414 bls: &T,
2415 ) -> Result<bool, ProtocolError> {
2416 let Some(signature) = self.signature() else {
2417 return Err(ProtocolError::InvalidVerificationWrongNumberOfElements {
2418 needed: self.required_number_of_private_keys(),
2419 using: 1,
2420 msg: "This state transition type should a single signature",
2421 });
2422 };
2423 if signature.is_empty() {
2424 return Err(ProtocolError::StateTransitionIsNotSignedError(
2425 StateTransitionIsNotSignedError::new(self.clone()),
2426 ));
2427 }
2428
2429 let data = self.signable_bytes()?;
2430
2431 bls.verify_signature(signature.as_slice(), &data, public_key)
2432 .map_err(|e| {
2433 ProtocolError::from(ConsensusError::SignatureError(
2435 SignatureError::InvalidStateTransitionSignatureError(
2436 InvalidStateTransitionSignatureError::new(e.to_string()),
2437 ),
2438 ))
2439 })
2440 }
2441}
2442
2443#[cfg(feature = "state-transition-validation")]
2444impl StateTransitionStructureValidation for StateTransition {
2445 fn validate_structure(
2446 &self,
2447 platform_version: &PlatformVersion,
2448 ) -> crate::validation::SimpleConsensusValidationResult {
2449 match self {
2450 StateTransition::DataContractCreate(_)
2451 | StateTransition::DataContractUpdate(_)
2452 | StateTransition::ContractUserModeration(_)
2453 | StateTransition::ContractFeeClaim(_)
2454 | StateTransition::Batch(_)
2455 | StateTransition::IdentityCreate(_)
2456 | StateTransition::IdentityTopUp(_)
2457 | StateTransition::IdentityCreditWithdrawal(_)
2458 | StateTransition::IdentityUpdate(_)
2459 | StateTransition::IdentityKeyLimitsUpdate(_)
2460 | StateTransition::IdentityCreditTransfer(_)
2461 | StateTransition::MasternodeVote(_) => {
2462 crate::validation::SimpleConsensusValidationResult::new_with_error(
2463 UnsupportedFeatureError::new(
2464 "structure validation for identity-based state transitions".to_string(),
2465 platform_version.protocol_version,
2466 )
2467 .into(),
2468 )
2469 }
2470 StateTransition::IdentityCreditTransferToAddresses(transition) => {
2471 transition.validate_structure(platform_version)
2472 }
2473 StateTransition::IdentityCreateFromAddresses(transition) => {
2474 transition.validate_structure(platform_version)
2475 }
2476 StateTransition::IdentityTopUpFromAddresses(transition) => {
2477 transition.validate_structure(platform_version)
2478 }
2479 StateTransition::AddressFundsTransfer(transition) => {
2480 transition.validate_structure(platform_version)
2481 }
2482 StateTransition::AddressFundingFromAssetLock(transition) => {
2483 transition.validate_structure(platform_version)
2484 }
2485 StateTransition::AddressCreditWithdrawal(transition) => {
2486 transition.validate_structure(platform_version)
2487 }
2488 StateTransition::Shield(transition) => transition.validate_structure(platform_version),
2489 StateTransition::ShieldedTransfer(transition) => {
2490 transition.validate_structure(platform_version)
2491 }
2492 StateTransition::Unshield(transition) => {
2493 transition.validate_structure(platform_version)
2494 }
2495 StateTransition::IdentityTopUpFromShieldedPool(transition) => {
2496 transition.validate_structure(platform_version)
2497 }
2498 StateTransition::TokenShieldedTransferWithShieldedFee(transition) => {
2499 transition.validate_structure(platform_version)
2500 }
2501 StateTransition::TokenUnshieldWithShieldedFee(transition) => {
2502 transition.validate_structure(platform_version)
2503 }
2504 StateTransition::TokenPurchaseFromShieldedPool(transition) => {
2505 transition.validate_structure(platform_version)
2506 }
2507 StateTransition::ShieldFromAssetLock(transition) => {
2508 transition.validate_structure(platform_version)
2509 }
2510 StateTransition::ShieldedWithdrawal(transition) => {
2511 transition.validate_structure(platform_version)
2512 }
2513 StateTransition::IdentityCreateFromShieldedPool(transition) => {
2514 transition.validate_structure(platform_version)
2515 }
2516 StateTransition::ShieldFromIdentity(transition) => {
2517 transition.validate_structure(platform_version)
2518 }
2519 }
2520 }
2521}
2522
2523#[cfg(test)]
2524mod tests {
2525 use super::*;
2526
2527 #[test]
2530 fn should_refuse_only_a_transition_larger_than_the_limit() {
2531 for protocol_version in 1..=LATEST_VERSION {
2532 let platform_version = PlatformVersion::get(protocol_version).expect("known version");
2533 let max_size = platform_version.system_limits.max_state_transition_size;
2534
2535 assert!(validate_state_transition_size(0, platform_version).is_ok());
2536 assert!(validate_state_transition_size(max_size, platform_version).is_ok());
2537 let error = validate_state_transition_size(max_size + 1, platform_version)
2538 .expect_err("one byte over the limit is refused");
2539 assert_eq!(error.actual_size_bytes(), max_size + 1);
2540 assert_eq!(error.max_size_bytes(), max_size);
2541 }
2542 }
2543
2544 #[test]
2549 fn test_signing_options_default() {
2550 let opts = StateTransitionSigningOptions::default();
2551 assert!(!opts.allow_signing_with_any_security_level);
2552 assert!(!opts.allow_signing_with_any_purpose);
2553 }
2554
2555 #[test]
2556 fn test_signing_options_equality() {
2557 let a = StateTransitionSigningOptions {
2558 allow_signing_with_any_security_level: true,
2559 allow_signing_with_any_purpose: false,
2560 };
2561 let b = StateTransitionSigningOptions {
2562 allow_signing_with_any_security_level: true,
2563 allow_signing_with_any_purpose: false,
2564 };
2565 assert_eq!(a, b);
2566 }
2567
2568 #[test]
2569 fn test_signing_options_inequality() {
2570 let a = StateTransitionSigningOptions {
2571 allow_signing_with_any_security_level: true,
2572 allow_signing_with_any_purpose: false,
2573 };
2574 let b = StateTransitionSigningOptions {
2575 allow_signing_with_any_security_level: false,
2576 allow_signing_with_any_purpose: false,
2577 };
2578 assert_ne!(a, b);
2579 }
2580
2581 #[test]
2582 #[allow(clippy::clone_on_copy)]
2583 fn test_signing_options_clone() {
2584 let original = StateTransitionSigningOptions {
2585 allow_signing_with_any_security_level: true,
2586 allow_signing_with_any_purpose: true,
2587 };
2588 let cloned = original.clone();
2589 assert_eq!(original, cloned);
2590 }
2591
2592 #[test]
2593 fn test_signing_options_copy() {
2594 let original = StateTransitionSigningOptions {
2595 allow_signing_with_any_security_level: true,
2596 allow_signing_with_any_purpose: false,
2597 };
2598 let copied = original;
2599 assert_eq!(original, copied);
2600 }
2601
2602 #[test]
2603 fn test_signing_options_debug() {
2604 let opts = StateTransitionSigningOptions::default();
2605 let debug_str = format!("{:?}", opts);
2606 assert!(debug_str.contains("StateTransitionSigningOptions"));
2607 assert!(debug_str.contains("allow_signing_with_any_security_level"));
2608 assert!(debug_str.contains("allow_signing_with_any_purpose"));
2609 }
2610
2611 use crate::identity::core_script::CoreScript;
2621 use crate::identity::{Purpose, SecurityLevel};
2622 use crate::prelude::Identifier;
2623 use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0;
2624 use crate::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition;
2625 use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0;
2626 use crate::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition;
2627 use crate::state_transition::masternode_vote_transition::v0::MasternodeVoteTransitionV0;
2628 use crate::state_transition::masternode_vote_transition::MasternodeVoteTransition;
2629 use crate::withdrawal::Pooling;
2630
2631 fn sample_transfer_st() -> StateTransition {
2632 let v0 = IdentityCreditTransferTransitionV0 {
2633 identity_id: Identifier::from([1u8; 32]),
2634 recipient_id: Identifier::from([2u8; 32]),
2635 amount: 1_000,
2636 nonce: 7,
2637 user_fee_increase: 3,
2638 signature_public_key_id: 11,
2639 signature: BinaryData::new(vec![0u8; 65]),
2640 };
2641 StateTransition::IdentityCreditTransfer(IdentityCreditTransferTransition::V0(v0))
2642 }
2643
2644 fn sample_masternode_vote_st() -> StateTransition {
2645 let v0 = MasternodeVoteTransitionV0 {
2646 pro_tx_hash: Identifier::from([3u8; 32]),
2647 voter_identity_id: Identifier::from([4u8; 32]),
2648 vote: Default::default(),
2649 nonce: 2,
2650 signature_public_key_id: 5,
2651 signature: BinaryData::new(vec![9u8; 10]),
2652 };
2653 StateTransition::MasternodeVote(MasternodeVoteTransition::V0(v0))
2654 }
2655
2656 fn sample_withdrawal_st() -> StateTransition {
2657 let v0 = IdentityCreditWithdrawalTransitionV0 {
2658 identity_id: Identifier::from([5u8; 32]),
2659 amount: 42,
2660 core_fee_per_byte: 1,
2661 pooling: Pooling::Never,
2662 output_script: CoreScript::from_bytes(vec![0x76, 0xa9]),
2663 nonce: 4,
2664 user_fee_increase: 1,
2665 signature_public_key_id: 3,
2666 signature: BinaryData::new(vec![8u8; 65]),
2667 };
2668 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V0(v0))
2669 }
2670
2671 #[test]
2672 fn test_name_returns_variant_names() {
2673 assert_eq!(sample_transfer_st().name(), "IdentityCreditTransfer");
2674 assert_eq!(sample_masternode_vote_st().name(), "MasternodeVote");
2675 assert_eq!(sample_withdrawal_st().name(), "IdentityCreditWithdrawal");
2676 }
2677
2678 #[test]
2679 fn test_state_transition_type_matches_variant() {
2680 assert_eq!(
2681 sample_transfer_st().state_transition_type(),
2682 StateTransitionType::IdentityCreditTransfer
2683 );
2684 assert_eq!(
2685 sample_masternode_vote_st().state_transition_type(),
2686 StateTransitionType::MasternodeVote
2687 );
2688 assert_eq!(
2689 sample_withdrawal_st().state_transition_type(),
2690 StateTransitionType::IdentityCreditWithdrawal
2691 );
2692 }
2693
2694 #[test]
2695 fn test_is_identity_signed_excludes_asset_lock_and_shielded() {
2696 assert!(sample_transfer_st().is_identity_signed());
2697 assert!(sample_masternode_vote_st().is_identity_signed());
2698 assert!(sample_withdrawal_st().is_identity_signed());
2699 }
2700
2701 #[test]
2702 fn test_signature_accessor() {
2703 let st = sample_transfer_st();
2704 let sig = st.signature().expect("transfer should expose signature");
2705 assert_eq!(sig.len(), 65);
2706
2707 let st = sample_masternode_vote_st();
2708 let sig = st.signature().expect("masternode vote has signature");
2709 assert_eq!(sig.as_slice(), &[9u8; 10]);
2710 }
2711
2712 #[test]
2713 fn test_owner_id_accessor() {
2714 let transfer = sample_transfer_st();
2715 assert_eq!(transfer.owner_id(), Some(Identifier::from([1u8; 32])));
2716
2717 let vote = sample_masternode_vote_st();
2718 assert_eq!(vote.owner_id(), Some(Identifier::from([4u8; 32])));
2719
2720 let withdraw = sample_withdrawal_st();
2721 assert_eq!(withdraw.owner_id(), Some(Identifier::from([5u8; 32])));
2722 }
2723
2724 #[test]
2725 fn test_signature_public_key_id_accessor() {
2726 assert_eq!(sample_transfer_st().signature_public_key_id(), Some(11));
2727 assert_eq!(
2728 sample_masternode_vote_st().signature_public_key_id(),
2729 Some(5)
2730 );
2731 assert_eq!(sample_withdrawal_st().signature_public_key_id(), Some(3));
2732 }
2733
2734 #[test]
2735 fn test_user_fee_increase_for_various_variants() {
2736 assert_eq!(sample_transfer_st().user_fee_increase(), 3);
2738 assert_eq!(sample_masternode_vote_st().user_fee_increase(), 0);
2740 assert_eq!(sample_withdrawal_st().user_fee_increase(), 1);
2742 }
2743
2744 #[test]
2745 fn test_set_signature_returns_true_for_supported() {
2746 let mut st = sample_transfer_st();
2747 let ok = st.set_signature(BinaryData::new(vec![0xaa; 65]));
2748 assert!(ok);
2749 assert_eq!(st.signature().unwrap().as_slice(), &[0xaa; 65]);
2750 }
2751
2752 #[test]
2753 fn test_set_user_fee_increase_updates_value() {
2754 let mut st = sample_transfer_st();
2755 st.set_user_fee_increase(42);
2756 assert_eq!(st.user_fee_increase(), 42);
2757
2758 let mut vote = sample_masternode_vote_st();
2760 vote.set_user_fee_increase(99);
2761 assert_eq!(vote.user_fee_increase(), 0);
2762 }
2763
2764 #[test]
2765 fn test_set_signature_public_key_id() {
2766 let mut st = sample_transfer_st();
2767 st.set_signature_public_key_id(1234);
2768 assert_eq!(st.signature_public_key_id(), Some(1234));
2769 }
2770
2771 #[test]
2772 fn test_required_number_of_private_keys_default() {
2773 assert_eq!(sample_transfer_st().required_number_of_private_keys(), 1);
2775 assert_eq!(
2776 sample_masternode_vote_st().required_number_of_private_keys(),
2777 1
2778 );
2779 assert_eq!(sample_withdrawal_st().required_number_of_private_keys(), 1);
2780 }
2781
2782 #[test]
2783 fn test_inputs_none_for_legacy_variants() {
2784 assert!(sample_transfer_st().inputs().is_none());
2786 assert!(sample_masternode_vote_st().inputs().is_none());
2787 assert!(sample_withdrawal_st().inputs().is_none());
2788 }
2789
2790 #[test]
2791 fn test_active_version_range_legacy_transitions() {
2792 assert_eq!(sample_transfer_st().active_version_range(), ALL_VERSIONS);
2794 assert_eq!(
2795 sample_masternode_vote_st().active_version_range(),
2796 ALL_VERSIONS
2797 );
2798 assert_eq!(sample_withdrawal_st().active_version_range(), ALL_VERSIONS);
2799 }
2800
2801 #[test]
2805 fn should_make_a_masternode_vote_on_a_yes_no_poll_active_from_protocol_version_15() {
2806 use crate::serialization::PlatformSerializable;
2807 use crate::voting::vote_choices::resource_vote_choice::ResourceVoteChoice;
2808 use crate::voting::vote_choices::yes_no_abstain_vote_choice::YesNoAbstainVoteChoice;
2809 use crate::voting::vote_polls::yes_no_vote_poll::{
2810 YesNoMinimumVotingPower, YesNoVotePoll, YesNoVotePollPurpose,
2811 };
2812 use crate::voting::votes::resource_vote::v0::ResourceVoteV0;
2813 use crate::voting::votes::resource_vote::ResourceVote;
2814 use crate::voting::votes::yes_no_vote::v0::YesNoVoteV0;
2815 use crate::voting::votes::yes_no_vote::YesNoVote;
2816
2817 let vote_poll = YesNoVotePoll {
2818 purpose: YesNoVotePollPurpose::DecisionProposal {
2819 document_id: Identifier::from([7u8; 32]),
2820 },
2821 supermajority_numerator: 2,
2822 supermajority_denominator: 3,
2823 minimum_voting_power: YesNoMinimumVotingPower::Absolute { voting_power: 400 },
2824 };
2825 let votes = [
2826 Vote::YesNoVote(YesNoVote::V0(YesNoVoteV0 {
2827 vote_poll_id: vote_poll
2828 .unique_id(PlatformVersion::latest())
2829 .expect("poll id"),
2830 vote_choice: YesNoAbstainVoteChoice::Yes,
2831 })),
2832 Vote::ResourceVote(ResourceVote::V0(ResourceVoteV0 {
2833 vote_poll: VotePoll::YesNoVotePoll(vote_poll),
2834 resource_vote_choice: ResourceVoteChoice::Abstain,
2835 })),
2836 ];
2837 let version_14 = PlatformVersion::get(14).expect("protocol version 14");
2838 let version_15 = PlatformVersion::get(15).expect("protocol version 15");
2839
2840 for vote in votes {
2841 let v0 = MasternodeVoteTransitionV0 {
2842 pro_tx_hash: Identifier::from([3u8; 32]),
2843 voter_identity_id: Identifier::from([4u8; 32]),
2844 vote,
2845 nonce: 2,
2846 signature_public_key_id: 5,
2847 signature: BinaryData::new(vec![9u8; 10]),
2848 };
2849 let state_transition =
2850 StateTransition::MasternodeVote(MasternodeVoteTransition::V0(v0));
2851 assert_eq!(
2852 state_transition.active_version_range(),
2853 YES_NO_VOTE_POLL_INITIAL_PROTOCOL_VERSION..=LATEST_VERSION
2854 );
2855
2856 let bytes = state_transition
2857 .serialize_to_bytes()
2858 .expect("expected to serialize the vote");
2859 assert!(matches!(
2860 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_14),
2861 Err(ProtocolError::StateTransitionError(
2862 StateTransitionIsNotActiveError { .. }
2863 ))
2864 ));
2865 assert_eq!(
2866 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_15)
2867 .expect("expected the vote to be active at 15"),
2868 state_transition
2869 );
2870 }
2871 }
2872
2873 #[test]
2874 fn test_unique_identifiers_non_empty() {
2875 let ids = sample_transfer_st().unique_identifiers();
2876 assert_eq!(ids.len(), 1);
2877 assert!(!ids[0].is_empty());
2878 }
2879
2880 #[test]
2881 fn test_required_asset_lock_balance_rejects_non_asset_lock() {
2882 let platform_version = PlatformVersion::latest();
2883 let st = sample_transfer_st();
2884 let err = st
2885 .required_asset_lock_balance_for_processing_start(platform_version)
2886 .expect_err("credit transfer is not an asset lock state transition");
2887 match err {
2888 ProtocolError::CorruptedCodeExecution(msg) => {
2889 assert!(
2890 msg.contains("is not an asset lock transaction"),
2891 "unexpected error message: {msg}"
2892 );
2893 }
2894 other => panic!("expected CorruptedCodeExecution, got {other:?}"),
2895 }
2896 }
2897
2898 #[test]
2899 fn test_security_level_requirement_for_transfer() {
2900 let st = sample_transfer_st();
2902 let levels = st
2903 .security_level_requirement(Purpose::TRANSFER)
2904 .expect("transfer state transition should return a requirement");
2905 assert_eq!(levels, vec![SecurityLevel::CRITICAL]);
2906 }
2907
2908 #[test]
2909 fn test_purpose_requirement_for_transfer() {
2910 let st = sample_transfer_st();
2911 let purposes = st
2912 .purpose_requirement()
2913 .expect("transfer state transition should have a purpose");
2914 assert_eq!(purposes, vec![Purpose::TRANSFER]);
2915 }
2916
2917 #[test]
2918 fn test_optional_asset_lock_proof_none_for_transfer() {
2919 let st = sample_transfer_st();
2920 assert!(st.optional_asset_lock_proof().is_none());
2921 }
2922
2923 #[test]
2928 fn test_from_outer_enum_into_state_transition() {
2929 let outer: IdentityCreditTransferTransition =
2930 IdentityCreditTransferTransition::V0(IdentityCreditTransferTransitionV0::default());
2931 let st: StateTransition = outer.into();
2932 assert!(matches!(st, StateTransition::IdentityCreditTransfer(_)));
2933 }
2934
2935 #[test]
2936 fn test_from_masternode_vote_outer_into_state_transition() {
2937 let outer: MasternodeVoteTransition =
2938 MasternodeVoteTransition::V0(MasternodeVoteTransitionV0::default());
2939 let st: StateTransition = outer.into();
2940 assert!(matches!(st, StateTransition::MasternodeVote(_)));
2941 }
2942
2943 #[test]
2949 fn test_state_transition_platform_serialize_roundtrip() {
2950 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
2951 let original = sample_transfer_st();
2952 let bytes =
2953 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
2954 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
2955 .expect("deserialize should succeed");
2956 assert_eq!(original, restored);
2957 }
2958
2959 #[test]
2960 fn test_deserialize_from_bytes_in_version_succeeds_for_latest() {
2961 use crate::serialization::PlatformSerializable;
2962 let original = sample_transfer_st();
2963 let bytes =
2964 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2965 let restored = StateTransition::deserialize_from_bytes_untrusted_in_version(
2966 &bytes,
2967 PlatformVersion::latest(),
2968 )
2969 .expect("deserialize_from_bytes_in_version should succeed");
2970 assert_eq!(original, restored);
2971 }
2972
2973 #[test]
2974 fn should_refuse_bytes_after_the_transition_only_in_the_exact_in_version_decoder() {
2975 use crate::serialization::PlatformSerializable;
2976 let original = sample_transfer_st();
2977 let bytes =
2978 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
2979 let platform_version = PlatformVersion::latest();
2980
2981 assert_eq!(
2982 StateTransition::deserialize_from_bytes_untrusted_exact_in_version(
2983 &bytes,
2984 platform_version
2985 )
2986 .expect("the exact bytes decode"),
2987 original
2988 );
2989
2990 for left_over in [1usize, 100] {
2991 let mut padded = bytes.clone();
2992 padded.extend(std::iter::repeat_n(0xAB, left_over));
2993
2994 assert_eq!(
2995 StateTransition::deserialize_from_bytes_untrusted_in_version(
2996 &padded,
2997 platform_version
2998 )
2999 .expect("the loose decoder ignores the suffix"),
3000 original
3001 );
3002 match StateTransition::deserialize_from_bytes_untrusted_exact_in_version(
3003 &padded,
3004 platform_version,
3005 ) {
3006 Err(ProtocolError::PlatformDeserializationError(message)) => assert!(
3007 message.contains(&format!("{left_over} bytes left over")),
3008 "unexpected message: {message}"
3009 ),
3010 other => panic!("expected a deserialization error, got {other:?}"),
3011 }
3012 }
3013 }
3014
3015 #[test]
3016 fn test_transaction_id_is_deterministic() {
3017 let st = sample_transfer_st();
3018 let a = st.transaction_id().expect("hash should succeed");
3019 let b = st.transaction_id().expect("hash should succeed");
3020 assert_eq!(a, b);
3021 assert_eq!(a.len(), 32);
3022 }
3023
3024 #[test]
3025 fn test_transaction_id_changes_on_signature_change() {
3026 let mut st = sample_transfer_st();
3027 let before = st.transaction_id().expect("hash should succeed");
3028 st.set_signature(BinaryData::new(vec![0xbb; 65]));
3029 let after = st.transaction_id().expect("hash should succeed");
3030 assert_ne!(before, after);
3032 }
3033
3034 #[test]
3035 fn test_clone_preserves_inner_state() {
3036 let st = sample_transfer_st();
3037 let cloned = st.clone();
3038 assert_eq!(st, cloned);
3039 }
3040
3041 use crate::data_contract::serialized_version::DataContractInSerializationFormat;
3052 use crate::state_transition::batch_transition::document_base_transition::v0::DocumentBaseTransitionV0;
3053 use crate::state_transition::batch_transition::document_base_transition::DocumentBaseTransition;
3054 use crate::state_transition::batch_transition::document_delete_transition::{
3055 DocumentDeleteTransition, DocumentDeleteTransitionV0,
3056 };
3057 use crate::state_transition::batch_transition::{BatchTransition, BatchTransitionV0};
3058 use crate::state_transition::data_contract_create_transition::{
3059 DataContractCreateTransition, DataContractCreateTransitionV0,
3060 DataContractCreateTransitionV1,
3061 };
3062 use crate::state_transition::data_contract_update_transition::{
3063 DataContractUpdateTransition, DataContractUpdateTransitionV0,
3064 };
3065 use crate::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0;
3066 use crate::state_transition::identity_create_transition::IdentityCreateTransition;
3067 use crate::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0;
3068 use crate::state_transition::identity_topup_transition::IdentityTopUpTransition;
3069 use crate::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0;
3070 use crate::state_transition::identity_update_transition::IdentityUpdateTransition;
3071 use crate::state_transition::shielded_transfer_transition::v0::ShieldedTransferTransitionV0;
3072 use crate::state_transition::shielded_transfer_transition::ShieldedTransferTransition;
3073 use crate::state_transition::shielded_withdrawal_transition::v0::ShieldedWithdrawalTransitionV0;
3074 use crate::state_transition::shielded_withdrawal_transition::ShieldedWithdrawalTransition;
3075 use crate::state_transition::unshield_transition::v0::UnshieldTransitionV0;
3076 use crate::state_transition::unshield_transition::UnshieldTransition;
3077
3078 fn sample_data_contract_in_serialization_format() -> DataContractInSerializationFormat {
3081 use platform_version::TryIntoPlatformVersioned;
3082
3083 sample_data_contract()
3084 .try_into_platform_versioned(PlatformVersion::latest())
3085 .expect("expected to serialize a trivial contract")
3086 }
3087
3088 use crate::data_contract::DataContract;
3089
3090 fn sample_data_contract() -> DataContract {
3092 use crate::data_contract::config::v0::DataContractConfigV0;
3093 use crate::data_contract::config::DataContractConfig;
3094 use crate::data_contract::v1::DataContractV1;
3095 use std::collections::BTreeMap;
3096
3097 DataContract::V1(DataContractV1 {
3098 id: Identifier::from([9u8; 32]),
3099 version: 1,
3100 owner_id: Identifier::from([7u8; 32]),
3101 document_types: BTreeMap::new(),
3102 config: DataContractConfig::V0(DataContractConfigV0 {
3103 can_be_deleted: false,
3104 readonly: false,
3105 keeps_history: false,
3106 documents_keep_history_contract_default: false,
3107 documents_mutable_contract_default: false,
3108 documents_can_be_deleted_contract_default: false,
3109 requires_identity_encryption_bounded_key: None,
3110 requires_identity_decryption_bounded_key: None,
3111 }),
3112 schema_defs: None,
3113 created_at: None,
3114 updated_at: None,
3115 created_at_block_height: None,
3116 updated_at_block_height: None,
3117 created_at_epoch: None,
3118 updated_at_epoch: None,
3119 groups: BTreeMap::new(),
3120 tokens: BTreeMap::new(),
3121 keywords: Vec::new(),
3122 description: None,
3123 })
3124 }
3125
3126 fn sample_data_contract_create_st() -> StateTransition {
3127 StateTransition::DataContractCreate(DataContractCreateTransition::V0(
3128 DataContractCreateTransitionV0 {
3129 data_contract: sample_data_contract_in_serialization_format(),
3130 identity_nonce: 1,
3131 user_fee_increase: 5,
3132 signature_public_key_id: 2,
3133 signature: BinaryData::new(vec![0xAB; 65]),
3134 },
3135 ))
3136 }
3137
3138 fn sample_data_contract_create_v1_st() -> StateTransition {
3139 StateTransition::DataContractCreate(DataContractCreateTransition::V1(
3140 DataContractCreateTransitionV1 {
3141 data_contract: sample_data_contract_in_serialization_format(),
3142 identity_nonce: 1,
3143 contract_group: None,
3144 contract_group_memberships: vec![],
3145 user_fee_increase: 5,
3146 signature_public_key_id: 2,
3147 signature: BinaryData::new(vec![0xAB; 65]),
3148 },
3149 ))
3150 }
3151
3152 fn sample_data_contract_update_st() -> StateTransition {
3153 StateTransition::DataContractUpdate(DataContractUpdateTransition::V0(
3154 DataContractUpdateTransitionV0 {
3155 identity_contract_nonce: 4,
3156 data_contract: sample_data_contract_in_serialization_format(),
3157 user_fee_increase: 9,
3158 signature_public_key_id: 6,
3159 signature: BinaryData::new(vec![0xCD; 65]),
3160 },
3161 ))
3162 }
3163
3164 #[cfg(all(feature = "state-transition-signing", feature = "bls-signatures"))]
3165 #[test]
3166 fn should_enforce_contract_bounds_before_private_key_signing() {
3167 use crate::consensus::signature::{
3168 ContractBoundedKeyNonBatchError, ContractBoundedKeyOutOfBoundsError,
3169 };
3170 use crate::identity::contract_bounds::ContractBounds;
3171 use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
3172
3173 let private_key = [1; 32];
3174 let bls = crate::bls::native_bls::NativeBlsModule;
3175 let mut key = IdentityPublicKeyV0 {
3177 id: 7,
3178 purpose: Purpose::AUTHENTICATION,
3179 security_level: SecurityLevel::HIGH,
3180 key_type: KeyType::ECDSA_SECP256K1,
3181 data: get_compressed_public_ec_key(&private_key)
3182 .unwrap()
3183 .to_vec()
3184 .into(),
3185 contract_bounds: Some(ContractBounds::SingleContract {
3186 id: Identifier::from([2; 32]),
3187 }),
3188 ..Default::default()
3189 };
3190 sample_batch_st_with_delete()
3191 .sign(&key.clone().into(), &private_key, &bls)
3192 .expect("a batch inside the bounds must sign");
3193 key.contract_bounds = Some(ContractBounds::SingleContractDocumentType {
3194 id: Identifier::from([2; 32]),
3195 document_type_name: "preorder".to_string(),
3196 });
3197 sample_batch_st_with_delete()
3198 .sign(&key.clone().into(), &private_key, &bls)
3199 .expect("a batch of the bound document type must sign");
3200
3201 let err = sample_transfer_st()
3202 .sign(&key.clone().into(), &private_key, &bls)
3203 .unwrap_err();
3204 assert!(matches!(err, ProtocolError::ConsensusError(error)
3205 if *error == ContractBoundedKeyNonBatchError::new(key.id).into()));
3206
3207 for denied in [
3208 ContractBounds::SingleContract {
3209 id: Identifier::from([3; 32]),
3210 },
3211 ContractBounds::SingleContractDocumentType {
3212 id: Identifier::from([2; 32]),
3213 document_type_name: "other".to_string(),
3214 },
3215 ] {
3216 key.contract_bounds = Some(denied.clone());
3217 let mut transition = sample_batch_st_with_delete();
3218 let original = transition.clone();
3219 let err = transition
3220 .sign(&key.clone().into(), &private_key, &bls)
3221 .unwrap_err();
3222 assert!(
3223 matches!(err, ProtocolError::ConsensusError(error)
3224 if *error == ContractBoundedKeyOutOfBoundsError::new(key.id).into()),
3225 "{denied:?}"
3226 );
3227 assert_eq!(
3228 transition, original,
3229 "rejection must preserve the transition"
3230 );
3231 }
3232
3233 key.contract_bounds = None;
3234 sample_batch_st_with_delete()
3235 .sign(&key.into(), &private_key, &bls)
3236 .expect("unbounded keys must still sign");
3237 }
3238
3239 #[cfg(all(feature = "state-transition-signing", feature = "bls-signatures"))]
3242 #[test]
3243 fn should_refuse_a_bls_signature_that_does_not_verify_from_protocol_version_14() {
3244 use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
3245
3246 let bls = crate::bls::native_bls::NativeBlsModule;
3247 let private_key = [7; 32];
3248 let key: IdentityPublicKey = IdentityPublicKeyV0 {
3249 id: 11,
3250 purpose: Purpose::TRANSFER,
3251 security_level: SecurityLevel::CRITICAL,
3252 key_type: KeyType::BLS12_381,
3253 data: bls
3254 .private_key_to_public_key(&private_key)
3255 .expect("a valid BLS private key")
3256 .into(),
3257 ..Default::default()
3258 }
3259 .into();
3260
3261 let mut signed = sample_transfer_st();
3262 signed
3263 .sign(&key, &private_key, &bls)
3264 .expect("the key signs the transfer");
3265
3266 let mut by_another_key = signed.clone();
3269 by_another_key
3270 .sign_by_private_key(&[8; 32], KeyType::BLS12_381, &bls)
3271 .expect("another key signs the transfer");
3272 let mut not_a_point = signed.clone();
3273 not_a_point.set_signature(BinaryData::new(vec![0; 96]));
3274
3275 let latest = PlatformVersion::latest();
3276 let last_without_the_check = PlatformVersion::get(13).expect("protocol version 13");
3277 for platform_version in [latest, last_without_the_check] {
3278 signed
3279 .verify_identity_signed_signature(&key, &bls, platform_version)
3280 .expect("a signature by the key verifies");
3281 }
3282
3283 for (transition, label) in [
3284 (by_another_key, "signed by another key"),
3285 (not_a_point, "not a curve point"),
3286 ] {
3287 let error = transition
3288 .verify_identity_signed_signature(&key, &bls, latest)
3289 .expect_err(label);
3290 assert!(
3291 matches!(&error, ProtocolError::ConsensusError(consensus_error)
3292 if matches!(&**consensus_error, ConsensusError::SignatureError(
3293 SignatureError::InvalidStateTransitionSignatureError(e)
3294 ) if e.message() == "BLS12_381 signature does not verify")),
3295 "{label}: {error:?}"
3296 );
3297 transition
3298 .verify_identity_signed_signature(&key, &bls, last_without_the_check)
3299 .expect("protocol version 13 replays as it ran");
3300 }
3301 }
3302
3303 fn sample_batch_st_with_delete() -> StateTransition {
3304 let base = DocumentBaseTransition::V0(DocumentBaseTransitionV0 {
3305 id: Identifier::from([1u8; 32]),
3306 identity_contract_nonce: 3,
3307 document_type_name: "preorder".to_string(),
3308 data_contract_id: Identifier::from([2u8; 32]),
3309 });
3310 let delete =
3311 DocumentTransition::Delete(DocumentDeleteTransition::V0(DocumentDeleteTransitionV0 {
3312 base,
3313 }));
3314 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
3315 owner_id: Identifier::from([8u8; 32]),
3316 transitions: vec![delete],
3317 user_fee_increase: 2,
3318 signature_public_key_id: 7,
3319 signature: BinaryData::new(vec![0xEE; 65]),
3320 }))
3321 }
3322
3323 fn sample_batch_st_with_base(base: DocumentBaseTransition) -> StateTransition {
3324 let delete =
3325 DocumentTransition::Delete(DocumentDeleteTransition::V0(DocumentDeleteTransitionV0 {
3326 base,
3327 }));
3328 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
3329 owner_id: Identifier::from([8u8; 32]),
3330 transitions: vec![delete],
3331 user_fee_increase: 2,
3332 signature_public_key_id: 7,
3333 signature: BinaryData::new(vec![0xEE; 65]),
3334 }))
3335 }
3336
3337 #[test]
3342 fn should_refuse_a_contract_transition_carrying_the_stored_format_in_every_decoder() {
3343 use crate::serialization::{
3344 PlatformDeserializableTrusted, PlatformDeserializableUntrusted, PlatformSerializable,
3345 };
3346 use crate::state_transition::state_transition_factory::StateTransitionFactory;
3347
3348 fn assert_refused<T: std::fmt::Debug>(decoded: Result<T, ProtocolError>, decoder: &str) {
3350 let refusal = format!("{decoded:?}");
3351 assert!(
3352 decoded.is_err()
3353 && refusal.contains("UnexpectedVariant")
3354 && refusal.contains("TransitionContractFormat"),
3355 "{decoder}: {refusal}"
3356 );
3357 }
3358
3359 let stored = DataContractInSerializationFormat::try_from_data_contract_for_storage(
3360 &sample_data_contract(),
3361 PlatformVersion::latest(),
3362 )
3363 .expect("expected the contract to compile");
3364 assert!(matches!(stored, DataContractInSerializationFormat::V2(_)));
3365
3366 let transitions = [
3367 StateTransition::DataContractCreate(DataContractCreateTransition::V0(
3368 DataContractCreateTransitionV0 {
3369 data_contract: stored.clone(),
3370 identity_nonce: 1,
3371 user_fee_increase: 0,
3372 signature_public_key_id: 2,
3373 signature: BinaryData::new(vec![0xAB; 65]),
3374 },
3375 )),
3376 StateTransition::DataContractCreate(DataContractCreateTransition::V1(
3377 DataContractCreateTransitionV1 {
3378 data_contract: stored.clone(),
3379 identity_nonce: 1,
3380 contract_group: None,
3381 contract_group_memberships: vec![],
3382 user_fee_increase: 0,
3383 signature_public_key_id: 2,
3384 signature: BinaryData::new(vec![0xAB; 65]),
3385 },
3386 )),
3387 StateTransition::DataContractUpdate(DataContractUpdateTransition::V0(
3388 DataContractUpdateTransitionV0 {
3389 identity_contract_nonce: 4,
3390 data_contract: stored,
3391 user_fee_increase: 0,
3392 signature_public_key_id: 6,
3393 signature: BinaryData::new(vec![0xCD; 65]),
3394 },
3395 )),
3396 ];
3397 for transition in transitions {
3398 assert!(transition.active_version_range().is_empty());
3399 let bytes =
3400 PlatformSerializable::serialize_to_bytes(&transition).expect("serialize succeeds");
3401 assert_refused(
3402 StateTransition::deserialize_from_bytes_trusted(&bytes),
3403 "trusted",
3404 );
3405 assert_refused(
3406 StateTransition::deserialize_from_bytes_untrusted(&bytes),
3407 "untrusted",
3408 );
3409 assert_refused(
3410 StateTransition::deserialize_many_untrusted(std::slice::from_ref(&bytes)),
3411 "many",
3412 );
3413 assert_refused(StateTransitionFactory.create_from_buffer(&bytes), "factory");
3414 let untagged = match &transition {
3415 StateTransition::DataContractCreate(create) => {
3416 PlatformSerializable::serialize_to_bytes(create)
3417 }
3418 StateTransition::DataContractUpdate(update) => {
3419 PlatformSerializable::serialize_to_bytes(update)
3420 }
3421 other => panic!("expected a contract transition, got {other:?}"),
3422 }
3423 .expect("serialize succeeds");
3424 assert_refused(
3425 StateTransition::deserialize_untagged_untrusted_exact(
3426 transition.state_transition_type(),
3427 &untagged,
3428 ),
3429 "untagged",
3430 );
3431 #[cfg(feature = "serde-conversion")]
3433 {
3434 let assert_refused_by_serde = |refusal: String, decoder: &str| {
3435 assert!(
3436 refusal.contains("unknown variant `2`"),
3437 "{decoder}: {refusal}"
3438 );
3439 };
3440 let json = serde_json::to_value(&transition).expect("serialize to JSON");
3441 assert_refused_by_serde(
3442 serde_json::from_value::<StateTransition>(json)
3443 .expect_err("expected JSON to refuse the stored format")
3444 .to_string(),
3445 "JSON",
3446 );
3447 let value = platform_value::to_value(&transition).expect("serialize to a value");
3448 assert_refused_by_serde(
3449 platform_value::from_value::<StateTransition>(value)
3450 .expect_err("expected a platform value to refuse the stored format")
3451 .to_string(),
3452 "platform value",
3453 );
3454 }
3455 for protocol_version in 1..=LATEST_VERSION {
3456 let platform_version =
3457 PlatformVersion::get(protocol_version).expect("platform version exists");
3458 assert_refused(
3459 StateTransition::deserialize_from_bytes_untrusted_in_version(
3460 &bytes,
3461 platform_version,
3462 ),
3463 "in version",
3464 );
3465 assert_refused(
3466 StateTransition::deserialize_from_bytes_untrusted_exact_in_version(
3467 &bytes,
3468 platform_version,
3469 ),
3470 "exact in version",
3471 );
3472 }
3473 }
3474 }
3475
3476 #[test]
3481 fn should_not_activate_a_version_2_document_base_before_protocol_version_14() {
3482 use crate::serialization::PlatformSerializable;
3483 use crate::state_transition::batch_transition::document_base_transition::v1::DocumentBaseTransitionV1;
3484 use crate::state_transition::batch_transition::document_base_transition::v2::DocumentBaseTransitionV2;
3485
3486 let version_1 =
3487 sample_batch_st_with_base(DocumentBaseTransition::V1(DocumentBaseTransitionV1 {
3488 id: Identifier::from([1u8; 32]),
3489 identity_contract_nonce: 3,
3490 document_type_name: "preorder".to_string(),
3491 data_contract_id: Identifier::from([2u8; 32]),
3492 token_payment_info: None,
3493 }));
3494 assert_eq!(version_1.active_version_range(), ALL_VERSIONS);
3495
3496 let version_2 =
3497 sample_batch_st_with_base(DocumentBaseTransition::V2(DocumentBaseTransitionV2 {
3498 id: Identifier::from([1u8; 32]),
3499 identity_contract_nonce: 3,
3500 document_type_name: "preorder".to_string(),
3501 data_contract_id: Identifier::from([2u8; 32]),
3502 token_payment_info: None,
3503 action_fee_agreement: None,
3504 }));
3505 assert_eq!(version_2.active_version_range(), 14..=LATEST_VERSION);
3506
3507 let bytes =
3508 PlatformSerializable::serialize_to_bytes(&version_2).expect("serialize succeeds");
3509 let version_13 = PlatformVersion::get(13).expect("platform version 13 exists");
3510 assert!(matches!(
3511 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_13),
3512 Err(ProtocolError::StateTransitionError(
3513 StateTransitionIsNotActiveError { .. }
3514 ))
3515 ));
3516 let recovered = StateTransition::deserialize_from_bytes_untrusted_in_version(
3517 &bytes,
3518 PlatformVersion::latest(),
3519 )
3520 .expect("expected protocol version 14 to decode a version 2 base");
3521 assert_eq!(recovered, version_2);
3522 }
3523
3524 fn sample_batch_st_empty() -> StateTransition {
3525 StateTransition::Batch(BatchTransition::V0(BatchTransitionV0 {
3526 owner_id: Identifier::from([1u8; 32]),
3527 transitions: vec![],
3528 user_fee_increase: 0,
3529 signature_public_key_id: 0,
3530 signature: BinaryData::new(vec![]),
3531 }))
3532 }
3533
3534 fn sample_identity_create_st() -> StateTransition {
3535 StateTransition::IdentityCreate(IdentityCreateTransition::V0(IdentityCreateTransitionV0 {
3536 identity_id: Identifier::from([3u8; 32]),
3537 ..Default::default()
3538 }))
3539 }
3540
3541 fn sample_identity_top_up_st() -> StateTransition {
3542 StateTransition::IdentityTopUp(IdentityTopUpTransition::V0(IdentityTopUpTransitionV0 {
3543 identity_id: Identifier::from([4u8; 32]),
3544 ..Default::default()
3545 }))
3546 }
3547
3548 fn sample_identity_update_st() -> StateTransition {
3549 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
3550 identity_id: Identifier::from([5u8; 32]),
3551 revision: 1,
3552 nonce: 2,
3553 add_public_keys: vec![],
3554 disable_public_keys: vec![],
3555 user_fee_increase: 11,
3556 signature_public_key_id: 33,
3557 signature: BinaryData::new(vec![0xFF; 65]),
3558 }))
3559 }
3560
3561 fn sample_unshield_st() -> StateTransition {
3562 StateTransition::Unshield(UnshieldTransition::V0(UnshieldTransitionV0 {
3563 output_address: Default::default(),
3564 actions: vec![],
3565 unshielding_amount: 0,
3566 anchor: [0u8; 32],
3567 proof: vec![],
3568 binding_signature: [0u8; 64],
3569 }))
3570 }
3571
3572 fn sample_shielded_transfer_st() -> StateTransition {
3573 StateTransition::ShieldedTransfer(ShieldedTransferTransition::V0(
3574 ShieldedTransferTransitionV0 {
3575 actions: vec![],
3576 value_balance: 0,
3577 anchor: [0u8; 32],
3578 proof: vec![],
3579 binding_signature: [0u8; 64],
3580 },
3581 ))
3582 }
3583
3584 fn sample_shielded_withdrawal_st() -> StateTransition {
3585 use crate::identity::core_script::CoreScript;
3586 use crate::withdrawal::Pooling;
3587 StateTransition::ShieldedWithdrawal(ShieldedWithdrawalTransition::V0(
3588 ShieldedWithdrawalTransitionV0 {
3589 actions: vec![],
3590 unshielding_amount: 0,
3591 anchor: [0u8; 32],
3592 proof: vec![],
3593 binding_signature: [0u8; 64],
3594 core_fee_per_byte: 1,
3595 pooling: Pooling::Never,
3596 output_script: CoreScript::from_bytes(vec![]),
3597 },
3598 ))
3599 }
3600
3601 #[test]
3604 fn test_name_for_newly_covered_variants() {
3605 assert_eq!(
3606 sample_data_contract_create_st().name(),
3607 "DataContractCreate"
3608 );
3609 assert_eq!(
3610 sample_data_contract_update_st().name(),
3611 "DataContractUpdate"
3612 );
3613 assert_eq!(sample_identity_create_st().name(), "IdentityCreate");
3614 assert_eq!(sample_identity_top_up_st().name(), "IdentityTopUp");
3615 assert_eq!(sample_identity_update_st().name(), "IdentityUpdate");
3616 assert_eq!(sample_unshield_st().name(), "Unshield");
3617 assert_eq!(sample_shielded_transfer_st().name(), "ShieldedTransfer");
3618 assert_eq!(sample_shielded_withdrawal_st().name(), "ShieldedWithdrawal");
3619
3620 let batch_name = sample_batch_st_with_delete().name();
3623 assert_eq!(batch_name, "DocumentsBatch([Delete])");
3624
3625 let empty_name = sample_batch_st_empty().name();
3627 assert_eq!(empty_name, "DocumentsBatch([])");
3628 }
3629
3630 #[test]
3632 fn test_state_transition_type_for_newly_covered_variants() {
3633 assert_eq!(
3634 sample_data_contract_create_st().state_transition_type(),
3635 StateTransitionType::DataContractCreate
3636 );
3637 assert_eq!(
3638 sample_data_contract_update_st().state_transition_type(),
3639 StateTransitionType::DataContractUpdate
3640 );
3641 assert_eq!(
3642 sample_batch_st_with_delete().state_transition_type(),
3643 StateTransitionType::Batch
3644 );
3645 assert_eq!(
3646 sample_identity_create_st().state_transition_type(),
3647 StateTransitionType::IdentityCreate
3648 );
3649 assert_eq!(
3650 sample_identity_top_up_st().state_transition_type(),
3651 StateTransitionType::IdentityTopUp
3652 );
3653 assert_eq!(
3654 sample_identity_update_st().state_transition_type(),
3655 StateTransitionType::IdentityUpdate
3656 );
3657 assert_eq!(
3658 sample_unshield_st().state_transition_type(),
3659 StateTransitionType::Unshield
3660 );
3661 assert_eq!(
3662 sample_shielded_transfer_st().state_transition_type(),
3663 StateTransitionType::ShieldedTransfer
3664 );
3665 assert_eq!(
3666 sample_shielded_withdrawal_st().state_transition_type(),
3667 StateTransitionType::ShieldedWithdrawal
3668 );
3669 }
3670
3671 #[test]
3676 fn test_active_version_range_contract_and_shielded_branches() {
3677 let contract_v1_range = 9..=LATEST_VERSION;
3680 assert_eq!(
3681 sample_data_contract_create_st().active_version_range(),
3682 contract_v1_range
3683 );
3684 let contract_v1_range = 9..=LATEST_VERSION;
3685 assert_eq!(
3686 sample_data_contract_update_st().active_version_range(),
3687 contract_v1_range
3688 );
3689 assert_eq!(
3691 sample_batch_st_with_delete().active_version_range(),
3692 ALL_VERSIONS
3693 );
3694 assert_eq!(
3696 sample_identity_create_st().active_version_range(),
3697 ALL_VERSIONS
3698 );
3699 assert_eq!(
3700 sample_identity_top_up_st().active_version_range(),
3701 ALL_VERSIONS
3702 );
3703 assert_eq!(
3704 sample_identity_update_st().active_version_range(),
3705 ALL_VERSIONS
3706 );
3707 let shielded_range = 12..=LATEST_VERSION;
3709 assert_eq!(
3710 sample_shielded_transfer_st().active_version_range(),
3711 shielded_range.clone()
3712 );
3713 assert_eq!(
3714 sample_unshield_st().active_version_range(),
3715 shielded_range.clone()
3716 );
3717 assert_eq!(
3718 sample_shielded_withdrawal_st().active_version_range(),
3719 shielded_range
3720 );
3721 }
3722
3723 fn sample_identity_update_st_adding(
3724 public_key: crate::state_transition::public_key_in_creation::IdentityPublicKeyInCreation,
3725 ) -> StateTransition {
3726 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(IdentityUpdateTransitionV0 {
3727 identity_id: Identifier::from([5u8; 32]),
3728 revision: 1,
3729 nonce: 2,
3730 add_public_keys: vec![public_key],
3731 disable_public_keys: vec![],
3732 user_fee_increase: 0,
3733 signature_public_key_id: 0,
3734 signature: BinaryData::new(vec![0xFF; 65]),
3735 }))
3736 }
3737
3738 #[test]
3739 fn should_only_admit_a_version_1_public_key_in_creation_from_protocol_version_14() {
3740 use crate::serialization::PlatformSerializable;
3741 use crate::state_transition::public_key_in_creation::v0::IdentityPublicKeyInCreationV0;
3742 use crate::state_transition::public_key_in_creation::v1::IdentityPublicKeyInCreationV1;
3743
3744 let version_0_key = IdentityPublicKeyInCreationV0 {
3745 id: 1,
3746 data: BinaryData::new(vec![2; 33]),
3747 ..Default::default()
3748 };
3749 let limited_key = IdentityPublicKeyInCreationV1::from_v0_with_limits(
3750 version_0_key.clone(),
3751 Some(1_000),
3752 None,
3753 );
3754
3755 let plain = sample_identity_update_st_adding(version_0_key.into());
3756 assert_eq!(plain.active_version_range(), ALL_VERSIONS);
3757
3758 let limited = sample_identity_update_st_adding(limited_key.into());
3759 assert_eq!(limited.active_version_range(), 14..=LATEST_VERSION);
3760
3761 let bytes = limited.serialize_to_bytes().expect("expected to serialize");
3764 let version_13 = PlatformVersion::get(13).expect("expected protocol version 13");
3765 assert!(matches!(
3766 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_13),
3767 Err(ProtocolError::StateTransitionError(
3768 StateTransitionIsNotActiveError { .. }
3769 ))
3770 ));
3771 let version_14 = PlatformVersion::get(14).expect("expected protocol version 14");
3772 assert_eq!(
3773 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_14)
3774 .expect("expected protocol version 14 to decode a limited key"),
3775 limited
3776 );
3777 }
3778
3779 #[test]
3782 fn test_is_identity_signed_false_for_identity_create_topup_and_shielded() {
3783 assert!(!sample_identity_create_st().is_identity_signed());
3784 assert!(!sample_identity_top_up_st().is_identity_signed());
3785 assert!(!sample_unshield_st().is_identity_signed());
3786 assert!(!sample_shielded_transfer_st().is_identity_signed());
3787 assert!(!sample_shielded_withdrawal_st().is_identity_signed());
3788 }
3789
3790 #[test]
3794 fn test_signature_accessor_for_other_variants() {
3795 assert_eq!(
3797 sample_data_contract_create_st().signature().unwrap().len(),
3798 65
3799 );
3800 assert_eq!(
3801 sample_data_contract_update_st().signature().unwrap().len(),
3802 65
3803 );
3804 assert_eq!(sample_batch_st_with_delete().signature().unwrap().len(), 65);
3805 assert_eq!(sample_identity_update_st().signature().unwrap().len(), 65);
3806
3807 assert!(sample_unshield_st().signature().is_none());
3809 assert!(sample_shielded_transfer_st().signature().is_none());
3810 assert!(sample_shielded_withdrawal_st().signature().is_none());
3811 }
3812
3813 #[test]
3815 fn test_owner_id_accessor_for_other_variants() {
3816 assert_eq!(
3817 sample_data_contract_create_st().owner_id(),
3818 Some(Identifier::from([7u8; 32]))
3819 );
3820 assert_eq!(
3821 sample_data_contract_update_st().owner_id(),
3822 Some(Identifier::from([7u8; 32]))
3823 );
3824 assert_eq!(
3825 sample_batch_st_with_delete().owner_id(),
3826 Some(Identifier::from([8u8; 32]))
3827 );
3828 assert_eq!(
3829 sample_identity_update_st().owner_id(),
3830 Some(Identifier::from([5u8; 32]))
3831 );
3832 assert!(sample_unshield_st().owner_id().is_none());
3834 assert!(sample_shielded_transfer_st().owner_id().is_none());
3835 assert!(sample_shielded_withdrawal_st().owner_id().is_none());
3836 }
3837
3838 #[test]
3841 fn test_user_fee_increase_for_newly_covered_variants() {
3842 assert_eq!(sample_data_contract_create_st().user_fee_increase(), 5);
3843 assert_eq!(sample_data_contract_update_st().user_fee_increase(), 9);
3844 assert_eq!(sample_batch_st_with_delete().user_fee_increase(), 2);
3845 assert_eq!(sample_identity_update_st().user_fee_increase(), 11);
3846 assert_eq!(sample_shielded_transfer_st().user_fee_increase(), 0);
3848 assert_eq!(sample_shielded_withdrawal_st().user_fee_increase(), 0);
3849 assert_eq!(sample_unshield_st().user_fee_increase(), 0);
3850 }
3851
3852 #[test]
3855 fn test_set_user_fee_increase_for_newly_covered_variants() {
3856 let mut st = sample_data_contract_create_st();
3857 st.set_user_fee_increase(42);
3858 assert_eq!(st.user_fee_increase(), 42);
3859
3860 let mut st = sample_data_contract_update_st();
3861 st.set_user_fee_increase(13);
3862 assert_eq!(st.user_fee_increase(), 13);
3863
3864 let mut st = sample_batch_st_with_delete();
3865 st.set_user_fee_increase(101);
3866 assert_eq!(st.user_fee_increase(), 101);
3867
3868 let mut st = sample_identity_update_st();
3869 st.set_user_fee_increase(77);
3870 assert_eq!(st.user_fee_increase(), 77);
3871
3872 let mut shielded = sample_shielded_transfer_st();
3874 shielded.set_user_fee_increase(99);
3875 assert_eq!(shielded.user_fee_increase(), 0);
3876
3877 let mut withdrawal = sample_shielded_withdrawal_st();
3878 withdrawal.set_user_fee_increase(99);
3879 assert_eq!(withdrawal.user_fee_increase(), 0);
3880
3881 let mut unshield = sample_unshield_st();
3882 unshield.set_user_fee_increase(99);
3883 assert_eq!(unshield.user_fee_increase(), 0);
3884 }
3885
3886 #[test]
3890 fn test_set_signature_false_for_shielded_and_identity_create_topup() {
3891 let mut st = sample_unshield_st();
3893 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3894 let mut st = sample_shielded_transfer_st();
3895 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3896 let mut st = sample_shielded_withdrawal_st();
3897 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
3898 }
3899
3900 #[test]
3901 fn test_set_signature_true_for_newly_covered_variants() {
3902 let mut st = sample_data_contract_create_st();
3903 assert!(st.set_signature(BinaryData::new(vec![0x11; 65])));
3904 assert_eq!(st.signature().unwrap().as_slice(), &[0x11; 65]);
3905
3906 let mut st = sample_data_contract_update_st();
3907 assert!(st.set_signature(BinaryData::new(vec![0x22; 65])));
3908 assert_eq!(st.signature().unwrap().as_slice(), &[0x22; 65]);
3909
3910 let mut st = sample_batch_st_with_delete();
3911 assert!(st.set_signature(BinaryData::new(vec![0x33; 65])));
3912 assert_eq!(st.signature().unwrap().as_slice(), &[0x33; 65]);
3913
3914 let mut st = sample_identity_update_st();
3915 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
3916 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
3917 }
3918
3919 #[test]
3922 fn test_signature_public_key_id_returns_none_for_non_signed() {
3923 assert!(sample_identity_create_st()
3926 .signature_public_key_id()
3927 .is_none());
3928 assert!(sample_identity_top_up_st()
3929 .signature_public_key_id()
3930 .is_none());
3931 assert!(sample_unshield_st().signature_public_key_id().is_none());
3932 assert!(sample_shielded_transfer_st()
3933 .signature_public_key_id()
3934 .is_none());
3935 assert!(sample_shielded_withdrawal_st()
3936 .signature_public_key_id()
3937 .is_none());
3938 }
3939
3940 #[test]
3941 fn test_signature_public_key_id_for_signed_variants() {
3942 assert_eq!(
3943 sample_data_contract_create_st().signature_public_key_id(),
3944 Some(2)
3945 );
3946 assert_eq!(
3947 sample_data_contract_update_st().signature_public_key_id(),
3948 Some(6)
3949 );
3950 assert_eq!(
3951 sample_batch_st_with_delete().signature_public_key_id(),
3952 Some(7)
3953 );
3954 assert_eq!(
3955 sample_identity_update_st().signature_public_key_id(),
3956 Some(33)
3957 );
3958 }
3959
3960 #[test]
3963 fn test_set_signature_public_key_id_noop_for_non_signed() {
3964 let mut st = sample_identity_create_st();
3967 st.set_signature_public_key_id(100);
3968 assert_eq!(st.signature_public_key_id(), None);
3969
3970 let mut st = sample_identity_top_up_st();
3971 st.set_signature_public_key_id(100);
3972 assert_eq!(st.signature_public_key_id(), None);
3973
3974 let mut st = sample_unshield_st();
3975 st.set_signature_public_key_id(100);
3976 assert_eq!(st.signature_public_key_id(), None);
3977 }
3978
3979 #[test]
3980 fn test_set_signature_public_key_id_updates_for_signed_variants() {
3981 let mut st = sample_data_contract_create_st();
3982 st.set_signature_public_key_id(42);
3983 assert_eq!(st.signature_public_key_id(), Some(42));
3984
3985 let mut st = sample_batch_st_with_delete();
3986 st.set_signature_public_key_id(43);
3987 assert_eq!(st.signature_public_key_id(), Some(43));
3988
3989 let mut st = sample_identity_update_st();
3990 st.set_signature_public_key_id(44);
3991 assert_eq!(st.signature_public_key_id(), Some(44));
3992 }
3993
3994 #[test]
3997 fn test_required_number_of_private_keys_various_variants() {
3998 assert_eq!(
3999 sample_data_contract_create_st().required_number_of_private_keys(),
4000 1
4001 );
4002 assert_eq!(
4003 sample_data_contract_update_st().required_number_of_private_keys(),
4004 1
4005 );
4006 assert_eq!(
4007 sample_batch_st_with_delete().required_number_of_private_keys(),
4008 1
4009 );
4010 assert_eq!(
4011 sample_identity_update_st().required_number_of_private_keys(),
4012 1
4013 );
4014 assert_eq!(
4015 sample_identity_create_st().required_number_of_private_keys(),
4016 1
4017 );
4018 assert_eq!(
4020 sample_shielded_transfer_st().required_number_of_private_keys(),
4021 0
4022 );
4023 assert_eq!(
4024 sample_shielded_withdrawal_st().required_number_of_private_keys(),
4025 0
4026 );
4027 assert_eq!(sample_unshield_st().required_number_of_private_keys(), 0);
4028 }
4029
4030 #[test]
4033 fn test_inputs_none_for_many_variants() {
4034 assert!(sample_data_contract_create_st().inputs().is_none());
4035 assert!(sample_data_contract_update_st().inputs().is_none());
4036 assert!(sample_batch_st_with_delete().inputs().is_none());
4037 assert!(sample_identity_create_st().inputs().is_none());
4038 assert!(sample_identity_top_up_st().inputs().is_none());
4039 assert!(sample_identity_update_st().inputs().is_none());
4040 assert!(sample_unshield_st().inputs().is_none());
4042 assert!(sample_shielded_transfer_st().inputs().is_none());
4043 assert!(sample_shielded_withdrawal_st().inputs().is_none());
4044 }
4045
4046 #[test]
4051 fn test_optional_asset_lock_proof_returns_none_for_wildcard_arms() {
4052 assert!(sample_data_contract_create_st()
4053 .optional_asset_lock_proof()
4054 .is_none());
4055 assert!(sample_data_contract_update_st()
4056 .optional_asset_lock_proof()
4057 .is_none());
4058 assert!(sample_batch_st_with_delete()
4059 .optional_asset_lock_proof()
4060 .is_none());
4061 assert!(sample_identity_update_st()
4062 .optional_asset_lock_proof()
4063 .is_none());
4064 assert!(sample_unshield_st().optional_asset_lock_proof().is_none());
4065 assert!(sample_shielded_transfer_st()
4066 .optional_asset_lock_proof()
4067 .is_none());
4068 assert!(sample_shielded_withdrawal_st()
4069 .optional_asset_lock_proof()
4070 .is_none());
4071 }
4072
4073 #[test]
4077 fn test_required_asset_lock_balance_errors_for_other_non_asset_lock_variants() {
4078 let platform_version = PlatformVersion::latest();
4079
4080 let cases: Vec<(&str, StateTransition)> = vec![
4081 ("DataContractCreate", sample_data_contract_create_st()),
4082 ("DataContractUpdate", sample_data_contract_update_st()),
4083 ("Batch", sample_batch_st_with_delete()),
4084 ("IdentityUpdate", sample_identity_update_st()),
4085 ("MasternodeVote", sample_masternode_vote_st()),
4086 ("Unshield", sample_unshield_st()),
4087 ("ShieldedTransfer", sample_shielded_transfer_st()),
4088 ("ShieldedWithdrawal", sample_shielded_withdrawal_st()),
4089 ];
4090
4091 for (label, st) in cases {
4092 let err = st
4093 .required_asset_lock_balance_for_processing_start(platform_version)
4094 .expect_err(&format!("expected error for {label}"));
4095 match err {
4096 ProtocolError::CorruptedCodeExecution(msg) => {
4097 assert!(
4098 msg.contains("is not an asset lock transaction"),
4099 "unexpected error for {label}: {msg}"
4100 );
4101 }
4102 other => panic!("expected CorruptedCodeExecution for {label}, got {other:?}"),
4103 }
4104 }
4105 }
4106
4107 #[test]
4113 fn test_unique_identifiers_non_empty_for_other_variants() {
4114 for st in [
4115 sample_data_contract_create_st(),
4116 sample_data_contract_update_st(),
4117 sample_batch_st_with_delete(),
4118 sample_identity_create_st(),
4119 sample_identity_top_up_st(),
4120 sample_identity_update_st(),
4121 ] {
4122 let ids = st.unique_identifiers();
4123 assert!(!ids.is_empty(), "unique_identifiers should not be empty");
4124 }
4125 }
4126
4127 #[test]
4131 fn test_security_level_requirement_returns_none_for_non_signed_variants() {
4132 let purpose = Purpose::AUTHENTICATION;
4133 assert!(sample_identity_create_st()
4134 .security_level_requirement(purpose)
4135 .is_none());
4136 assert!(sample_identity_top_up_st()
4137 .security_level_requirement(purpose)
4138 .is_none());
4139 assert!(sample_unshield_st()
4140 .security_level_requirement(purpose)
4141 .is_none());
4142 assert!(sample_shielded_transfer_st()
4143 .security_level_requirement(purpose)
4144 .is_none());
4145 assert!(sample_shielded_withdrawal_st()
4146 .security_level_requirement(purpose)
4147 .is_none());
4148 }
4149
4150 #[test]
4151 fn test_purpose_requirement_returns_none_for_non_signed_variants() {
4152 assert!(sample_identity_create_st().purpose_requirement().is_none());
4153 assert!(sample_identity_top_up_st().purpose_requirement().is_none());
4154 assert!(sample_unshield_st().purpose_requirement().is_none());
4155 assert!(sample_shielded_transfer_st()
4156 .purpose_requirement()
4157 .is_none());
4158 assert!(sample_shielded_withdrawal_st()
4159 .purpose_requirement()
4160 .is_none());
4161 }
4162
4163 #[test]
4165 fn test_from_outer_data_contract_create_into_state_transition() {
4166 let outer: DataContractCreateTransition =
4167 DataContractCreateTransition::V0(DataContractCreateTransitionV0 {
4168 data_contract: sample_data_contract_in_serialization_format(),
4169 identity_nonce: 1,
4170 user_fee_increase: 0,
4171 signature_public_key_id: 0,
4172 signature: Default::default(),
4173 });
4174 let st: StateTransition = outer.into();
4175 assert!(matches!(st, StateTransition::DataContractCreate(_)));
4176 }
4177
4178 #[test]
4179 fn test_from_outer_data_contract_update_into_state_transition() {
4180 let outer: DataContractUpdateTransition =
4181 DataContractUpdateTransition::V0(DataContractUpdateTransitionV0 {
4182 identity_contract_nonce: 2,
4183 data_contract: sample_data_contract_in_serialization_format(),
4184 user_fee_increase: 0,
4185 signature_public_key_id: 0,
4186 signature: Default::default(),
4187 });
4188 let st: StateTransition = outer.into();
4189 assert!(matches!(st, StateTransition::DataContractUpdate(_)));
4190 }
4191
4192 #[test]
4193 fn test_from_outer_batch_into_state_transition() {
4194 let outer: BatchTransition = BatchTransition::V0(BatchTransitionV0::default());
4195 let st: StateTransition = outer.into();
4196 assert!(matches!(st, StateTransition::Batch(_)));
4197 }
4198
4199 #[test]
4200 fn test_from_outer_identity_create_into_state_transition() {
4201 let outer: IdentityCreateTransition =
4202 IdentityCreateTransition::V0(IdentityCreateTransitionV0::default());
4203 let st: StateTransition = outer.into();
4204 assert!(matches!(st, StateTransition::IdentityCreate(_)));
4205 }
4206
4207 #[test]
4208 fn test_from_outer_identity_update_into_state_transition() {
4209 let outer: IdentityUpdateTransition =
4210 IdentityUpdateTransition::V0(IdentityUpdateTransitionV0::default());
4211 let st: StateTransition = outer.into();
4212 assert!(matches!(st, StateTransition::IdentityUpdate(_)));
4213 }
4214
4215 #[test]
4218 fn test_transaction_id_and_clone_for_identity_update() {
4219 let st = sample_identity_update_st();
4220 let id_a = st.transaction_id().expect("hash should succeed");
4221 let cloned = st.clone();
4222 let id_b = cloned.transaction_id().expect("hash should succeed");
4223 assert_eq!(id_a, id_b);
4224 assert_eq!(id_a.len(), 32);
4225 }
4226
4227 #[test]
4228 fn test_transaction_id_and_clone_for_data_contract_create() {
4229 let st = sample_data_contract_create_st();
4230 let id_a = st.transaction_id().expect("hash should succeed");
4231 let cloned = st.clone();
4232 let id_b = cloned.transaction_id().expect("hash should succeed");
4233 assert_eq!(id_a, id_b);
4234 assert_eq!(id_a.len(), 32);
4235 }
4236
4237 #[test]
4239 fn test_serialize_roundtrip_identity_update() {
4240 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
4241 let original = sample_identity_update_st();
4242 let bytes =
4243 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
4244 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
4245 .expect("deserialize should succeed");
4246 assert_eq!(original, restored);
4247 }
4248
4249 #[test]
4250 fn test_serialize_roundtrip_data_contract_update() {
4251 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
4252 let original = sample_data_contract_update_st();
4253 let bytes =
4254 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
4255 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
4256 .expect("deserialize should succeed");
4257 assert_eq!(original, restored);
4258 }
4259
4260 #[test]
4261 fn test_serialize_roundtrip_batch_empty() {
4262 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
4263 let original = sample_batch_st_empty();
4264 let bytes =
4265 PlatformSerializable::serialize_to_bytes(&original).expect("serialize should succeed");
4266 let restored = StateTransition::deserialize_from_bytes_untrusted(&bytes)
4267 .expect("deserialize should succeed");
4268 assert_eq!(original, restored);
4269 }
4270
4271 #[cfg(all(feature = "state-transitions", feature = "validation"))]
4278 #[test]
4279 fn test_deserialize_from_bytes_in_version_returns_not_active_error() {
4280 use crate::serialization::PlatformSerializable;
4281
4282 let original = sample_shielded_transfer_st();
4284 let bytes =
4285 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
4286
4287 let low_version = PlatformVersion::get(1).expect("platform version 1 exists");
4291 assert!(
4292 low_version.protocol_version < 12,
4293 "expected sub-12 version for this test, got {}",
4294 low_version.protocol_version
4295 );
4296
4297 let err = StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, low_version)
4298 .expect_err("expected StateTransitionIsNotActiveError for sub-12 protocol");
4299 match err {
4300 ProtocolError::StateTransitionError(
4301 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
4302 state_transition_type,
4303 active_version_range,
4304 current_protocol_version,
4305 },
4306 ) => {
4307 assert_eq!(state_transition_type, "ShieldedTransfer");
4308 assert_eq!(current_protocol_version, low_version.protocol_version);
4309 assert!(active_version_range.start() >= &12);
4310 }
4311 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
4312 }
4313 }
4314
4315 #[cfg(all(feature = "state-transitions", feature = "validation"))]
4318 #[test]
4319 fn should_refuse_an_inactive_transition_in_the_exact_in_version_decoder() {
4320 use crate::serialization::PlatformSerializable;
4321
4322 let bytes = PlatformSerializable::serialize_to_bytes(&sample_shielded_transfer_st())
4324 .expect("serialize succeeds");
4325 let low_version = PlatformVersion::get(1).expect("platform version 1 exists");
4326
4327 assert!(matches!(
4328 StateTransition::deserialize_from_bytes_untrusted_exact_in_version(&bytes, low_version),
4329 Err(ProtocolError::StateTransitionError(
4330 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError { .. }
4331 ))
4332 ));
4333 }
4334
4335 #[cfg(all(feature = "state-transitions", feature = "validation"))]
4339 #[test]
4340 fn should_decode_each_shield_from_asset_lock_version_only_where_it_is_active() {
4341 use crate::serialization::PlatformSerializable;
4342 use crate::state_transition::shield_from_asset_lock_transition::v1::ShieldFromAssetLockTransitionV1;
4343
4344 let v0 = sample_shield_from_asset_lock_st();
4345 let StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V0(body)) = &v0
4346 else {
4347 panic!("expected a version 0 shield from asset lock");
4348 };
4349 let v1 = StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V1(
4350 ShieldFromAssetLockTransitionV1 {
4351 asset_lock_proof: body.asset_lock_proof.clone(),
4352 actions: body.actions.clone(),
4353 value_balance: body.value_balance,
4354 anchor: body.anchor,
4355 proof: body.proof.clone(),
4356 binding_signature: body.binding_signature,
4357 surplus_output: body.surplus_output,
4358 signature: body.signature.clone(),
4359 },
4360 ));
4361 assert_eq!(v0.active_version_range(), 12..=13);
4362 assert_eq!(v1.active_version_range(), 14..=LATEST_VERSION);
4363
4364 let decodes_at = |transition: &StateTransition, protocol_version: u32| {
4365 let bytes =
4366 PlatformSerializable::serialize_to_bytes(transition).expect("serialize succeeds");
4367 let platform_version =
4368 PlatformVersion::get(protocol_version).expect("known protocol version");
4369 match StateTransition::deserialize_from_bytes_untrusted_in_version(
4370 &bytes,
4371 platform_version,
4372 ) {
4373 Ok(decoded) => {
4374 assert_eq!(&decoded, transition);
4375 true
4376 }
4377 Err(ProtocolError::StateTransitionError(
4378 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
4379 current_protocol_version,
4380 ..
4381 },
4382 )) => {
4383 assert_eq!(current_protocol_version, protocol_version);
4384 false
4385 }
4386 Err(other) => panic!("unexpected decode error: {other:?}"),
4387 }
4388 };
4389
4390 for protocol_version in [12, 13] {
4391 assert!(decodes_at(&v0, protocol_version));
4392 assert!(!decodes_at(&v1, protocol_version));
4393 }
4394 assert!(
4395 !decodes_at(&v0, 14),
4396 "protocol version 14 must refuse version 0"
4397 );
4398 assert!(decodes_at(&v1, 14));
4399 }
4400
4401 #[test]
4405 fn test_data_contract_create_v1_is_not_active_before_protocol_version_14() {
4406 use crate::serialization::PlatformSerializable;
4407
4408 let original = sample_data_contract_create_v1_st();
4409 assert_eq!(original.active_version_range(), 14..=LATEST_VERSION);
4410
4411 let bytes =
4412 PlatformSerializable::serialize_to_bytes(&original).expect("serialize succeeds");
4413
4414 let version_13 = PlatformVersion::get(13).expect("platform version 13 exists");
4415 let err = StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_13)
4416 .expect_err("expected StateTransitionIsNotActiveError at protocol version 13");
4417 match err {
4418 ProtocolError::StateTransitionError(
4419 crate::state_transition::errors::StateTransitionError::StateTransitionIsNotActiveError {
4420 active_version_range,
4421 current_protocol_version,
4422 ..
4423 },
4424 ) => {
4425 assert_eq!(current_protocol_version, 13);
4426 assert_eq!(*active_version_range.start(), 14);
4427 }
4428 other => panic!("expected StateTransitionIsNotActiveError, got {other:?}"),
4429 }
4430
4431 let version_14 = PlatformVersion::get(14).expect("platform version 14 exists");
4432 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_14)
4433 .expect("a version 1 create is active at protocol version 14");
4434 }
4435
4436 #[test]
4437 fn should_gate_identity_transitions_carrying_a_contract_group_bound_key_to_protocol_version_14()
4438 {
4439 use crate::identity::contract_bounds::ContractBounds;
4440 use crate::identity::{KeyType, Purpose, SecurityLevel};
4441 use crate::serialization::PlatformSerializable;
4442 use crate::state_transition::errors::StateTransitionError;
4443 use crate::state_transition::public_key_in_creation::v0::IdentityPublicKeyInCreationV0;
4444 use crate::state_transition::public_key_in_creation::IdentityPublicKeyInCreation;
4445
4446 let key = |contract_bounds| {
4447 IdentityPublicKeyInCreation::V0(IdentityPublicKeyInCreationV0 {
4448 id: 1,
4449 key_type: KeyType::ECDSA_SECP256K1,
4450 purpose: Purpose::AUTHENTICATION,
4451 security_level: SecurityLevel::HIGH,
4452 contract_bounds,
4453 read_only: false,
4454 data: BinaryData::new(vec![2u8; 33]),
4455 signature: BinaryData::default(),
4456 })
4457 };
4458 let contract_bound = Some(ContractBounds::SingleContract {
4459 id: Identifier::from([9u8; 32]),
4460 });
4461 let group_bound = Some(ContractBounds::ContractGroup {
4462 id: Identifier::from([9u8; 32]),
4463 });
4464 let create = |bounds| {
4465 StateTransition::IdentityCreate(IdentityCreateTransition::V0(
4466 IdentityCreateTransitionV0 {
4467 identity_id: Identifier::from([3u8; 32]),
4468 public_keys: vec![key(bounds)],
4469 ..Default::default()
4470 },
4471 ))
4472 };
4473 let update = |bounds| {
4474 let StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(mut v0)) =
4475 sample_identity_update_st()
4476 else {
4477 panic!("expected a version 0 identity update");
4478 };
4479 v0.add_public_keys = vec![key(bounds)];
4480 StateTransition::IdentityUpdate(IdentityUpdateTransition::V0(v0))
4481 };
4482
4483 assert_eq!(
4484 create(contract_bound.clone()).active_version_range(),
4485 ALL_VERSIONS
4486 );
4487 assert_eq!(
4488 create(group_bound.clone()).active_version_range(),
4489 14..=LATEST_VERSION
4490 );
4491 assert_eq!(update(contract_bound).active_version_range(), ALL_VERSIONS);
4492 assert_eq!(
4493 update(group_bound.clone()).active_version_range(),
4494 14..=LATEST_VERSION
4495 );
4496
4497 let bytes = PlatformSerializable::serialize_to_bytes(&update(group_bound))
4498 .expect("serialize succeeds");
4499 let version_13 = PlatformVersion::get(13).expect("platform version 13 exists");
4500 let err = StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_13)
4501 .expect_err("a contract group bound key is not active at protocol version 13");
4502 assert!(
4503 matches!(
4504 err,
4505 ProtocolError::StateTransitionError(
4506 StateTransitionError::StateTransitionIsNotActiveError { .. }
4507 )
4508 ),
4509 "{err:?}"
4510 );
4511 let version_14 = PlatformVersion::get(14).expect("platform version 14 exists");
4512 StateTransition::deserialize_from_bytes_untrusted_in_version(&bytes, version_14)
4513 .expect("a contract group bound key is active at protocol version 14");
4514 }
4515
4516 use crate::state_transition::identity_credit_transfer_to_addresses_transition::v0::IdentityCreditTransferToAddressesTransitionV0;
4529 use crate::state_transition::identity_credit_transfer_to_addresses_transition::IdentityCreditTransferToAddressesTransition;
4530 use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1;
4531 use crate::withdrawal::Pooling as WithdrawalPooling;
4532
4533 fn sample_withdrawal_v1_st() -> StateTransition {
4534 let v1 = IdentityCreditWithdrawalTransitionV1 {
4535 identity_id: Identifier::from([12u8; 32]),
4536 amount: 777,
4537 core_fee_per_byte: 2,
4538 pooling: WithdrawalPooling::Standard,
4539 output_script: None,
4540 nonce: 9,
4541 user_fee_increase: 4,
4542 signature_public_key_id: 21,
4543 signature: BinaryData::new(vec![0x12; 65]),
4544 };
4545 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(v1))
4546 }
4547
4548 fn sample_credit_transfer_to_addresses_st() -> StateTransition {
4549 let v0 = IdentityCreditTransferToAddressesTransitionV0 {
4550 identity_id: Identifier::from([13u8; 32]),
4551 ..Default::default()
4552 };
4553 StateTransition::IdentityCreditTransferToAddresses(
4554 IdentityCreditTransferToAddressesTransition::V0(v0),
4555 )
4556 }
4557
4558 fn sample_address_credit_withdrawal_st() -> StateTransition {
4559 use crate::state_transition::address_credit_withdrawal_transition::v0::AddressCreditWithdrawalTransitionV0;
4560 StateTransition::AddressCreditWithdrawal(AddressCreditWithdrawalTransition::V0(
4561 AddressCreditWithdrawalTransitionV0::default(),
4562 ))
4563 }
4564
4565 fn sample_shield_from_asset_lock_st() -> StateTransition {
4566 use crate::state_transition::shield_from_asset_lock_transition::v0::ShieldFromAssetLockTransitionV0;
4567 StateTransition::ShieldFromAssetLock(ShieldFromAssetLockTransition::V0(
4568 ShieldFromAssetLockTransitionV0 {
4569 asset_lock_proof: Default::default(),
4570 actions: vec![],
4571 value_balance: 100,
4572 anchor: [0u8; 32],
4573 proof: vec![],
4574 binding_signature: [0u8; 64],
4575 surplus_output: None,
4576 signature: BinaryData::new(vec![0x55; 65]),
4577 },
4578 ))
4579 }
4580
4581 #[test]
4584 fn test_withdrawal_v1_name_and_type() {
4585 let st = sample_withdrawal_v1_st();
4586 assert_eq!(st.name(), "IdentityCreditWithdrawal");
4587 assert_eq!(
4588 st.state_transition_type(),
4589 StateTransitionType::IdentityCreditWithdrawal
4590 );
4591 }
4592
4593 #[test]
4594 fn test_withdrawal_v1_is_identity_signed_true() {
4595 assert!(sample_withdrawal_v1_st().is_identity_signed());
4596 }
4597
4598 #[test]
4599 fn test_withdrawal_v1_signature_and_owner_and_key_id() {
4600 let st = sample_withdrawal_v1_st();
4601 let sig = st.signature().expect("V1 withdrawal has a signature");
4603 assert_eq!(sig.as_slice(), &[0x12; 65]);
4604 assert_eq!(st.owner_id(), Some(Identifier::from([12u8; 32])));
4606 assert_eq!(st.signature_public_key_id(), Some(21));
4607 assert_eq!(st.user_fee_increase(), 4);
4608 }
4609
4610 #[test]
4611 fn test_withdrawal_v1_set_signature_and_fee_and_key_id() {
4612 let mut st = sample_withdrawal_v1_st();
4613 assert!(st.set_signature(BinaryData::new(vec![0x99; 65])));
4614 assert_eq!(st.signature().unwrap().as_slice(), &[0x99; 65]);
4615
4616 st.set_user_fee_increase(33);
4617 assert_eq!(st.user_fee_increase(), 33);
4618
4619 st.set_signature_public_key_id(64);
4620 assert_eq!(st.signature_public_key_id(), Some(64));
4621 }
4622
4623 #[test]
4624 fn test_withdrawal_v1_serialize_roundtrip_via_state_transition() {
4625 use crate::serialization::{PlatformDeserializableUntrusted, PlatformSerializable};
4626 let original = sample_withdrawal_v1_st();
4627 let bytes = PlatformSerializable::serialize_to_bytes(&original).expect("serialize ok");
4628 let restored =
4629 StateTransition::deserialize_from_bytes_untrusted(&bytes).expect("deserialize ok");
4630 assert_eq!(original, restored);
4631 match restored {
4634 StateTransition::IdentityCreditWithdrawal(IdentityCreditWithdrawalTransition::V1(
4635 _,
4636 )) => {}
4637 other => panic!("expected V1 inner variant, got: {:?}", other),
4638 }
4639 }
4640
4641 #[test]
4642 fn test_withdrawal_v1_transaction_id_differs_from_v0() {
4643 let v0 = sample_withdrawal_st();
4645 let v1 = sample_withdrawal_v1_st();
4646 let id_v0 = v0.transaction_id().expect("v0 hash");
4647 let id_v1 = v1.transaction_id().expect("v1 hash");
4648 assert_ne!(id_v0, id_v1);
4649 }
4650
4651 #[test]
4652 fn test_withdrawal_v1_required_asset_lock_balance_errors() {
4653 let err = sample_withdrawal_v1_st()
4654 .required_asset_lock_balance_for_processing_start(PlatformVersion::latest())
4655 .expect_err("withdrawal is not an asset lock ST");
4656 matches!(err, ProtocolError::CorruptedCodeExecution(_));
4657 }
4658
4659 #[test]
4662 fn test_credit_transfer_to_addresses_name_and_type() {
4663 let st = sample_credit_transfer_to_addresses_st();
4664 assert_eq!(st.name(), "IdentityCreditTransferToAddresses");
4665 assert_eq!(
4666 st.state_transition_type(),
4667 StateTransitionType::IdentityCreditTransferToAddresses
4668 );
4669 }
4670
4671 #[test]
4672 fn test_credit_transfer_to_addresses_signature_some_and_owner_some() {
4673 let st = sample_credit_transfer_to_addresses_st();
4674 assert!(st.signature().is_some(), "has a signature field");
4675 assert_eq!(st.owner_id(), Some(Identifier::from([13u8; 32])));
4676 }
4677
4678 #[test]
4679 fn test_credit_transfer_to_addresses_is_identity_signed_true() {
4680 assert!(sample_credit_transfer_to_addresses_st().is_identity_signed());
4682 }
4683
4684 #[test]
4685 fn test_credit_transfer_to_addresses_inputs_none_active_range_11_latest() {
4686 let st = sample_credit_transfer_to_addresses_st();
4687 assert!(st.inputs().is_none());
4688 let range = st.active_version_range();
4690 assert_eq!(*range.start(), 11);
4691 assert_eq!(*range.end(), LATEST_VERSION);
4692 }
4693
4694 #[test]
4695 fn test_credit_transfer_to_addresses_set_signature_returns_true() {
4696 let mut st = sample_credit_transfer_to_addresses_st();
4697 let ok = st.set_signature(BinaryData::new(vec![0x77; 65]));
4698 assert!(ok);
4699 assert_eq!(st.signature().unwrap().as_slice(), &[0x77; 65]);
4700 }
4701
4702 #[test]
4703 fn test_credit_transfer_to_addresses_from_outer_enum() {
4704 let outer = IdentityCreditTransferToAddressesTransition::V0(
4705 IdentityCreditTransferToAddressesTransitionV0::default(),
4706 );
4707 let st: StateTransition = outer.into();
4708 assert!(matches!(
4709 st,
4710 StateTransition::IdentityCreditTransferToAddresses(_)
4711 ));
4712 }
4713
4714 #[test]
4715 fn test_credit_transfer_to_addresses_user_fee_increase_setter() {
4716 let mut st = sample_credit_transfer_to_addresses_st();
4717 st.set_user_fee_increase(55);
4718 assert_eq!(st.user_fee_increase(), 55);
4719 }
4720
4721 #[test]
4724 fn test_address_credit_withdrawal_name_type_and_accessors() {
4725 let st = sample_address_credit_withdrawal_st();
4726 assert_eq!(st.name(), "AddressCreditWithdrawal");
4727 assert_eq!(
4728 st.state_transition_type(),
4729 StateTransitionType::AddressCreditWithdrawal
4730 );
4731 assert!(st.signature().is_none());
4733 assert!(st.owner_id().is_none());
4735 assert!(st.inputs().is_some());
4737 }
4738
4739 #[test]
4740 fn test_address_credit_withdrawal_set_signature_returns_false() {
4741 let mut st = sample_address_credit_withdrawal_st();
4742 assert!(!st.set_signature(BinaryData::new(vec![0xAB; 65])));
4743 }
4744
4745 #[test]
4746 fn test_address_credit_withdrawal_is_identity_signed_true() {
4747 assert!(sample_address_credit_withdrawal_st().is_identity_signed());
4750 }
4751
4752 #[test]
4753 fn test_address_credit_withdrawal_active_range_is_11_latest() {
4754 let range = sample_address_credit_withdrawal_st().active_version_range();
4755 assert_eq!(*range.start(), 11);
4756 assert_eq!(*range.end(), LATEST_VERSION);
4757 }
4758
4759 #[test]
4762 fn test_shield_from_asset_lock_name_type_and_accessors() {
4763 let st = sample_shield_from_asset_lock_st();
4764 assert_eq!(st.name(), "ShieldFromAssetLock");
4765 assert_eq!(
4766 st.state_transition_type(),
4767 StateTransitionType::ShieldFromAssetLock
4768 );
4769 let sig = st
4771 .signature()
4772 .expect("shield-from-asset-lock has signature");
4773 assert_eq!(sig.as_slice(), &[0x55; 65]);
4774 assert!(st.owner_id().is_none());
4776 }
4777
4778 #[test]
4779 fn test_shield_from_asset_lock_is_not_identity_signed() {
4780 assert!(!sample_shield_from_asset_lock_st().is_identity_signed());
4781 }
4782
4783 #[test]
4784 fn test_shield_from_asset_lock_optional_asset_lock_proof_some() {
4785 let st = sample_shield_from_asset_lock_st();
4789 assert!(st.optional_asset_lock_proof().is_some());
4790 }
4791
4792 #[test]
4793 fn test_shield_from_asset_lock_user_fee_increase_is_zero_and_setter_noop() {
4794 let mut st = sample_shield_from_asset_lock_st();
4795 assert_eq!(st.user_fee_increase(), 0);
4796 st.set_user_fee_increase(123);
4797 assert_eq!(st.user_fee_increase(), 0);
4799 }
4800
4801 #[test]
4802 fn test_shield_from_asset_lock_set_signature_returns_true() {
4803 let mut st = sample_shield_from_asset_lock_st();
4804 assert!(st.set_signature(BinaryData::new(vec![0x44; 65])));
4805 assert_eq!(st.signature().unwrap().as_slice(), &[0x44; 65]);
4806 }
4807
4808 #[test]
4809 fn test_shield_from_asset_lock_required_asset_lock_balance_succeeds() {
4810 let st = sample_shield_from_asset_lock_st();
4813 let result = st.required_asset_lock_balance_for_processing_start(PlatformVersion::latest());
4814 assert!(
4815 result.is_ok(),
4816 "ShieldFromAssetLock should return Ok, got {:?}",
4817 result
4818 );
4819 }
4820
4821 #[test]
4822 fn test_shield_from_asset_lock_version_0_active_range_12_to_13() {
4823 let range = sample_shield_from_asset_lock_st().active_version_range();
4825 assert_eq!(range, 12..=13);
4826 }
4827
4828 #[test]
4832 fn test_batch_with_token_transfer_name_contains_token_transfer() {
4833 use crate::state_transition::batch_transition::batched_transition::token_transition::TokenTransition as TT;
4834 use crate::state_transition::batch_transition::batched_transition::BatchedTransition;
4835 use crate::state_transition::batch_transition::token_base_transition::v0::TokenBaseTransitionV0;
4836 use crate::state_transition::batch_transition::token_base_transition::TokenBaseTransition;
4837 use crate::state_transition::batch_transition::token_transfer_transition::v0::TokenTransferTransitionV0;
4838 use crate::state_transition::batch_transition::token_transfer_transition::TokenTransferTransition;
4839 use crate::state_transition::batch_transition::BatchTransitionV1;
4840
4841 let base = TokenBaseTransition::V0(TokenBaseTransitionV0 {
4842 identity_contract_nonce: 1,
4843 token_contract_position: 0,
4844 data_contract_id: Identifier::from([1u8; 32]),
4845 token_id: Identifier::from([2u8; 32]),
4846 using_group_info: None,
4847 });
4848 let token_transfer = TokenTransferTransition::V0(TokenTransferTransitionV0 {
4849 base,
4850 amount: 100,
4851 recipient_id: Identifier::from([3u8; 32]),
4852 public_note: None,
4853 shared_encrypted_note: None,
4854 private_encrypted_note: None,
4855 });
4856
4857 let batch = BatchTransition::V1(BatchTransitionV1 {
4859 owner_id: Identifier::from([9u8; 32]),
4860 transitions: vec![BatchedTransition::Token(TT::Transfer(token_transfer))],
4861 user_fee_increase: 0,
4862 signature_public_key_id: 0,
4863 signature: BinaryData::new(vec![0u8; 65]),
4864 });
4865 let st = StateTransition::Batch(batch);
4866
4867 assert_eq!(st.name(), "DocumentsBatch([TokenTransfer])");
4868 }
4869
4870 #[test]
4877 fn test_transaction_id_length_32_for_new_variants() {
4878 for st in [
4879 sample_withdrawal_v1_st(),
4880 sample_credit_transfer_to_addresses_st(),
4881 sample_shield_from_asset_lock_st(),
4882 ] {
4883 let id = st.transaction_id().expect("hash");
4884 assert_eq!(id.len(), 32);
4885 }
4886 }
4887
4888 #[test]
4894 fn test_clone_eq_for_new_variants() {
4895 let cases = [
4896 sample_withdrawal_v1_st(),
4897 sample_credit_transfer_to_addresses_st(),
4898 sample_address_credit_withdrawal_st(),
4899 sample_shield_from_asset_lock_st(),
4900 ];
4901 for st in cases {
4902 let cloned = st.clone();
4903 assert_eq!(st, cloned, "clone must be equal for {}", st.name());
4904 }
4905 }
4906
4907 #[test]
4913 fn test_unique_identifiers_for_address_and_shielded_variants() {
4914 for st in [
4919 sample_address_credit_withdrawal_st(),
4920 sample_shield_from_asset_lock_st(),
4921 sample_credit_transfer_to_addresses_st(),
4922 sample_withdrawal_v1_st(),
4923 ] {
4924 let _ids = st.unique_identifiers();
4928 }
4929 assert!(!sample_withdrawal_v1_st().unique_identifiers().is_empty());
4931 assert!(!sample_credit_transfer_to_addresses_st()
4932 .unique_identifiers()
4933 .is_empty());
4934 }
4935
4936 #[cfg(all(
4948 feature = "state-transition-signing",
4949 feature = "core_key_wallet",
4950 feature = "bls-signatures"
4951 ))]
4952 #[tokio::test]
4953 async fn sign_with_core_signer_matches_sign_by_private_key_byte_for_byte() {
4954 use async_trait::async_trait;
4955 use dashcore::crypto::ecdsa::{EcdsaPublicKey, EcdsaSignature};
4956 use key_wallet::bip32::{DerivationPath, ExtendedPubKey};
4957 use key_wallet::signer::{ExtendedPubKeySigner, Signer as KwSigner, SignerMethod};
4958 use secp256k1::{self, rand, Message, PublicKey, SecretKey};
4959
4960 #[derive(Debug)]
4965 struct FixedKeySigner {
4966 secret: SecretKey,
4967 public: PublicKey,
4968 }
4969
4970 #[async_trait]
4971 impl KwSigner for FixedKeySigner {
4972 type Error = String;
4973
4974 fn supported_methods(&self) -> &[SignerMethod] {
4975 &[SignerMethod::Digest]
4976 }
4977
4978 async fn sign_ecdsa(
4979 &self,
4980 _path: &DerivationPath,
4981 sighash: [u8; 32],
4982 ) -> Result<(EcdsaSignature, EcdsaPublicKey), Self::Error> {
4983 let msg = Message::from_digest(sighash);
4984 let sig = self.secret.sign_ecdsa(msg);
4985 Ok((sig.into(), self.public.into()))
4986 }
4987
4988 async fn public_key(
4989 &self,
4990 _path: &DerivationPath,
4991 ) -> Result<EcdsaPublicKey, Self::Error> {
4992 Ok(self.public.into())
4993 }
4994 }
4995
4996 #[async_trait]
4997 impl ExtendedPubKeySigner for FixedKeySigner {
4998 async fn extended_public_key(
4999 &self,
5000 _path: &DerivationPath,
5001 ) -> Result<ExtendedPubKey, Self::Error> {
5002 Err("FixedKeySigner does not derive extended public keys".to_string())
5003 }
5004 }
5005
5006 let (secret_key, public_key) = secp256k1::generate_keypair(&mut rand::rng());
5011 let private_key_bytes = secret_key.to_secret_bytes();
5012
5013 let signer = FixedKeySigner {
5014 secret: secret_key,
5015 public: public_key,
5016 };
5017 let path = DerivationPath::default();
5018
5019 let mut st_legacy = sample_transfer_st();
5023 let mut st_signer = sample_transfer_st();
5024
5025 assert_eq!(
5027 st_legacy.signable_bytes().expect("legacy signable_bytes"),
5028 st_signer.signable_bytes().expect("signer signable_bytes"),
5029 "signable_bytes pre-image must match across copies"
5030 );
5031
5032 let bls = crate::bls::native_bls::NativeBlsModule;
5037 st_legacy
5038 .sign_by_private_key(&private_key_bytes, KeyType::ECDSA_HASH160, &bls)
5039 .expect("sign_by_private_key");
5040
5041 st_signer
5044 .sign_with_core_signer(&path, &signer)
5045 .await
5046 .expect("sign_with_core_signer");
5047
5048 let sig_legacy = st_legacy.signature().expect("legacy signature set");
5049 let sig_signer = st_signer.signature().expect("signer signature set");
5050
5051 assert_eq!(
5052 sig_legacy.as_slice().len(),
5053 65,
5054 "legacy ECDSA signature must be 65 bytes (recoverable compact)"
5055 );
5056 assert_eq!(
5057 sig_signer.as_slice().len(),
5058 65,
5059 "signer ECDSA signature must be 65 bytes (recoverable compact)"
5060 );
5061 assert_eq!(
5062 sig_legacy.as_slice(),
5063 sig_signer.as_slice(),
5064 "sign_with_core_signer must produce byte-identical output to sign_by_private_key"
5065 );
5066 }
5067}