def instantiate_wasm_invoke_func(filename,funcname,args): file_ = open(filename, 'rb') if not file_: return "error, could not open "+filename bytestar = memoryview(file_.read()) if not bytestar: return "error, could not read "+filename module = wasm.decode_module(bytestar) #get module as abstract syntax #print("module",module) if not module: return "error, could not decode "+filename store = wasm.init_store() #do this once for each VM instance externvalstar = [] #imports, hopefully none store,moduleinst,ret = wasm.instantiate_module(store,module,externvalstar) if moduleinst == "error": return "error, module could not be instantiated" #print("moduleinst",moduleinst) externval = wasm.get_export(moduleinst, funcname) if not externval or externval[0]!="func": return "error, "+funcname+" is not a function export of the module" #print("externval",externval) funcaddr = externval[1] valstar = [["i32.const",int(arg)] for arg in args] #print("valstar",valstar) store,ret = wasm.invoke_func(store,funcaddr,valstar) if ret=="trap": return "error, invokation resulted in a trap" #print("ret",ret) if type(ret)==list and len(ret)>0: ret = ret[0] return ret
def test_opcode_action_invoke(test, store, modules, registered_modules, moduleinst): if verbose > 1: print(test["action"]["field"]) #print(test["action"]) if "module" in test["action"]: moduleinst = modules[test["action"]["module"]] #print("moduleinst",moduleinst) #get function name, which could include unicode bytes like \u001b which must be converted to unicode string funcname = test["action"]["field"] #print("funcname",funcname) funcname_with_codepoints_translated = "" idx = 0 utf8_bytes = bytearray() for c in funcname: utf8_bytes += bytearray([ord(c)]) utf8_bytes = pywebassembly.spec_binary_uN_inv(len(funcname), 32) + utf8_bytes _, funcname = pywebassembly.spec_binary_name(utf8_bytes, 0) #print("funcname",funcname) #get function address funcaddr = None #print(moduleinst["exports"]) #print("moduleinst",moduleinst) #print("ok moduleinst",moduleinst) #print("test ok",test) for export in moduleinst["exports"]: #print("export[\"name\"]",export["name"]) if export["name"] == funcname: funcaddr = export["value"][1] if verbose > 2: print("funcaddr", funcaddr) #print("funcaddr",funcaddr) #funcbody = store["funcs"][funcaddr]["code"]["body"] #print(pywebassembly.print_tree_expr(funcbody)) #get args args = [] float_flag = 0 #print(test["action"]["args"]) for idx in range(len(test["action"]["args"])): type_ = test["action"]["args"][idx]["type"] value = test["action"]["args"][idx]["value"] value = int( value) #wabt outputs integers (even floats are encoded as ints) if type_ in {"f32", "f64"}: if verbose > 1: print("found float arg so skipping") float_flag = 1 #this is a hack to avoid floating point until implemented value = int2float(int(type_[1:]), value) args += [[type_ + ".const", value]] #print("args: ",args) #invoke func ret = [] #if not float_flag: _, ret = pywebassembly.invoke_func(store, funcaddr, args) #else: # num_tests_tried-=1 return ret
def juno_execute(state, msg, tx_context, computation): logger = logging.getLogger('juno') code = computation.code gas = msg.gas args = msg.data if not has_wasm_preamble(code): raise Exception("Invalid code") logger.debug('juno_execute') # if VERBOSE>1: print("call_contract(",moduleid,funcname,arg,gas,")") # spin-up a VM instance with eei module modules = {} #all moduleinst's indexed by their names, used to call funcs and resolve exports registered_modules = {} #all moduleinst's which can be imported from, indexed by their registered name store = wasm.init_store() #done once and lasts for lifetime of this abstract machine instantiate_eei_module(store,modules,gas) #instantiate the EEI module "ethereum" registered_modules["ethereum"] = modules["ethereum"] #register module "ethereum" to be import-able # instantiate module which contains the func to-be-called # module_as_bytes = trinity.get_module_as_bytes(moduleid) #TODO: need API to get module bytes module = wasm.decode_module(code) #get module as abstract syntax externvalstar = [] #populate imports for import_ in module["imports"]: if import_["module"] not in registered_modules: logger.error('module not in registered_modules') return -1, -1 importmoduleinst = registered_modules[import_["module"]] externval = None for export in importmoduleinst["exports"]: if export["name"] == import_["name"]: externval = export["value"] if externval == None: logger.error('Missing import') return None #error if externval[0] != import_["desc"][0]: logger.error('Bad imported function signature') return None #error externvalstar += [externval] store,moduleinst,ret = wasm.instantiate_module(store,module,externvalstar) # finally, call the function externval = wasm.get_export(moduleinst, "main") #we want to call function "main" funcaddr = externval[1] #the address of the funcname store,ret = wasm.invoke_func(store,funcaddr,args) #finally, invoke the function print("Initial gas was ", gas, ", gas left is ", store["gasLeft"]) computation.consume_gas(gas - store["gasLeft"], "juno")
def test_opcode_action_invoke(test, store, modules, registered_modules, moduleinst): if verbose > 1: print(test["action"]["field"]) if "module" in test["action"]: moduleinst = modules[test["action"]["module"]] #get function name, which could include unicode bytes like \u001b which must be converted to unicode string funcname = test["action"]["field"] funcname_with_codepoints_translated = "" idx = 0 utf8_bytes = binary_format.spec_binary_name_inv(funcname) _, funcname = binary_format.spec_binary_name(utf8_bytes, 0) #get function address funcaddr = None for export in moduleinst["exports"]: if export["name"] == funcname: funcaddr = export["value"][1] if verbose > 2: print("funcaddr", funcaddr) #get args args = [] float_flag = 0 for idx in range(len(test["action"]["args"])): type_ = test["action"]["args"][idx]["type"] value = test["action"]["args"][idx]["value"] value = int( value) #wabt outputs integers (even floats are encoded as ints) if type_ in {"f32", "f64"}: if verbose > 1: print("found float arg so skipping") float_flag = 1 #this is a hack to avoid floating point until implemented value = int2float(int(type_[1:]), value) args += [[type_ + ".const", value]] #invoke func ret = [] #if not float_flag: _, ret = wasm.invoke_func(store, funcaddr, args) #else: # num_tests_tried-=1 return ret
modules["mod1"] = moduleinst #instantiate second module file_ = open('mod2.wasm', 'rb') bytecode = memoryview( file_.read()) #can also use bytearray or bytes instead of memoryview module = wasm.decode_module(bytecode) #get module as abstract syntax externvalstar = [] #imports, none for fibonacci.wasm for import_ in module[ "imports"]: #for each import, look for it's matching export importmoduleinst = modules[import_["module"]] externval = None for export in importmoduleinst["exports"]: if export["name"] == import_["name"]: externval = export["value"] if externval[0] != import_["desc"][0]: print("unlinkable" ) #error: import type (func, table, mem, globa) doesn't match externvalstar += [externval] store, moduleinst, ret = wasm.instantiate_module(store, module, externvalstar) modules["mod2"] = moduleinst #call function externval = wasm.get_export(modules["mod2"], "f1") #we want to call the function "fib" funcaddr = externval[1] #the address of the funcinst for "fib" store, ret = wasm.invoke_func(store, funcaddr, []) #finally, invoke the function print(ret) #list of return values, limitted to one value in Wasm 1.0
def exec_(self, calldata): """ The following function is used to call a given contract. This will "spin-up" a new VM, execute the contract, and output the contract's return values and gas used. """ self.calldata = calldata[:] # spin-up a VM modules = {} # all moduleinst's indexed by their names, used to call funcs and resolve exports registered_modules = {} # all moduleinst's which can be imported from, indexed by their registered name store = wasm.init_store() # done once and lasts for lifetime of this abstract machine # create host module def eth2_loadPreStateRoot(store,arg): if verbose: print("eth2_loadPreStateRoot") offset = arg[0] self.module_memory[offset:offset+32] = self.state_root[:] return store,[] def eth2_blockDataSize(store,arg): if verbose: print("eth2_blockDataSize", len(self.calldata)) return store,[len(self.calldata)] def eth2_blockDataCopy(store,arg): if verbose: print("eth2_blockDataCopy") memory_offset = arg[0] calldata_offset = arg[1] length = arg[2] self.module_memory[memory_offset:memory_offset+length] = self.calldata[calldata_offset:calldata_offset+length] return store,[] def eth2_savePostStateRoot(store,arg): if verbose: print("eth2_savePostStateRoot", arg[0]) offset = arg[0] self.state_root[:] = self.module_memory[offset:offset+32] return store,[] def eth2_pushNewDeposit(store,arg): if verbose: print("eth2_pushNewDeposit") offset = arg[0] length = arg[1] return store,[] def eth2_debugPrintMem(store,arg): if verbose: print("eth2_debugPrintMem") offset = arg[0] length = arg[1] print(self.module_memory[offset:offset+length]) return store,[] wasm.alloc_func(store, [["i32"],[]], eth2_loadPreStateRoot) wasm.alloc_func(store, [[],["i32"]], eth2_blockDataSize) wasm.alloc_func(store, [["i32","i32","i32"],[]], eth2_blockDataCopy) wasm.alloc_func(store, [["i32"],[]], eth2_savePostStateRoot) wasm.alloc_func(store, [["i32","i32"],[]], eth2_pushNewDeposit) wasm.alloc_func(store, [["i32","i32"],[]], eth2_debugPrintMem) modules["env"] = {"types":[[["i32"],[]], [[],["i32"]], [["i32","i32","i32"],[]], [["i32","i32"],[]], ], "funcaddrs":[0,1,2,3,4,5], "tableaddrs":[], "memaddrs":[], "globaladdrs":[], "exports":[{"name":"eth2_loadPreStateRoot","value":["func",0]}, {"name":"eth2_blockDataSize","value":["func",1]}, {"name":"eth2_blockDataCopy","value":["func",2]}, {"name":"eth2_savePostStateRoot","value":["func",3]}, {"name":"eth2_pushNewDeposit","value":["func",4]}, {"name":"eth2_debugPrintMem","value":["func",5]}, ] } # register the host module registered_modules["env"] = modules["env"] #register module "ethereum" to be import-able # instantiate module which contains the func to-be-called module = wasm.decode_module(self.bytecode) #get module as abstract syntax externvalstar = [] #populate imports for import_ in module["imports"]: if import_["module"] not in registered_modules: return None #error importmoduleinst = registered_modules[import_["module"]] externval = None for export in importmoduleinst["exports"]: if export["name"] == import_["name"]: externval = export["value"] if externval == None: return None #error if externval[0] != import_["desc"][0]: return None #error externvalstar += [externval] store,moduleinst,ret = wasm.instantiate_module(store,module,externvalstar) # get its memory self.module_memory = store["mems"][0]["data"] # finally, call the function externval = wasm.get_export(moduleinst, "main") #we want to call function "main" funcaddr = externval[1] #the address of the funcname args = [] store,ret = wasm.invoke_func(store,funcaddr,args) #finally, invoke the function return ret