You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为Offchain Worker注册自定义扩展后取值为None的问题求助

问题:Offchain Worker自定义扩展注册后调用返回None

我已完成以下操作来为Offchain Worker注册自定义扩展:

  1. 编写了如下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)
    }
}
  1. 在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(),
    );
}
  1. 在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 15:26:31