neo_solidity/cli/cli_parts/cli_manifest/permissions/
native.rs

1fn require_native_method(
2    native_methods: &mut BTreeMap<String, BTreeSet<String>>,
3    contract: ir::NativeContract,
4    method: &str,
5) {
6    let hash_le = bytecode::native_contract_hash(contract);
7    let hash_be = hash_le.iter().rev().copied().collect::<Vec<_>>();
8    let contract_str = format!("0x{}", hex::encode(hash_be));
9    native_methods
10        .entry(contract_str)
11        .or_default()
12        .insert(method.to_string());
13}
14
15fn collect_native_permissions(ir_module: &ir::Module) -> BTreeMap<String, BTreeSet<String>> {
16    let mut native_methods: BTreeMap<String, BTreeSet<String>> = BTreeMap::new();
17
18    for function in &ir_module.functions {
19        for block in &function.basic_blocks {
20            for instr in &block.instructions {
21                match instr {
22                    ir::Instruction::CallBuiltin { builtin, .. } => match builtin {
23                        ir::BuiltinCall::ContractCall | ir::BuiltinCall::ContractCallWithFlags => {
24                            // Builtin lowering always deserializes args and serializes return.
25                            require_native_method(
26                                &mut native_methods,
27                                ir::NativeContract::StdLib,
28                                "deserialize",
29                            );
30                            require_native_method(
31                                &mut native_methods,
32                                ir::NativeContract::StdLib,
33                                "serialize",
34                            );
35                        }
36                        ir::BuiltinCall::NativeCall { contract, method } => {
37                            // Backwards-compatible alias: older devpacks used
38                            // `ContractManagement.listContracts`, but the Neo native contract
39                            // exposes this as `getContractHashes`.
40                            if matches!(contract, ir::NativeContract::ContractManagement)
41                                && method == "listContracts"
42                            {
43                                require_native_method(
44                                    &mut native_methods,
45                                    ir::NativeContract::ContractManagement,
46                                    "getContractHashes",
47                                );
48                            } else {
49                                require_native_method(
50                                    &mut native_methods,
51                                    *contract,
52                                    method.as_str(),
53                                );
54                            }
55                        }
56                        ir::BuiltinCall::DeployContract => {
57                            require_native_method(
58                                &mut native_methods,
59                                ir::NativeContract::ContractManagement,
60                                "deploy",
61                            );
62                        }
63                        ir::BuiltinCall::GetContract => {
64                            require_native_method(
65                                &mut native_methods,
66                                ir::NativeContract::ContractManagement,
67                                "getContract",
68                            );
69                            require_native_method(
70                                &mut native_methods,
71                                ir::NativeContract::StdLib,
72                                "serialize",
73                            );
74                        }
75                        ir::BuiltinCall::GetContractScript => {
76                            require_native_method(
77                                &mut native_methods,
78                                ir::NativeContract::ContractManagement,
79                                "getContract",
80                            );
81                        }
82                        ir::BuiltinCall::GetNeoAccountState => {
83                            require_native_method(
84                                &mut native_methods,
85                                ir::NativeContract::Neo,
86                                "getAccountState",
87                            );
88                        }
89                        ir::BuiltinCall::AbiEncode | ir::BuiltinCall::AbiEncodePacked => {
90                            require_native_method(
91                                &mut native_methods,
92                                ir::NativeContract::StdLib,
93                                "serialize",
94                            );
95                        }
96                        ir::BuiltinCall::AbiDecode => {
97                            require_native_method(
98                                &mut native_methods,
99                                ir::NativeContract::StdLib,
100                                "deserialize",
101                            );
102                        }
103                        ir::BuiltinCall::RuntimeNotify | ir::BuiltinCall::NotifySerialized => {
104                            require_native_method(
105                                &mut native_methods,
106                                ir::NativeContract::StdLib,
107                                "deserialize",
108                            );
109                        }
110                        ir::BuiltinCall::Keccak256 => {
111                            require_native_method(
112                                &mut native_methods,
113                                ir::NativeContract::CryptoLib,
114                                "keccak256",
115                            );
116                        }
117                        ir::BuiltinCall::Ecrecover => {
118                            require_native_method(
119                                &mut native_methods,
120                                ir::NativeContract::CryptoLib,
121                                "recoverSecp256K1",
122                            );
123                        }
124                        ir::BuiltinCall::VerifySignature => {
125                            require_native_method(
126                                &mut native_methods,
127                                ir::NativeContract::CryptoLib,
128                                "verifyWithECDsa",
129                            );
130                        }
131                        ir::BuiltinCall::Syscall(name) => {
132                            let _ = name;
133                        }
134                        _ => {}
135                    },
136                    ir::Instruction::LoadRuntimeValue(value) => {
137                        if matches!(value, ir::RuntimeValue::BlockNumber) {
138                            require_native_method(
139                                &mut native_methods,
140                                ir::NativeContract::Ledger,
141                                "currentIndex",
142                            );
143                        }
144                    }
145                    ir::Instruction::LoadMappingElement { key_types, .. }
146                    | ir::Instruction::StoreMappingElement { key_types, .. } => {
147                        // Mapping storage slots are derived via:
148                        //   StdLib.serialize(key) + CryptoLib.keccak256(...)
149                        //
150                        // When `key_types` is empty, the slot is the base slot and no hashing is
151                        // performed, so avoid adding unnecessary permissions.
152                        if !key_types.is_empty() {
153                            require_native_method(
154                                &mut native_methods,
155                                ir::NativeContract::StdLib,
156                                "serialize",
157                            );
158                            require_native_method(
159                                &mut native_methods,
160                                ir::NativeContract::CryptoLib,
161                                "keccak256",
162                            );
163                        }
164                    }
165                    ir::Instruction::LoadStructField { key_types, .. }
166                    | ir::Instruction::StoreStructField { key_types, .. } => {
167                        // Struct field storage slots always require at least one keccak256
168                        // (base slot + field key). If the struct is nested under a mapping,
169                        // additional key serialization + hashing occurs.
170                        require_native_method(
171                            &mut native_methods,
172                            ir::NativeContract::CryptoLib,
173                            "keccak256",
174                        );
175                        if !key_types.is_empty() {
176                            require_native_method(
177                                &mut native_methods,
178                                ir::NativeContract::StdLib,
179                                "serialize",
180                            );
181                        }
182                    }
183                    ir::Instruction::LoadStructArrayElement { key_types, .. }
184                    | ir::Instruction::StoreStructArrayElement { key_types, .. } => {
185                        // Array elements stored under a struct field require:
186                        // - keccak256 for the field-slot derivation and the element-slot derivation
187                        // - StdLib.serialize for the element index (and any mapping keys)
188                        require_native_method(
189                            &mut native_methods,
190                            ir::NativeContract::CryptoLib,
191                            "keccak256",
192                        );
193                        require_native_method(
194                            &mut native_methods,
195                            ir::NativeContract::StdLib,
196                            "serialize",
197                        );
198                        if !key_types.is_empty() {
199                            // mapping keys also require serialize/keccak256; already covered above,
200                            // but keep this for clarity in case the rules evolve.
201                            require_native_method(
202                                &mut native_methods,
203                                ir::NativeContract::CryptoLib,
204                                "keccak256",
205                            );
206                        }
207                    }
208                    _ => {}
209                }
210            }
211        }
212    }
213
214    native_methods
215}