github.com/filecoin-project/bacalhau@v0.3.23-0.20230228154132-45c989550ace/pkg/executor/wasm/validator.go (about) 1 package wasm 2 3 import ( 4 "fmt" 5 6 "github.com/filecoin-project/bacalhau/pkg/model" 7 "github.com/tetratelabs/wazero" 8 "github.com/tetratelabs/wazero/api" 9 "golang.org/x/exp/maps" 10 ) 11 12 // ValidateModuleAgainstJob will return an error if the passed job does not 13 // represent a valid WASM executor job or the passed module is not able to be 14 // run to fulfill the job. 15 func ValidateModuleAgainstJob( 16 module wazero.CompiledModule, 17 job model.Spec, 18 importModules ...wazero.CompiledModule, 19 ) error { 20 err := ValidateModuleImports(module, importModules...) 21 if err != nil { 22 return err 23 } 24 25 return ValidateModuleAsEntryPoint(module, job.Wasm.EntryPoint) 26 } 27 28 // ValidateModuleImports will return an error if the passed module requires 29 // imports that are not found in any of the passed importModules. Imports have 30 // to match exactly, i.e. function names and signatures must be an exact match. 31 func ValidateModuleImports( 32 module wazero.CompiledModule, 33 importModules ...wazero.CompiledModule, 34 ) error { 35 availableImports := make(map[string]api.FunctionDefinition) 36 for _, importModule := range importModules { 37 maps.Copy(importModule.ExportedFunctions(), availableImports) 38 } 39 40 for _, requiredImport := range module.ImportedFunctions() { 41 importNamespace, funcName, _ := requiredImport.Import() 42 exists := false 43 for _, importModule := range importModules { 44 _, exists = importModule.ExportedFunctions()[funcName] 45 if exists { 46 err := ValidateModuleHasFunction( 47 importModule, 48 funcName, 49 requiredImport.ParamTypes(), 50 requiredImport.ResultTypes(), 51 ) 52 53 // If the module has the import but the signature doesn't match, 54 // as we enforce that imports are unique, this will break even 55 // if there is another import with correct name and signature. 56 if err != nil { 57 return err 58 } 59 } 60 } 61 62 if !exists { 63 // We didn't find an export from any module. 64 return fmt.Errorf("no export found for '%s::%s' required by module", importNamespace, funcName) 65 } 66 } 67 68 return nil 69 } 70 71 // ValidateModuleAsEntryPoint returns an error if the passed module is not 72 // capable of being an entry point to a job, i.e. that it contains a function of 73 // the passed name that meets the specification of: 74 // 75 // - the named function exists and is exported 76 // - the function takes no parameters 77 // - the function returns one i32 value (exit code) 78 func ValidateModuleAsEntryPoint( 79 module wazero.CompiledModule, 80 name string, 81 ) error { 82 return ValidateModuleHasFunction( 83 module, 84 name, 85 []api.ValueType{}, 86 []api.ValueType{}, 87 ) 88 } 89 90 // ValidateModuleHasFunction returns an error if the passed module does not 91 // contain an exported function with the passed name, parameters and return 92 // values. 93 func ValidateModuleHasFunction( 94 module wazero.CompiledModule, 95 name string, 96 parameters []api.ValueType, 97 results []api.ValueType, 98 ) error { 99 function, ok := module.ExportedFunctions()[name] 100 if !ok { 101 return fmt.Errorf("function '%s' required but no WASM export with that name was found", name) 102 } 103 104 if len(function.ParamTypes()) != len(parameters) { 105 return fmt.Errorf("function '%s' should take %d parameters", name, len(parameters)) 106 } 107 for i := range parameters { 108 expectedType := parameters[i] 109 actualType := function.ParamTypes()[i] 110 if expectedType != actualType { 111 return fmt.Errorf("function '%s': expected param %d to have type %v", name, i, expectedType) 112 } 113 } 114 115 if len(function.ResultTypes()) != len(results) { 116 return fmt.Errorf("function '%s' should return %d results", name, len(results)) 117 } 118 for i := range results { 119 expectedType := results[i] 120 actualType := function.ResultTypes()[i] 121 if expectedType != actualType { 122 return fmt.Errorf("function '%s': expected result %d to have type %v", name, i, expectedType) 123 } 124 } 125 126 return nil 127 }