contract_analyze/
lib.rs

1// Copyright (C) Use Ink (UK) Ltd.
2// This file is part of cargo-contract.
3//
4// cargo-contract is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// cargo-contract is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with cargo-contract.  If not, see <http://www.gnu.org/licenses/>.
16
17#![deny(unused_crate_dependencies)]
18
19use anyhow::{
20    bail,
21    Result,
22};
23pub use contract_metadata::Language;
24
25/// Detects the programming language of a smart contract from its PolkaVM
26/// binary code.
27///
28/// This function accepts a binary contract as input and employs a set of heuristics
29/// to identify the contract's source language. It currently supports detection of the
30/// ink! and Solidity languages.
31pub fn determine_language(_code: &[u8]) -> Result<Language> {
32    /*
33    // todo
34    if !start_section && module.custom_sections.keys().any(|e| e == &"producers") {
35        return Ok(Language::Solidity)
36    } else if start_section
37        && module
38            .custom_sections
39            .keys()
40            .any(|e| e == &"sourceMappingURL")
41    {
42        return Ok(Language::AssemblyScript)
43    } else if !start_section
44        && (is_ink_function_present(&module)
45            || matches!(module.has_function_name("ink_env"), Ok(true)))
46    {
47        return Ok(Language::Ink)
48    }
49    */
50
51    bail!("Language unsupported or unrecognized.")
52}
53
54#[cfg(test)]
55mod tests {
56    /*
57    // todo
58
59    #[test]
60    fn fails_with_unsupported_language() {
61        let contract = r#"
62        (module
63            (type $none_=>_none (func))
64            (type (;0;) (func (param i32 i32 i32)))
65            (import "env" "memory" (func (;5;) (type 0)))
66            (start $~start)
67            (func $~start (type $none_=>_none))
68            (func (;5;) (type 0))
69        )
70        "#;
71        let code = &wat::parse_str(contract).expect("Invalid wat.");
72        let lang = determine_language(code);
73        assert!(lang.is_err());
74        assert_eq!(
75            lang.unwrap_err().to_string(),
76            "Language unsupported or unrecognized."
77        );
78    }
79
80    #[test]
81    fn determines_ink_language() {
82        let contract = r#"
83        (module
84            (type (;0;) (func (param i32 i32 i32)))
85            (type (;1;) (func (result i32)))
86            (type (;2;) (func (param i32 i32)))
87            (import "seal" "foo" (func (;0;) (type 0)))
88            (import "seal0" "value_transferred" (func (;1;) (type 2)))
89            (import "env" "memory" (memory (;0;) 2 16))
90            (func (;2;) (type 2))
91            (func (;3;) (type 1) (result i32)
92            (local i32 i64 i64)
93            global.get 0
94            i32.const 32
95            i32.sub
96            local.tee 0
97            global.set 0
98            local.get 0
99            i64.const 0
100            i64.store offset=8
101            local.get 0
102            i64.const 0
103            i64.store
104            local.get 0
105            i32.const 16
106            i32.store offset=28
107            local.get 0
108            local.get 0
109            i32.const 28
110            i32.add
111            call 1
112            local.get 0
113            i64.load offset=8
114            local.set 1
115            local.get 0
116            i64.load
117            local.set 2
118            local.get 0
119            i32.const 32
120            i32.add
121            global.set 0
122            i32.const 5
123            i32.const 4
124            local.get 1
125            local.get 2
126            i64.or
127            i64.eqz
128            select
129        )
130            (global (;0;) (mut i32) (i32.const 65536))
131        )"#;
132        let code = &wat::parse_str(contract).expect("Invalid wat.");
133        let lang = determine_language(code);
134        assert!(
135            matches!(lang, Ok(Language::Ink)),
136            "Failed to detect Ink! language."
137        );
138    }
139
140    #[test]
141    fn determines_solidity_language() {
142        let contract = r#"
143        (module
144            (type (;0;) (func (param i32 i32 i32)))
145            (import "env" "memory" (memory (;0;) 16 16))
146            (func (;0;) (type 0))
147            (@custom "producers" "data")
148        )
149        "#;
150        let code = &wat::parse_str(contract).expect("Invalid wat.");
151        let lang = determine_language(code);
152        assert!(
153            matches!(lang, Ok(Language::Solidity)),
154            "Failed to detect Solidity language."
155        );
156    }
157
158    #[test]
159    fn determines_assembly_script_language() {
160        let contract = r#"
161        (module
162            (type $none_=>_none (func))
163            (type (;0;) (func (param i32 i32 i32)))
164            (import "seal" "foo" (func (;0;) (type 0)))
165            (import "env" "memory" (memory $0 2 16))
166            (start $~start)
167            (func $~start (type $none_=>_none))
168            (func (;1;) (type 0))
169            (@custom "sourceMappingURL" "data")
170        )
171        "#;
172        let code = &wat::parse_str(contract).expect("Invalid wat.");
173        let lang = determine_language(code);
174        assert!(
175            matches!(lang, Ok(Language::AssemblyScript)),
176            "Failed to detect AssemblyScript language."
177        );
178    }
179     */
180}