Skip to content

Commit 54fcf35

Browse files
feat(RUN-931): Add doc links to Validation Errors (#1188)
Add links to the Execution Errors reference page for all user errors that can occur during smart contract validation. Part of https://dfinity.atlassian.net/browse/RUN-931
1 parent 66b547b commit 54fcf35

File tree

5 files changed

+120
-51
lines changed

5 files changed

+120
-51
lines changed

rs/embedders/src/wasm_utils/validation.rs

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -905,11 +905,9 @@ fn validate_export_section(
905905
func_name = parts[0];
906906
let unmangled_func_name = parts[1];
907907
if seen_funcs.contains(unmangled_func_name) {
908-
return Err(WasmValidationError::UserInvalidExportSection(format!(
909-
"Duplicate function '{}' exported multiple times \
910-
with different call types: update, query, or composite_query.",
911-
unmangled_func_name
912-
)));
908+
return Err(WasmValidationError::DuplicateExport {
909+
name: unmangled_func_name.to_string(),
910+
});
913911
}
914912
seen_funcs.insert(unmangled_func_name);
915913
number_exported_functions += 1;
@@ -947,13 +945,19 @@ fn validate_export_section(
947945
}
948946
}
949947
}
948+
950949
if number_exported_functions > max_number_exported_functions {
951-
let err = format!("The number of exported functions called `canister_update <name>`, `canister_query <name>`, or `canister_composite_query <name>` exceeds {}.", max_number_exported_functions);
952-
return Err(WasmValidationError::UserInvalidExportSection(err));
950+
return Err(WasmValidationError::TooManyExports {
951+
defined: number_exported_functions,
952+
allowed: max_number_exported_functions,
953+
});
953954
}
955+
954956
if sum_exported_function_name_lengths > max_sum_exported_function_name_lengths {
955-
let err = format!("The sum of `<name>` lengths in exported functions called `canister_update <name>`, `canister_query <name>`, or `canister_composite_query <name>` exceeds {}.", max_sum_exported_function_name_lengths);
956-
return Err(WasmValidationError::UserInvalidExportSection(err));
957+
return Err(WasmValidationError::ExportedNamesTooLong {
958+
total_length: sum_exported_function_name_lengths,
959+
allowed: max_sum_exported_function_name_lengths,
960+
});
957961
}
958962
}
959963
Ok(())

rs/embedders/tests/validation.rs

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,9 @@ fn can_validate_duplicate_update_and_query_methods() {
334334
.unwrap();
335335
assert_eq!(
336336
validate_wasm_binary(&wasm, &EmbeddersConfig::default()),
337-
Err(WasmValidationError::UserInvalidExportSection(
338-
"Duplicate function 'read' exported multiple times \
339-
with different call types: update, query, or composite_query."
340-
.to_string()
341-
))
337+
Err(WasmValidationError::DuplicateExport {
338+
name: "read".to_string()
339+
})
342340
);
343341
}
344342

@@ -353,11 +351,9 @@ fn can_validate_duplicate_update_and_composite_query_methods() {
353351
.unwrap();
354352
assert_eq!(
355353
validate_wasm_binary(&wasm, &EmbeddersConfig::default()),
356-
Err(WasmValidationError::UserInvalidExportSection(
357-
"Duplicate function 'read' exported multiple times \
358-
with different call types: update, query, or composite_query."
359-
.to_string()
360-
))
354+
Err(WasmValidationError::DuplicateExport {
355+
name: "read".to_string()
356+
})
361357
);
362358
}
363359

@@ -372,11 +368,9 @@ fn can_validate_duplicate_query_and_composite_query_methods() {
372368
.unwrap();
373369
assert_eq!(
374370
validate_wasm_binary(&wasm, &EmbeddersConfig::default()),
375-
Err(WasmValidationError::UserInvalidExportSection(
376-
"Duplicate function 'read' exported multiple times \
377-
with different call types: update, query, or composite_query."
378-
.to_string()
379-
))
371+
Err(WasmValidationError::DuplicateExport {
372+
name: "read".to_string()
373+
})
380374
);
381375
}
382376

