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