1
+ use std:: cell:: RefCell ;
1
2
use std:: collections:: BTreeSet ;
2
3
use std:: collections:: HashSet ;
4
+ use std:: iter:: empty;
5
+ use std:: rc:: Rc ;
6
+ use std:: sync:: Mutex ;
3
7
4
8
use wasmer:: wasmparser:: Import ;
5
9
use wasmer:: wasmparser:: TypeRef ;
@@ -97,17 +101,62 @@ const MAX_TOTAL_FUNCTION_PARAMS: usize = 10_000;
97
101
/// during static validation.
98
102
const MAX_FUNCTION_RESULTS : usize = 1 ;
99
103
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
+
100
137
/// Checks if the data is valid wasm and compatibility with the CosmWasm API (imports and exports)
101
138
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
+
102
151
let mut module = ParsedWasm :: parse ( wasm_code) ?;
103
152
104
153
check_wasm_tables ( & module) ?;
105
154
check_wasm_memories ( & module) ?;
106
155
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 ( ) ) ?;
111
160
112
161
module. validate_funcs ( )
113
162
}
@@ -188,8 +237,16 @@ fn check_interface_version(module: &ParsedWasm) -> VmResult<()> {
188
237
}
189
238
}
190
239
191
- fn check_wasm_exports ( module : & ParsedWasm ) -> VmResult < ( ) > {
240
+ fn check_wasm_exports ( module : & ParsedWasm , mut logs : Logs ) -> VmResult < ( ) > {
192
241
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
+
193
250
for required_export in REQUIRED_EXPORTS {
194
251
if !available_exports. contains ( * required_export) {
195
252
return Err ( VmError :: static_validation_err ( format ! (
@@ -203,7 +260,24 @@ fn check_wasm_exports(module: &ParsedWasm) -> VmResult<()> {
203
260
/// Checks if the import requirements of the contract are satisfied.
204
261
/// When this is not the case, we either have an incompatibility between contract and VM
205
262
/// 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
+
207
281
if module. imports . len ( ) > MAX_IMPORTS {
208
282
return Err ( VmError :: static_validation_err ( format ! (
209
283
"Import count exceeds limit. Imports: {}. Limit: {}." ,
@@ -240,8 +314,15 @@ fn full_import_name(ie: &Import) -> String {
240
314
fn check_wasm_capabilities (
241
315
module : & ParsedWasm ,
242
316
available_capabilities : & HashSet < String > ,
317
+ mut logs : Logs ,
243
318
) -> VmResult < ( ) > {
244
319
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
+ }
245
326
if !required_capabilities. is_subset ( available_capabilities) {
246
327
// We switch to BTreeSet to get a sorted error message
247
328
let unavailable: BTreeSet < _ > = required_capabilities
@@ -255,7 +336,20 @@ fn check_wasm_capabilities(
255
336
Ok ( ( ) )
256
337
}
257
338
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
+
259
353
if module. function_count > MAX_FUNCTIONS {
260
354
return Err ( VmError :: static_validation_err ( format ! (
261
355
"Wasm contract contains more than {MAX_FUNCTIONS} functions"
@@ -298,6 +392,31 @@ mod tests {
298
392
capabilities_from_csv ( "cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3,iterator,staking,stargate" )
299
393
}
300
394
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
+
301
420
#[ test]
302
421
fn check_wasm_passes_for_latest_contract ( ) {
303
422
// this is our reference check, must pass
@@ -599,7 +718,7 @@ mod tests {
599
718
)
600
719
. unwrap ( ) ;
601
720
let module = ParsedWasm :: parse ( & wasm) . unwrap ( ) ;
602
- check_wasm_exports ( & module) . unwrap ( ) ;
721
+ check_wasm_exports ( & module, Off ) . unwrap ( ) ;
603
722
604
723
// this is invalid, as it doesn't any required export
605
724
let wasm = wat:: parse_str (
@@ -611,7 +730,7 @@ mod tests {
611
730
)
612
731
. unwrap ( ) ;
613
732
let module = ParsedWasm :: parse ( & wasm) . unwrap ( ) ;
614
- match check_wasm_exports ( & module) {
733
+ match check_wasm_exports ( & module, Off ) {
615
734
Err ( VmError :: StaticValidationErr { msg, .. } ) => {
616
735
assert ! ( msg. starts_with( "Wasm contract doesn't have required export: \" allocate\" " ) ) ;
617
736
}
@@ -630,7 +749,7 @@ mod tests {
630
749
)
631
750
. unwrap ( ) ;
632
751
let module = ParsedWasm :: parse ( & wasm) . unwrap ( ) ;
633
- match check_wasm_exports ( & module) {
752
+ match check_wasm_exports ( & module, Off ) {
634
753
Err ( VmError :: StaticValidationErr { msg, .. } ) => {
635
754
assert ! (
636
755
msg. starts_with( "Wasm contract doesn't have required export: \" deallocate\" " )
@@ -660,7 +779,7 @@ mod tests {
660
779
)"# ,
661
780
)
662
781
. unwrap ( ) ;
663
- check_wasm_imports ( & ParsedWasm :: parse ( & wasm) . unwrap ( ) , SUPPORTED_IMPORTS ) . unwrap ( ) ;
782
+ check_wasm_imports ( & ParsedWasm :: parse ( & wasm) . unwrap ( ) , SUPPORTED_IMPORTS , Off ) . unwrap ( ) ;
664
783
}
665
784
666
785
#[ test]
@@ -771,8 +890,8 @@ mod tests {
771
890
)"# ,
772
891
)
773
892
. 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 ( ) ;
776
895
match err {
777
896
VmError :: StaticValidationErr { msg, .. } => {
778
897
assert_eq ! ( msg, "Import count exceeds limit. Imports: 101. Limit: 100." ) ;
@@ -809,7 +928,7 @@ mod tests {
809
928
"env.debug" ,
810
929
"env.query_chain" ,
811
930
] ;
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 ) ;
813
932
match result. unwrap_err ( ) {
814
933
VmError :: StaticValidationErr { msg, .. } => {
815
934
println ! ( "{msg}" ) ;
@@ -825,7 +944,7 @@ mod tests {
825
944
#[ test]
826
945
fn check_wasm_imports_of_old_contract ( ) {
827
946
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 ) ;
829
948
match result. unwrap_err ( ) {
830
949
VmError :: StaticValidationErr { msg, .. } => {
831
950
assert ! (
@@ -839,7 +958,7 @@ mod tests {
839
958
#[ test]
840
959
fn check_wasm_imports_wrong_type ( ) {
841
960
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 ) ;
843
962
match result. unwrap_err ( ) {
844
963
VmError :: StaticValidationErr { msg, .. } => {
845
964
assert ! (
@@ -874,7 +993,7 @@ mod tests {
874
993
]
875
994
. into_iter ( )
876
995
. collect ( ) ;
877
- check_wasm_capabilities ( & module, & available) . unwrap ( ) ;
996
+ check_wasm_capabilities ( & module, & available, Off ) . unwrap ( ) ;
878
997
}
879
998
880
999
#[ test]
@@ -902,7 +1021,7 @@ mod tests {
902
1021
]
903
1022
. into_iter ( )
904
1023
. collect ( ) ;
905
- match check_wasm_capabilities ( & module, & available) . unwrap_err ( ) {
1024
+ match check_wasm_capabilities ( & module, & available, Off ) . unwrap_err ( ) {
906
1025
VmError :: StaticValidationErr { msg, .. } => assert_eq ! (
907
1026
msg,
908
1027
"Wasm contract requires unavailable capabilities: {\" sun\" }"
@@ -918,7 +1037,7 @@ mod tests {
918
1037
]
919
1038
. into_iter ( )
920
1039
. collect ( ) ;
921
- match check_wasm_capabilities ( & module, & available) . unwrap_err ( ) {
1040
+ match check_wasm_capabilities ( & module, & available, Off ) . unwrap_err ( ) {
922
1041
VmError :: StaticValidationErr { msg, .. } => assert_eq ! (
923
1042
msg,
924
1043
"Wasm contract requires unavailable capabilities: {\" sun\" , \" water\" }"
@@ -928,7 +1047,7 @@ mod tests {
928
1047
929
1048
// Available set 3
930
1049
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 ( ) {
932
1051
VmError :: StaticValidationErr { msg, .. } => assert_eq ! (
933
1052
msg,
934
1053
"Wasm contract requires unavailable capabilities: {\" nutrients\" , \" sun\" , \" water\" }"
@@ -938,7 +1057,7 @@ mod tests {
938
1057
939
1058
// Available set 4
940
1059
let available = [ ] . into_iter ( ) . collect ( ) ;
941
- match check_wasm_capabilities ( & module, & available) . unwrap_err ( ) {
1060
+ match check_wasm_capabilities ( & module, & available, Off ) . unwrap_err ( ) {
942
1061
VmError :: StaticValidationErr { msg, .. } => assert_eq ! (
943
1062
msg,
944
1063
"Wasm contract requires unavailable capabilities: {\" nutrients\" , \" sun\" , \" water\" }"
@@ -960,7 +1079,7 @@ mod tests {
960
1079
. unwrap ( ) ;
961
1080
let module = ParsedWasm :: parse ( & wasm) . unwrap ( ) ;
962
1081
963
- match check_wasm_functions ( & module) . unwrap_err ( ) {
1082
+ match check_wasm_functions ( & module, Off ) . unwrap_err ( ) {
964
1083
VmError :: StaticValidationErr { msg, .. } => assert_eq ! (
965
1084
msg,
966
1085
"Wasm contract contains function with more than 100 parameters"
@@ -979,7 +1098,7 @@ mod tests {
979
1098
) )
980
1099
. unwrap ( ) ;
981
1100
let module = ParsedWasm :: parse ( & wasm) . unwrap ( ) ;
982
- match check_wasm_functions ( & module) . unwrap_err ( ) {
1101
+ match check_wasm_functions ( & module, Off ) . unwrap_err ( ) {
983
1102
VmError :: StaticValidationErr { msg, .. } => assert_eq ! (
984
1103
msg,
985
1104
"Wasm contract contains function with more than 1 results"
@@ -998,7 +1117,7 @@ mod tests {
998
1117
) )
999
1118
. unwrap ( ) ;
1000
1119
let module = ParsedWasm :: parse ( & wasm) . unwrap ( ) ;
1001
- match check_wasm_functions ( & module) . unwrap_err ( ) {
1120
+ match check_wasm_functions ( & module, Off ) . unwrap_err ( ) {
1002
1121
VmError :: StaticValidationErr { msg, .. } => {
1003
1122
assert_eq ! ( msg, "Wasm contract contains more than 20000 functions" )
1004
1123
}
0 commit comments