Skip to content

Commit 541d3ce

Browse files
committed
Unfinished: Add debug logs to cosmwasm-check
1 parent 8f8f0ef commit 541d3ce

File tree

3 files changed

+151
-27
lines changed

3 files changed

+151
-27
lines changed

packages/check/src/main.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use clap::{Arg, ArgAction, Command};
88
use colored::Colorize;
99

1010
use cosmwasm_vm::capabilities_from_csv;
11-
use cosmwasm_vm::internals::{check_wasm, compile, make_compiling_engine};
11+
use cosmwasm_vm::internals::{check_wasm_with_logs, compile, make_compiling_engine, Logs};
1212

1313
const DEFAULT_AVAILABLE_CAPABILITIES: &str =
1414
"iterator,staking,stargate,cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3,cosmwasm_1_4,cosmwasm_2_0,cosmwasm_2_1";
@@ -95,8 +95,13 @@ fn check_contract(
9595
let mut wasm = Vec::<u8>::new();
9696
file.read_to_end(&mut wasm)?;
9797

98+
let logs = Logs::new();
9899
// Check wasm
99-
check_wasm(&wasm, available_capabilities)?;
100+
let res = check_wasm_with_logs(&wasm, available_capabilities, logs.clone());
101+
for line in logs.iter() {
102+
eprintln!("{}", line);
103+
}
104+
res?;
100105

101106
// Compile module
102107
let engine = make_compiling_engine(None);

packages/vm/src/compatibility.rs

Lines changed: 143 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
use std::cell::RefCell;
12
use std::collections::BTreeSet;
23
use std::collections::HashSet;
4+
use std::iter::empty;
5+
use std::rc::Rc;
6+
use std::sync::Mutex;
37

48
use wasmer::wasmparser::Import;
59
use wasmer::wasmparser::TypeRef;
@@ -97,17 +101,62 @@ const MAX_TOTAL_FUNCTION_PARAMS: usize = 10_000;
97101
/// during static validation.
98102
const MAX_FUNCTION_RESULTS: usize = 1;
99103

104+
#[derive(Clone)]
105+
pub enum Logs {
106+
On(RefCell<Vec<String>>),
107+
Off,
108+
}
109+
110+
impl Logs {
111+
pub fn new() -> Self {
112+
On(RefCell::new(Vec::new()))
113+
}
114+
115+
// Gets access to logs for writing
116+
pub fn open(&mut self) -> Option<&mut Vec<String>> {
117+
match self {
118+
On(data) => {
119+
let mut data = data.borrow_mut();
120+
Some(data.as_mut())
121+
}
122+
Off => None,
123+
}
124+
}
125+
126+
pub fn iter(&self) -> impl Iterator<Item = String> {
127+
let iter = match self {
128+
On(data) => data.borrow().iter(),
129+
Off => Vec::new().into_iter().into(), // How to create am empty Iter<String> ?!?!?!?
130+
};
131+
iter
132+
}
133+
}
134+
135+
use Logs::*;
136+
100137
/// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports)
101138
pub fn check_wasm(wasm_code: &[u8], available_capabilities: &HashSet<String>) -> VmResult<()> {
139+
check_wasm_with_logs(wasm_code, available_capabilities, Off)
140+
}
141+
142+
pub fn check_wasm_with_logs(
143+
wasm_code: &[u8],
144+
available_capabilities: &HashSet<String>,
145+
logs: Logs,
146+
) -> VmResult<()> {
147+
if let Some(logs) = logs.clone().open() {
148+
logs.push(format!("Size of Wasm blob: {}", wasm_code.len()));
149+
}
150+
102151
let mut module = ParsedWasm::parse(wasm_code)?;
103152

104153
check_wasm_tables(&module)?;
105154
check_wasm_memories(&module)?;
106155
check_interface_version(&module)?;
107-
check_wasm_exports(&module)?;
108-
check_wasm_imports(&module, SUPPORTED_IMPORTS)?;
109-
check_wasm_capabilities(&module, available_capabilities)?;
110-
check_wasm_functions(&module)?;
156+
check_wasm_exports(&module, logs.clone())?;
157+
check_wasm_imports(&module, SUPPORTED_IMPORTS, logs.clone())?;
158+
check_wasm_capabilities(&module, available_capabilities, logs.clone())?;
159+
check_wasm_functions(&module, logs.clone())?;
111160

112161
module.validate_funcs()
113162
}
@@ -188,8 +237,16 @@ fn check_interface_version(module: &ParsedWasm) -> VmResult<()> {
188237
}
189238
}
190239

