1use crate::identity::state_transition::asset_lock_proof::chain::ChainAssetLockProof;
2#[cfg(all(feature = "state-transitions", feature = "client"))]
3use crate::identity::state_transition::asset_lock_proof::AssetLockProof;
4use crate::identity::state_transition::asset_lock_proof::InstantAssetLockProof;
5#[cfg(all(feature = "state-transitions", feature = "client"))]
6use crate::identity::state_transition::AssetLockProved;
7#[cfg(all(feature = "state-transitions", feature = "client"))]
8use crate::identity::IdentityV0;
9
10use crate::identity::{Identity, IdentityPublicKey, KeyID};
11
12use crate::ProtocolError;
13
14use dashcore::{InstantLock, Transaction};
15use platform_value::Identifier;
16use std::collections::BTreeMap;
17
18#[cfg(all(feature = "identity-serialization", feature = "client"))]
19use crate::consensus::basic::decode::SerializedObjectParsingError;
20#[cfg(all(feature = "identity-serialization", feature = "client"))]
21use crate::consensus::basic::BasicError;
22#[cfg(all(feature = "identity-serialization", feature = "client"))]
23use crate::consensus::ConsensusError;
24#[cfg(all(feature = "state-transitions", feature = "client"))]
25use crate::identity::accessors::IdentityGettersV0;
26#[cfg(all(feature = "state-transitions", feature = "client"))]
27use crate::identity::core_script::CoreScript;
28#[cfg(all(feature = "state-transitions", feature = "client"))]
29use crate::prelude::IdentityNonce;
30#[cfg(all(feature = "identity-serialization", feature = "client"))]
31use crate::serialization::PlatformDeserializable;
32#[cfg(all(feature = "state-transitions", feature = "client"))]
33use crate::state_transition::identity_create_transition::v0::IdentityCreateTransitionV0;
34#[cfg(all(feature = "state-transitions", feature = "client"))]
35use crate::state_transition::identity_create_transition::IdentityCreateTransition;
36#[cfg(all(feature = "state-transitions", feature = "client"))]
37use crate::state_transition::identity_credit_transfer_transition::v0::IdentityCreditTransferTransitionV0;
38#[cfg(all(feature = "state-transitions", feature = "client"))]
39use crate::state_transition::identity_credit_transfer_transition::IdentityCreditTransferTransition;
40#[cfg(all(feature = "state-transitions", feature = "client"))]
41use crate::state_transition::identity_credit_withdrawal_transition::v0::IdentityCreditWithdrawalTransitionV0;
42#[cfg(all(feature = "state-transitions", feature = "client"))]
43use crate::state_transition::identity_credit_withdrawal_transition::v1::IdentityCreditWithdrawalTransitionV1;
44#[cfg(all(feature = "state-transitions", feature = "client"))]
45use crate::state_transition::identity_credit_withdrawal_transition::IdentityCreditWithdrawalTransition;
46#[cfg(all(feature = "state-transitions", feature = "client"))]
47use crate::state_transition::identity_topup_transition::accessors::IdentityTopUpTransitionAccessorsV0;
48#[cfg(all(feature = "state-transitions", feature = "client"))]
49use crate::state_transition::identity_topup_transition::v0::IdentityTopUpTransitionV0;
50#[cfg(all(feature = "state-transitions", feature = "client"))]
51use crate::state_transition::identity_topup_transition::IdentityTopUpTransition;
52#[cfg(all(feature = "state-transitions", feature = "client"))]
53use crate::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0;
54#[cfg(all(feature = "state-transitions", feature = "client"))]
55use crate::state_transition::identity_update_transition::v0::IdentityUpdateTransitionV0;
56#[cfg(all(feature = "state-transitions", feature = "client"))]
57use crate::state_transition::identity_update_transition::IdentityUpdateTransition;
58#[cfg(all(feature = "state-transitions", feature = "client"))]
59use crate::state_transition::public_key_in_creation::IdentityPublicKeyInCreation;
60use crate::version::PlatformVersion;
61#[cfg(all(feature = "state-transitions", feature = "client"))]
62use crate::withdrawal::Pooling;
63
64pub const IDENTITY_PROTOCOL_VERSION: u32 = 1;
65
66#[derive(Clone)]
67pub struct IdentityFactory {
68 protocol_version: u32,
69}
70
71impl IdentityFactory {
72 pub fn new(protocol_version: u32) -> Self {
73 IdentityFactory { protocol_version }
74 }
75
76 pub fn create(
77 &self,
78 id: Identifier,
79 public_keys: BTreeMap<KeyID, IdentityPublicKey>,
80 ) -> Result<Identity, ProtocolError> {
81 Identity::new_with_id_and_keys(
82 id,
83 public_keys,
84 PlatformVersion::get(self.protocol_version)?,
85 )
86 }
87
88 #[cfg(all(feature = "identity-serialization", feature = "client"))]
89 pub fn create_from_buffer(
90 &self,
91 buffer: Vec<u8>,
92 #[cfg(feature = "validation")] skip_validation: bool,
93 ) -> Result<Identity, ProtocolError> {
94 let identity: Identity =
95 Identity::deserialize_from_bytes_no_limit(&buffer).map_err(|e| {
96 ConsensusError::BasicError(BasicError::SerializedObjectParsingError(
97 SerializedObjectParsingError::new(format!("Decode protocol entity: {:#?}", e)),
98 ))
99 })?;
100
101 #[cfg(feature = "validation")]
102 if !skip_validation {
103 }
105
106 Ok(identity)
107 }
108
109 pub fn create_instant_lock_proof(
110 instant_lock: InstantLock,
111 asset_lock_transaction: Transaction,
112 output_index: u32,
113 ) -> InstantAssetLockProof {
114 InstantAssetLockProof::new(instant_lock, asset_lock_transaction, output_index)
115 }
116
117 pub fn create_chain_asset_lock_proof(
118 core_chain_locked_height: u32,
119 out_point: [u8; 36],
120 ) -> ChainAssetLockProof {
121 ChainAssetLockProof::new(core_chain_locked_height, out_point)
122 }
123
124 #[cfg(all(feature = "state-transitions", feature = "client"))]
125 pub fn create_identity_create_transition(
126 &self,
127 identity: &Identity,
128 asset_lock_proof: AssetLockProof,
129 ) -> Result<IdentityCreateTransition, ProtocolError> {
130 let transition =
131 IdentityCreateTransitionV0::try_from_identity_v0(identity, asset_lock_proof)?;
132
133 Ok(IdentityCreateTransition::V0(transition))
134 }
135
136 #[cfg(all(feature = "state-transitions", feature = "client"))]
137 pub fn create_identity_with_create_transition(
138 &self,
139 public_keys: BTreeMap<KeyID, IdentityPublicKey>,
140 asset_lock_proof: AssetLockProof,
141 ) -> Result<(Identity, IdentityCreateTransition), ProtocolError> {
142 let identifier = asset_lock_proof.create_identifier()?;
143 let identity = Identity::V0(IdentityV0 {
144 id: identifier,
145 public_keys: public_keys.clone(),
146 balance: 0,
147 revision: 0,
148 });
149
150 let identity_create_transition = IdentityCreateTransition::V0(
151 IdentityCreateTransitionV0::try_from_identity_v0(&identity, asset_lock_proof)?,
152 );
153 Ok((identity, identity_create_transition))
154 }
155
156 #[cfg(all(feature = "state-transitions", feature = "client"))]
157 pub fn create_identity_topup_transition(
158 &self,
159 identity_id: Identifier,
160 asset_lock_proof: AssetLockProof,
161 ) -> Result<IdentityTopUpTransition, ProtocolError> {
162 let mut identity_topup_transition = IdentityTopUpTransitionV0::default();
163
164 identity_topup_transition.set_identity_id(identity_id);
165 identity_topup_transition.set_asset_lock_proof(asset_lock_proof)?;
166
167 Ok(IdentityTopUpTransition::V0(identity_topup_transition))
168 }
169
170 #[cfg(all(feature = "state-transitions", feature = "client"))]
171 pub fn create_identity_credit_transfer_transition(
172 &self,
173 identity: &Identity,
174 recipient_id: Identifier,
175 amount: u64,
176 identity_nonce: IdentityNonce,
177 ) -> Result<IdentityCreditTransferTransition, ProtocolError> {
178 let identity_credit_transfer_transition = IdentityCreditTransferTransitionV0 {
179 identity_id: identity.id(),
180 recipient_id,
181 amount,
182 nonce: identity_nonce,
183 ..Default::default()
184 };
185
186 Ok(IdentityCreditTransferTransition::from(
187 identity_credit_transfer_transition,
188 ))
189 }
190
191 #[cfg(all(feature = "state-transitions", feature = "client"))]
192 pub fn create_identity_credit_withdrawal_transition(
193 &self,
194 identity_id: Identifier,
195 amount: u64,
196 core_fee_per_byte: u32,
197 pooling: Pooling,
198 output_script: Option<CoreScript>,
199 identity_nonce: IdentityNonce,
200 ) -> Result<IdentityCreditWithdrawalTransition, ProtocolError> {
201 let platform_version = PlatformVersion::get(self.protocol_version)?;
202
203 let identity_credit_withdrawal_transition = match platform_version
204 .dpp
205 .state_transitions
206 .identities
207 .credit_withdrawal
208 .default_constructor
209 {
210 0 => {
211 let output_script = output_script.ok_or_else(|| {
212 ProtocolError::Generic(
213 "Output script is required for IdentityCreditWithdrawalTransitionV0"
214 .to_string(),
215 )
216 })?;
217
218 let transition = IdentityCreditWithdrawalTransitionV0 {
219 identity_id,
220 amount,
221 core_fee_per_byte,
222 pooling,
223 output_script,
224 nonce: identity_nonce,
225 ..Default::default()
226 };
227
228 IdentityCreditWithdrawalTransition::from(transition)
229 }
230 1 => {
231 let transition = IdentityCreditWithdrawalTransitionV1 {
232 identity_id,
233 amount,
234 core_fee_per_byte,
235 pooling,
236 output_script,
237 nonce: identity_nonce,
238 ..Default::default()
239 };
240
241 IdentityCreditWithdrawalTransition::from(transition)
242 }
243 version => {
244 return Err(ProtocolError::UnknownVersionMismatch {
245 method: "create_identity_credit_withdrawal_transition".to_string(),
246 known_versions: vec![0, 1],
247 received: version,
248 });
249 }
250 };
251
252 Ok(identity_credit_withdrawal_transition)
253 }
254
255 #[cfg(all(feature = "state-transitions", feature = "client"))]
256 pub fn create_identity_update_transition(
257 &self,
258 identity: Identity,
259 identity_nonce: u64,
260 add_public_keys: Option<Vec<IdentityPublicKeyInCreation>>,
261 public_key_ids_to_disable: Option<Vec<KeyID>>,
262 ) -> Result<IdentityUpdateTransition, ProtocolError> {
263 let mut identity_update_transition = IdentityUpdateTransitionV0::default();
264 identity_update_transition.set_identity_id(identity.id().to_owned());
265 identity_update_transition.set_revision(identity.revision() + 1);
266 identity_update_transition.set_nonce(identity_nonce);
267
268 if let Some(add_public_keys) = add_public_keys {
269 identity_update_transition.set_public_keys_to_add(add_public_keys);
270 }
271
272 if let Some(public_key_ids_to_disable) = public_key_ids_to_disable {
273 identity_update_transition.set_public_key_ids_to_disable(public_key_ids_to_disable);
274 }
275
276 Ok(IdentityUpdateTransition::V0(identity_update_transition))
277 }
278}
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283 use crate::identity::accessors::IdentityGettersV0;
284 use platform_value::Identifier;
285 use std::collections::BTreeMap;
286
287 #[test]
288 fn factory_new_sets_protocol_version() {
289 let factory = IdentityFactory::new(1);
290 assert_eq!(factory.protocol_version, 1);
291 }
292
293 #[test]
294 fn factory_clone() {
295 let factory = IdentityFactory::new(1);
296 let cloned = factory.clone();
297 assert_eq!(cloned.protocol_version, 1);
298 }
299
300 #[test]
301 fn create_identity_with_empty_keys() {
302 let factory = IdentityFactory::new(1);
303 let id = Identifier::random();
304 let public_keys = BTreeMap::new();
305
306 let identity = factory.create(id, public_keys).unwrap();
307
308 assert_eq!(identity.id(), id);
309 }
310
311 #[test]
312 fn create_identity_with_invalid_version() {
313 let factory = IdentityFactory::new(u32::MAX);
314 let id = Identifier::random();
315 let public_keys = BTreeMap::new();
316
317 let result = factory.create(id, public_keys);
318 assert!(result.is_err());
319 }
320
321 #[test]
322 fn create_identity_preserves_id() {
323 let factory = IdentityFactory::new(1);
324 let id = Identifier::random();
325 let public_keys = BTreeMap::new();
326
327 let identity = factory.create(id, public_keys).unwrap();
328 assert_eq!(identity.id(), id);
329 }
330
331 #[test]
332 fn create_identity_has_zero_balance() {
333 let factory = IdentityFactory::new(1);
334 let id = Identifier::random();
335 let public_keys = BTreeMap::new();
336
337 let identity = factory.create(id, public_keys).unwrap();
338 assert_eq!(identity.balance(), 0);
339 }
340
341 #[test]
342 fn create_identity_has_zero_revision() {
343 let factory = IdentityFactory::new(1);
344 let id = Identifier::random();
345 let public_keys = BTreeMap::new();
346
347 let identity = factory.create(id, public_keys).unwrap();
348 assert_eq!(identity.revision(), 0);
349 }
350
351 #[test]
352 fn create_identity_with_multiple_versions() {
353 let factory_v1 = IdentityFactory::new(1);
355 let id = Identifier::random();
356 let result = factory_v1.create(id, BTreeMap::new());
357 assert!(result.is_ok());
358 }
359
360 #[test]
361 fn create_chain_asset_lock_proof_test() {
362 let out_point = [0u8; 36];
363 let proof = IdentityFactory::create_chain_asset_lock_proof(100, out_point);
364
365 assert_eq!(proof.core_chain_locked_height, 100);
366 }
367
368 #[test]
369 fn create_chain_asset_lock_proof_different_heights() {
370 let out_point = [1u8; 36];
371 let proof = IdentityFactory::create_chain_asset_lock_proof(500, out_point);
372 assert_eq!(proof.core_chain_locked_height, 500);
373
374 let proof2 = IdentityFactory::create_chain_asset_lock_proof(0, out_point);
375 assert_eq!(proof2.core_chain_locked_height, 0);
376
377 let proof3 = IdentityFactory::create_chain_asset_lock_proof(u32::MAX, out_point);
378 assert_eq!(proof3.core_chain_locked_height, u32::MAX);
379 }
380
381 #[test]
382 fn create_two_identities_have_different_ids() {
383 let factory = IdentityFactory::new(1);
384 let id1 = Identifier::random();
385 let id2 = Identifier::random();
386
387 let identity1 = factory.create(id1, BTreeMap::new()).unwrap();
388 let identity2 = factory.create(id2, BTreeMap::new()).unwrap();
389
390 assert_ne!(identity1.id(), identity2.id());
391 }
392
393 #[test]
394 fn create_identity_with_public_keys() {
395 use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
396 use crate::identity::{KeyType, Purpose, SecurityLevel};
397
398 let factory = IdentityFactory::new(1);
399 let id = Identifier::random();
400
401 let key = IdentityPublicKey::V0(IdentityPublicKeyV0 {
402 id: 0,
403 purpose: Purpose::AUTHENTICATION,
404 security_level: SecurityLevel::MASTER,
405 contract_bounds: None,
406 key_type: KeyType::ECDSA_SECP256K1,
407 read_only: false,
408 data: vec![0u8; 33].into(),
409 disabled_at: None,
410 });
411
412 let mut public_keys = BTreeMap::new();
413 public_keys.insert(0u32, key);
414
415 let identity = factory.create(id, public_keys).unwrap();
416 assert_eq!(identity.public_keys().len(), 1);
417 }
418
419 #[cfg(all(feature = "identity-serialization", feature = "client"))]
422 mod serialization_tests {
423 use super::*;
424 use crate::serialization::PlatformSerializable;
425
426 fn latest_version() -> &'static PlatformVersion {
427 PlatformVersion::latest()
428 }
429
430 #[test]
431 fn create_from_buffer_roundtrip_empty_keys() {
432 let factory = IdentityFactory::new(latest_version().protocol_version);
433 let id = Identifier::from([9u8; 32]);
434 let identity = factory.create(id, BTreeMap::new()).unwrap();
435
436 let bytes = PlatformSerializable::serialize_to_bytes(&identity).unwrap();
437
438 let roundtripped = factory
439 .create_from_buffer(
440 bytes,
441 #[cfg(feature = "validation")]
442 true,
443 )
444 .unwrap();
445
446 assert_eq!(roundtripped.id(), identity.id());
447 assert_eq!(roundtripped.balance(), 0);
448 assert_eq!(roundtripped.revision(), 0);
449 assert_eq!(roundtripped.public_keys().len(), 0);
450 }
451
452 #[test]
453 fn create_from_buffer_fails_on_garbage() {
454 let factory = IdentityFactory::new(latest_version().protocol_version);
455 let garbage = vec![0xFFu8; 16];
456
457 let result = factory.create_from_buffer(
458 garbage,
459 #[cfg(feature = "validation")]
460 true,
461 );
462 assert!(result.is_err());
463 }
464
465 #[test]
466 fn create_from_buffer_fails_on_empty() {
467 let factory = IdentityFactory::new(latest_version().protocol_version);
468 let result = factory.create_from_buffer(
469 Vec::new(),
470 #[cfg(feature = "validation")]
471 true,
472 );
473 assert!(result.is_err());
474 }
475 }
476
477 #[cfg(all(feature = "state-transitions", feature = "client"))]
478 mod state_transition_tests {
479 use super::*;
480 use crate::identity::core_script::CoreScript;
481 use crate::identity::identity_public_key::v0::IdentityPublicKeyV0;
482 use crate::identity::state_transition::asset_lock_proof::AssetLockProof;
483 use crate::identity::{KeyType, Purpose, SecurityLevel};
484 use crate::state_transition::identity_credit_transfer_transition::accessors::IdentityCreditTransferTransitionAccessorsV0;
485 use crate::state_transition::identity_credit_withdrawal_transition::accessors::IdentityCreditWithdrawalTransitionAccessorsV0;
486 use crate::state_transition::identity_topup_transition::accessors::IdentityTopUpTransitionAccessorsV0;
487 use crate::state_transition::identity_update_transition::accessors::IdentityUpdateTransitionAccessorsV0;
488 use crate::tests::fixtures::instant_asset_lock_proof_fixture;
489 use crate::withdrawal::Pooling;
490
491 fn sample_public_key(id: u32) -> IdentityPublicKey {
492 IdentityPublicKey::V0(IdentityPublicKeyV0 {
493 id,
494 purpose: Purpose::AUTHENTICATION,
495 security_level: SecurityLevel::MASTER,
496 contract_bounds: None,
497 key_type: KeyType::ECDSA_SECP256K1,
498 read_only: false,
499 data: vec![0u8; 33].into(),
500 disabled_at: None,
501 })
502 }
503
504 #[test]
505 fn create_identity_create_transition_from_identity() {
506 let factory = IdentityFactory::new(1);
507 let asset_lock_proof = instant_asset_lock_proof_fixture(None, None);
508 let expected_identifier = asset_lock_proof.create_identifier().unwrap();
509
510 let identity = factory
511 .create(expected_identifier, BTreeMap::new())
512 .unwrap();
513
514 let transition = factory
515 .create_identity_create_transition(&identity, asset_lock_proof)
516 .expect("expected transition");
517
518 match transition {
519 IdentityCreateTransition::V0(v0) => {
520 assert_eq!(v0.identity_id, expected_identifier);
521 assert!(v0.public_keys.is_empty());
522 }
523 }
524 }
525
526 #[test]
527 fn create_identity_with_create_transition_computes_id_from_asset_lock_proof() {
528 let factory = IdentityFactory::new(1);
529 let asset_lock_proof = instant_asset_lock_proof_fixture(None, None);
530 let expected_id = asset_lock_proof.create_identifier().unwrap();
531
532 let mut public_keys = BTreeMap::new();
533 public_keys.insert(0u32, sample_public_key(0));
534
535 let (identity, transition) = factory
536 .create_identity_with_create_transition(public_keys, asset_lock_proof)
537 .unwrap();
538
539 assert_eq!(identity.id(), expected_id);
540 assert_eq!(identity.balance(), 0);
541 assert_eq!(identity.revision(), 0);
542 assert_eq!(identity.public_keys().len(), 1);
543
544 match transition {
545 IdentityCreateTransition::V0(v0) => {
546 assert_eq!(v0.identity_id, expected_id);
547 assert_eq!(v0.public_keys.len(), 1);
548 }
549 }
550 }
551
552 #[test]
553 fn create_identity_topup_transition_sets_identity_and_proof() {
554 let factory = IdentityFactory::new(1);
555 let identity_id = Identifier::from([3u8; 32]);
556 let asset_lock_proof = instant_asset_lock_proof_fixture(None, None);
557
558 let transition = factory
559 .create_identity_topup_transition(identity_id, asset_lock_proof.clone())
560 .unwrap();
561
562 assert_eq!(*transition.identity_id(), identity_id);
563 match &transition {
565 IdentityTopUpTransition::V0(v0) => match &v0.asset_lock_proof {
566 AssetLockProof::Instant(_) => {}
567 _ => panic!("expected instant asset lock proof"),
568 },
569 }
570 }
571
572 #[test]
573 fn create_identity_credit_transfer_transition_fields() {
574 let factory = IdentityFactory::new(1);
575 let id = Identifier::from([11u8; 32]);
576 let identity = factory.create(id, BTreeMap::new()).unwrap();
577 let recipient = Identifier::from([22u8; 32]);
578 let amount = 123_456u64;
579 let nonce = 7u64;
580
581 let transition = factory
582 .create_identity_credit_transfer_transition(&identity, recipient, amount, nonce)
583 .unwrap();
584
585 assert_eq!(transition.identity_id(), id);
586 assert_eq!(transition.recipient_id(), recipient);
587 assert_eq!(transition.amount(), amount);
588 assert_eq!(transition.nonce(), nonce);
589 }
590
591 #[test]
592 fn create_identity_credit_withdrawal_transition_v0_requires_output_script() {
593 let factory = IdentityFactory::new(1);
595 let identity_id = Identifier::from([5u8; 32]);
596
597 let result = factory.create_identity_credit_withdrawal_transition(
598 identity_id,
599 1000,
600 1,
601 Pooling::Never,
602 None, 1,
604 );
605 match result {
606 Err(ProtocolError::Generic(msg)) => {
607 assert!(msg.contains("Output script is required"));
608 }
609 other => panic!("expected Generic error, got {:?}", other),
610 }
611 }
612
613 #[test]
614 fn create_identity_credit_withdrawal_transition_v0_with_output_script_succeeds() {
615 let factory = IdentityFactory::new(1);
616 let identity_id = Identifier::from([5u8; 32]);
617 let script = CoreScript::new_p2pkh([0xAB; 20]);
618
619 let transition = factory
620 .create_identity_credit_withdrawal_transition(
621 identity_id,
622 10_000,
623 2,
624 Pooling::IfAvailable,
625 Some(script.clone()),
626 42,
627 )
628 .unwrap();
629
630 assert_eq!(transition.identity_id(), identity_id);
631 assert_eq!(transition.amount(), 10_000);
632 assert_eq!(transition.core_fee_per_byte(), 2);
633 assert_eq!(transition.pooling(), Pooling::IfAvailable);
634 assert_eq!(transition.output_script(), Some(script));
635 assert_eq!(transition.nonce(), 42);
636 match transition {
638 IdentityCreditWithdrawalTransition::V0(_) => {}
639 other => panic!("expected V0 variant, got {:?}", other),
640 }
641 }
642
643 #[test]
644 fn create_identity_credit_withdrawal_transition_v1_accepts_none_output_script() {
645 let latest = PlatformVersion::latest();
647 let factory = IdentityFactory::new(latest.protocol_version);
648 let identity_id = Identifier::from([6u8; 32]);
649
650 let transition = factory
651 .create_identity_credit_withdrawal_transition(
652 identity_id,
653 500,
654 3,
655 Pooling::Standard,
656 None,
657 99,
658 )
659 .unwrap();
660
661 assert_eq!(transition.identity_id(), identity_id);
662 assert_eq!(transition.amount(), 500);
663 assert_eq!(transition.pooling(), Pooling::Standard);
664 assert_eq!(transition.nonce(), 99);
665 assert_eq!(transition.output_script(), None);
666 match transition {
667 IdentityCreditWithdrawalTransition::V1(_) => {}
668 other => panic!("expected V1 variant, got {:?}", other),
669 }
670 }
671
672 #[test]
673 fn create_identity_credit_withdrawal_transition_invalid_version() {
674 let factory = IdentityFactory::new(u32::MAX);
675 let identity_id = Identifier::from([5u8; 32]);
676
677 let result = factory.create_identity_credit_withdrawal_transition(
678 identity_id,
679 1000,
680 1,
681 Pooling::Never,
682 Some(CoreScript::new_p2pkh([0u8; 20])),
683 1,
684 );
685 assert!(result.is_err());
686 }
687
688 #[test]
689 fn create_identity_update_transition_increments_revision_and_applies_adds() {
690 let factory = IdentityFactory::new(1);
691 let id = Identifier::from([7u8; 32]);
692 let mut identity = factory.create(id, BTreeMap::new()).unwrap();
693 match &mut identity {
695 Identity::V0(v0) => {
696 v0.revision = 5;
697 }
698 }
699
700 let new_key: IdentityPublicKeyInCreation = sample_public_key(1).into();
701 let update = factory
702 .create_identity_update_transition(identity, 33, Some(vec![new_key.clone()]), None)
703 .unwrap();
704
705 assert_eq!(update.identity_id(), id);
706 assert_eq!(update.revision(), 6); assert_eq!(update.nonce(), 33);
708 assert_eq!(update.public_keys_to_add().len(), 1);
709 assert!(update.public_key_ids_to_disable().is_empty());
710 }
711
712 #[test]
713 fn create_identity_update_transition_with_disabled_keys() {
714 let factory = IdentityFactory::new(1);
715 let id = Identifier::from([8u8; 32]);
716 let identity = factory.create(id, BTreeMap::new()).unwrap();
717
718 let update = factory
719 .create_identity_update_transition(identity, 1, None, Some(vec![2u32, 3u32, 5u32]))
720 .unwrap();
721
722 assert_eq!(update.identity_id(), id);
723 assert_eq!(update.revision(), 1);
725 assert_eq!(update.nonce(), 1);
726 assert!(update.public_keys_to_add().is_empty());
727 assert_eq!(update.public_key_ids_to_disable(), &[2u32, 3, 5][..]);
728 }
729
730 #[test]
731 fn create_instant_lock_proof_preserves_output_index() {
732 let asset_lock_proof = instant_asset_lock_proof_fixture(None, None);
733 let tx = match &asset_lock_proof {
734 AssetLockProof::Instant(p) => p.transaction.clone(),
735 _ => panic!("expected Instant"),
736 };
737 let il = match &asset_lock_proof {
738 AssetLockProof::Instant(p) => p.instant_lock.clone(),
739 _ => panic!("expected Instant"),
740 };
741
742 let built = IdentityFactory::create_instant_lock_proof(il.clone(), tx.clone(), 7);
743 assert_eq!(built.output_index, 7);
744 assert_eq!(built.transaction.txid(), tx.txid());
745 let as_proof = AssetLockProof::Instant(built.clone());
747 assert!(matches!(as_proof, AssetLockProof::Instant(_)));
748 }
749 }
750}