@@ -416,9 +410,10 @@ fn can_validate_too_many_exported_functions() {
416410
let wasm = wat2wasm(&many_exported_functions(1001)).unwrap();
417411
assert_eq!(
418412
validate_wasm_binary(&wasm, &EmbeddersConfig::default()),
419-
Err(WasmValidationError::UserInvalidExportSection(
420-
"The number of exported functions called `canister_update <name>`, `canister_query <name>`, or `canister_composite_query <name>` exceeds 1000.".to_string()
421-
))
413+
Err(WasmValidationError::TooManyExports {
414+
defined: 1001,
415+
allowed: 1000
416+
})
422417
);
423418
}
424419

@@ -462,9 +457,10 @@ fn can_validate_too_large_sum_exported_function_name_lengths() {
462457
.unwrap();
463458
assert_eq!(
464459
validate_wasm_binary(&wasm, &EmbeddersConfig::default()),
465-
Err(WasmValidationError::UserInvalidExportSection(
466-
"The sum of `<name>` lengths in exported functions called `canister_update <name>`, `canister_query <name>`, or `canister_composite_query <name>` exceeds 20000.".to_string()
467-
))
460+
Err(WasmValidationError::ExportedNamesTooLong {
461+
total_length: 20001,
462+
allowed: 20000
463+
})
468464
);
469465
}
470466

rs/interfaces/src/execution_environment/errors.rs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use ic_base_types::{NumBytes, PrincipalIdBlobParseError};
22
use ic_error_types::UserError;
33
use ic_types::{methods::WasmMethod, CanisterId, CountBytes, Cycles, NumInstructions};
44
use ic_wasm_types::{
5-
AsErrorHelp, ErrorHelp, WasmEngineError, WasmInstrumentationError, WasmValidationError,
5+
doc_ref, AsErrorHelp, ErrorHelp, WasmEngineError, WasmInstrumentationError, WasmValidationError,
66
};
77
use serde::{Deserialize, Serialize};
88