191-
fn check_wasm_exports(module: &ParsedWasm) -> VmResult<()> {
240+
fn check_wasm_exports(module: &ParsedWasm, mut logs: Logs) -> VmResult<()> {
192241
let available_exports: HashSet<String> = module.exported_function_names(None);
242+
243+
if let Some(logs) = logs.open() {
244+
logs.push(format!(
245+
"Exports: {}",
246+
available_exports.to_string_limited(20_000)
247+
));
248+
}
249+
193250
for required_export in REQUIRED_EXPORTS {
194251
if !available_exports.contains(*required_export) {
195252
return Err(VmError::static_validation_err(format!(
@@ -203,7 +260,24 @@ fn check_wasm_exports(module: &ParsedWasm) -> VmResult<()> {
203260
/// Checks if the import requirements of the contract are satisfied.
204261
/// When this is not the case, we either have an incompatibility between contract and VM
205262
/// or a error in the contract.
206-
fn check_wasm_imports(module: &ParsedWasm, supported_imports: &[&str]) -> VmResult<()> {
263+
fn check_wasm_imports(
264+
module: &ParsedWasm,
265+
supported_imports: &[&str],
266+
mut logs: Logs,
267+
) -> VmResult<()> {
268+
if let Some(logs) = logs.open() {
269+
logs.push(format!(
270+
"Imports ({}): {}",
271+
module.imports.len(),
272+
module
273+
.imports
274+
.iter()
275+
.map(|import| full_import_name(import))
276+
.collect::<Vec<_>>()
277+
.join(", ")
278+
));
279+
}
280+
207281
if module.imports.len() > MAX_IMPORTS {
208282
return Err(VmError::static_validation_err(format!(
209283
"Import count exceeds limit. Imports: {}. Limit: {}.",
@@ -240,8 +314,15 @@ fn full_import_name(ie: &Import) -> String {
240314
fn check_wasm_capabilities(
241315
module: &ParsedWasm,
242316
available_capabilities: &HashSet<String>,
317+
mut logs: Logs,
243318
) -> VmResult<()> {
244319
let required_capabilities = required_capabilities_from_module(module);
320+
if let Some(logs) = logs.open() {
321+
logs.push(format!(
322+
"Required capabilities: {}",
323+
required_capabilities.to_string_limited(20_000)
324+
));
325+
}
245326
if !required_capabilities.is_subset(available_capabilities) {
246327
// We switch to BTreeSet to get a sorted error message
247328
let unavailable: BTreeSet<_> = required_capabilities
@@ -255,7 +336,20 @@ fn check_wasm_capabilities(
255336
Ok(())
256337
}
257338

258-
fn check_wasm_functions(module: &ParsedWasm) -> VmResult<()> {
339+
fn check_wasm_functions(module: &ParsedWasm, mut logs: Logs) -> VmResult<()> {
340+
if let Some(logs) = logs.open() {
341+
logs.push(format!("Function count: {}", module.function_count));
342+
logs.push(format!(
343+
"Max function parameters: {}",
344+
module.max_func_params
345+
));
346+
logs.push(format!("Max function results: {}", module.max_func_results));
347+
logs.push(format!(
348+
"Total function parameter count: {}",
349+
module.total_func_params
350+
));
351+
}
352+
259353
if module.function_count > MAX_FUNCTIONS {
260354
return Err(VmError::static_validation_err(format!(
261355
"Wasm contract contains more than {MAX_FUNCTIONS} functions"
@@ -298,6 +392,31 @@ mod tests {
298392
capabilities_from_csv("cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3,iterator,staking,stargate")
299393
}
300394

395+
#[test]
396+
fn logs_works() {
397+
let mut logs = Logs::new();
398+
399+
if let Some(logs) = logs.open() {
400+
logs.push(format!("a test"));
401+
}
402+
403+
if let Some(logs) = logs.open() {
404+
logs.push(format!("second test"));
405+
logs.push(format!("third test"));
406+
}
407+
408+
let mut logs_b = logs.clone();
409+
if let Some(logs) = logs_b.open() {
410+
logs.push(format!("added in b"));
411+
}
412+
413+
let mut iter = logs.iter();
414+
assert_eq!(iter.next(), Some(String::from("a test")));
415+
assert_eq!(iter.next(), Some(String::from("second test")));
416+
assert_eq!(iter.next(), Some(String::from("third test")));
417+
assert_eq!(iter.next(), None);
418+
}
419+
301420
#[test]
302421
fn check_wasm_passes_for_latest_contract() {
303422
// this is our reference check, must pass
@@ -599,7 +718,7 @@ mod tests {
599718
)
600719
.unwrap();
601720
let module = ParsedWasm::parse(&wasm).unwrap();
602-
check_wasm_exports(&module).unwrap();
721+
check_wasm_exports(&module, Off).unwrap();
603722

604723
// this is invalid, as it doesn't any required export
605724
let wasm = wat::parse_str(
@@ -611,7 +730,7 @@ mod tests {
611730
)
612731
.unwrap();
613732
let module = ParsedWasm::parse(&wasm).unwrap();
614-
match check_wasm_exports(&module) {
733+
match check_wasm_exports(&module, Off) {
615734
Err(VmError::StaticValidationErr { msg, .. }) => {
616735
assert!(msg.starts_with("Wasm contract doesn't have required export: \"allocate\""));
617736
}
@@ -630,7 +749,7 @@ mod tests {
630749
)
631750
.unwrap();
632751
let module = ParsedWasm::parse(&wasm).unwrap();
633-
match check_wasm_exports(&module) {
752+
match check_wasm_exports(&module, Off) {
634753
Err(VmError::StaticValidationErr { msg, .. }) => {
635754
assert!(
636755
msg.starts_with("Wasm contract doesn't have required export: \"deallocate\"")
@@ -660,7 +779,7 @@ mod tests {
660779
)"#,
661780
)
662781
.unwrap();
663-
check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), SUPPORTED_IMPORTS).unwrap();
782+
check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), SUPPORTED_IMPORTS, Off).unwrap();
664783
}
665784

666785
#[test]
@@ -771,8 +890,8 @@ mod tests {
771890
)"#,
772891
)
773892
.unwrap();
774-
let err =
775-
check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), SUPPORTED_IMPORTS).unwrap_err();
893+
let err = check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), SUPPORTED_IMPORTS, Off)
894+
.unwrap_err();
776895
match err {
777896
VmError::StaticValidationErr { msg, .. } => {
778897
assert_eq!(msg, "Import count exceeds limit. Imports: 101. Limit: 100.");
@@ -809,7 +928,7 @@ mod tests {
809928
"env.debug",
810929
"env.query_chain",
811930
];
812-
let result = check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), supported_imports);
931+
let result = check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), supported_imports, Off);
813932
match result.unwrap_err() {
814933
VmError::StaticValidationErr { msg, .. } => {
815934
println!("{msg}");
@@ -825,7 +944,7 @@ mod tests {
825944
#[test]
826945
fn check_wasm_imports_of_old_contract() {
827946
let module = &ParsedWasm::parse(CONTRACT_0_7).unwrap();
828-
let result = check_wasm_imports(module, SUPPORTED_IMPORTS);
947+
let result = check_wasm_imports(module, SUPPORTED_IMPORTS, Off);
829948
match result.unwrap_err() {
830949
VmError::StaticValidationErr { msg, .. } => {
831950
assert!(
@@ -839,7 +958,7 @@ mod tests {
839958
#[test]
840959
fn check_wasm_imports_wrong_type() {
841960
let wasm = wat::parse_str(r#"(module (import "env" "db_read" (memory 1 1)))"#).unwrap();
842-
let result = check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), SUPPORTED_IMPORTS);
961+
let result = check_wasm_imports(&ParsedWasm::parse(&wasm).unwrap(), SUPPORTED_IMPORTS, Off);
843962
match result.unwrap_err() {
844963
VmError::StaticValidationErr { msg, .. } => {
845964
assert!(
@@ -874,7 +993,7 @@ mod tests {
874993
]
875994
.into_iter()
876995
.collect();
877-
check_wasm_capabilities(&module, &available).unwrap();
996+
check_wasm_capabilities(&module, &available, Off).unwrap();
878997
}
879998

880999
#[test]
@@ -902,7 +1021,7 @@ mod tests {
9021021
]
9031022
.into_iter()
9041023
.collect();
905-
match check_wasm_capabilities(&module, &available).unwrap_err() {
1024+
match check_wasm_capabilities(&module, &available, Off).unwrap_err() {
9061025
VmError::StaticValidationErr { msg, .. } => assert_eq!(
9071026
msg,
9081027
"Wasm contract requires unavailable capabilities: {\"sun\"}"
@@ -918,7 +1037,7 @@ mod tests {
9181037
]
9191038
.into_iter()
9201039
.collect();
921-
match check_wasm_capabilities(&module, &available).unwrap_err() {
1040+
match check_wasm_capabilities(&module, &available, Off).unwrap_err() {
9221041
VmError::StaticValidationErr { msg, .. } => assert_eq!(
9231042
msg,
9241043
"Wasm contract requires unavailable capabilities: {\"sun\", \"water\"}"
@@ -928,7 +1047,7 @@ mod tests {
9281047

9291048
// Available set 3
9301049
let available = ["freedom".to_string()].into_iter().collect();
931-
match check_wasm_capabilities(&module, &available).unwrap_err() {
1050+
match check_wasm_capabilities(&module, &available, Off).unwrap_err() {
9321051
VmError::StaticValidationErr { msg, .. } => assert_eq!(
9331052
msg,
9341053
"Wasm contract requires unavailable capabilities: {\"nutrients\", \"sun\", \"water\"}"
@@ -938,7 +1057,7 @@ mod tests {
9381057

9391058
// Available set 4
9401059
let available = [].into_iter().collect();
941-
match check_wasm_capabilities(&module, &available).unwrap_err() {
1060+
match check_wasm_capabilities(&module, &available, Off).unwrap_err() {
9421061
VmError::StaticValidationErr { msg, .. } => assert_eq!(
9431062
msg,
9441063
"Wasm contract requires unavailable capabilities: {\"nutrients\", \"sun\", \"water\"}"
@@ -960,7 +1079,7 @@ mod tests {
9601079
.unwrap();
9611080
let module = ParsedWasm::parse(&wasm).unwrap();
9621081

963-
match check_wasm_functions(&module).unwrap_err() {
1082+
match check_wasm_functions(&module, Off).unwrap_err() {
9641083
VmError::StaticValidationErr { msg, .. } => assert_eq!(
9651084
msg,
9661085
"Wasm contract contains function with more than 100 parameters"
@@ -979,7 +1098,7 @@ mod tests {
9791098
))
9801099
.unwrap();
9811100
let module = ParsedWasm::parse(&wasm).unwrap();
982-
match check_wasm_functions(&module).unwrap_err() {
1101+
match check_wasm_functions(&module, Off).unwrap_err() {
9831102
VmError::StaticValidationErr { msg, .. } => assert_eq!(
9841103
msg,
9851104
"Wasm contract contains function with more than 1 results"
@@ -998,7 +1117,7 @@ mod tests {
9981117
))
9991118
.unwrap();
10001119
let module = ParsedWasm::parse(&wasm).unwrap();
1001-
match check_wasm_functions(&module).unwrap_err() {
1120+
match check_wasm_functions(&module, Off).unwrap_err() {
10021121
VmError::StaticValidationErr { msg, .. } => {
10031122
assert_eq!(msg, "Wasm contract contains more than 20000 functions")
10041123
}

packages/vm/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ pub mod internals {
5555
//! Please don't use any of these types directly, as
5656
//! they might change frequently or be removed in the future.
5757
58-
pub use crate::compatibility::check_wasm;
58+
pub use crate::compatibility::{check_wasm, check_wasm_with_logs, Logs};
5959
pub use crate::instance::instance_from_module;
6060
pub use crate::wasm_backend::{compile, make_compiling_engine, make_runtime_engine};
6161
}

0 commit comments

Comments
 (0)