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.
98fn 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}