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}