radix_engine/utils/
native_blueprint_call_validator.rs

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/// * An `Err` is returned if something is invalid about the arguments given to this method. As an
95///   example: they've specified a method on the account blueprint that does not exist.
96/// * A `None` is returned if the schema for this type is not well know. As an example: arbitrary
97///   packages.
98#[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}