- Published on
Polkadot Guide: Offchain Workers là gì?
Language: Vietnamese
Cấp độ: Intermediate
Offchain workers là gì?
Thông thường, khi các Extrinsics
được khai báo ở tầng Pallet hay các API được khai báo ở tấng Runtime, các hàm thức này sẽ được thực thi tại môi trường Runtime cốt lõi của Substrate. Tuy nhiên, điều này sẽ tạo ra một số rào cản nhất định khi phát triển công nghệ nhưng chỉ phụ thuộc vào onchain. Lý do đến từ việc onchain có bảo mật chặt chẽ và tài nguyên giới hạn về mặt lưu trữ dữ liệu và tốc độ thực thi.
Vậy nên, Offchain workers là một phần quan trọng không thể thiếu của Substrate để giải quyết các vấn đề không cần dùng đến tài nguyên onchain như query dữ liệu từ các nguồn dữ liệu offchain trước khi cập nhật trạng thái trên blockchain. Một ví dụ điển hình mà bạn có thể tham khảo là các Oracle như Chainlink
trên Ethereum hay Pyth Network
trên Solana.
Offchain workers được xem là thành phần phụ của các hệ thống liên quan đến các tác phụ cần thời gian xử lý lâu và không nhất quán như là gửi / nhận yêu cầu đến các máy chủ web2, sản sinh số tự nhiên, tính toán cần thông lượng CPU lớn...Hiểu một cách đơn giản thì các tác vụ nào có thời gian thực thi lớn hơn thời gian thực thi tối đa trên một block thì tác vụ đó có thể giải quyết được bằng Offchain workers.
Bạn có thể xem thêm mã nguồn của Price Oracle Pallet được triển khai sử dụng Offchain workers tại đây: Code Breakdown @ Pallet Price Oracles
Ví dụ về cách triển khai Offchain workers trong Pallet
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
/// Offchain Worker entry point.
///
/// By implementing `fn offchain_worker` you declare a new offchain worker.
/// This function will be called when the node is fully synced and a new best block is
/// successfully imported.
fn offchain_worker(block_number: BlockNumberFor<T>)
Bên trong Pallet của bạn, nếu bạn chưa biết về Pallet xem qua bài này Polkadot Guide: Pallet là gì? hoặc Code Breakdown: Template for FRAME pallet để hiểu thêm về Pallet trước khi tìm hiểu đến Offchain Workers.
Để khởi tạo entry point
của Offchain workers bên trong Pallet, bạn sẽ cần khởi tạo trait Hooks
cho struct Pallet
#[pallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T>
#[pallet::hooks]
là macro định nghĩa code triển khai và cung cấp thông tin cho Runtime về offchain workers.BlockNumberFor
là alias của loại dữ liệuBlockNumber
Bằng việc khai báo hàm thức fn offchain_worker(block_number: BlockNumberFor<T>)
, chúng ta đã khởi tạo một offchain worker mới. Hàm thức này sẽ được gọi khi mà node hoàn toàn đồng bộ với trạng thái của toàn bộ mạng lưới và một best block
mới được thêm thành công vào mạng lưới.
Tính năng của Offchain Workers
Offchain workers cho phép nhà phát triển sử dụng một số API giúp giao tiếp với thế giới bên ngoài:
- Khả năng
submit transaction
(gửi giao dịch lên chain), cả transaction đã đượcsign
hoặcunsign
.
// -- Sign using any account
let (_, result) = Signer::<T, T::AuthorityId>::any_account()
.send_unsigned_transaction(
|account| PricePayload { price, block_number, public: account.public.clone() },
|payload, signature| Call::submit_price_unsigned_with_signed_payload {
price_payload: payload,
signature,
},
)
.ok_or("No local accounts accounts available.")?;
Ví dụ ở trên là cách gửi một transaction chưa được signed
(hay unsigned
) bằng function send_usigned_transaction
. Nói sơ một chút thì ở đây, trước đó chúng ta đã khai AuthorityId
tại phần cấu hình config
. Ở phía Runtime, chúng ta sẽ cần khai báo account
cho offchain worker trước.
Mình sẽ không nói sâu về phần này trong bài viết này, tạm thời bạn cứ hiểu là offchain worker cũng cần 1 account gắn với nó để thực thi việc ký gửi giao dịch
Cỏn đây là code mẫu cho ký và gửi signed transaction
signer.send_signed_transaction(|_account| {
Call::submit_price { price }
});
- HTTP Client cho phép offchain worker truy cập và lấy dữ liệu về từ các dịch vụ bên ngoài.
let request = sp_runtime::http::offchain::Request::get("https://min-api.cryptocompare.com/data/price?fsym=BTC&tsyms=USD");
Ví dụ truy vấn dữ liệu giá của BTC từ public API
- Truy cập vào
local keystore
để ký và xác thựcstatement
hoặctransaction
// pallet/template/lib.rs
pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"btc!");
// node/service.rs
sp_keystore::SyncCryptoStore::sr25519_generate_new(
&*keystore,
node_template_runtime::pallet_template::KEY_TYPE,
Some("//Alice"),
)
.expect("Creating key with account Alice should succeed.");
Mỗi module làm việc với signature
cần phải khai báo mã định danh đặc biết (unique identifier
) và key. Khi offchain worker ký transaction
, worker sẽ yêu cầu key với loại là KeyTypeId
như được khai báo ở trên
// pallet/template/lib.rs
pub const KEY_TYPE: KeyTypeId = KeyTypeId(*b"btc!");
Và tìm key phù hợp với các thông tin vừa cung cấp từ local keystore. Sau đó dùng key vừa tim được ký giao dịch. Các key có thể được thêm vào thủ công từ RPC (xem thêm về author_insertKey
)
- Offchain storage được chia sẻ giữa các offchain workers
- Truy cập dữ liệu thời gian cục bộ của toàn chain.
- Khả năng
sleep
vàresume
các tác vụ