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
94fn get_arguments_schema<'s>(
99 invocation: Invocation,
100) -> Result<
101 Option<(TypeRef<LocalTypeId>, &'s Schema<ScryptoCustomSchema>)>,
102 InstructionSchemaValidationError,
103> {
104 let entity_type = invocation.entity_type();
105
106 let blueprint_schema = match invocation {
107 Invocation::Function(package_address @ PACKAGE_PACKAGE, ref blueprint, _) => {
108 get_blueprint_schema(&PACKAGE_PACKAGE_DEFINITION, package_address, &blueprint)
109 .map(Some)?
110 }
111 Invocation::Function(package_address @ RESOURCE_PACKAGE, ref blueprint, _) => {
112 get_blueprint_schema(&RESOURCE_PACKAGE_DEFINITION, package_address, blueprint)
113 .map(Some)?
114 }
115 Invocation::Function(package_address @ ACCOUNT_PACKAGE, ref blueprint, _) => {
116 get_blueprint_schema(&ACCOUNT_PACKAGE_DEFINITION, package_address, blueprint)
117 .map(Some)?
118 }
119 Invocation::Function(package_address @ IDENTITY_PACKAGE, ref blueprint, _) => {
120 get_blueprint_schema(&IDENTITY_PACKAGE_DEFINITION, package_address, blueprint)
121 .map(Some)?
122 }
123 Invocation::Function(package_address @ CONSENSUS_MANAGER_PACKAGE, ref blueprint, _) => {
124 get_blueprint_schema(
125 &CONSENSUS_MANAGER_PACKAGE_DEFINITION,
126 package_address,
127 blueprint,
128 )
129 .map(Some)?
130 }
131 Invocation::Function(package_address @ ACCESS_CONTROLLER_PACKAGE, ref blueprint, _) => {
132 get_blueprint_schema(
133 &ACCESS_CONTROLLER_PACKAGE_DEFINITION_V1_0,
134 package_address,
135 blueprint,
136 )
137 .map(Some)?
138 }
139 Invocation::Function(package_address @ POOL_PACKAGE, ref blueprint, _) => {
140 get_blueprint_schema(&POOL_PACKAGE_DEFINITION_V1_0, package_address, blueprint)
141 .map(Some)?
142 }
143 Invocation::Function(package_address @ TRANSACTION_PROCESSOR_PACKAGE, ref blueprint, _) => {
144 get_blueprint_schema(
145 &TRANSACTION_PROCESSOR_PACKAGE_DEFINITION,
146 package_address,
147 blueprint,
148 )
149 .map(Some)?
150 }
151 Invocation::Function(package_address @ METADATA_MODULE_PACKAGE, ref blueprint, _) => {
152 get_blueprint_schema(&METADATA_PACKAGE_DEFINITION, package_address, blueprint)
153 .map(Some)?
154 }
155 Invocation::Function(package_address @ ROYALTY_MODULE_PACKAGE, ref blueprint, _) => {
156 get_blueprint_schema(&ROYALTY_PACKAGE_DEFINITION, package_address, blueprint)
157 .map(Some)?
158 }
159 Invocation::Function(
160 package_address @ ROLE_ASSIGNMENT_MODULE_PACKAGE,
161 ref blueprint,
162 _,
163 ) => get_blueprint_schema(
164 &ROLE_ASSIGNMENT_PACKAGE_DEFINITION,
165 package_address,
166 blueprint,
167 )
168 .map(Some)?,
169 Invocation::Function(..) => None,
170 Invocation::Method(_, ModuleId::Main, _) | Invocation::DirectMethod(..) => {
171 match entity_type {
172 EntityType::GlobalPackage => {
173 PACKAGE_PACKAGE_DEFINITION.blueprints.get(PACKAGE_BLUEPRINT)
174 }
175
176 EntityType::GlobalConsensusManager => CONSENSUS_MANAGER_PACKAGE_DEFINITION
177 .blueprints
178 .get(CONSENSUS_MANAGER_BLUEPRINT),
179 EntityType::GlobalValidator => CONSENSUS_MANAGER_PACKAGE_DEFINITION
180 .blueprints
181 .get(VALIDATOR_BLUEPRINT),
182
183 EntityType::GlobalAccount
184 | EntityType::GlobalPreallocatedEd25519Account
185 | EntityType::GlobalPreallocatedSecp256k1Account => {
186 ACCOUNT_PACKAGE_DEFINITION.blueprints.get(ACCOUNT_BLUEPRINT)
187 }
188
189 EntityType::GlobalIdentity
190 | EntityType::GlobalPreallocatedEd25519Identity
191 | EntityType::GlobalPreallocatedSecp256k1Identity => IDENTITY_PACKAGE_DEFINITION
192 .blueprints
193 .get(IDENTITY_BLUEPRINT),
194
195 EntityType::GlobalAccessController => ACCESS_CONTROLLER_PACKAGE_DEFINITION_V1_0
196 .blueprints
197 .get(ACCESS_CONTROLLER_BLUEPRINT),
198
199 EntityType::GlobalOneResourcePool => POOL_PACKAGE_DEFINITION_V1_0
200 .blueprints
201 .get(ONE_RESOURCE_POOL_BLUEPRINT_IDENT),
202 EntityType::GlobalTwoResourcePool => POOL_PACKAGE_DEFINITION_V1_0
203 .blueprints
204 .get(TWO_RESOURCE_POOL_BLUEPRINT_IDENT),
205 EntityType::GlobalMultiResourcePool => POOL_PACKAGE_DEFINITION_V1_0
206 .blueprints
207 .get(MULTI_RESOURCE_POOL_BLUEPRINT_IDENT),
208
209 EntityType::GlobalTransactionTracker => TRANSACTION_TRACKER_PACKAGE_DEFINITION
210 .blueprints
211 .get(TRANSACTION_TRACKER_BLUEPRINT),
212
213 EntityType::GlobalFungibleResourceManager => RESOURCE_PACKAGE_DEFINITION
214 .blueprints
215 .get(FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT),
216 EntityType::GlobalNonFungibleResourceManager => RESOURCE_PACKAGE_DEFINITION
217 .blueprints
218 .get(NON_FUNGIBLE_RESOURCE_MANAGER_BLUEPRINT),
219 EntityType::InternalFungibleVault => RESOURCE_PACKAGE_DEFINITION
220 .blueprints
221 .get(FUNGIBLE_VAULT_BLUEPRINT),
222 EntityType::InternalNonFungibleVault => RESOURCE_PACKAGE_DEFINITION
223 .blueprints
224 .get(NON_FUNGIBLE_VAULT_BLUEPRINT),
225 EntityType::GlobalAccountLocker => LOCKER_PACKAGE_DEFINITION
226 .blueprints
227 .get(ACCOUNT_LOCKER_BLUEPRINT),
228 EntityType::GlobalGenericComponent
229 | EntityType::InternalGenericComponent
230 | EntityType::InternalKeyValueStore => None,
231 }
232 }
233 Invocation::Method(_, ModuleId::Metadata, _) => METADATA_PACKAGE_DEFINITION
234 .blueprints
235 .get(METADATA_BLUEPRINT),
236 Invocation::Method(_, ModuleId::RoleAssignment, _) => ROLE_ASSIGNMENT_PACKAGE_DEFINITION
237 .blueprints
238 .get(ROLE_ASSIGNMENT_BLUEPRINT),
239 Invocation::Method(_, ModuleId::Royalty, _) => ROYALTY_PACKAGE_DEFINITION
240 .blueprints
241 .get(COMPONENT_ROYALTY_BLUEPRINT),
242 };
243
244 if let Some(blueprint_schema) = blueprint_schema {
245 if let Some(function_schema) = blueprint_schema
246 .schema
247 .functions
248 .functions
249 .get(invocation.method())
250 {
251 if is_self_or_mut_self_receiver(&function_schema.receiver) && invocation.is_method()
252 || is_direct_access_receiver(&function_schema.receiver)
253 && invocation.is_direct_access_method()
254 || is_function_receiver(&function_schema.receiver) && invocation.is_function()
255 {
256 Ok(Some((
257 function_schema.input.clone(),
258 blueprint_schema.schema.schema.v1(),
259 )))
260 } else {
261 Err(InstructionSchemaValidationError::InvalidReceiver)
262 }
263 } else {
264 Err(InstructionSchemaValidationError::MethodNotFound(
265 invocation.method().to_owned(),
266 ))
267 }
268 } else {
269 Ok(None)
270 }
271}
272
273fn is_self_or_mut_self_receiver(receiver: &Option<ReceiverInfo>) -> bool {
274 if let Some(ref receiver) = receiver {
275 match (&receiver.receiver, receiver.ref_types) {
276 (Receiver::SelfRef | Receiver::SelfRefMut, RefTypes::NORMAL) => true,
277 _ => false,
278 }
279 } else {
280 false
281 }
282}
283
284fn is_direct_access_receiver(receiver: &Option<ReceiverInfo>) -> bool {
285 if let Some(ref receiver) = receiver {
286 match (&receiver.receiver, receiver.ref_types) {
287 (Receiver::SelfRef | Receiver::SelfRefMut, RefTypes::DIRECT_ACCESS) => true,
288 _ => false,
289 }
290 } else {
291 false
292 }
293}
294
295fn is_function_receiver(receiver: &Option<ReceiverInfo>) -> bool {
296 receiver.is_none()
297}
298
299#[derive(Clone, Debug)]
300enum Invocation {
301 DirectMethod(InternalAddress, String),
302 Method(GlobalAddress, ModuleId, String),
303 Function(PackageAddress, String, String),
304}
305
306impl Invocation {
307 fn method(&self) -> &str {
308 match self {
309 Self::DirectMethod(_, method) => method,
310 Self::Method(_, _, method) => method,
311 Self::Function(_, _, method) => method,
312 }
313 }
314
315 fn entity_type(&self) -> EntityType {
316 match self {
317 Self::DirectMethod(address, ..) => address.as_node_id().entity_type().unwrap(),
318 Self::Method(address, ..) => address.as_node_id().entity_type().unwrap(),
319 Self::Function(address, ..) => address.as_node_id().entity_type().unwrap(),
320 }
321 }
322
323 fn is_function(&self) -> bool {
324 match self {
325 Self::Function(..) => true,
326 Self::Method(..) | Self::DirectMethod(..) => false,
327 }
328 }
329
330 fn is_method(&self) -> bool {
331 match self {
332 Self::Method(..) | Self::DirectMethod(..) => true,
333 Self::Function(..) => false,
334 }
335 }
336
337 fn is_direct_access_method(&self) -> bool {
338 match self {
339 Self::DirectMethod(..) => true,
340 Self::Function(..) | Self::Method(..) => false,
341 }
342 }
343}
344
345#[derive(Clone, Debug)]
346pub struct LocatedInstructionSchemaValidationError {
347 pub instruction_index: usize,
348 pub cause: InstructionSchemaValidationError,
349}
350
351#[derive(Clone, Debug)]
352pub enum InstructionSchemaValidationError {
353 MethodNotFound(String),
354 SchemaValidationError(String),
355
356 InvalidAddress(GlobalAddress),
357 InvalidBlueprint(PackageAddress, String),
358 InvalidReceiver,
359}