radix_engine_interface/blueprints/resource/
proof_rule.rs1use crate::blueprints::resource::CompositeRequirement::{AllOf, AnyOf};
2use crate::internal_prelude::*;
3
4use radix_common::define_untyped_manifest_type_wrapper;
5
6#[cfg_attr(
7 feature = "fuzzing",
8 derive(::arbitrary::Arbitrary, ::serde::Serialize, ::serde::Deserialize)
9)]
10#[derive(
11 Debug,
12 Clone,
13 PartialEq,
14 Eq,
15 Hash,
16 Ord,
17 PartialOrd,
18 ManifestSbor,
19 ScryptoCategorize,
20 ScryptoEncode,
21 ScryptoDecode,
22)]
23pub enum ResourceOrNonFungible {
24 NonFungible(NonFungibleGlobalId),
25 Resource(ResourceAddress),
26}
27
28#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd, ManifestSbor, ScryptoDescribe)]
29pub enum ManifestResourceOrNonFungible {
30 NonFungible(NonFungibleGlobalId),
31 Resource(ManifestResourceAddress),
32}
33
34impl From<ResourceOrNonFungible> for ManifestResourceOrNonFungible {
35 fn from(value: ResourceOrNonFungible) -> Self {
36 match value {
37 ResourceOrNonFungible::NonFungible(non_fungible_global_id) => {
38 Self::NonFungible(non_fungible_global_id)
39 }
40 ResourceOrNonFungible::Resource(resource_address) => {
41 Self::Resource(ManifestResourceAddress::Static(resource_address))
42 }
43 }
44 }
45}
46
47impl Describe<ScryptoCustomTypeKind> for ResourceOrNonFungible {
48 const TYPE_ID: RustTypeId =
49 RustTypeId::WellKnown(well_known_scrypto_custom_types::RESOURCE_OR_NON_FUNGIBLE_TYPE);
50
51 fn type_data() -> ScryptoTypeData<RustTypeId> {
52 well_known_scrypto_custom_types::resource_or_non_fungible_type_data()
53 }
54}
55
56impl From<NonFungibleGlobalId> for ResourceOrNonFungible {
57 fn from(non_fungible_global_id: NonFungibleGlobalId) -> Self {
58 ResourceOrNonFungible::NonFungible(non_fungible_global_id)
59 }
60}
61
62impl From<ResourceAddress> for ResourceOrNonFungible {
63 fn from(resource_address: ResourceAddress) -> Self {
64 ResourceOrNonFungible::Resource(resource_address)
65 }
66}
67
68pub struct ResourceOrNonFungibleList {
69 list: Vec<ResourceOrNonFungible>,
70}
71
72impl<T> From<Vec<T>> for ResourceOrNonFungibleList
73where
74 T: Into<ResourceOrNonFungible>,
75{
76 fn from(addresses: Vec<T>) -> Self {
77 ResourceOrNonFungibleList {
78 list: addresses.into_iter().map(|a| a.into()).collect(),
79 }
80 }
81}
82
83#[cfg_attr(
85 feature = "fuzzing",
86 derive(::arbitrary::Arbitrary, ::serde::Serialize, ::serde::Deserialize)
87)]
88#[derive(
89 Debug,
90 Clone,
91 PartialEq,
92 Eq,
93 Hash,
94 Ord,
95 PartialOrd,
96 ManifestSbor,
97 ScryptoCategorize,
98 ScryptoEncode,
99 ScryptoDecode,
100)]
101pub enum BasicRequirement {
102 Require(ResourceOrNonFungible),
103 AmountOf(Decimal, ResourceAddress),
104 CountOf(u8, Vec<ResourceOrNonFungible>),
105 AllOf(Vec<ResourceOrNonFungible>),
106 AnyOf(Vec<ResourceOrNonFungible>),
107}
108
109impl Describe<ScryptoCustomTypeKind> for BasicRequirement {
110 const TYPE_ID: RustTypeId =
111 RustTypeId::WellKnown(well_known_scrypto_custom_types::BASIC_REQUIREMENT_TYPE);
112
113 fn type_data() -> ScryptoTypeData<RustTypeId> {
114 well_known_scrypto_custom_types::basic_requirement_type_data()
115 }
116}
117
118impl From<ResourceAddress> for CompositeRequirement {
119 fn from(resource_address: ResourceAddress) -> Self {
120 CompositeRequirement::BasicRequirement(BasicRequirement::Require(resource_address.into()))
121 }
122}
123
124impl From<NonFungibleGlobalId> for CompositeRequirement {
125 fn from(id: NonFungibleGlobalId) -> Self {
126 CompositeRequirement::BasicRequirement(BasicRequirement::Require(id.into()))
127 }
128}
129
130impl From<ResourceOrNonFungible> for CompositeRequirement {
131 fn from(resource_or_non_fungible: ResourceOrNonFungible) -> Self {
132 CompositeRequirement::BasicRequirement(BasicRequirement::Require(resource_or_non_fungible))
133 }
134}
135
136define_untyped_manifest_type_wrapper!(
137 BasicRequirement => ManifestBasicRequirement(EnumVariantValue<ManifestCustomValueKind, ManifestCustomValue>)
138);
139
140#[cfg_attr(
141 feature = "fuzzing",
142 derive(::arbitrary::Arbitrary, ::serde::Serialize, ::serde::Deserialize)
143)]
144#[derive(
145 Debug,
146 Clone,
147 PartialEq,
148 Eq,
149 Hash,
150 Ord,
151 PartialOrd,
152 ManifestSbor,
153 ScryptoCategorize,
154 ScryptoEncode,
155 ScryptoDecode,
156)]
157pub enum CompositeRequirement {
158 BasicRequirement(BasicRequirement),
159 AnyOf(Vec<CompositeRequirement>),
160 AllOf(Vec<CompositeRequirement>),
161}
162
163impl Describe<ScryptoCustomTypeKind> for CompositeRequirement {
164 const TYPE_ID: RustTypeId =
165 RustTypeId::WellKnown(well_known_scrypto_custom_types::COMPOSITE_REQUIREMENT_TYPE);
166
167 fn type_data() -> ScryptoTypeData<RustTypeId> {
168 well_known_scrypto_custom_types::composite_requirement_type_data()
169 }
170}
171
172impl CompositeRequirement {
173 pub fn or(self, other: CompositeRequirement) -> Self {
174 match self {
175 CompositeRequirement::AnyOf(mut rules) => {
176 rules.push(other);
177 AnyOf(rules)
178 }
179 _ => AnyOf(vec![self, other]),
180 }
181 }
182
183 pub fn and(self, other: CompositeRequirement) -> Self {
184 match self {
185 CompositeRequirement::AllOf(mut rules) => {
186 rules.push(other);
187 AllOf(rules)
188 }
189 _ => AllOf(vec![self, other]),
190 }
191 }
192}
193
194define_untyped_manifest_type_wrapper!(
195 CompositeRequirement => ManifestCompositeRequirement(EnumVariantValue<ManifestCustomValueKind, ManifestCustomValue>)
196);
197
198pub fn package_of_direct_caller(package: PackageAddress) -> ResourceOrNonFungible {
200 ResourceOrNonFungible::NonFungible(NonFungibleGlobalId::package_of_direct_caller_badge(package))
201}
202
203pub fn global_caller(global_caller: impl Into<GlobalCaller>) -> ResourceOrNonFungible {
207 ResourceOrNonFungible::NonFungible(NonFungibleGlobalId::global_caller_badge(global_caller))
208}
209
210pub fn signature(public_key: impl HasPublicKeyHash) -> ResourceOrNonFungible {
212 ResourceOrNonFungible::NonFungible(NonFungibleGlobalId::from_public_key(public_key))
213}
214
215pub fn system_execution(transaction_type: SystemExecution) -> NonFungibleGlobalId {
217 transaction_type.into()
218}
219
220pub fn require<T>(required: T) -> CompositeRequirement
221where
222 T: Into<CompositeRequirement>,
223{
224 required.into()
225}
226
227pub fn require_any_of<T>(resources: T) -> CompositeRequirement
228where
229 T: Into<ResourceOrNonFungibleList>,
230{
231 let list: ResourceOrNonFungibleList = resources.into();
232 CompositeRequirement::BasicRequirement(BasicRequirement::AnyOf(list.list))
233}
234
235pub fn require_all_of<T>(resources: T) -> CompositeRequirement
236where
237 T: Into<ResourceOrNonFungibleList>,
238{
239 let list: ResourceOrNonFungibleList = resources.into();
240 CompositeRequirement::BasicRequirement(BasicRequirement::AllOf(list.list))
241}
242
243pub fn require_n_of<C, T>(count: C, resources: T) -> CompositeRequirement
244where
245 C: Into<u8>,
246 T: Into<ResourceOrNonFungibleList>,
247{
248 let list: ResourceOrNonFungibleList = resources.into();
249 CompositeRequirement::BasicRequirement(BasicRequirement::CountOf(count.into(), list.list))
250}
251
252pub fn require_amount<D, T>(amount: D, resource: T) -> CompositeRequirement
253where
254 D: Into<Decimal>,
255 T: Into<ResourceAddress>,
256{
257 CompositeRequirement::BasicRequirement(BasicRequirement::AmountOf(
258 amount.into(),
259 resource.into(),
260 ))
261}
262
263#[cfg_attr(
264 feature = "fuzzing",
265 derive(::arbitrary::Arbitrary, ::serde::Serialize, ::serde::Deserialize)
266)]
267#[derive(
268 Debug,
269 Clone,
270 PartialEq,
271 Eq,
272 Hash,
273 Ord,
274 PartialOrd,
275 ManifestSbor,
276 ScryptoCategorize,
277 ScryptoEncode,
278 ScryptoDecode,
279)]
280pub enum AccessRule {
281 AllowAll,
282 DenyAll,
283 Protected(CompositeRequirement),
284}
285
286impl Describe<ScryptoCustomTypeKind> for AccessRule {
287 const TYPE_ID: RustTypeId =
288 RustTypeId::WellKnown(well_known_scrypto_custom_types::ACCESS_RULE_TYPE);
289
290 fn type_data() -> ScryptoTypeData<RustTypeId> {
291 well_known_scrypto_custom_types::access_rule_type_data()
292 }
293}
294
295impl From<CompositeRequirement> for AccessRule {
296 fn from(value: CompositeRequirement) -> Self {
297 AccessRule::Protected(value)
298 }
299}
300
301define_untyped_manifest_type_wrapper!(
302 AccessRule => ManifestAccessRule(EnumVariantValue<ManifestCustomValueKind, ManifestCustomValue>)
303);
304
305pub trait AccessRuleVisitor {
306 type Error;
307 fn visit(&mut self, node: &CompositeRequirement, depth: usize) -> Result<(), Self::Error>;
308}
309
310impl AccessRule {
311 pub fn dfs_traverse_nodes<V: AccessRuleVisitor>(
312 &self,
313 visitor: &mut V,
314 ) -> Result<(), V::Error> {
315 match self {
316 AccessRule::Protected(node) => node.dfs_traverse_recursive(visitor, 0),
317 _ => Ok(()),
318 }
319 }
320}
321
322impl CompositeRequirement {
323 fn dfs_traverse_recursive<V: AccessRuleVisitor>(
324 &self,
325 visitor: &mut V,
326 depth: usize,
327 ) -> Result<(), V::Error> {
328 visitor.visit(self, depth)?;
329
330 match self {
331 CompositeRequirement::BasicRequirement(..) => {}
332 CompositeRequirement::AnyOf(nodes) | CompositeRequirement::AllOf(nodes) => {
333 for node in nodes {
334 node.dfs_traverse_recursive(visitor, depth + 1)?;
335 }
336 }
337 }
338
339 Ok(())
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346 use radix_common::prelude::*;
347
348 #[test]
349 fn require_signature_secp256k1() {
350 let private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
351 let public_key = private_key.public_key();
352
353 let r1 = rule!(require(NonFungibleGlobalId::from_public_key(public_key)));
354 let r2 = rule!(require(signature(public_key)));
355
356 assert_eq!(r1, r2);
357 }
358
359 #[test]
360 fn require_signature_ed25519() {
361 let private_key = Ed25519PrivateKey::from_u64(1).unwrap();
362 let public_key = private_key.public_key();
363
364 let r1 = rule!(require(NonFungibleGlobalId::from_public_key(public_key)));
365 let r2 = rule!(require(signature(public_key)));
366
367 assert_eq!(r1, r2);
368 }
369
370 #[test]
371 fn access_rule_can_be_converted_to_manifest_access_rule() {
372 let _ = ManifestAccessRule::from(rule!(require(XRD) && require(SYSTEM_EXECUTION_RESOURCE)));
373 }
374
375 #[test]
376 fn sbor_encoding_of_access_rule_and_manifest_access_rule_are_the_same() {
377 let rule = rule!(require(XRD) && require(SYSTEM_EXECUTION_RESOURCE));
379
380 let encoded_access_rule = scrypto_encode(&rule).unwrap();
382 let encoded_manifest_access_rule =
383 manifest_encode(&ManifestAccessRule::from(rule)).unwrap();
384
385 let (local_type_id, versioned_schema) =
387 generate_full_schema_from_single_type::<AccessRule, ScryptoCustomSchema>();
388 validate_payload_against_schema::<ScryptoCustomExtension, _>(
389 &encoded_access_rule,
390 versioned_schema.v1(),
391 local_type_id,
392 &(),
393 SCRYPTO_SBOR_V1_MAX_DEPTH,
394 )
395 .expect("Scrypto access rule payload should match AccessRule schema");
396 validate_payload_against_schema::<ManifestCustomExtension, _>(
397 &encoded_manifest_access_rule,
398 versioned_schema.v1(),
399 local_type_id,
400 &(),
401 MANIFEST_SBOR_V1_MAX_DEPTH,
402 )
403 .expect("Manifest access rule payload should match AccessRule schema");
404
405 let (local_type_id, versioned_schema) =
406 generate_full_schema_from_single_type::<ManifestAccessRule, ScryptoCustomSchema>();
407 validate_payload_against_schema::<ScryptoCustomExtension, _>(
408 &encoded_access_rule,
409 versioned_schema.v1(),
410 local_type_id,
411 &(),
412 SCRYPTO_SBOR_V1_MAX_DEPTH,
413 )
414 .expect("Scrypto access rule payload should match Manifest schema");
415 validate_payload_against_schema::<ManifestCustomExtension, _>(
416 &encoded_manifest_access_rule,
417 versioned_schema.v1(),
418 local_type_id,
419 &(),
420 MANIFEST_SBOR_V1_MAX_DEPTH,
421 )
422 .expect("Manifest access rule payload should match Manifest schema");
423 }
424
425 #[test]
426 fn non_enums_cant_be_decoded_as_a_manifest_access_rule() {
427 let enum_value = ManifestValue::U8 { value: 1 };
429 let encoded = manifest_encode(&enum_value).unwrap();
430
431 let manifest_access_rule = manifest_decode::<ManifestAccessRule>(&encoded);
433
434 manifest_access_rule.expect_err("Expected decoding to fail");
436 }
437}