@@ -343,12 +343,6 @@ impl CountBytes for HypervisorError {
343343

344344
impl AsErrorHelp for HypervisorError {
345345
fn error_help(&self) -> ErrorHelp {
346-
fn doc_ref(section: &str) -> String {
347-
format!(
348-
"http://internetcomputer.org/docs/current/references/execution-errors#{}",
349-
section
350-
)
351-
}
352346
match self {
353347
Self::FunctionNotFound(_, _)
354348
| Self::ToolchainContractViolation { .. }

rs/types/wasm_types/src/errors.rs

Lines changed: 88 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
use serde::{Deserialize, Serialize};
22

3+
/// Create a link to this section of the Execution Errors documentation.
4+
pub fn doc_ref(section: &str) -> String {
5+
format!(
6+
"http://internetcomputer.org/docs/current/references/execution-errors#{}",
7+
section
8+
)
9+
}
10+
311
pub enum ErrorHelp {
412
UserError {
513
suggestion: String,
@@ -55,8 +63,12 @@ pub enum WasmValidationError {
5563
InvalidImportSection(String),
5664
/// Module contains an invalid export section
5765
InvalidExportSection(String),
58-
/// Module contains an invalid export section caused by a user error.
59-
UserInvalidExportSection(String),
66+
/// Same function name is exported multiple times (with different types).
67+
DuplicateExport { name: String },
68+
/// There are too many exports defined in the module.
69+
TooManyExports { defined: usize, allowed: usize },
70+
/// The total length of exported function names is too large.
71+
ExportedNamesTooLong { total_length: usize, allowed: usize },
6072
/// Module contains an invalid data section
6173
InvalidDataSection(String),
6274
/// Module contains an invalid custom section
@@ -107,8 +119,32 @@ impl std::fmt::Display for WasmValidationError {
107119
Self::InvalidExportSection(err) => {
108120
write!(f, "Wasm module has an invalid export section. {err}")
109121
}
110-
Self::UserInvalidExportSection(err) => {
111-
write!(f, "Wasm module has an invalid export section. {}", err)
122+
Self::DuplicateExport { name } => {
123+
write!(
124+
f,
125+
"Duplicate function '{name}' exported multiple times \
126+
with different call types: update, query, or composite_query."
127+
)
128+
}
129+
Self::TooManyExports { defined, allowed } => {
130+
write!(
131+
f,
132+
"The number of exported functions called \
133+
`canister_update <name>`, `canister_query <name>`, or \
134+
`canister_composite_query <name>` is {defined} which exceeds {allowed}."
135+
)
136+
}
137+
Self::ExportedNamesTooLong {
138+
total_length,
139+
allowed,
140+
} => {
141+
write!(
142+
f,
143+
"The sum of `<name>` lengths in exported \
144+
functions called `canister_update <name>`, `canister_query <name>`, \
145+
or `canister_composite_query <name>` is {total_length} which exceeds \
146+
the allowed limit of {allowed}."
147+
)
112148
}
113149
Self::InvalidDataSection(err) => {
114150
write!(f, "Wasm module has an invalid data section. {err}")
@@ -185,15 +221,54 @@ impl AsErrorHelp for WasmValidationError {
185221
| WasmValidationError::InvalidGlobalSection(_)
186222
| WasmValidationError::UnsupportedWasmInstruction { .. }
187223
| WasmValidationError::TooManyCustomSections { .. } => ErrorHelp::ToolchainError,
188-
WasmValidationError::UserInvalidExportSection(_)
189-
| WasmValidationError::TooManyFunctions { .. }
190-
| WasmValidationError::TooManyGlobals { .. }
191-
| WasmValidationError::FunctionComplexityTooHigh { .. }
192-
| WasmValidationError::FunctionTooLarge { .. }
193-
| WasmValidationError::CodeSectionTooLarge { .. }
194-
| WasmValidationError::ModuleTooLarge { .. } => ErrorHelp::UserError {
195-
suggestion: "".to_string(),
196-
doc_link: "".to_string(),
224+
WasmValidationError::DuplicateExport { name } => ErrorHelp::UserError {
225+
suggestion: format!(
226+
"Try defining different versions of the function for each \
227+
call type, e.g. `{name}_update`, `{name}_query`, etc."
228+
),
229+
doc_link: doc_ref("wasm-module-duplicate-exports"),
230+
},
231+
WasmValidationError::TooManyExports { .. } => ErrorHelp::UserError {
232+
suggestion: "Try combining multiple endpoints into a single endpoint.".to_string(),
233+
doc_link: doc_ref("wasm-module-exports-too-many-methods"),
234+
},
235+
WasmValidationError::ExportedNamesTooLong { .. } => ErrorHelp::UserError {
236+
suggestion: "Try using shorter method names.".to_string(),
237+
doc_link: doc_ref("wasm-module-sum-of-exported-name-lengths-too-large"),
238+
},
239+
WasmValidationError::TooManyFunctions { .. } => ErrorHelp::UserError {
240+
suggestion: "Try spliting this canister into multiple canisters.".to_string(),
241+
doc_link: doc_ref("wasm-module-too-many-functions"),
242+
},
243+
WasmValidationError::TooManyGlobals { .. } => ErrorHelp::UserError {
244+
suggestion: "Try collecting multiple globals into a single \
245+
structured which can be stored on the heap."
246+
.to_string(),
247+
doc_link: doc_ref("wasm-module-too-many-globals"),
248+
},
249+
WasmValidationError::FunctionComplexityTooHigh { .. } => ErrorHelp::UserError {
250+
suggestion: "Try breaking large functions up into multiple \
251+
smaller functions."
252+
.to_string(),
253+
doc_link: doc_ref("wasm-module-function-complexity-too-high"),
254+
},
255+
WasmValidationError::FunctionTooLarge { .. } => ErrorHelp::UserError {
256+
suggestion: "Try breaking large functions up into multiple \
257+
smaller functions."
258+
.to_string(),
259+
doc_link: doc_ref("wasm-module-function-too-large"),
260+
},
261+
WasmValidationError::CodeSectionTooLarge { .. } => ErrorHelp::UserError {
262+
suggestion: "Try shrinking the module code section using tools like \
263+
`ic-wasm` or splitting the logic across multiple canisters."
264+
.to_string(),
265+
doc_link: doc_ref("wasm-module-code-section-too-large"),
266+
},
267+
WasmValidationError::ModuleTooLarge { .. } => ErrorHelp::UserError {
268+
suggestion: "Try shrinking the module using tools like \
269+
`ic-wasm` or splitting the logic across multiple canisters."
270+
.to_string(),
271+
doc_link: doc_ref("wasm-module-too-large"),
197272
},
198273
}
199274
}

rs/types/wasm_types/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
mod errors;
44

55
pub use errors::{
6-
AsErrorHelp, ErrorHelp, WasmEngineError, WasmError, WasmInstrumentationError,
6+
doc_ref, AsErrorHelp, ErrorHelp, WasmEngineError, WasmError, WasmInstrumentationError,
77
WasmValidationError,
88
};
99
use ic_types::CountBytes;

0 commit comments

Comments
 (0)