mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 23:18:01 +00:00
Initial: Offchain Workers (#1942)
* Refactor state-machine stuff. * Fix tests. * WiP * WiP2 * Service support for offchain workers. * Service support for offchain workers. * Testing offchain worker. * Initial version working. * Pass side effects in call. * Pass OffchainExt in context. * Submit extrinsics to the pool. * Support inherents. * Insert to inherents pool. * Inserting to the pool asynchronously. * Add test to offchain worker. * Implement convenience syntax for modules. * Dispatching offchain worker through executive. * Fix offchain test. * Remove offchain worker from timestamp. * Update Cargo.lock. * Address review comments. * Use latest patch version for futures. * Add CLI parameter for offchain worker. * Fix compilation. * Fix test. * Fix extrinsics format for tests. * Fix RPC test. * Bump spec version. * Fix executive. * Fix support macro. * Address grumbles. * Bump runtime
This commit is contained in:
@@ -24,6 +24,7 @@ use trie::trie_root;
|
||||
use primitives::storage::well_known_keys::{CHANGES_TRIE_CONFIG, CODE, HEAP_PAGES};
|
||||
use parity_codec::Encode;
|
||||
use super::{Externalities, OverlayedChanges};
|
||||
use log::warn;
|
||||
|
||||
/// Simple HashMap-based Externalities impl.
|
||||
pub struct BasicExternalities {
|
||||
@@ -151,6 +152,11 @@ impl<H: Hasher> Externalities<H> for BasicExternalities where H::Out: Ord + Heap
|
||||
fn storage_changes_root(&mut self, _parent: H::Out, _parent_num: u64) -> Option<H::Out> {
|
||||
None
|
||||
}
|
||||
|
||||
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) -> Result<(), ()> {
|
||||
warn!("Call to submit_extrinsic without offchain externalities set.");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -20,7 +20,7 @@ use std::{error, fmt, cmp::Ord};
|
||||
use log::warn;
|
||||
use crate::backend::{Backend, Consolidate};
|
||||
use crate::changes_trie::{AnchorBlockId, Storage as ChangesTrieStorage, compute_changes_trie_root};
|
||||
use crate::{Externalities, OverlayedChanges};
|
||||
use crate::{Externalities, OverlayedChanges, OffchainExt};
|
||||
use hash_db::Hasher;
|
||||
use primitives::storage::well_known_keys::is_child_storage_key;
|
||||
use trie::{MemoryDB, TrieDBMut, TrieMut, default_child_trie_root, is_child_trie_key_valid};
|
||||
@@ -58,12 +58,11 @@ impl<B: error::Error, E: error::Error> error::Error for Error<B, E> {
|
||||
}
|
||||
|
||||
/// Wraps a read-only backend, call executor, and current overlayed changes.
|
||||
pub struct Ext<'a, H, B, T>
|
||||
pub struct Ext<'a, H, B, T, O>
|
||||
where
|
||||
H: Hasher,
|
||||
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H>,
|
||||
{
|
||||
/// The overlayed changes to write to.
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
@@ -81,23 +80,34 @@ where
|
||||
/// `storage_changes_root` is called matters + we need to remember additional
|
||||
/// data at this moment (block number).
|
||||
changes_trie_transaction: Option<(u64, MemoryDB<H>, H::Out)>,
|
||||
/// Additional externalities for offchain workers.
|
||||
///
|
||||
/// If None, some methods from the trait might not supported.
|
||||
offchain_externalities: Option<&'a mut O>,
|
||||
}
|
||||
|
||||
impl<'a, H, B, T> Ext<'a, H, B, T>
|
||||
impl<'a, H, B, T, O> Ext<'a, H, B, T, O>
|
||||
where
|
||||
H: Hasher,
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H>,
|
||||
O: 'a + OffchainExt,
|
||||
H::Out: Ord + HeapSizeOf,
|
||||
{
|
||||
/// Create a new `Ext` from overlayed changes and read-only backend
|
||||
pub fn new(overlay: &'a mut OverlayedChanges, backend: &'a B, changes_trie_storage: Option<&'a T>) -> Self {
|
||||
pub fn new(
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
backend: &'a B,
|
||||
changes_trie_storage: Option<&'a T>,
|
||||
offchain_externalities: Option<&'a mut O>,
|
||||
) -> Self {
|
||||
Ext {
|
||||
overlay,
|
||||
backend,
|
||||
storage_transaction: None,
|
||||
changes_trie_storage,
|
||||
changes_trie_transaction: None,
|
||||
offchain_externalities,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,12 +162,13 @@ where
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<'a, H, B, T> Ext<'a, H, B, T>
|
||||
impl<'a, H, B, T, O> Ext<'a, H, B, T, O>
|
||||
where
|
||||
H: Hasher,
|
||||
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H>,
|
||||
O: 'a + OffchainExt,
|
||||
{
|
||||
pub fn storage_pairs(&self) -> Vec<(Vec<u8>, Vec<u8>)> {
|
||||
use std::collections::HashMap;
|
||||
@@ -173,11 +184,12 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, B: 'a, T: 'a, H> Externalities<H> for Ext<'a, H, B, T>
|
||||
impl<'a, B, T, H, O> Externalities<H> for Ext<'a, H, B, T, O>
|
||||
where
|
||||
H: Hasher,
|
||||
B: 'a + Backend<H>,
|
||||
T: 'a + ChangesTrieStorage<H>,
|
||||
O: 'a + OffchainExt,
|
||||
H::Out: Ord + HeapSizeOf,
|
||||
{
|
||||
fn storage(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
@@ -329,6 +341,17 @@ where
|
||||
self.changes_trie_transaction = root_and_tx;
|
||||
root
|
||||
}
|
||||
|
||||
fn submit_extrinsic(&mut self, extrinsic: Vec<u8>) -> Result<(), ()> {
|
||||
let _guard = panic_handler::AbortGuard::new(true);
|
||||
if let Some(ext) = self.offchain_externalities.as_mut() {
|
||||
ext.submit_extrinsic(extrinsic);
|
||||
Ok(())
|
||||
} else {
|
||||
warn!("Call to submit_extrinsic without offchain externalities set.");
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -345,7 +368,7 @@ mod tests {
|
||||
|
||||
type TestBackend = InMemory<Blake2Hasher>;
|
||||
type TestChangesTrieStorage = InMemoryChangesTrieStorage<Blake2Hasher>;
|
||||
type TestExt<'a> = Ext<'a, Blake2Hasher, TestBackend, TestChangesTrieStorage>;
|
||||
type TestExt<'a> = Ext<'a, Blake2Hasher, TestBackend, TestChangesTrieStorage, crate::NeverOffchainExt>;
|
||||
|
||||
fn prepare_overlay_with_changes() -> OverlayedChanges {
|
||||
OverlayedChanges {
|
||||
@@ -371,7 +394,7 @@ mod tests {
|
||||
fn storage_changes_root_is_none_when_storage_is_not_provided() {
|
||||
let mut overlay = prepare_overlay_with_changes();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, None);
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, None, None);
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 100), None);
|
||||
}
|
||||
|
||||
@@ -381,7 +404,7 @@ mod tests {
|
||||
overlay.changes_trie_config = None;
|
||||
let storage = TestChangesTrieStorage::new();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 100), None);
|
||||
}
|
||||
|
||||
@@ -390,7 +413,7 @@ mod tests {
|
||||
let mut overlay = prepare_overlay_with_changes();
|
||||
let storage = TestChangesTrieStorage::new();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 99),
|
||||
Some(hex!("5b829920b9c8d554a19ee2a1ba593c4f2ee6fc32822d083e04236d693e8358d5").into()));
|
||||
}
|
||||
@@ -401,7 +424,7 @@ mod tests {
|
||||
overlay.prospective.top.get_mut(&vec![1]).unwrap().value = None;
|
||||
let storage = TestChangesTrieStorage::new();
|
||||
let backend = TestBackend::default();
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage));
|
||||
let mut ext = TestExt::new(&mut overlay, &backend, Some(&storage), None);
|
||||
assert_eq!(ext.storage_changes_root(Default::default(), 99),
|
||||
Some(hex!("bcf494e41e29a15c9ae5caa053fe3cb8b446ee3e02a254efbdec7a19235b76e4").into()));
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ use log::warn;
|
||||
use hash_db::Hasher;
|
||||
use heapsize::HeapSizeOf;
|
||||
use parity_codec::{Decode, Encode};
|
||||
use primitives::{storage::well_known_keys, NativeOrEncoded, NeverNativeValue};
|
||||
use primitives::{storage::well_known_keys, NativeOrEncoded, NeverNativeValue, OffchainExt};
|
||||
|
||||
pub mod backend;
|
||||
mod changes_trie;
|
||||
@@ -153,6 +153,25 @@ pub trait Externalities<H: Hasher> {
|
||||
|
||||
/// Get the change trie root of the current storage overlay at a block with given parent.
|
||||
fn storage_changes_root(&mut self, parent: H::Out, parent_num: u64) -> Option<H::Out> where H::Out: Ord;
|
||||
|
||||
/// Submit extrinsic.
|
||||
///
|
||||
/// Returns an error in case the API is not available.
|
||||
fn submit_extrinsic(&mut self, extrinsic: Vec<u8>) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
/// An implementation of offchain extensions that should never be triggered.
|
||||
pub enum NeverOffchainExt {}
|
||||
|
||||
impl NeverOffchainExt {
|
||||
/// Create new offchain extensions.
|
||||
pub fn new<'a>() -> Option<&'a mut Self> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl OffchainExt for NeverOffchainExt {
|
||||
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) { unreachable!() }
|
||||
}
|
||||
|
||||
/// Code execution engine.
|
||||
@@ -252,17 +271,19 @@ pub fn always_wasm<E, R: Decode>() -> ExecutionManager<DefaultHandler<R, E>> {
|
||||
}
|
||||
|
||||
/// Creates new substrate state machine.
|
||||
pub fn new<'a, H, B, T, Exec>(
|
||||
pub fn new<'a, H, B, T, O, Exec>(
|
||||
backend: &'a B,
|
||||
changes_trie_storage: Option<&'a T>,
|
||||
offchain_ext: Option<&'a mut O>,
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
exec: &'a Exec,
|
||||
method: &'a str,
|
||||
call_data: &'a [u8],
|
||||
) -> StateMachine<'a, H, B, T, Exec> {
|
||||
) -> StateMachine<'a, H, B, T, O, Exec> {
|
||||
StateMachine {
|
||||
backend,
|
||||
changes_trie_storage,
|
||||
offchain_ext,
|
||||
overlay,
|
||||
exec,
|
||||
method,
|
||||
@@ -272,9 +293,10 @@ pub fn new<'a, H, B, T, Exec>(
|
||||
}
|
||||
|
||||
/// The substrate state machine.
|
||||
pub struct StateMachine<'a, H, B, T, Exec> {
|
||||
pub struct StateMachine<'a, H, B, T, O, Exec> {
|
||||
backend: &'a B,
|
||||
changes_trie_storage: Option<&'a T>,
|
||||
offchain_ext: Option<&'a mut O>,
|
||||
overlay: &'a mut OverlayedChanges,
|
||||
exec: &'a Exec,
|
||||
method: &'a str,
|
||||
@@ -282,11 +304,12 @@ pub struct StateMachine<'a, H, B, T, Exec> {
|
||||
_hasher: PhantomData<H>,
|
||||
}
|
||||
|
||||
impl<'a, H, B, T, Exec> StateMachine<'a, H, B, T, Exec> where
|
||||
impl<'a, H, B, T, O, Exec> StateMachine<'a, H, B, T, O, Exec> where
|
||||
H: Hasher,
|
||||
Exec: CodeExecutor<H>,
|
||||
B: Backend<H>,
|
||||
T: ChangesTrieStorage<H>,
|
||||
O: OffchainExt,
|
||||
H::Out: Ord + HeapSizeOf,
|
||||
{
|
||||
/// Execute a call using the given state backend, overlayed changes, and call executor.
|
||||
@@ -324,7 +347,13 @@ impl<'a, H, B, T, Exec> StateMachine<'a, H, B, T, Exec> where
|
||||
R: Decode + Encode + PartialEq,
|
||||
NC: FnOnce() -> result::Result<R, &'static str> + UnwindSafe,
|
||||
{
|
||||
let mut externalities = ext::Ext::new(self.overlay, self.backend, self.changes_trie_storage);
|
||||
let offchain = self.offchain_ext.as_mut();
|
||||
let mut externalities = ext::Ext::new(
|
||||
self.overlay,
|
||||
self.backend,
|
||||
self.changes_trie_storage,
|
||||
offchain.map(|x| &mut **x),
|
||||
);
|
||||
let (result, was_native) = self.exec.call(
|
||||
&mut externalities,
|
||||
self.method,
|
||||
@@ -505,6 +534,7 @@ where
|
||||
let mut sm = StateMachine {
|
||||
backend: &proving_backend,
|
||||
changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>,
|
||||
offchain_ext: NeverOffchainExt::new(),
|
||||
overlay,
|
||||
exec,
|
||||
method,
|
||||
@@ -554,6 +584,7 @@ where
|
||||
let mut sm = StateMachine {
|
||||
backend: trie_backend,
|
||||
changes_trie_storage: None as Option<&changes_trie::InMemoryStorage<H>>,
|
||||
offchain_ext: NeverOffchainExt::new(),
|
||||
overlay,
|
||||
exec,
|
||||
method,
|
||||
@@ -730,6 +761,7 @@ mod tests {
|
||||
assert_eq!(new(
|
||||
&trie_backend::tests::test_trie(),
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
NeverOffchainExt::new(),
|
||||
&mut Default::default(),
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: false,
|
||||
@@ -750,6 +782,7 @@ mod tests {
|
||||
assert_eq!(new(
|
||||
&trie_backend::tests::test_trie(),
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
NeverOffchainExt::new(),
|
||||
&mut Default::default(),
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: false,
|
||||
@@ -770,6 +803,7 @@ mod tests {
|
||||
assert!(new(
|
||||
&trie_backend::tests::test_trie(),
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
NeverOffchainExt::new(),
|
||||
&mut Default::default(),
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: false,
|
||||
@@ -838,7 +872,7 @@ mod tests {
|
||||
|
||||
{
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::new();
|
||||
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage));
|
||||
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage), NeverOffchainExt::new());
|
||||
ext.clear_prefix(b"ab");
|
||||
}
|
||||
overlay.commit_prospective();
|
||||
@@ -862,7 +896,7 @@ mod tests {
|
||||
let backend = InMemory::<Blake2Hasher>::default().try_into_trie_backend().unwrap();
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::new();
|
||||
let mut overlay = OverlayedChanges::default();
|
||||
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage));
|
||||
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage), NeverOffchainExt::new());
|
||||
|
||||
assert!(ext.set_child_storage(b":child_storage:testchild".to_vec(), b"abc".to_vec(), b"def".to_vec()));
|
||||
assert_eq!(ext.child_storage(b":child_storage:testchild", b"abc"), Some(b"def".to_vec()));
|
||||
@@ -889,6 +923,7 @@ mod tests {
|
||||
assert!(new(
|
||||
&trie_backend::tests::test_trie(),
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
NeverOffchainExt::new(),
|
||||
&mut Default::default(),
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: true,
|
||||
@@ -908,6 +943,7 @@ mod tests {
|
||||
assert!(new(
|
||||
&trie_backend::tests::test_trie(),
|
||||
Some(&InMemoryChangesTrieStorage::new()),
|
||||
NeverOffchainExt::new(),
|
||||
&mut Default::default(),
|
||||
&DummyCodeExecutor {
|
||||
change_changes_trie_config: true,
|
||||
|
||||
@@ -375,7 +375,12 @@ mod tests {
|
||||
};
|
||||
|
||||
let changes_trie_storage = InMemoryChangesTrieStorage::new();
|
||||
let mut ext = Ext::new(&mut overlay, &backend, Some(&changes_trie_storage));
|
||||
let mut ext = Ext::new(
|
||||
&mut overlay,
|
||||
&backend,
|
||||
Some(&changes_trie_storage),
|
||||
crate::NeverOffchainExt::new(),
|
||||
);
|
||||
const ROOT: [u8; 32] = hex!("0b41e488cccbd67d1f1089592c2c235f5c5399b053f7fe9152dd4b5f279914cd");
|
||||
assert_eq!(ext.storage_root(), H256::from(ROOT));
|
||||
}
|
||||
|
||||
@@ -168,6 +168,10 @@ impl<H: Hasher> Externalities<H> for TestExternalities<H> where H::Out: Ord + He
|
||||
&AnchorBlockId { hash: parent, number: parent_num },
|
||||
).map(|(root, _)| root.clone())
|
||||
}
|
||||
|
||||
fn submit_extrinsic(&mut self, _extrinsic: Vec<u8>) -> Result<(), ()> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
Reference in New Issue
Block a user