1use crate::blueprints::util::{PresecurifiedRoleAssignment, SecurifiedRoleAssignment};
2use crate::errors::{ApplicationError, RuntimeError};
3use crate::internal_prelude::*;
4use crate::roles_template;
5use radix_blueprint_schema_init::{
6 BlueprintEventSchemaInit, BlueprintFunctionsSchemaInit, FunctionSchemaInit, ReceiverInfo,
7 TypeRef,
8};
9use radix_blueprint_schema_init::{BlueprintSchemaInit, BlueprintStateSchemaInit};
10use radix_engine_interface::api::{AttachedModuleId, SystemApi};
11use radix_engine_interface::blueprints::hooks::{OnVirtualizeInput, OnVirtualizeOutput};
12use radix_engine_interface::blueprints::identity::*;
13use radix_engine_interface::blueprints::package::{
14 AuthConfig, BlueprintDefinitionInit, BlueprintType, FunctionAuth, MethodAuthTemplate,
15 PackageDefinition,
16};
17use radix_engine_interface::blueprints::resource::*;
18use radix_engine_interface::metadata_init;
19use radix_engine_interface::object_modules::metadata::*;
20use radix_native_sdk::modules::metadata::Metadata;
21use radix_native_sdk::modules::role_assignment::RoleAssignment;
22use radix_native_sdk::modules::royalty::ComponentRoyalty;
23use radix_native_sdk::runtime::Runtime;
24
25pub const IDENTITY_ON_VIRTUALIZE_EXPORT_NAME: &str = "on_virtualize";
26
27pub const IDENTITY_CREATE_PREALLOCATED_SECP256K1_ID: u8 = 0u8;
28pub const IDENTITY_CREATE_PREALLOCATED_ED25519_ID: u8 = 1u8;
29
30pub struct IdentityNativePackage;
31
32impl IdentityNativePackage {
33 pub fn definition() -> PackageDefinition {
34 let mut aggregator = TypeAggregator::<ScryptoCustomTypeKind>::new();
35
36 let fields = Vec::new();
37
38 let mut functions = index_map_new();
39 functions.insert(
40 IDENTITY_CREATE_ADVANCED_IDENT.to_string(),
41 FunctionSchemaInit {
42 receiver: None,
43 input: TypeRef::Static(
44 aggregator.add_child_type_and_descendents::<IdentityCreateAdvancedInput>(),
45 ),
46 output: TypeRef::Static(
47 aggregator.add_child_type_and_descendents::<IdentityCreateAdvancedOutput>(),
48 ),
49 export: IDENTITY_CREATE_ADVANCED_IDENT.to_string(),
50 },
51 );
52 functions.insert(
53 IDENTITY_CREATE_IDENT.to_string(),
54 FunctionSchemaInit {
55 receiver: None,
56 input: TypeRef::Static(
57 aggregator.add_child_type_and_descendents::<IdentityCreateInput>(),
58 ),
59 output: TypeRef::Static(
60 aggregator.add_child_type_and_descendents::<IdentityCreateOutput>(),
61 ),
62 export: IDENTITY_CREATE_IDENT.to_string(),
63 },
64 );
65 functions.insert(
66 IDENTITY_SECURIFY_IDENT.to_string(),
67 FunctionSchemaInit {
68 receiver: Some(ReceiverInfo::normal_ref_mut()),
69 input: TypeRef::Static(
70 aggregator
71 .add_child_type_and_descendents::<IdentitySecurifyToSingleBadgeInput>(),
72 ),
73 output: TypeRef::Static(
74 aggregator
75 .add_child_type_and_descendents::<IdentitySecurifyToSingleBadgeOutput>(),
76 ),
77 export: IDENTITY_SECURIFY_IDENT.to_string(),
78 },
79 );
80
81 let schema = generate_full_schema(aggregator);
82 let blueprints = indexmap!(
83 IDENTITY_BLUEPRINT.to_string() => BlueprintDefinitionInit {
84 blueprint_type: BlueprintType::default(),
85 is_transient: false,
86 feature_set: indexset!(),
87 dependencies: indexset!(
88 SECP256K1_SIGNATURE_RESOURCE.into(),
89 ED25519_SIGNATURE_RESOURCE.into(),
90 IDENTITY_OWNER_BADGE.into(),
91 PACKAGE_OF_DIRECT_CALLER_RESOURCE.into(),
92 ),
93 schema: BlueprintSchemaInit {
94 generics: vec![],
95 schema,
96 state: BlueprintStateSchemaInit {
97 fields,
98 collections: vec![],
99 },
100 events: BlueprintEventSchemaInit::default(),
101 types: BlueprintTypeSchemaInit::default(),
102 functions: BlueprintFunctionsSchemaInit {
103 functions,
104 },
105 hooks: BlueprintHooksInit {
106 hooks: indexmap!(BlueprintHook::OnVirtualize => IDENTITY_ON_VIRTUALIZE_EXPORT_NAME.to_string())
107 }
108 },
109 royalty_config: PackageRoyaltyConfig::default(),
110 auth_config: AuthConfig {
111 function_auth: FunctionAuth::AllowAll,
112 method_auth: MethodAuthTemplate::StaticRoleDefinition(roles_template! {
113 roles {
114 SECURIFY_ROLE => updaters: [SELF_ROLE];
115 },
116 methods {
117 IDENTITY_SECURIFY_IDENT => [SECURIFY_ROLE];
118 }
119 }),
120 },
121 }
122 );
123
124 PackageDefinition { blueprints }
125 }
126
127 pub fn invoke_export<Y: SystemApi<RuntimeError>>(
128 export_name: &str,
129 input: &IndexedScryptoValue,
130 version: IdentityV1MinorVersion,
131 api: &mut Y,
132 ) -> Result<IndexedScryptoValue, RuntimeError> {
133 match export_name {
134 IDENTITY_CREATE_ADVANCED_IDENT => {
135 let input: IdentityCreateAdvancedInput = input.as_typed().map_err(|e| {
136 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
137 })?;
138
139 let rtn = IdentityBlueprint::create_advanced(input.owner_role, version, api)?;
140
141 Ok(IndexedScryptoValue::from_typed(&rtn))
142 }
143 IDENTITY_CREATE_IDENT => {
144 let _input: IdentityCreateInput = input.as_typed().map_err(|e| {
145 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
146 })?;
147
148 let rtn = IdentityBlueprint::create(version, api)?;
149
150 Ok(IndexedScryptoValue::from_typed(&rtn))
151 }
152 IDENTITY_SECURIFY_IDENT => {
153 let _input: IdentitySecurifyToSingleBadgeInput = input.as_typed().map_err(|e| {
154 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
155 })?;
156
157 let rtn = IdentityBlueprint::securify(api)?;
158
159 Ok(IndexedScryptoValue::from_typed(&rtn))
160 }
161 IDENTITY_ON_VIRTUALIZE_EXPORT_NAME => {
162 let input: OnVirtualizeInput = input.as_typed().map_err(|e| {
163 RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e))
164 })?;
165
166 let rtn = IdentityBlueprint::on_virtualize(input, version, api)?;
167
168 Ok(IndexedScryptoValue::from_typed(&rtn))
169 }
170 _ => Err(RuntimeError::ApplicationError(
171 ApplicationError::ExportDoesNotExist(export_name.to_string()),
172 )),
173 }
174 }
175}
176
177const SECURIFY_ROLE: &'static str = "securify";
178
179struct SecurifiedIdentity;
180
181impl SecurifiedRoleAssignment for SecurifiedIdentity {
182 type OwnerBadgeNonFungibleData = IdentityOwnerBadgeData;
183 const OWNER_BADGE: ResourceAddress = IDENTITY_OWNER_BADGE;
184 const SECURIFY_ROLE: Option<&'static str> = Some(SECURIFY_ROLE);
185}
186
187impl PresecurifiedRoleAssignment for SecurifiedIdentity {}
188
189#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Sbor)]
190pub enum IdentityV1MinorVersion {
191 Zero,
192 One,
193}
194
195pub struct IdentityBlueprint;
196
197impl IdentityBlueprint {
198 pub fn create_advanced<Y: SystemApi<RuntimeError>>(
199 owner_role: OwnerRole,
200 version: IdentityV1MinorVersion,
201 api: &mut Y,
202 ) -> Result<GlobalAddress, RuntimeError> {
203 let role_assignment = SecurifiedIdentity::create_advanced(owner_role, api)?;
204
205 let (node_id, modules) = Self::create_object(
206 role_assignment,
207 metadata_init!(
208 "owner_badge" => EMPTY, locked;
209 ),
210 version,
211 api,
212 )?;
213 let modules = modules.into_iter().map(|(id, own)| (id, own.0)).collect();
214 let address = api.globalize(node_id, modules, None)?;
215 Ok(address)
216 }
217
218 pub fn create<Y: SystemApi<RuntimeError>>(
219 version: IdentityV1MinorVersion,
220 api: &mut Y,
221 ) -> Result<(GlobalAddress, Bucket), RuntimeError> {
222 let (address_reservation, address) = api.allocate_global_address(BlueprintId {
223 package_address: IDENTITY_PACKAGE,
224 blueprint_name: IDENTITY_BLUEPRINT.to_string(),
225 })?;
226 let (role_assignment, bucket) = SecurifiedIdentity::create_securified(
227 IdentityOwnerBadgeData {
228 name: "Identity Owner Badge".to_string(),
229 identity: address.try_into().expect("Impossible Case"),
230 },
231 Some(NonFungibleLocalId::bytes(address.as_node_id().0).unwrap()),
232 api,
233 )?;
234
235 let (node_id, modules) = Self::create_object(
236 role_assignment,
237 metadata_init! {
238 "owner_badge" => NonFungibleLocalId::bytes(address.as_node_id().0).unwrap(), locked;
239 },
240 version,
241 api,
242 )?;
243 let modules = modules.into_iter().map(|(id, own)| (id, own.0)).collect();
244 let address = api.globalize(node_id, modules, Some(address_reservation))?;
245 Ok((address, bucket))
246 }
247
248 pub fn on_virtualize<Y: SystemApi<RuntimeError>>(
249 input: OnVirtualizeInput,
250 version: IdentityV1MinorVersion,
251 api: &mut Y,
252 ) -> Result<OnVirtualizeOutput, RuntimeError> {
253 match input.variant_id {
254 IDENTITY_CREATE_PREALLOCATED_SECP256K1_ID => {
255 let public_key_hash = PublicKeyHash::Secp256k1(Secp256k1PublicKeyHash(input.rid));
256 Self::create_virtual(public_key_hash, input.address_reservation, version, api)
257 }
258 IDENTITY_CREATE_PREALLOCATED_ED25519_ID => {
259 let public_key_hash = PublicKeyHash::Ed25519(Ed25519PublicKeyHash(input.rid));
260 Self::create_virtual(public_key_hash, input.address_reservation, version, api)
261 }
262 x => Err(RuntimeError::ApplicationError(
263 ApplicationError::PanicMessage(format!("Unexpected variant id: {:?}", x)),
264 )),
265 }
266 }
267
268 fn create_virtual<Y: SystemApi<RuntimeError>>(
269 public_key_hash: PublicKeyHash,
270 address_reservation: GlobalAddressReservation,
271 version: IdentityV1MinorVersion,
272 api: &mut Y,
273 ) -> Result<(), RuntimeError> {
274 let owner_badge = {
275 let bytes = public_key_hash.get_hash_bytes();
276 let entity_type = match public_key_hash {
277 PublicKeyHash::Ed25519(..) => EntityType::GlobalPreallocatedEd25519Identity,
278 PublicKeyHash::Secp256k1(..) => EntityType::GlobalPreallocatedSecp256k1Identity,
279 };
280
281 let mut id_bytes = vec![entity_type as u8];
282 id_bytes.extend(bytes);
283
284 NonFungibleLocalId::bytes(id_bytes).unwrap()
285 };
286
287 let owner_id = NonFungibleGlobalId::from_public_key_hash(public_key_hash);
288 let role_assignment = SecurifiedIdentity::create_presecurified(owner_id, api)?;
289
290 let (node_id, modules) = Self::create_object(
291 role_assignment,
292 metadata_init! {
293 "owner_keys" => vec![public_key_hash], updatable;
300 "owner_badge" => owner_badge, locked;
301 },
302 version,
303 api,
304 )?;
305
306 api.globalize(
307 node_id,
308 modules.into_iter().map(|(k, v)| (k, v.0)).collect(),
309 Some(address_reservation),
310 )?;
311 Ok(())
312 }
313
314 fn securify<Y: SystemApi<RuntimeError>>(api: &mut Y) -> Result<Bucket, RuntimeError> {
315 let receiver = Runtime::get_node_id(api)?;
316 let owner_badge_data = IdentityOwnerBadgeData {
317 name: "Identity Owner Badge".into(),
318 identity: ComponentAddress::new_or_panic(receiver.0),
319 };
320 let bucket = SecurifiedIdentity::securify(
321 &receiver,
322 owner_badge_data,
323 Some(NonFungibleLocalId::bytes(receiver.0).unwrap()),
324 api,
325 )?;
326 Ok(bucket.into())
327 }
328
329 fn create_object<Y: SystemApi<RuntimeError>>(
330 role_assignment: RoleAssignment,
331 metadata_init: MetadataInit,
332 version: IdentityV1MinorVersion,
333 api: &mut Y,
334 ) -> Result<(NodeId, IndexMap<AttachedModuleId, Own>), RuntimeError> {
335 match version {
336 IdentityV1MinorVersion::Zero => {
337 let metadata = Metadata::create_with_data(metadata_init, api)?;
338 let royalty = ComponentRoyalty::create(ComponentRoyaltyConfig::default(), api)?;
339 let object_id = api.new_simple_object(IDENTITY_BLUEPRINT, indexmap!())?;
340 let modules = indexmap!(
341 AttachedModuleId::RoleAssignment => role_assignment.0,
342 AttachedModuleId::Metadata => metadata,
343 AttachedModuleId::Royalty => royalty,
344 );
345 Ok((object_id, modules))
346 }
347 IdentityV1MinorVersion::One => {
348 let metadata = Metadata::create_with_data(metadata_init, api)?;
349 let object_id = api.new_simple_object(IDENTITY_BLUEPRINT, indexmap!())?;
350 let modules = indexmap!(
351 AttachedModuleId::RoleAssignment => role_assignment.0,
352 AttachedModuleId::Metadata => metadata,
353 );
354 Ok((object_id, modules))
355 }
356 }
357 }
358}
359
360#[derive(ScryptoSbor)]
361pub struct IdentityOwnerBadgeData {
362 pub name: String,
363 pub identity: ComponentAddress,
364}
365
366impl NonFungibleData for IdentityOwnerBadgeData {
367 const MUTABLE_FIELDS: &'static [&'static str] = &[];
368}