为Offchain Worker注册自定义扩展后取值为None的问题求助
问题:Offchain Worker自定义扩展注册后调用返回None
我已完成以下操作来为Offchain Worker注册自定义扩展:
- 编写了如下Rust代码定义CustomExt及相关接口:
#[cfg(feature = "std")] use sp_externalities::ExternalitiesExt; #[cfg(feature = "std")] sp_externalities::decl_extension! { pub struct CustomExt(u32); } #[cfg(feature = "std")] pub type HostFunctions = (custom::HostFunctions,); #[runtime_interface] pub trait Custom { fn get_val(&mut self) -> Option<u32> { self.extension::<CustomExt>().map(|ext| ext.0) } }
- 在service.rs中将扩展传入custom_extensions:
if enable_offchain_worker { task_manager.spawn_handle().spawn( "offchain-workers-runner", "offchain-work", sc_offchain::OffchainWorkers::new(sc_offchain::OffchainWorkerOptions { ... custom_extensions: move |_| { vec![ Box::new(statement_store.clone().as_statement_store_ext()) as Box<_>, // This should register my extension. Box::new(CustomExt(10)) as Box<_>, ] }, }) .run(client.clone(), task_manager.spawn_handle()) .boxed(), ); }
- 在executor.rs中添加了对应的HostFunctions:
impl sc_executor::NativeExecutionDispatch for ExecutorDispatch { type ExtendHostFunctions = ( frame_benchmarking::benchmarking::HostFunctions, sp_statement_store::runtime_api::HostFunctions, my_custom_ext::HostFunctions, ); fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> { // Omitted } fn native_version() -> sc_executor::NativeVersion { // Omitted } }
但当我调用以下代码尝试获取值时,始终得到None:
if let Some(key) = my_custom_ext::get_val() { log::info!("Got value {key}"); } else { log::warn!("Got None"); }
我无法找出扩展失效的原因,请问还有哪些配置步骤遗漏了?
解决方案:检查以下遗漏步骤
1. 对齐runtime_interface的命名空间
你定义的HostFunctions是(custom::HostFunctions,),但#[runtime_interface]标注的trait未指定命名空间,导致生成的HostFunctions不属于custom命名空间。需要显式指定命名空间:
#[runtime_interface(namespace = "custom")] pub trait Custom { fn get_val(&mut self) -> Option<u32> { self.extension::<CustomExt>().map(|ext| ext.0) } }
2. 验证ExecutorDispatch的dispatch方法转发逻辑
ExecutorDispatch的dispatch方法需要正确处理自定义runtime接口的调用,否则会导致请求无法传递到扩展。补充实现对应逻辑:
fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> { match method { "custom_get_val" => { let result = sp_externalities::with_externalities(|ext| { my_custom_ext::Custom::get_val(ext) }); Some(scale::Encode::encode(&result)) } // 保留原有其他方法的处理逻辑 _ => { // 原有的dispatch实现 } } }
3. 确认扩展在Offchain上下文的挂载有效性
在custom_extensions闭包中添加调试日志,确认CustomExt是否被正确创建并返回:
custom_extensions: move |_| { let custom_ext = Box::new(CustomExt(10)) as Box<_>; log::debug!("CustomExt created with value: {}", (custom_ext.as_ref() as &CustomExt).0); vec![ Box::new(statement_store.clone().as_statement_store_ext()) as Box<_>, custom_ext, ] },
同时检查日志输出,确认扩展确实被初始化。
4. 确保调用get_val()的上下文拥有有效Externalities
get_val()必须在已挂载Externalities的上下文(比如Offchain Worker的hook函数内)调用。如果是在普通native代码中直接调用,会因没有上下文而返回None。确认调用位置是在offchain_worker hook或其他拥有Externalities的环境中:
#[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { fn offchain_worker(block_number: BlockNumberFor<T>) { // 此处拥有有效Externalities上下文 if let Some(val) = my_custom_ext::get_val() { log::info!("Got value: {}", val); } } }
5. 检查依赖版本兼容性
确保sp_externalities、sc_executor等依赖的版本与Substrate框架版本完全匹配,版本不兼容可能导致扩展的trait实现失效,无法被上下文识别。
内容的提问来源于stack exchange,提问作者Sachin
相关产品推荐
相关产品推荐

