cosmwasm_vm/
capabilities.rs

1use std::collections::HashSet;
2
3use crate::static_analysis::ExportInfo;
4
5const REQUIRES_PREFIX: &str = "requires_";
6
7/// Takes a comma-separated string, splits it by commas, removes empty elements and returns a set of capabilities.
8/// This can be used e.g. to initialize the cache.
9pub fn capabilities_from_csv(csv: &str) -> HashSet<String> {
10    csv.split(',')
11        .map(|x| x.trim().to_string())
12        .filter(|f| !f.is_empty())
13        .collect()
14}
15
16/// Implementation for check_wasm, based on static analysis of the bytecode.
17/// This is used for code upload, to perform check before compiling the Wasm.
18pub fn required_capabilities_from_module(module: impl ExportInfo) -> HashSet<String> {
19    module
20        .exported_function_names(Some(REQUIRES_PREFIX))
21        .into_iter()
22        .filter_map(|name| {
23            if name.len() > REQUIRES_PREFIX.len() {
24                let (_, required_capability) = name.split_at(REQUIRES_PREFIX.len());
25                Some(required_capability.to_string())
26            } else {
27                None
28            }
29        })
30        .collect()
31}
32
33#[cfg(test)]
34mod tests {
35    use crate::parsed_wasm::ParsedWasm;
36
37    use super::*;
38
39    #[test]
40    fn capabilities_from_csv_works() {
41        let set = capabilities_from_csv("foo, bar,baz ");
42        assert_eq!(set.len(), 3);
43        assert!(set.contains("foo"));
44        assert!(set.contains("bar"));
45        assert!(set.contains("baz"));
46    }
47
48    #[test]
49    fn capabilities_from_csv_skips_empty() {
50        let set = capabilities_from_csv("");
51        assert_eq!(set.len(), 0);
52        let set = capabilities_from_csv("a,,b");
53        assert_eq!(set.len(), 2);
54        assert!(set.contains("a"));
55        assert!(set.contains("b"));
56        let set = capabilities_from_csv("a,b,");
57        assert_eq!(set.len(), 2);
58        assert!(set.contains("a"));
59        assert!(set.contains("b"));
60    }
61
62    #[test]
63    fn required_capabilities_from_module_works() {
64        let wasm = wat::parse_str(
65            r#"(module
66            (type (func))
67            (func (type 0) nop)
68            (export "requires_water" (func 0))
69            (export "requires_" (func 0))
70            (export "requires_nutrients" (func 0))
71            (export "require_milk" (func 0))
72            (export "REQUIRES_air" (func 0))
73            (export "requires_sun" (func 0))
74            )"#,
75        )
76        .unwrap();
77        let module = ParsedWasm::parse(&wasm).unwrap();
78
79        let required_capabilities = required_capabilities_from_module(&module);
80        assert_eq!(required_capabilities.len(), 3);
81        assert!(required_capabilities.contains("nutrients"));
82        assert!(required_capabilities.contains("sun"));
83        assert!(required_capabilities.contains("water"));
84    }
85
86    #[test]
87    fn required_capabilities_from_module_works_without_exports_section() {
88        let wasm = wat::parse_str(r#"(module)"#).unwrap();
89        let module = ParsedWasm::parse(&wasm).unwrap();
90        let required_capabilities = required_capabilities_from_module(&module);
91        assert_eq!(required_capabilities.len(), 0);
92    }
93}