@@ -249,65 +249,82 @@ impl WasmInstance {
249249 }
250250}
251251
252- /// Import names that are handled by builtin host functions. Any import name
253- /// in `import_name_to_modules` that is not in this set and is not `gas` is
254- /// assumed to be a chain-specific host function.
255- const BUILTIN_IMPORT_NAMES : & [ & str ] = & [
256- "ethereum.encode" ,
257- "ethereum.decode" ,
258- "abort" ,
259- "store.get" ,
260- "store.loadRelated" ,
261- "store.get_in_block" ,
262- "store.set" ,
263- "store.remove" ,
264- "ipfs.cat" ,
265- "ipfs.map" ,
266- "ipfs.getBlock" ,
267- "typeConversion.bytesToString" ,
268- "typeConversion.bytesToHex" ,
269- "typeConversion.bigIntToString" ,
270- "typeConversion.bigIntToHex" ,
271- "typeConversion.stringToH160" ,
272- "typeConversion.bytesToBase58" ,
273- "json.fromBytes" ,
274- "json.try_fromBytes" ,
275- "json.toI64" ,
276- "json.toU64" ,
277- "json.toF64" ,
278- "json.toBigInt" ,
279- "yaml.fromBytes" ,
280- "yaml.try_fromBytes" ,
281- "crypto.keccak256" ,
282- "bigInt.plus" ,
283- "bigInt.minus" ,
284- "bigInt.times" ,
285- "bigInt.dividedBy" ,
286- "bigInt.dividedByDecimal" ,
287- "bigInt.mod" ,
288- "bigInt.pow" ,
289- "bigInt.fromString" ,
290- "bigInt.bitOr" ,
291- "bigInt.bitAnd" ,
292- "bigInt.leftShift" ,
293- "bigInt.rightShift" ,
294- "bigDecimal.toString" ,
295- "bigDecimal.fromString" ,
296- "bigDecimal.plus" ,
297- "bigDecimal.minus" ,
298- "bigDecimal.times" ,
299- "bigDecimal.dividedBy" ,
300- "bigDecimal.equals" ,
301- "dataSource.create" ,
302- "dataSource.createWithContext" ,
303- "dataSource.address" ,
304- "dataSource.network" ,
305- "dataSource.context" ,
306- "ens.nameByHash" ,
307- "log.log" ,
308- "arweave.transactionData" ,
309- "box.profile" ,
310- ] ;
252+ /// Register a chain-specific host function dispatcher for the given import name.
253+ /// The registered closure looks up the `HostFn` by name from `caller.data().ctx.host_fns`
254+ /// at call time. If the module doesn't import this function, this is a no-op.
255+ fn link_chain_host_fn (
256+ linker : & mut Linker < WasmInstanceData > ,
257+ import_name_to_modules : & BTreeMap < String , Vec < String > > ,
258+ name : & ' static str ,
259+ ) -> Result < ( ) , anyhow:: Error > {
260+ let modules = match import_name_to_modules. get ( name) {
261+ Some ( m) => m,
262+ None => return Ok ( ( ) ) ,
263+ } ;
264+
265+ let name_for_metrics = name. replace ( '.' , "_" ) ;
266+ let section_name = format ! ( "host_export_{}" , name_for_metrics) ;
267+
268+ for module in modules {
269+ let name_for_metrics = name_for_metrics. clone ( ) ;
270+ let section_name = section_name. clone ( ) ;
271+ linker. func_wrap_async (
272+ module,
273+ name,
274+ move |mut caller : wasmtime:: Caller < ' _ , WasmInstanceData > , ( call_ptr, ) : ( u32 , ) | {
275+ let name_for_metrics = name_for_metrics. clone ( ) ;
276+ let section_name = section_name. clone ( ) ;
277+ Box :: new ( async move {
278+ let host_fn = caller
279+ . data ( )
280+ . ctx
281+ . host_fns
282+ . iter ( )
283+ . find ( |hf| hf. name == name)
284+ . ok_or_else ( || {
285+ anyhow:: anyhow!(
286+ "chain host function '{}' is not available for this chain" ,
287+ name
288+ )
289+ } ) ?
290+ . cheap_clone ( ) ;
291+
292+ let start = Instant :: now ( ) ;
293+
294+ let gas = caller. data ( ) . gas . cheap_clone ( ) ;
295+ let host_metrics = caller. data ( ) . host_metrics . cheap_clone ( ) ;
296+ let stopwatch = host_metrics. stopwatch . cheap_clone ( ) ;
297+ let _section = stopwatch. start_section ( & section_name) ;
298+
299+ let ctx = HostFnCtx {
300+ logger : caller. data ( ) . ctx . logger . cheap_clone ( ) ,
301+ block_ptr : caller. data ( ) . ctx . block_ptr . cheap_clone ( ) ,
302+ gas : gas. cheap_clone ( ) ,
303+ metrics : host_metrics. cheap_clone ( ) ,
304+ heap : & mut WasmInstanceContext :: new ( & mut caller) ,
305+ } ;
306+ let ret = ( host_fn. func ) ( ctx, call_ptr) . await . map_err ( |e| match e {
307+ HostExportError :: Deterministic ( e) => {
308+ caller. data_mut ( ) . deterministic_host_trap = true ;
309+ e
310+ }
311+ HostExportError :: PossibleReorg ( e) => {
312+ caller. data_mut ( ) . possible_reorg = true ;
313+ e
314+ }
315+ HostExportError :: Unknown ( e) => e,
316+ } ) ?;
317+ host_metrics. observe_host_fn_execution_time (
318+ start. elapsed ( ) . as_secs_f64 ( ) ,
319+ & name_for_metrics,
320+ ) ;
321+ Ok ( ret)
322+ } )
323+ } ,
324+ ) ?;
325+ }
326+ Ok ( ( ) )
327+ }
311328
312329/// Build a pre-linked `Linker` for a WASM module. This linker can be reused across triggers by
313330/// calling `linker.instantiate_pre()` once and then `instance_pre.instantiate_async()` per trigger.
@@ -431,70 +448,11 @@ pub(crate) fn build_linker(
431448 } ;
432449 }
433450
434- // Link chain-specific host fns. Any import name not in BUILTIN_IMPORT_NAMES and not "gas"
435- // is assumed to be a chain-specific host function. We register a generic dispatcher that
436- // looks up the actual HostFn by name from caller.data().ctx.host_fns at call time.
437- for ( import_name, modules) in import_name_to_modules {
438- if import_name == "gas" || BUILTIN_IMPORT_NAMES . contains ( & import_name. as_str ( ) ) {
439- continue ;
440- }
441-
442- // Leak the name so we get a &'static str for metrics. These are a small, fixed set of
443- // chain host_fn names (e.g. "ethereum.call") so the leak is bounded.
444- let name: & ' static str = Box :: leak ( import_name. clone ( ) . into_boxed_str ( ) ) ;
445-
446- for module in modules {
447- linker. func_wrap_async (
448- module,
449- name,
450- move |mut caller : wasmtime:: Caller < ' _ , WasmInstanceData > , ( call_ptr, ) : ( u32 , ) | {
451- Box :: new ( async move {
452- let host_fn = caller
453- . data ( )
454- . ctx
455- . host_fns
456- . iter ( )
457- . find ( |hf| hf. name == name)
458- . expect ( "chain host_fn not found" )
459- . cheap_clone ( ) ;
460-
461- let start = Instant :: now ( ) ;
462-
463- let gas = caller. data ( ) . gas . cheap_clone ( ) ;
464- let name_for_metrics = name. replace ( '.' , "_" ) ;
465- let host_metrics = caller. data ( ) . host_metrics . cheap_clone ( ) ;
466- let stopwatch = host_metrics. stopwatch . cheap_clone ( ) ;
467- let _section =
468- stopwatch. start_section ( & format ! ( "host_export_{}" , name_for_metrics) ) ;
469-
470- let ctx = HostFnCtx {
471- logger : caller. data ( ) . ctx . logger . cheap_clone ( ) ,
472- block_ptr : caller. data ( ) . ctx . block_ptr . cheap_clone ( ) ,
473- gas : gas. cheap_clone ( ) ,
474- metrics : host_metrics. cheap_clone ( ) ,
475- heap : & mut WasmInstanceContext :: new ( & mut caller) ,
476- } ;
477- let ret = ( host_fn. func ) ( ctx, call_ptr) . await . map_err ( |e| match e {
478- HostExportError :: Deterministic ( e) => {
479- caller. data_mut ( ) . deterministic_host_trap = true ;
480- e
481- }
482- HostExportError :: PossibleReorg ( e) => {
483- caller. data_mut ( ) . possible_reorg = true ;
484- e
485- }
486- HostExportError :: Unknown ( e) => e,
487- } ) ?;
488- host_metrics. observe_host_fn_execution_time (
489- start. elapsed ( ) . as_secs_f64 ( ) ,
490- & name_for_metrics,
491- ) ;
492- Ok ( ret)
493- } )
494- } ,
495- ) ?;
496- }
497- }
451+ // Chain-specific host functions. Each is registered explicitly rather than
452+ // discovered dynamically from imports.
453+ link_chain_host_fn ( & mut linker, import_name_to_modules, "ethereum.call" ) ?;
454+ link_chain_host_fn ( & mut linker, import_name_to_modules, "ethereum.getBalance" ) ?;
455+ link_chain_host_fn ( & mut linker, import_name_to_modules, "ethereum.hasCode" ) ?;
498456
499457 link ! ( "ethereum.encode" , ethereum_encode, params_ptr) ;
500458 link ! ( "ethereum.decode" , ethereum_decode, params_ptr, data_ptr) ;
0 commit comments