1use crate::blueprints::native_schema::*;
2use crate::blueprints::package::*;
3use crate::blueprints::pool::v1::constants::*;
4use radix_blueprint_schema_init::*;
5use radix_common::data::manifest::*;
6use radix_common::prelude::*;
7use radix_engine_interface::api::ModuleId;
8use radix_engine_interface::blueprints::access_controller::*;
9use radix_engine_interface::blueprints::account::*;
10use radix_engine_interface::blueprints::consensus_manager::*;
11use radix_engine_interface::blueprints::identity::*;
12use radix_engine_interface::blueprints::locker::*;
13use radix_engine_interface::blueprints::resource::*;
14use radix_engine_interface::object_modules::metadata::*;
15use radix_engine_interface::object_modules::role_assignment::*;
16use radix_engine_interface::object_modules::royalty::*;
17use radix_transactions::manifest::*;
18
19pub fn validate_call_arguments_to_native_components(
20 manifest: &impl ReadableManifest,
21) -> Result<(), LocatedInstructionSchemaValidationError> {
22 for (instruction_index, effect) in manifest.iter_instruction_effects().enumerate() {
23 validate_instruction_call_arguments_to_native_components(effect).map_err(|cause| {
24 LocatedInstructionSchemaValidationError {
25 instruction_index,
26 cause,
27 }
28 })?
29 }
30 Ok(())
31}
32
33fn validate_instruction_call_arguments_to_native_components(
34 instruction_effect: ManifestInstructionEffect,
35) -> Result<(), InstructionSchemaValidationError> {
36 let (invocation, args) = match instruction_effect {
37 ManifestInstructionEffect::Invocation {
38 kind:
39 InvocationKind::Function {
40 address: ManifestPackageAddress::Static(address),
41 blueprint,
42 function,
43 },
44 args,
45 } => (
46 Invocation::Function(*address, blueprint.to_owned(), function.to_owned()),
47 args,
48 ),
49 ManifestInstructionEffect::Invocation {
50 kind:
51 InvocationKind::Method {
52 address: ManifestGlobalAddress::Static(address),
53 module_id,
54 method,
55 },
56 args,
57 } => (
58 Invocation::Method(*address, module_id, method.to_owned()),
59 args,
60 ),
61 ManifestInstructionEffect::Invocation {
62 kind: InvocationKind::DirectMethod { address, method },
63 args,
64 } => (Invocation::DirectMethod(*address, method.to_owned()), args),
65 _ => return Ok(()),
66 };
67
68 if let Some((TypeRef::Static(local_type_id), schema)) = get_arguments_schema(invocation)? {
69 validate_payload_against_schema::<ManifestCustomExtension, _>(
70 &manifest_encode(&args).unwrap(),
71 schema,
72 local_type_id,
73 &(),
74 MANIFEST_SBOR_V1_MAX_DEPTH,
75 )
76 .map_err(|error| {
77 InstructionSchemaValidationError::SchemaValidationError(error.error_message(schema))
78 })?;
79 }
80
81 Ok(())
82}
83
84fn get_blueprint_schema<'p>(
85 package_definition: &'p PackageDefinition,
86 package_address: PackageAddress,
87 blueprint: &str,
88) -> Result<&'p BlueprintDefinitionInit, InstructionSchemaValidationError> {
89 package_definition.blueprints.get(blueprint).ok_or(
90 InstructionSchemaValidationError::InvalidBlueprint(package_address, blueprint.to_owned()),
91 )
92}
93
94#[allow(clippy::type_complexity)]
99fn get_arguments_schema<'s>(
100 invocation: Invocation,
101) -> Result<
102 Option<(TypeRef<LocalTypeId>, &'s Schema<ScryptoCustomSchema>)>,
103 InstructionSchemaValidationError,
104> {
105 let entity_type = invocation.entity_type();
106
107 let blueprint_schema = match invocation {
108 Invocation::Function(package_address @ PACKAGE_PACKAGE, ref blueprint, _) => {
109 get_blueprint_schema(&PACKAGE_PACKAGE_DEFINITION, package_address, blueprint)
110 .map(Some)?
111 }
112 Invocation::Function(package_address @ RESOURCE_PACKAGE, ref blueprint, _) => {
113 get_blueprint_schema(&RESOURCE_PACKAGE_DEFINITION, package_address, blueprint)
114 .map(Some)?
115 }
116 Invocation::Function(package_address @ ACCOUNT_PACKAGE, ref blueprint, _) => {
117 get_blueprint_schema(&ACCOUNT_PACKAGE_DEFINITION, package_address, blueprint)
118 .map(Some)?
119 }
120 Invocation::Function(package_address @ IDENTITY_PACKAGE, ref blueprint, _) => {
121 get_blueprint_schema(&IDENTITY_PACKAGE_DEFINITION, package_address, blueprint)
122 .map(Some)?
123 }
124 Invocation::Function(package_address @ CONSENSUS_MANAGER_PACKAGE, ref blueprint, _) => {
125 get_blueprint_schema(
126 &CONSENSUS_MANAGER_PACKAGE_DEFINITION,
127 package_address,
128 blueprint,
129 )
130 .map(Some)?
131 }
132 Invocation::Function(package_address @ ACCESS_CONTROLLER_PACKAGE, ref blueprint, _) => {
133 get_blueprint_schema(
134 &ACCESS_CONTROLLER_PACKAGE_DEFINITION_V1_0,
135 package_address,
136 blueprint,
137 )
138 .map(Some)?
139 }
140 Invocation::Function(package_address @ POOL_PACKAGE, ref blueprint, _) => {
141 get_blueprint_schema(&POOL_PACKAGE_DEFINITION_V1_0, package_address, blueprint)
142 .map(Some)?
143 }
144 Invocation::Function(package_address @ TRANSACTION_PROCESSOR_PACKAGE, ref blueprint, _) => {
145 get_blueprint_schema(
146 &TRANSACTION_PROCESSOR_PACKAGE_DEFINITION,
147 package_address,
148 blueprint,
149 )
150 .map(Some)?
151 }
152 Invocation::Function(package_address @ METADATA_MODULE_PACKAGE, ref blueprint, _) => {
153 get_blueprint_schema(&METADATA_PACKAGE_DEFINITION, package_address, blueprint)
154 .map(Some)?
155 }
156 Invocation::Function(package_address @ ROYALTY_MODULE_PACKAGE, ref blueprint, _) => {
157 get_blueprint_schema(&ROYALTY_PACKAGE_DEFINITION, package_address, blueprint)
158 .map(Some)?
159 }
160 Invocation::Function(
161 package_address @ ROLE_ASSIGNMENT_MODULE_PACKAGE,
162 ref blueprint,
163 _,
164 ) => get_blueprint_schema(
165 &ROLE_ASSIGNMENT_PACKAGE_DEFINITION,
166 package_address,
167 blueprint,
168 )
169 .map(Some)?,
170 Invocation::Function(..) => None,
171 Invocation::Method(_, ModuleId::Main, _) | Invocation::DirectMethod(..) => {
172 match entity_type {
173 EntityType::GlobalPackage => {
174 PACKAGE_PACKAGE_DEFINITION.blueprints.get(PACKAGE_BLUEPRINT)
175 }
176
177 EntityType::GlobalConsensusManager => CONSENSUS_MANAGER_PACKAGE_DEFINITION
178 .blueprints
179 .get(CONSENSUS_MANAGER_BLUEPRINT),
180 EntityType::GlobalValidator => CONSENSUS_MANAGER_PACKAGE_DEFINITION
181 .blueprints
182 .get(VALIDATOR_BLUEPRINT),
183
184 EntityType::GlobalAccount
185 | EntityType::GlobalPreallocatedEd25519Account
186 | EntityType::GlobalPreallocatedSecp256k1Account => {
187 ACCOUNT_PACKAGE_DEFINITION.blueprints.get(ACCOUNT_BLUEPRINT)
188 }
189
190 EntityType::GlobalIdentity
191 | EntityType::GlobalPreallocatedEd25519Identity
192 | EntityType::GlobalPreallocatedSecp256k1Identity => IDENTITY_PACKAGE_DEFINITION
193 .blueprints
194 .get(IDENTITY_BLUEPRINT),
195
196 EntityType::GlobalAccessController => ACCESS_CONTROLLER_PACKAGE_DEFINITION_V1_0
197 .blueprints
198 .get(ACCESS_CONTROLLER_BLUEPRINT),
199
200 EntityType::GlobalOneResourcePool => POOL_PACKAGE_DEFINITION_V1_0
201 .blueprints
202 .get(ONE_RESOURCE_POOL_BLUEPRINT_IDENT),
203 EntityType::GlobalTwoResourcePool => POOL_PACKAGE_DEFINITION_V1_0
204 .blueprints
205 .get(TWO_RESOURCE_POOL_BLUEPRINT_IDENT),
206 EntityType::GlobalMultiResourcePool => POOL_PACKAGE_DEFINITION_V1_0
207 .blueprints
208 .get(MULTI_RESOURCE_POOL_BLUEPRINT_IDENT),
209
210 EntityType::GlobalTransactionTracker => TRANSACTION_TRACKER_PACKAGE_DEFINITION
211 .blueprints
212 .get(TRANSACTION_TRACKER_BLUEPRINT),
213
214 EntityType::GlobalFungibleResourceManager => RESOURCE_PACKAGE_DEFINITION
215 .blueprints
216 .get(FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT),
217 EntityType::GlobalNonFungibleResourceManager => RESOURCE_PACKAGE_DEFINITION
218 .blueprints
219 .get(NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT),
220 EntityType::InternalFungibleVault => RESOURCE_PACKAGE_DEFINITION
221 .blueprints
222 .get(FUNGIBLE_VAULT_BLUEPRINT),
223 EntityType::InternalNonFungibleVault => RESOURCE_PACKAGE_DEFINITION
224 .blueprints
225 .get(NON_FUNGIBLE_VAULT_BLUEPRINT),
226 EntityType::GlobalAccountLocker => LOCKER_PACKAGE_DEFINITION
227 .blueprints
228 .get(ACCOUNT_LOCKER_BLUEPRINT),
229 EntityType::GlobalGenericComponent
230 | EntityType::InternalGenericComponent
231 | EntityType::InternalKeyValueStore => None,
232 }
233 }
234 Invocation::Method(_, ModuleId::Metadata, _) => METADATA_PACKAGE_DEFINITION
235 .blueprints
236 .get(METADATA_BLUEPRINT),
237 Invocation::Method(_, ModuleId::RoleAssignment, _) => ROLE_ASSIGNMENT_PACKAGE_DEFINITION
238 .blueprints
239 .get(ROLE_ASSIGNMENT_BLUEPRINT),
240 Invocation::Method(_, ModuleId::Royalty, _) => ROYALTY_PACKAGE_DEFINITION
241 .blueprints
242 .get(COMPONENT_ROYALTY_BLUEPRINT),
243 };
244
245 if let Some(blueprint_schema) = blueprint_schema {
246 if let Some(function_schema) = blueprint_schema
247 .schema
248 .functions
249 .functions
250 .get(invocation.method())
251 {
252 if is_self_or_mut_self_receiver(&function_schema.receiver) && invocation.is_method()
253 || is_direct_access_receiver(&function_schema.receiver)
254 && invocation.is_direct_access_method()
255 || is_function_receiver(&function_schema.receiver) && invocation.is_function()
256 {
257 Ok(Some((
258 function_schema.input,
259 blueprint_schema.schema.schema.v1(),
260 )))
261 } else {
262 Err(InstructionSchemaValidationError::InvalidReceiver)
263 }
264 } else {
265 Err(InstructionSchemaValidationError::MethodNotFound(
266 invocation.method().to_owned(),
267 ))
268 }
269 } else {
270 Ok(None)
271 }
272}
273
274fn is_self_or_mut_self_receiver(receiver: &Option<ReceiverInfo>) -> bool {
275 if let Some(ref receiver) = receiver {
276 matches!(
277 (&receiver.receiver, receiver.ref_types),
278 (Receiver::SelfRef | Receiver::SelfRefMut, RefTypes::NORMAL)
279 )
280 } else {
281 false
282 }
283}
284
285fn is_direct_access_receiver(receiver: &Option<ReceiverInfo>) -> bool {
286 if let Some(ref receiver) = receiver {
287 matches!(
288 (&receiver.receiver, receiver.ref_types),
289 (
290 Receiver::SelfRef | Receiver::SelfRefMut,
291 RefTypes::DIRECT_ACCESS
292 )
293 )
294 } else {
295 false
296 }
297}
298
299fn is_function_receiver(receiver: &Option<ReceiverInfo>) -> bool {
300 receiver.is_none()
301}
302
303#[derive(Clone, Debug)]
304enum Invocation {
305 DirectMethod(InternalAddress, String),
306 Method(GlobalAddress, ModuleId, String),
307 Function(PackageAddress, String, String),
308}
309
310impl Invocation {
311 fn method(&self) -> &str {
312 match self {
313 Self::DirectMethod(_, method) => method,
314 Self::Method(_, _, method) => method,
315 Self::Function(_, _, method) => method,
316 }
317 }
318
319 fn entity_type(&self) -> EntityType {
320 match self {
321 Self::DirectMethod(address, ..) => address.as_node_id().entity_type().unwrap(),
322 Self::Method(address, ..) => address.as_node_id().entity_type().unwrap(),
323 Self::Function(address, ..) => address.as_node_id().entity_type().unwrap(),
324 }
325 }
326
327 fn is_function(&self) -> bool {
328 match self {
329 Self::Function(..) => true,
330 Self::Method(..) | Self::DirectMethod(..) => false,
331 }
332 }
333
334 fn is_method(&self) -> bool {
335 match self {
336 Self::Method(..) | Self::DirectMethod(..) => true,
337 Self::Function(..) => false,
338 }
339 }
340
341 fn is_direct_access_method(&self) -> bool {
342 match self {
343 Self::DirectMethod(..) => true,
344 Self::Function(..) | Self::Method(..) => false,
345 }
346 }
347}
348
349#[derive(Clone, Debug)]
350pub struct LocatedInstructionSchemaValidationError {
351 pub instruction_index: usize,
352 pub cause: InstructionSchemaValidationError,
353}
354
355#[derive(Clone, Debug)]
356pub enum InstructionSchemaValidationError {
357 MethodNotFound(String),
358 SchemaValidationError(String),
359
360 InvalidAddress(GlobalAddress),
361 InvalidBlueprint(PackageAddress, String),
362 InvalidReceiver,
363}