radix_engine_interface/blueprints/resource/
proof_rule.rs

1use crate::blueprints::resource::CompositeRequirement::{AllOf, AnyOf};
2use crate::internal_prelude::*;
3#[cfg(feature = "fuzzing")]
4use arbitrary::Arbitrary;
5
6#[cfg_attr(
7    feature = "fuzzing",
8    derive(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)]
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/// Resource Proof Rules
84#[cfg_attr(
85    feature = "fuzzing",
86    derive(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
136#[cfg_attr(
137    feature = "fuzzing",
138    derive(Arbitrary, serde::Serialize, serde::Deserialize)
139)]
140#[derive(
141    Debug,
142    Clone,
143    PartialEq,
144    Eq,
145    Hash,
146    Ord,
147    PartialOrd,
148    ManifestSbor,
149    ScryptoCategorize,
150    ScryptoEncode,
151    ScryptoDecode,
152)]
153pub enum CompositeRequirement {
154    BasicRequirement(BasicRequirement),
155    AnyOf(Vec<CompositeRequirement>),
156    AllOf(Vec<CompositeRequirement>),
157}
158
159impl Describe<ScryptoCustomTypeKind> for CompositeRequirement {
160    const TYPE_ID: RustTypeId =
161        RustTypeId::WellKnown(well_known_scrypto_custom_types::COMPOSITE_REQUIREMENT_TYPE);
162
163    fn type_data() -> ScryptoTypeData<RustTypeId> {
164        well_known_scrypto_custom_types::composite_requirement_type_data()
165    }
166}
167
168impl CompositeRequirement {
169    pub fn or(self, other: CompositeRequirement) -> Self {
170        match self {
171            CompositeRequirement::AnyOf(mut rules) => {
172                rules.push(other);
173                AnyOf(rules)
174            }
175            _ => AnyOf(vec![self, other]),
176        }
177    }
178
179    pub fn and(self, other: CompositeRequirement) -> Self {
180        match self {
181            CompositeRequirement::AllOf(mut rules) => {
182                rules.push(other);
183                AllOf(rules)
184            }
185            _ => AllOf(vec![self, other]),
186        }
187    }
188}
189
190/// A requirement for the immediate caller's package to equal the given package.
191pub fn package_of_direct_caller(package: PackageAddress) -> ResourceOrNonFungible {
192    ResourceOrNonFungible::NonFungible(NonFungibleGlobalId::package_of_direct_caller_badge(package))
193}
194
195/// A requirement for the global ancestor of the actor who made the latest global call to either be:
196/// * The main module of the given global component (pass a `ComponentAddress` or `GlobalAddress`)
197/// * A package function on the given blueprint (pass `(PackageAddress, String)` or `Blueprint`)
198pub fn global_caller(global_caller: impl Into<GlobalCaller>) -> ResourceOrNonFungible {
199    ResourceOrNonFungible::NonFungible(NonFungibleGlobalId::global_caller_badge(global_caller))
200}
201
202/// A requirement for the transaction to be signed using a specific key.
203pub fn signature(public_key: &impl HasPublicKeyHash) -> ResourceOrNonFungible {
204    ResourceOrNonFungible::NonFungible(NonFungibleGlobalId::from_public_key(public_key))
205}
206
207/// A requirement for the transaction to be a system transaction.
208pub fn system_execution(transaction_type: SystemExecution) -> NonFungibleGlobalId {
209    transaction_type.into()
210}
211
212pub fn require<T>(required: T) -> CompositeRequirement
213where
214    T: Into<CompositeRequirement>,
215{
216    required.into()
217}
218
219pub fn require_any_of<T>(resources: T) -> CompositeRequirement
220where
221    T: Into<ResourceOrNonFungibleList>,
222{
223    let list: ResourceOrNonFungibleList = resources.into();
224    CompositeRequirement::BasicRequirement(BasicRequirement::AnyOf(list.list))
225}
226
227pub fn require_all_of<T>(resources: T) -> CompositeRequirement
228where
229    T: Into<ResourceOrNonFungibleList>,
230{
231    let list: ResourceOrNonFungibleList = resources.into();
232    CompositeRequirement::BasicRequirement(BasicRequirement::AllOf(list.list))
233}
234
235pub fn require_n_of<C, T>(count: C, resources: T) -> CompositeRequirement
236where
237    C: Into<u8>,
238    T: Into<ResourceOrNonFungibleList>,
239{
240    let list: ResourceOrNonFungibleList = resources.into();
241    CompositeRequirement::BasicRequirement(BasicRequirement::CountOf(count.into(), list.list))
242}
243
244pub fn require_amount<D, T>(amount: D, resource: T) -> CompositeRequirement
245where
246    D: Into<Decimal>,
247    T: Into<ResourceAddress>,
248{
249    CompositeRequirement::BasicRequirement(BasicRequirement::AmountOf(
250        amount.into(),
251        resource.into(),
252    ))
253}
254
255#[cfg_attr(
256    feature = "fuzzing",
257    derive(Arbitrary, serde::Serialize, serde::Deserialize)
258)]
259#[derive(
260    Debug,
261    Clone,
262    PartialEq,
263    Eq,
264    Hash,
265    Ord,
266    PartialOrd,
267    ManifestSbor,
268    ScryptoCategorize,
269    ScryptoEncode,
270    ScryptoDecode,
271)]
272pub enum AccessRule {
273    AllowAll,
274    DenyAll,
275    Protected(CompositeRequirement),
276}
277
278impl Describe<ScryptoCustomTypeKind> for AccessRule {
279    const TYPE_ID: RustTypeId =
280        RustTypeId::WellKnown(well_known_scrypto_custom_types::ACCESS_RULE_TYPE);
281
282    fn type_data() -> ScryptoTypeData<RustTypeId> {
283        well_known_scrypto_custom_types::access_rule_type_data()
284    }
285}
286
287impl From<CompositeRequirement> for AccessRule {
288    fn from(value: CompositeRequirement) -> Self {
289        AccessRule::Protected(value)
290    }
291}
292
293pub trait AccessRuleVisitor {
294    type Error;
295    fn visit(&mut self, node: &CompositeRequirement, depth: usize) -> Result<(), Self::Error>;
296}
297
298impl AccessRule {
299    pub fn dfs_traverse_nodes<V: AccessRuleVisitor>(
300        &self,
301        visitor: &mut V,
302    ) -> Result<(), V::Error> {
303        match self {
304            AccessRule::Protected(node) => node.dfs_traverse_recursive(visitor, 0),
305            _ => Ok(()),
306        }
307    }
308}
309
310impl CompositeRequirement {
311    fn dfs_traverse_recursive<V: AccessRuleVisitor>(
312        &self,
313        visitor: &mut V,
314        depth: usize,
315    ) -> Result<(), V::Error> {
316        visitor.visit(self, depth)?;
317
318        match self {
319            CompositeRequirement::BasicRequirement(..) => {}
320            CompositeRequirement::AnyOf(nodes) | CompositeRequirement::AllOf(nodes) => {
321                for node in nodes {
322                    node.dfs_traverse_recursive(visitor, depth + 1)?;
323                }
324            }
325        }
326
327        Ok(())
328    }
329}
330
331#[cfg(test)]
332mod tests {
333    use super::*;
334    use radix_common::prelude::*;
335
336    #[test]
337    fn require_signature_secp256k1() {
338        let private_key = Secp256k1PrivateKey::from_u64(1).unwrap();
339        let public_key = private_key.public_key();
340
341        let r1 = rule!(require(NonFungibleGlobalId::from_public_key(&public_key)));
342        let r2 = rule!(require(signature(&public_key)));
343
344        assert_eq!(r1, r2);
345    }
346
347    #[test]
348    fn require_signature_ed25519() {
349        let private_key = Ed25519PrivateKey::from_u64(1).unwrap();
350        let public_key = private_key.public_key();
351
352        let r1 = rule!(require(NonFungibleGlobalId::from_public_key(&public_key)));
353        let r2 = rule!(require(signature(&public_key)));
354
355        assert_eq!(r1, r2);
356    }
357}