作者: songtianyi create@2022-07-24
相较于 Oracle 这类通过 events log 方式, substrate 提供了一种集成度更高的链上链下的数据交互方式。
Access to the local keystore to sign and verify statements or transactions.
Off-chain storage 只存在于当前节点,不会同步到其它节点,由于 off-chain worker 会在出块的时候被触发执行,且执行是异步的,所以会存在多个 worker 执行实例,如下图:
在开发 worker 的时候要注意并发问题。
多个 worker 实例之间是可以共享 storage 的。storage 的内容可以从外部通过 rpc 来获取。
除了 off-chain storage, substrate 提供了 off-chain indexing, 它是专门用于 on-chain logic 的,而 storage 主要用于 worker. off-chain indexing 会被同步到其它节点
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T:: Origin {
// --snip--
fn offchain_worker(block: T::BlockNumber) {
debug::info!("Hello World.");
}
}
}
offchain_worker
是固定的入口函数
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T:: Origin {
// --snip--
pub fn onchain_callback(origin, _block: T::BlockNumber, input: Vec<u8>) -> dispatch::Result {
let who = ensure_signed(origin)?;
debug::info!("{:?}", core::str::from_utf8(&input).unwrap());
Ok(())
}
fn offchain_worker(block: T::BlockNumber) {
// Here we specify the function to be called back on-chain in next block import.
let call = Call::onchain_callback(block, b"hello world!".to_vec());
T::SubmitSignedTransaction::submit_signed(call);
}
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T:: Origin {
// --snip--
pub fn onchain_callback(_origin, _block: T::BlockNumber, input: Vec<u8>) -> dispatch::Result {
debug::info!("{:?}", core::str::from_utf8(&input).unwrap());
Ok(())
}
fn offchain_worker(block: T::BlockNumber) {
// Here we specify the function to be called back on-chain in next block import.
let call = Call::onchain_callback(block, b"hello world!".to_vec());
T::SubmitUnsignedTransaction::submit_unsigned(call);
}
}
}
去掉校验即可
use sp_runtime::{
offchain::http,
transaction_validity::{
TransactionValidity, TransactionLongevity, ValidTransaction, InvalidTransaction
}
}; // --snip--decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T:: Origin {
// --snip--
fn offchain_worker(block: T:: BlockNumber) {
match Self::fetch_data() {
Ok(res) => debug::info!("Result: {}", core::str::from_utf8(&res).unwrap()),
Err(e) => debug::error!("Error fetch_data: {}", e),
};
}
}
}impl<T: Trait> Module<T> {
fn fetch_data() -> Result<Vec<u8>, &'static str> {
// Specifying the request
let pending = http::Request::get("https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD")
.send()
.map_err(|_| "Error in sending http GET request")?;
// Waiting for the response
let response = pending.wait()
.map_err(|_| "Error in waiting http response back")?;
// Check if the HTTP response is okay
if response.code != 200 {
debug::warn!("Unexpected status code: {}", response.code);
return Err("Non-200 status code returned from http request");
}
// Collect the result in the form of bytes
Ok(response.body().collect::<Vec<u8>>())
}
}