Custom RPC for Merkle Mountain Range pallet (#8137)

* Add MMR custom RPC.

* Change RuntimeApi to avoid hardcoding leaf type.

* Properly implement the new RuntimeAPI and wire up RPC.

* Extract Offchain DB as separate execution extension.

* Enable offchain DB access for offchain calls.

* Fix offchain_election tests.

* Skip block initialisation for proof generation.

* Fix integration test setup.

* Fix offchain tests. Not sure how I missed them earlier 🤷.

* Fix long line.

* One more test missing.

* Update mock for multi-phase.

* Address review grumbbles.

* Address review grumbles.

* Fix line width of a comment
This commit is contained in:
Tomasz Drwięga
2021-03-10 16:28:56 +01:00
committed by GitHub
parent 9637faae0c
commit f3d4355a20
37 changed files with 837 additions and 350 deletions
+21
View File
@@ -4112,6 +4112,7 @@ dependencies = [
"node-primitives",
"node-runtime",
"pallet-contracts-rpc",
"pallet-mmr-rpc",
"pallet-transaction-payment-rpc",
"sc-chain-spec",
"sc-client-api",
@@ -5041,6 +5042,24 @@ dependencies = [
"sp-std",
]
[[package]]
name = "pallet-mmr-rpc"
version = "3.0.0"
dependencies = [
"jsonrpc-core",
"jsonrpc-core-client",
"jsonrpc-derive",
"pallet-mmr-primitives",
"parity-scale-codec",
"serde",
"serde_json",
"sp-api",
"sp-blockchain",
"sp-core",
"sp-rpc",
"sp-runtime",
]
[[package]]
name = "pallet-multisig"
version = "3.0.0"
@@ -7548,6 +7567,7 @@ dependencies = [
"fnv",
"futures 0.3.12",
"futures-timer 3.0.2",
"hex",
"hyper 0.13.9",
"hyper-rustls",
"lazy_static",
@@ -9336,6 +9356,7 @@ dependencies = [
"sc-consensus",
"sc-executor",
"sc-light",
"sc-offchain",
"sc-service",
"serde",
"serde_json",
+1
View File
@@ -89,6 +89,7 @@ members = [
"frame/membership",
"frame/merkle-mountain-range",
"frame/merkle-mountain-range/primitives",
"frame/merkle-mountain-range/rpc",
"frame/metadata",
"frame/multisig",
"frame/nicks",
@@ -141,7 +141,7 @@ pub fn new_full(mut config: Configuration) -> Result<TaskManager, ServiceError>
if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}
@@ -323,7 +323,7 @@ pub fn new_light(mut config: Configuration) -> Result<TaskManager, ServiceError>
if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}
+2 -2
View File
@@ -219,7 +219,7 @@ pub fn new_full_base(
if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}
@@ -430,7 +430,7 @@ pub fn new_light_base(mut config: Configuration) -> Result<(
if config.offchain_worker.enabled {
sc_service::build_offchain_workers(
&config, backend.clone(), task_manager.spawn_handle(), client.clone(), network.clone(),
&config, task_manager.spawn_handle(), client.clone(), network.clone(),
);
}
+1
View File
@@ -15,6 +15,7 @@ jsonrpc-core = "15.1.0"
node-primitives = { version = "2.0.0", path = "../primitives" }
node-runtime = { version = "2.0.0", path = "../runtime" }
pallet-contracts-rpc = { version = "3.0.0", path = "../../../frame/contracts/rpc/" }
pallet-mmr-rpc = { version = "3.0.0", path = "../../../frame/merkle-mountain-range/rpc/" }
pallet-transaction-payment-rpc = { version = "3.0.0", path = "../../../frame/transaction-payment/rpc/" }
sc-client-api = { version = "3.0.0", path = "../../../client/api" }
sc-consensus-babe = { version = "0.9.0", path = "../../../client/consensus/babe" }
+5
View File
@@ -116,6 +116,7 @@ pub fn create_full<C, P, SC, B>(
HeaderMetadata<Block, Error=BlockChainError> + Sync + Send + 'static,
C::Api: substrate_frame_rpc_system::AccountNonceApi<Block, AccountId, Index>,
C::Api: pallet_contracts_rpc::ContractsRuntimeApi<Block, AccountId, Balance, BlockNumber>,
C::Api: pallet_mmr_rpc::MmrRuntimeApi<Block, <Block as sp_runtime::traits::Block>::Hash>,
C::Api: pallet_transaction_payment_rpc::TransactionPaymentRuntimeApi<Block, Balance>,
C::Api: BabeApi<Block>,
C::Api: BlockBuilder<Block>,
@@ -126,6 +127,7 @@ pub fn create_full<C, P, SC, B>(
{
use substrate_frame_rpc_system::{FullSystem, SystemApi};
use pallet_contracts_rpc::{Contracts, ContractsApi};
use pallet_mmr_rpc::{MmrApi, Mmr};
use pallet_transaction_payment_rpc::{TransactionPayment, TransactionPaymentApi};
let mut io = jsonrpc_core::IoHandler::default();
@@ -161,6 +163,9 @@ pub fn create_full<C, P, SC, B>(
io.extend_with(
ContractsApi::to_delegate(Contracts::new(client.clone()))
);
io.extend_with(
MmrApi::to_delegate(Mmr::new(client.clone()))
);
io.extend_with(
TransactionPaymentApi::to_delegate(TransactionPayment::new(client.clone()))
);
+13 -5
View File
@@ -1377,23 +1377,31 @@ impl_runtime_apis! {
impl pallet_mmr::primitives::MmrApi<
Block,
mmr::Leaf,
mmr::Hash,
> for Runtime {
fn generate_proof(leaf_index: u64) -> Result<(mmr::Leaf, mmr::Proof<mmr::Hash>), mmr::Error> {
fn generate_proof(leaf_index: u64)
-> Result<(mmr::EncodableOpaqueLeaf, mmr::Proof<mmr::Hash>), mmr::Error>
{
Mmr::generate_proof(leaf_index)
.map(|(leaf, proof)| (mmr::EncodableOpaqueLeaf::from_leaf(&leaf), proof))
}
fn verify_proof(leaf: mmr::Leaf, proof: mmr::Proof<mmr::Hash>) -> Result<(), mmr::Error> {
fn verify_proof(leaf: mmr::EncodableOpaqueLeaf, proof: mmr::Proof<mmr::Hash>)
-> Result<(), mmr::Error>
{
let leaf: mmr::Leaf = leaf
.into_opaque_leaf()
.try_decode()
.ok_or(mmr::Error::Verify)?;
Mmr::verify_leaf(leaf, proof)
}
fn verify_proof_stateless(
root: mmr::Hash,
leaf: Vec<u8>,
leaf: mmr::EncodableOpaqueLeaf,
proof: mmr::Proof<mmr::Hash>
) -> Result<(), mmr::Error> {
let node = mmr::DataOrHash::Data(mmr::OpaqueLeaf(leaf));
let node = mmr::DataOrHash::Data(leaf.into_opaque_leaf());
pallet_mmr::verify_leaf_proof::<mmr::Hashing, _>(root, node, proof)
}
}
+4 -3
View File
@@ -417,13 +417,14 @@ impl BenchDb {
};
let task_executor = TaskExecutor::new();
let (client, backend) = sc_service::new_client(
db_config,
let backend = sc_service::new_db_backend(db_config).expect("Should not fail");
let client = sc_service::new_client(
backend.clone(),
NativeExecutor::new(WasmExecutionMethod::Compiled, None, 8),
&keyring.generate_genesis(),
None,
None,
ExecutionExtensions::new(profile.into_execution_strategies(), None),
ExecutionExtensions::new(profile.into_execution_strategies(), None, None),
Box::new(task_executor.clone()),
None,
Default::default(),
@@ -26,7 +26,7 @@ use std::sync::{Weak, Arc};
use codec::Decode;
use sp_core::{
ExecutionContext,
offchain::{self, OffchainExt, TransactionPoolExt},
offchain::{self, OffchainWorkerExt, TransactionPoolExt, OffchainDbExt},
};
use sp_keystore::{KeystoreExt, SyncCryptoStorePtr};
use sp_runtime::{
@@ -76,6 +76,18 @@ impl ExtensionsFactory for () {
}
}
/// Create a Offchain DB accessor object.
pub trait DbExternalitiesFactory: Send + Sync {
/// Create [`offchain::DbExternalities`] instance.
fn create(&self) -> Box<dyn offchain::DbExternalities>;
}
impl<T: offchain::DbExternalities + Clone + Sync + Send + 'static> DbExternalitiesFactory for T {
fn create(&self) -> Box<dyn offchain::DbExternalities> {
Box::new(self.clone())
}
}
/// A producer of execution extensions for offchain calls.
///
/// This crate aggregates extensions available for the offchain calls
@@ -84,6 +96,7 @@ impl ExtensionsFactory for () {
pub struct ExecutionExtensions<Block: traits::Block> {
strategies: ExecutionStrategies,
keystore: Option<SyncCryptoStorePtr>,
offchain_db: Option<Box<dyn DbExternalitiesFactory>>,
// FIXME: these two are only RwLock because of https://github.com/paritytech/substrate/issues/4587
// remove when fixed.
// To break retain cycle between `Client` and `TransactionPool` we require this
@@ -99,6 +112,7 @@ impl<Block: traits::Block> Default for ExecutionExtensions<Block> {
Self {
strategies: Default::default(),
keystore: None,
offchain_db: None,
transaction_pool: RwLock::new(None),
extensions_factory: RwLock::new(Box::new(())),
}
@@ -110,12 +124,14 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
pub fn new(
strategies: ExecutionStrategies,
keystore: Option<SyncCryptoStorePtr>,
offchain_db: Option<Box<dyn DbExternalitiesFactory>>,
) -> Self {
let transaction_pool = RwLock::new(None);
let extensions_factory = Box::new(());
Self {
strategies,
keystore,
offchain_db,
extensions_factory: RwLock::new(extensions_factory),
transaction_pool,
}
@@ -164,9 +180,22 @@ impl<Block: traits::Block> ExecutionExtensions<Block> {
}
}
if capabilities.has(offchain::Capability::OffchainDbRead) ||
capabilities.has(offchain::Capability::OffchainDbWrite)
{
if let Some(offchain_db) = self.offchain_db.as_ref() {
extensions.register(
OffchainDbExt::new(offchain::LimitedExternalities::new(
capabilities,
offchain_db.create(),
))
);
}
}
if let ExecutionContext::OffchainCall(Some(ext)) = context {
extensions.register(
OffchainExt::new(offchain::LimitedExternalities::new(capabilities, ext.0)),
OffchainWorkerExt::new(offchain::LimitedExternalities::new(capabilities, ext.0)),
);
}
@@ -21,7 +21,7 @@ use codec::{Encode, Decode};
use hex_literal::hex;
use sp_core::{
blake2_128, blake2_256, ed25519, sr25519, map, Pair,
offchain::{OffchainExt, testing},
offchain::{OffchainWorkerExt, OffchainDbExt, testing},
traits::{Externalities, CallInWasm},
};
use sc_runtime_test::wasm_binary_unwrap;
@@ -468,7 +468,7 @@ test_wasm_execution!(offchain_index);
fn offchain_index(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, _state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainWorkerExt::new(offchain));
call_in_wasm(
"test_offchain_index_set",
&[0],
@@ -487,7 +487,8 @@ test_wasm_execution!(offchain_local_storage_should_work);
fn offchain_local_storage_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
assert_eq!(
call_in_wasm(
"test_offchain_local_storage",
@@ -504,7 +505,7 @@ test_wasm_execution!(offchain_http_should_work);
fn offchain_http_should_work(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let (offchain, state) = testing::TestOffchainExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainWorkerExt::new(offchain));
state.write().expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:12345".into(),
+9 -8
View File
@@ -14,23 +14,24 @@ targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
bytes = "0.5"
sc-client-api = { version = "3.0.0", path = "../api" }
sp-api = { version = "3.0.0", path = "../../primitives/api" }
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"] }
hex = "0.4"
fnv = "1.0.6"
futures = "0.3.9"
futures-timer = "3.0.1"
log = "0.4.8"
threadpool = "1.7"
num_cpus = "1.10"
sp-offchain = { version = "3.0.0", path = "../../primitives/offchain" }
codec = { package = "parity-scale-codec", version = "2.0.0", features = ["derive"] }
parking_lot = "0.11.1"
sp-core = { version = "3.0.0", path = "../../primitives/core" }
rand = "0.7.2"
sc-client-api = { version = "3.0.0", path = "../api" }
sc-keystore = { version = "3.0.0", path = "../keystore" }
sc-network = { version = "0.9.0", path = "../network" }
sp-api = { version = "3.0.0", path = "../../primitives/api" }
sp-core = { version = "3.0.0", path = "../../primitives/core" }
sp-offchain = { version = "3.0.0", path = "../../primitives/offchain" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }
sp-utils = { version = "3.0.0", path = "../../primitives/utils" }
sc-network = { version = "0.9.0", path = "../network" }
sc-keystore = { version = "3.0.0", path = "../keystore" }
threadpool = "1.7"
[target.'cfg(not(target_os = "unknown"))'.dependencies]
hyper = "0.13.9"
+118 -65
View File
@@ -26,12 +26,11 @@ use std::{
use crate::NetworkProvider;
use futures::Future;
use log::error;
use sc_network::{PeerId, Multiaddr};
use codec::{Encode, Decode};
use sp_core::OpaquePeerId;
use sp_core::offchain::{
Externalities as OffchainExt, HttpRequestId, Timestamp, HttpRequestStatus, HttpError,
self, HttpRequestId, Timestamp, HttpRequestStatus, HttpError,
OffchainStorage, OpaqueNetworkState, OpaqueMultiaddr, StorageKind,
};
pub use sp_offchain::STORAGE_PREFIX;
@@ -47,22 +46,9 @@ mod http_dummy;
mod timestamp;
/// Asynchronous offchain API.
///
/// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently).
pub(crate) struct Api<Storage> {
/// Offchain Workers database.
db: Storage,
/// A provider for substrate networking.
network_provider: Arc<dyn NetworkProvider + Send + Sync>,
/// Is this node a potential validator?
is_validator: bool,
/// Everything HTTP-related is handled by a different struct.
http: http::HttpApi,
}
fn unavailable_yet<R: Default>(name: &str) -> R {
error!(
log::error!(
target: "sc_offchain",
"The {:?} API is not available for offchain workers yet. Follow \
https://github.com/paritytech/substrate/issues/1458 for details", name
);
@@ -71,7 +57,109 @@ fn unavailable_yet<R: Default>(name: &str) -> R {
const LOCAL_DB: &str = "LOCAL (fork-aware) DB";
impl<Storage: OffchainStorage> OffchainExt for Api<Storage> {
/// Offchain DB reference.
#[derive(Debug, Clone)]
pub struct Db<Storage> {
/// Persistent storage database.
persistent: Storage,
}
impl<Storage: OffchainStorage> Db<Storage> {
/// Create new instance of Offchain DB.
pub fn new(persistent: Storage) -> Self {
Self { persistent }
}
/// Create new instance of Offchain DB, backed by given backend.
pub fn factory_from_backend<Backend, Block>(backend: &Backend) -> Option<
Box<dyn sc_client_api::execution_extensions::DbExternalitiesFactory>
> where
Backend: sc_client_api::Backend<Block, OffchainStorage = Storage>,
Block: sp_runtime::traits::Block,
Storage: 'static,
{
sc_client_api::Backend::offchain_storage(backend).map(|db|
Box::new(Self::new(db)) as _
)
}
}
impl<Storage: OffchainStorage> offchain::DbExternalities for Db<Storage> {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
log::debug!(
target: "sc_offchain",
"{:?}: Write: {:?} <= {:?}", kind, hex::encode(key), hex::encode(value)
);
match kind {
StorageKind::PERSISTENT => self.persistent.set(STORAGE_PREFIX, key, value),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
log::debug!(
target: "sc_offchain",
"{:?}: Clear: {:?}", kind, hex::encode(key)
);
match kind {
StorageKind::PERSISTENT => self.persistent.remove(STORAGE_PREFIX, key),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
log::debug!(
target: "sc_offchain",
"{:?}: CAS: {:?} <= {:?} vs {:?}",
kind,
hex::encode(key),
hex::encode(new_value),
old_value.as_ref().map(hex::encode),
);
match kind {
StorageKind::PERSISTENT => {
self.persistent.compare_and_set(STORAGE_PREFIX, key, old_value, new_value)
},
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
let result = match kind {
StorageKind::PERSISTENT => self.persistent.get(STORAGE_PREFIX, key),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
};
log::debug!(
target: "sc_offchain",
"{:?}: Read: {:?} => {:?}",
kind,
hex::encode(key),
result.as_ref().map(hex::encode)
);
result
}
}
/// Asynchronous offchain API.
///
/// NOTE this is done to prevent recursive calls into the runtime
/// (which are not supported currently).
pub(crate) struct Api {
/// A provider for substrate networking.
network_provider: Arc<dyn NetworkProvider + Send + Sync>,
/// Is this node a potential validator?
is_validator: bool,
/// Everything HTTP-related is handled by a different struct.
http: http::HttpApi,
}
impl offchain::Externalities for Api {
fn is_validator(&self) -> bool {
self.is_validator
}
@@ -98,42 +186,6 @@ impl<Storage: OffchainStorage> OffchainExt for Api<Storage> {
rand::random()
}
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
match kind {
StorageKind::PERSISTENT => self.db.set(STORAGE_PREFIX, key, value),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
match kind {
StorageKind::PERSISTENT => self.db.remove(STORAGE_PREFIX, key),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
match kind {
StorageKind::PERSISTENT => {
self.db.compare_and_set(STORAGE_PREFIX, key, old_value, new_value)
},
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
match kind {
StorageKind::PERSISTENT => self.db.get(STORAGE_PREFIX, key),
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
}
}
fn http_request_start(
&mut self,
method: &str,
@@ -270,16 +322,14 @@ pub(crate) struct AsyncApi {
impl AsyncApi {
/// Creates new Offchain extensions API implementation an the asynchronous processing part.
pub fn new<S: OffchainStorage>(
db: S,
pub fn new(
network_provider: Arc<dyn NetworkProvider + Send + Sync>,
is_validator: bool,
shared_client: SharedClient,
) -> (Api<S>, Self) {
) -> (Api, Self) {
let (http_api, http_worker) = http::http(shared_client);
let api = Api {
db,
network_provider,
is_validator,
http: http_api,
@@ -303,9 +353,10 @@ impl AsyncApi {
#[cfg(test)]
mod tests {
use super::*;
use std::{convert::{TryFrom, TryInto}, time::SystemTime};
use sc_client_db::offchain::LocalStorage;
use sc_network::{NetworkStateInfo, PeerId};
use sp_core::offchain::{Externalities, DbExternalities};
use std::{convert::{TryFrom, TryInto}, time::SystemTime};
struct TestNetwork();
@@ -329,20 +380,22 @@ mod tests {
}
}
fn offchain_api() -> (Api<LocalStorage>, AsyncApi) {
fn offchain_api() -> (Api, AsyncApi) {
sp_tracing::try_init_simple();
let db = LocalStorage::new_test();
let mock = Arc::new(TestNetwork());
let shared_client = SharedClient::new();
AsyncApi::new(
db,
mock,
false,
shared_client,
)
}
fn offchain_db() -> Db<LocalStorage> {
Db::new(LocalStorage::new_test())
}
#[test]
fn should_get_timestamp() {
let mut api = offchain_api().0;
@@ -381,7 +434,7 @@ mod tests {
fn should_set_and_get_local_storage() {
// given
let kind = StorageKind::PERSISTENT;
let mut api = offchain_api().0;
let mut api = offchain_db();
let key = b"test";
// when
@@ -396,7 +449,7 @@ mod tests {
fn should_compare_and_set_local_storage() {
// given
let kind = StorageKind::PERSISTENT;
let mut api = offchain_api().0;
let mut api = offchain_db();
let key = b"test";
api.local_storage_set(kind, key, b"value");
@@ -413,7 +466,7 @@ mod tests {
fn should_compare_and_set_local_storage_with_none() {
// given
let kind = StorageKind::PERSISTENT;
let mut api = offchain_api().0;
let mut api = offchain_db();
let key = b"test";
// when
+14 -20
View File
@@ -46,13 +46,13 @@ use sp_api::{ApiExt, ProvideRuntimeApi};
use futures::future::Future;
use log::{debug, warn};
use sc_network::{ExHashT, NetworkService, NetworkStateInfo, PeerId};
use sp_core::{offchain::{self, OffchainStorage}, ExecutionContext, traits::SpawnNamed};
use sp_core::{offchain, ExecutionContext, traits::SpawnNamed};
use sp_runtime::{generic::BlockId, traits::{self, Header}};
use futures::{prelude::*, future::ready};
mod api;
use api::SharedClient;
pub use api::Db as OffchainDb;
pub use sp_offchain::{OffchainWorkerApi, STORAGE_PREFIX};
/// NetworkProvider provides [`OffchainWorkers`] with all necessary hooks into the
@@ -80,21 +80,19 @@ where
}
/// An offchain workers manager.
pub struct OffchainWorkers<Client, Storage, Block: traits::Block> {
pub struct OffchainWorkers<Client, Block: traits::Block> {
client: Arc<Client>,
db: Storage,
_block: PhantomData<Block>,
thread_pool: Mutex<ThreadPool>,
shared_client: SharedClient,
shared_client: api::SharedClient,
}
impl<Client, Storage, Block: traits::Block> OffchainWorkers<Client, Storage, Block> {
impl<Client, Block: traits::Block> OffchainWorkers<Client, Block> {
/// Creates new `OffchainWorkers`.
pub fn new(client: Arc<Client>, db: Storage) -> Self {
let shared_client = SharedClient::new();
pub fn new(client: Arc<Client>) -> Self {
let shared_client = api::SharedClient::new();
Self {
client,
db,
_block: PhantomData,
thread_pool: Mutex::new(ThreadPool::new(num_cpus::get())),
shared_client,
@@ -102,9 +100,8 @@ impl<Client, Storage, Block: traits::Block> OffchainWorkers<Client, Storage, Blo
}
}
impl<Client, Storage, Block: traits::Block> fmt::Debug for OffchainWorkers<
impl<Client, Block: traits::Block> fmt::Debug for OffchainWorkers<
Client,
Storage,
Block,
> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@@ -112,15 +109,13 @@ impl<Client, Storage, Block: traits::Block> fmt::Debug for OffchainWorkers<
}
}
impl<Client, Storage, Block> OffchainWorkers<
impl<Client, Block> OffchainWorkers<
Client,
Storage,
Block,
> where
Block: traits::Block,
Client: ProvideRuntimeApi<Block> + Send + Sync + 'static,
Client::Api: OffchainWorkerApi<Block>,
Storage: OffchainStorage + 'static,
{
/// Start the offchain workers after given block.
#[must_use]
@@ -150,7 +145,6 @@ impl<Client, Storage, Block> OffchainWorkers<
debug!("Checking offchain workers at {:?}: version:{}", at, version);
if version > 0 {
let (api, runner) = api::AsyncApi::new(
self.db.clone(),
network_provider,
is_validator,
self.shared_client.clone(),
@@ -197,10 +191,10 @@ impl<Client, Storage, Block> OffchainWorkers<
}
/// Inform the offchain worker about new imported blocks
pub async fn notification_future<Client, Storage, Block, Spawner>(
pub async fn notification_future<Client, Block, Spawner>(
is_validator: bool,
client: Arc<Client>,
offchain: Arc<OffchainWorkers<Client, Storage, Block>>,
offchain: Arc<OffchainWorkers<Client, Block>>,
spawner: Spawner,
network_provider: Arc<dyn NetworkProvider + Send + Sync>,
)
@@ -208,7 +202,6 @@ pub async fn notification_future<Client, Storage, Block, Spawner>(
Block: traits::Block,
Client: ProvideRuntimeApi<Block> + sc_client_api::BlockchainEvents<Block> + Send + Sync + 'static,
Client::Api: OffchainWorkerApi<Block>,
Storage: OffchainStorage + 'static,
Spawner: SpawnNamed
{
client.import_notification_stream().for_each(move |n| {
@@ -300,12 +293,11 @@ mod tests {
spawner,
client.clone(),
));
let db = sc_client_db::offchain::LocalStorage::new_test();
let network = Arc::new(TestNetwork());
let header = client.header(&BlockId::number(0)).unwrap().unwrap();
// when
let offchain = OffchainWorkers::new(client, db);
let offchain = OffchainWorkers::new(client);
futures::executor::block_on(
offchain.on_block_imported(&header, network, false)
);
@@ -317,6 +309,8 @@ mod tests {
#[test]
fn offchain_index_set_and_clear_works() {
use sp_core::offchain::OffchainStorage;
sp_tracing::try_init_simple();
let (client, backend) =
+37 -38
View File
@@ -39,7 +39,7 @@ use futures::{
channel::oneshot,
};
use sc_keystore::LocalKeystore;
use log::{info, warn};
use log::info;
use sc_network::config::{Role, OnDemand};
use sc_network::NetworkService;
use sc_network::block_request_handler::{self, BlockRequestHandler};
@@ -338,13 +338,17 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
transaction_storage: config.transaction_storage.clone(),
};
let backend = new_db_backend(db_config)?;
let extensions = sc_client_api::execution_extensions::ExecutionExtensions::new(
config.execution_strategies.clone(),
Some(keystore_container.sync_keystore()),
sc_offchain::OffchainDb::factory_from_backend(&*backend),
);
new_client(
db_config,
let client = new_client(
backend.clone(),
executor,
chain_spec.as_storage_builder(),
fork_blocks,
@@ -357,7 +361,9 @@ pub fn new_full_parts<TBl, TRtApi, TExecDisp>(
offchain_indexing_api: config.offchain_worker.indexing_enabled,
wasm_runtime_overrides: config.wasm_runtime_overrides.clone(),
},
)?
)?;
(client, backend)
};
Ok((
@@ -420,9 +426,20 @@ pub fn new_light_parts<TBl, TRtApi, TExecDisp>(
Ok((client, backend, keystore_container, task_manager, on_demand))
}
/// Create an instance of db-backed client.
pub fn new_client<E, Block, RA>(
/// Create an instance of default DB-backend backend.
pub fn new_db_backend<Block>(
settings: DatabaseSettings,
) -> Result<Arc<Backend<Block>>, sp_blockchain::Error> where
Block: BlockT,
{
const CANONICALIZATION_DELAY: u64 = 4096;
Ok(Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?))
}
/// Create an instance of client backed by given backend.
pub fn new_client<E, Block, RA>(
backend: Arc<Backend<Block>>,
executor: E,
genesis_storage: &dyn BuildStorage,
fork_blocks: ForkBlocks<Block>,
@@ -431,38 +448,30 @@ pub fn new_client<E, Block, RA>(
spawn_handle: Box<dyn SpawnNamed>,
prometheus_registry: Option<Registry>,
config: ClientConfig,
) -> Result<(
) -> Result<
crate::client::Client<
Backend<Block>,
crate::client::LocalCallExecutor<Backend<Block>, E>,
Block,
RA,
>,
Arc<Backend<Block>>,
),
sp_blockchain::Error,
>
where
Block: BlockT,
E: CodeExecutor + RuntimeInfo,
{
const CANONICALIZATION_DELAY: u64 = 4096;
let backend = Arc::new(Backend::new(settings, CANONICALIZATION_DELAY)?);
let executor = crate::client::LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?;
Ok((
crate::client::Client::new(
backend.clone(),
executor,
genesis_storage,
fork_blocks,
bad_blocks,
execution_extensions,
prometheus_registry,
config,
)?,
Ok(crate::client::Client::new(
backend,
))
executor,
genesis_storage,
fork_blocks,
bad_blocks,
execution_extensions,
prometheus_registry,
config,
)?)
}
/// Parameters to pass into `build`.
@@ -499,28 +508,18 @@ pub struct SpawnTasksParams<'a, TBl: BlockT, TCl, TExPool, TRpc, Backend> {
}
/// Build a shared offchain workers instance.
pub fn build_offchain_workers<TBl, TBackend, TCl>(
pub fn build_offchain_workers<TBl, TCl>(
config: &Configuration,
backend: Arc<TBackend>,
spawn_handle: SpawnTaskHandle,
client: Arc<TCl>,
network: Arc<NetworkService<TBl, <TBl as BlockT>::Hash>>,
) -> Option<Arc<sc_offchain::OffchainWorkers<TCl, TBackend::OffchainStorage, TBl>>>
) -> Option<Arc<sc_offchain::OffchainWorkers<TCl, TBl>>>
where
TBl: BlockT, TBackend: sc_client_api::Backend<TBl>,
<TBackend as sc_client_api::Backend<TBl>>::OffchainStorage: 'static,
TBl: BlockT,
TCl: Send + Sync + ProvideRuntimeApi<TBl> + BlockchainEvents<TBl> + 'static,
<TCl as ProvideRuntimeApi<TBl>>::Api: sc_offchain::OffchainWorkerApi<TBl>,
{
let offchain_workers = match backend.offchain_storage() {
Some(db) => {
Some(Arc::new(sc_offchain::OffchainWorkers::new(client.clone(), db)))
},
None => {
warn!("Offchain workers disabled, due to lack of offchain storage support in backend.");
None
},
};
let offchain_workers = Some(Arc::new(sc_offchain::OffchainWorkers::new(client.clone())));
// Inform the offchain worker about new imported blocks
if let Some(offchain) = offchain_workers.clone() {
@@ -205,7 +205,11 @@ pub fn new_with_backend<B, E, Block, S, RA>(
B: backend::LocalBackend<Block> + 'static,
{
let call_executor = LocalCallExecutor::new(backend.clone(), executor, spawn_handle, config.clone())?;
let extensions = ExecutionExtensions::new(Default::default(), keystore);
let extensions = ExecutionExtensions::new(
Default::default(),
keystore,
sc_offchain::OffchainDb::factory_from_backend(&*backend),
);
Client::new(
backend,
call_executor,
+1 -1
View File
@@ -51,7 +51,7 @@ use sp_utils::{status_sinks, mpsc::{tracing_unbounded, TracingUnboundedReceiver}
pub use self::error::Error;
pub use self::builder::{
new_full_client, new_client, new_full_parts, new_light_parts,
new_full_client, new_db_backend, new_client, new_full_parts, new_light_parts,
spawn_tasks, build_network, build_offchain_workers,
BuildNetworkParams, KeystoreContainer, NetworkStarter, SpawnTasksParams, TFullClient, TLightClient,
TFullBackend, TLightBackend, TLightBackendWithHash, TLightClientWithBackend,
@@ -27,7 +27,7 @@ use parking_lot::RwLock;
use sp_core::{
offchain::{
testing::{PoolState, TestOffchainExt, TestTransactionPoolExt},
OffchainExt, TransactionPoolExt,
OffchainDbExt, OffchainWorkerExt, TransactionPoolExt,
},
H256,
};
@@ -369,7 +369,8 @@ impl ExtBuilder {
seed[0..4].copy_from_slice(&iters.to_le_bytes());
offchain_state.write().seed = seed;
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
ext.register_extension(TransactionPoolExt::new(pool));
(ext, pool_state)
@@ -22,7 +22,7 @@ use codec::Decode;
use frame_support::{assert_ok, parameter_types};
use sp_core::{
H256,
offchain::{OffchainExt, TransactionPoolExt, testing},
offchain::{OffchainWorkerExt, TransactionPoolExt, testing},
sr25519::Signature,
};
@@ -144,7 +144,7 @@ fn it_aggregates_the_price() {
fn should_make_http_call_and_parse_result() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
price_oracle_response(&mut state.write());
@@ -160,7 +160,7 @@ fn should_make_http_call_and_parse_result() {
fn knows_how_to_mock_several_http_calls() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
{
let mut state = state.write();
@@ -217,7 +217,7 @@ fn should_submit_signed_transaction_on_chain() {
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
t.register_extension(TransactionPoolExt::new(pool));
t.register_extension(KeystoreExt(Arc::new(keystore)));
@@ -255,7 +255,7 @@ fn should_submit_unsigned_transaction_on_chain_for_any_account() {
.clone();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
t.register_extension(TransactionPoolExt::new(pool));
t.register_extension(KeystoreExt(Arc::new(keystore)));
@@ -308,7 +308,7 @@ fn should_submit_unsigned_transaction_on_chain_for_all_accounts() {
.clone();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
t.register_extension(TransactionPoolExt::new(pool));
t.register_extension(KeystoreExt(Arc::new(keystore)));
@@ -349,7 +349,7 @@ fn should_submit_raw_unsigned_transaction_on_chain() {
let keystore = KeyStore::new();
let mut t = sp_io::TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
t.register_extension(TransactionPoolExt::new(pool));
t.register_extension(KeystoreExt(Arc::new(keystore)));
+6 -3
View File
@@ -23,7 +23,8 @@ use super::*;
use crate::mock::*;
use sp_core::OpaquePeerId;
use sp_core::offchain::{
OffchainExt,
OffchainDbExt,
OffchainWorkerExt,
TransactionPoolExt,
testing::{TestOffchainExt, TestTransactionPoolExt},
};
@@ -205,7 +206,8 @@ fn should_generate_heartbeats() {
let mut ext = new_test_ext();
let (offchain, _state) = TestOffchainExt::new();
let (pool, state) = TestTransactionPoolExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
ext.register_extension(TransactionPoolExt::new(pool));
ext.execute_with(|| {
@@ -310,7 +312,8 @@ fn should_not_send_a_report_if_already_online() {
let mut ext = new_test_ext();
let (offchain, _state) = TestOffchainExt::new();
let (pool, pool_state) = TestTransactionPoolExt::new();
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
ext.register_extension(TransactionPoolExt::new(pool));
ext.execute_with(|| {
@@ -360,6 +360,11 @@ impl OpaqueLeaf {
pub fn from_encoded_leaf(encoded_leaf: Vec<u8>) -> Self {
OpaqueLeaf(encoded_leaf)
}
/// Attempt to decode the leaf into expected concrete type.
pub fn try_decode<T: codec::Decode>(&self) -> Option<T> {
codec::Decode::decode(&mut &*self.0).ok()
}
}
impl FullLeaf for OpaqueLeaf {
@@ -368,18 +373,49 @@ impl FullLeaf for OpaqueLeaf {
}
}
/// A type-safe wrapper for the concrete leaf type.
///
/// This structure serves merely to avoid passing raw `Vec<u8>` around.
/// It must be `Vec<u8>`-encoding compatible.
///
/// It is different from [`OpaqueLeaf`], because it does implement `Codec`
/// and the encoding has to match raw `Vec<u8>` encoding.
#[derive(codec::Encode, codec::Decode, RuntimeDebug, PartialEq, Eq)]
pub struct EncodableOpaqueLeaf(pub Vec<u8>);
impl EncodableOpaqueLeaf {
/// Convert a concrete leaf into encodable opaque version.
pub fn from_leaf<T: FullLeaf>(leaf: &T) -> Self {
let opaque = OpaqueLeaf::from_leaf(leaf);
Self::from_opaque_leaf(opaque)
}
/// Given an opaque leaf, make it encodable.
pub fn from_opaque_leaf(opaque: OpaqueLeaf) -> Self {
Self(opaque.0)
}
/// Try to convert into a [OpaqueLeaf].
pub fn into_opaque_leaf(self) -> OpaqueLeaf {
// wrap into `OpaqueLeaf` type
OpaqueLeaf::from_encoded_leaf(self.0)
}
}
sp_api::decl_runtime_apis! {
/// API to interact with MMR pallet.
pub trait MmrApi<Leaf: codec::Codec, Hash: codec::Codec> {
pub trait MmrApi<Hash: codec::Codec> {
/// Generate MMR proof for a leaf under given index.
fn generate_proof(leaf_index: u64) -> Result<(Leaf, Proof<Hash>), Error>;
#[skip_initialize_block]
fn generate_proof(leaf_index: u64) -> Result<(EncodableOpaqueLeaf, Proof<Hash>), Error>;
/// Verify MMR proof against on-chain MMR.
///
/// Note this function will use on-chain MMR root hash and check if the proof
/// matches the hash.
/// See [Self::verify_proof_stateless] for a stateless verifier.
fn verify_proof(leaf: Leaf, proof: Proof<Hash>) -> Result<(), Error>;
#[skip_initialize_block]
fn verify_proof(leaf: EncodableOpaqueLeaf, proof: Proof<Hash>) -> Result<(), Error>;
/// Verify MMR proof against given root hash.
///
@@ -387,7 +423,8 @@ sp_api::decl_runtime_apis! {
/// proof is verified against given MMR root hash.
///
/// The leaf data is expected to be encoded in it's compact form.
fn verify_proof_stateless(root: Hash, leaf: Vec<u8>, proof: Proof<Hash>)
#[skip_initialize_block]
fn verify_proof_stateless(root: Hash, leaf: EncodableOpaqueLeaf, proof: Proof<Hash>)
-> Result<(), Error>;
}
}
@@ -535,7 +572,7 @@ mod tests {
}
#[test]
fn opaque_leaves_should_be_scale_compatible_with_concrete_ones() {
fn opaque_leaves_should_be_full_leaf_compatible() {
// given
let a = Test::Data("Hello World!".into());
let b = Test::Data("".into());
@@ -564,4 +601,36 @@ mod tests {
opaque,
);
}
#[test]
fn encode_opaque_leaf_should_be_scale_compatible() {
use codec::Encode;
// given
let a = Test::Data("Hello World!".into());
let case1 = EncodableOpaqueLeaf::from_leaf(&a);
let case2 = EncodableOpaqueLeaf::from_opaque_leaf(OpaqueLeaf(a.encode()));
let case3 = a.encode().encode();
// when
let encoded = vec![&case1, &case2]
.into_iter()
.map(|x| x.encode())
.collect::<Vec<_>>();
let decoded = vec![&*encoded[0], &*encoded[1], &*case3]
.into_iter()
.map(|x| EncodableOpaqueLeaf::decode(&mut &*x))
.collect::<Vec<_>>();
// then
assert_eq!(case1, case2);
assert_eq!(encoded[0], encoded[1]);
// then encoding should also match double-encoded leaf.
assert_eq!(encoded[0], case3);
assert_eq!(decoded[0], decoded[1]);
assert_eq!(decoded[1], decoded[2]);
assert_eq!(decoded[0], Ok(case2));
assert_eq!(decoded[1], Ok(case1));
}
}
@@ -0,0 +1,29 @@
[package]
name = "pallet-mmr-rpc"
version = "3.0.0"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "Apache-2.0"
homepage = "https://substrate.dev"
repository = "https://github.com/paritytech/substrate/"
description = "Node-specific RPC methods for interaction with Merkle Mountain Range pallet."
publish = false
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0" }
jsonrpc-core = "15.1.0"
jsonrpc-core-client = "15.1.0"
jsonrpc-derive = "15.1.0"
pallet-mmr-primitives = { version = "3.0.0", path = "../primitives" }
serde = { version = "1.0.101", features = ["derive"] }
sp-api = { version = "3.0.0", path = "../../../primitives/api" }
sp-blockchain = { version = "3.0.0", path = "../../../primitives/blockchain" }
sp-core = { version = "3.0.0", path = "../../../primitives/core" }
sp-rpc = { version = "3.0.0", path = "../../../primitives/rpc" }
sp-runtime = { version = "3.0.0", path = "../../../primitives/runtime" }
[dev-dependencies]
serde_json = "1.0.41"
@@ -0,0 +1,222 @@
// This file is part of Substrate.
// Copyright (C) 2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#![warn(missing_docs)]
//! Node-specific RPC methods for interaction with Merkle Mountain Range pallet.
use std::sync::Arc;
use codec::{Codec, Encode};
use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc;
use serde::{Deserialize, Serialize};
use sp_api::ProvideRuntimeApi;
use sp_blockchain::HeaderBackend;
use sp_core::Bytes;
use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT},
};
use pallet_mmr_primitives::{Error as MmrError, Proof};
pub use pallet_mmr_primitives::MmrApi as MmrRuntimeApi;
/// Retrieved MMR leaf and its proof.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct LeafProof<BlockHash> {
/// Block hash the proof was generated for.
pub block_hash: BlockHash,
/// SCALE-encoded leaf data.
pub leaf: Bytes,
/// SCALE-encoded proof data. See [pallet_mmr_primitives::Proof].
pub proof: Bytes,
}
impl<BlockHash> LeafProof<BlockHash> {
/// Create new `LeafProof` from given concrete `leaf` and `proof`.
pub fn new<Leaf, MmrHash>(
block_hash: BlockHash,
leaf: Leaf,
proof: Proof<MmrHash>,
) -> Self where
Leaf: Encode,
MmrHash: Encode,
{
Self {
block_hash,
leaf: Bytes(leaf.encode()),
proof: Bytes(proof.encode()),
}
}
}
/// MMR RPC methods.
#[rpc]
pub trait MmrApi<BlockHash> {
/// Generate MMR proof for given leaf index.
///
/// This method calls into a runtime with MMR pallet included and attempts to generate
/// MMR proof for leaf at given `leaf_index`.
/// Optionally, a block hash at which the runtime should be queried can be specified.
///
/// Returns the (full) leaf itself and a proof for this leaf (compact encoding, i.e. hash of
/// the leaf). Both parameters are SCALE-encoded.
#[rpc(name = "mmr_generateProof")]
fn generate_proof(
&self,
leaf_index: u64,
at: Option<BlockHash>,
) -> Result<LeafProof<BlockHash>>;
}
/// An implementation of MMR specific RPC methods.
pub struct Mmr<C, B> {
client: Arc<C>,
_marker: std::marker::PhantomData<B>,
}
impl<C, B> Mmr<C, B> {
/// Create new `Mmr` with the given reference to the client.
pub fn new(client: Arc<C>) -> Self {
Self {
client,
_marker: Default::default(),
}
}
}
impl<C, Block, MmrHash> MmrApi<<Block as BlockT>::Hash,> for Mmr<C, (Block, MmrHash)>
where
Block: BlockT,
C: Send + Sync + 'static + ProvideRuntimeApi<Block> + HeaderBackend<Block>,
C::Api: MmrRuntimeApi<
Block,
MmrHash,
>,
MmrHash: Codec + Send + Sync + 'static,
{
fn generate_proof(
&self,
leaf_index: u64,
at: Option<<Block as BlockT>::Hash>,
) -> Result<LeafProof<<Block as BlockT>::Hash>> {
let api = self.client.runtime_api();
let block_hash = at.unwrap_or_else(||
// If the block hash is not supplied assume the best block.
self.client.info().best_hash
);
let (leaf, proof) = api
.generate_proof_with_context(
&BlockId::hash(block_hash),
sp_core::ExecutionContext::OffchainCall(None),
leaf_index,
)
.map_err(runtime_error_into_rpc_error)?
.map_err(mmr_error_into_rpc_error)?;
Ok(LeafProof::new(block_hash, leaf, proof))
}
}
const RUNTIME_ERROR: i64 = 8000;
const MMR_ERROR: i64 = 8010;
/// Converts a mmr-specific error into an RPC error.
fn mmr_error_into_rpc_error(err: MmrError) -> Error {
match err {
MmrError::LeafNotFound => Error {
code: ErrorCode::ServerError(MMR_ERROR + 1),
message: "Leaf was not found".into(),
data: Some(format!("{:?}", err).into()),
},
MmrError::GenerateProof => Error {
code: ErrorCode::ServerError(MMR_ERROR + 2),
message: "Error while generating the proof".into(),
data: Some(format!("{:?}", err).into()),
},
_ => Error {
code: ErrorCode::ServerError(MMR_ERROR),
message: "Unexpected MMR error".into(),
data: Some(format!("{:?}", err).into()),
},
}
}
/// Converts a runtime trap into an RPC error.
fn runtime_error_into_rpc_error(err: impl std::fmt::Debug) -> Error {
Error {
code: ErrorCode::ServerError(RUNTIME_ERROR),
message: "Runtime trapped".into(),
data: Some(format!("{:?}", err).into()),
}
}
#[cfg(test)]
mod tests {
use super::*;
use sp_core::H256;
#[test]
fn should_serialize_leaf_proof() {
// given
let leaf = vec![1_u8, 2, 3, 4];
let proof = Proof {
leaf_index: 1,
leaf_count: 9,
items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
};
let leaf_proof = LeafProof::new(H256::repeat_byte(0), leaf, proof);
// when
let actual = serde_json::to_string(&leaf_proof).unwrap();
// then
assert_eq!(
actual,
r#"{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000","leaf":"0x1001020304","proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"}"#
);
}
#[test]
fn should_deserialize_leaf_proof() {
// given
let expected = LeafProof {
block_hash: H256::repeat_byte(0),
leaf: Bytes(vec![1_u8, 2, 3, 4].encode()),
proof: Bytes(Proof {
leaf_index: 1,
leaf_count: 9,
items: vec![H256::repeat_byte(1), H256::repeat_byte(2)],
}.encode()),
};
// when
let actual: LeafProof<H256> = serde_json::from_str(r#"{
"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"leaf":"0x1001020304",
"proof":"0x010000000000000009000000000000000801010101010101010101010101010101010101010101010101010101010101010202020202020202020202020202020202020202020202020202020202020202"
}"#).unwrap();
// then
assert_eq!(actual, expected);
}
}
@@ -62,10 +62,9 @@ impl<T, I, L> mmr_lib::MMRStore<NodeOf<T, I, L>> for Storage<OffchainStorage, T,
fn get_elem(&self, pos: u64) -> mmr_lib::Result<Option<NodeOf<T, I, L>>> {
let key = Module::<T, I>::offchain_key(pos);
// Retrieve the element from Off-chain DB.
Ok(
sp_io::offchain ::local_storage_get(sp_core::offchain::StorageKind::PERSISTENT, &key)
.and_then(|v| codec::Decode::decode(&mut &*v).ok())
)
Ok(sp_io::offchain
::local_storage_get(sp_core::offchain::StorageKind::PERSISTENT, &key)
.and_then(|v| codec::Decode::decode(&mut &*v).ok()))
}
fn append(&mut self, _: u64, _: Vec<NodeOf<T, I, L>>) -> mmr_lib::Result<()> {
@@ -95,9 +94,8 @@ impl<T, I, L> mmr_lib::MMRStore<NodeOf<T, I, L>> for Storage<RuntimeStorage, T,
// on-chain we only store the hash (even if it's a leaf)
<Nodes<T, I>>::insert(size, elem.hash());
// Indexing API is used to store the full leaf content.
elem.using_encoded(|elem| {
sp_io::offchain_index::set(&Module::<T, I>::offchain_key(size), elem)
});
let key = Module::<T, I>::offchain_key(size);
elem.using_encoded(|elem| sp_io::offchain_index::set(&key, elem));
size += 1;
if let Node::Data(..) = elem {
@@ -23,7 +23,7 @@ use sp_core::{
H256,
offchain::{
testing::TestOffchainExt,
OffchainExt,
OffchainWorkerExt, OffchainDbExt,
},
};
use pallet_mmr_primitives::{Proof, Compact};
@@ -34,7 +34,8 @@ pub(crate) fn new_test_ext() -> sp_io::TestExternalities {
fn register_offchain_ext(ext: &mut sp_io::TestExternalities) {
let (offchain, _offchain_state) = TestOffchainExt::with_offchain_db(ext.offchain_db());
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
}
fn new_block() -> u64 {
@@ -147,7 +147,8 @@ mod tests {
use sp_core::crypto::key_types::DUMMY;
use sp_core::offchain::{
testing::TestOffchainExt,
OffchainExt,
OffchainDbExt,
OffchainWorkerExt,
StorageKind,
};
@@ -181,7 +182,8 @@ mod tests {
seed[0..4].copy_from_slice(&ITERATIONS.to_le_bytes());
offchain_state.write().seed = seed;
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
ext
}
@@ -26,7 +26,7 @@ use pallet_staking::testing_utils::*;
use frame_support::{assert_ok, storage::StorageValue, traits::UnfilteredDispatchable};
use frame_system::RawOrigin;
use sp_runtime::DispatchError;
use sp_core::offchain::{testing::TestOffchainExt, OffchainExt};
use sp_core::offchain::{testing::TestOffchainExt, OffchainWorkerExt, OffchainDbExt};
use pallet_staking::{EraElectionStatus, ElectionStatus, Module as Staking, Call as StakingCall};
mod mock;
@@ -55,7 +55,8 @@ pub fn new_test_ext(iterations: u32) -> sp_io::TestExternalities {
seed[0..4].copy_from_slice(&iterations.to_le_bytes());
offchain_state.write().seed = seed;
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
ext
}
+3 -2
View File
@@ -2917,7 +2917,7 @@ mod offchain_election {
use parking_lot::RwLock;
use sp_core::offchain::{
testing::{PoolState, TestOffchainExt, TestTransactionPoolExt},
OffchainExt, TransactionPoolExt,
OffchainWorkerExt, TransactionPoolExt, OffchainDbExt,
};
use sp_io::TestExternalities;
use sp_npos_elections::StakedAssignment;
@@ -2960,7 +2960,8 @@ mod offchain_election {
seed[0..4].copy_from_slice(&iterations.to_le_bytes());
offchain_state.write().seed = seed;
ext.register_extension(OffchainExt::new(offchain));
ext.register_extension(OffchainDbExt::new(offchain.clone()));
ext.register_extension(OffchainWorkerExt::new(offchain));
ext.register_extension(TransactionPoolExt::new(pool));
pool_state
+2 -1
View File
@@ -119,9 +119,10 @@ impl ExecutionContext {
match self {
Importing | Syncing | BlockConstruction =>
offchain::Capabilities::none(),
// Enable keystore and transaction pool by default for offchain calls.
// Enable keystore, transaction pool and Offchain DB reads by default for offchain calls.
OffchainCall(None) => [
offchain::Capability::Keystore,
offchain::Capability::OffchainDbRead,
offchain::Capability::TransactionPool,
][..].into(),
OffchainCall(Some((_, capabilities))) => *capabilities,
+114 -93
View File
@@ -29,10 +29,10 @@ pub mod storage;
#[cfg(feature = "std")]
pub mod testing;
/// Local storage prefix used by the Offchain Worker API to
/// Persistent storage prefix used by the Offchain Worker API when creating a DB key.
pub const STORAGE_PREFIX : &[u8] = b"storage";
/// Offchain workers local storage.
/// Offchain DB persistent (non-fork-aware) storage.
pub trait OffchainStorage: Clone + Send + Sync {
/// Persist a value in storage under given key and prefix.
fn set(&mut self, prefix: &[u8], key: &[u8], value: &[u8]);
@@ -263,9 +263,9 @@ pub enum Capability {
/// Access to opaque network state.
NetworkState = 16,
/// Access to offchain worker DB (read only).
OffchainWorkerDbRead = 32,
OffchainDbRead = 32,
/// Access to offchain worker DB (writes).
OffchainWorkerDbWrite = 64,
OffchainDbWrite = 64,
/// Manage the authorized nodes
NodeAuthorization = 128,
}
@@ -293,7 +293,7 @@ impl Capabilities {
[
Capability::TransactionPool,
Capability::Keystore,
Capability::OffchainWorkerDbRead,
Capability::OffchainDbRead,
][..].into()
}
@@ -337,42 +337,6 @@ pub trait Externalities: Send {
/// Obviously fine in the off-chain worker context.
fn random_seed(&mut self) -> [u8; 32];
/// Sets a value in the local storage.
///
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]);
/// Removes a value in the local storage.
///
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]);
/// Sets a value in the local storage if it matches current value.
///
/// Since multiple offchain workers may be running concurrently, to prevent
/// data races use CAS to coordinate between them.
///
/// Returns `true` if the value has been set, `false` otherwise.
///
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool;
/// Gets a value from the local storage.
///
/// If the value does not exist in the storage `None` will be returned.
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>>;
/// Initiates a http request given HTTP verb and the URL.
///
/// Meta is a future-reserved field containing additional, parity-scale-codec encoded parameters.
@@ -521,28 +485,6 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
(&mut **self).random_seed()
}
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
(&mut **self).local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
(&mut **self).local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
(&mut **self).local_storage_compare_and_set(kind, key, old_value, new_value)
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
(&mut **self).local_storage_get(kind, key)
}
fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result<HttpRequestId, ()> {
(&mut **self).http_request_start(method, uri, meta)
}
@@ -582,7 +524,7 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
}
}
/// An `OffchainExternalities` implementation with limited capabilities.
/// An `*Externalities` implementation with limited capabilities.
pub struct LimitedExternalities<T> {
capabilities: Capabilities,
externalities: T,
@@ -633,32 +575,6 @@ impl<T: Externalities> Externalities for LimitedExternalities<T> {
self.externalities.random_seed()
}
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
self.check(Capability::OffchainWorkerDbWrite, "local_storage_set");
self.externalities.local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
self.check(Capability::OffchainWorkerDbWrite, "local_storage_clear");
self.externalities.local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
self.check(Capability::OffchainWorkerDbWrite, "local_storage_compare_and_set");
self.externalities.local_storage_compare_and_set(kind, key, old_value, new_value)
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
self.check(Capability::OffchainWorkerDbRead, "local_storage_get");
self.externalities.local_storage_get(kind, key)
}
fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result<HttpRequestId, ()> {
self.check(Capability::Http, "http_request_start");
self.externalities.http_request_start(method, uri, meta)
@@ -707,18 +623,123 @@ impl<T: Externalities> Externalities for LimitedExternalities<T> {
#[cfg(feature = "std")]
sp_externalities::decl_extension! {
/// The offchain extension that will be registered at the Substrate externalities.
pub struct OffchainExt(Box<dyn Externalities>);
/// The offchain worker extension that will be registered at the Substrate externalities.
pub struct OffchainWorkerExt(Box<dyn Externalities>);
}
#[cfg(feature = "std")]
impl OffchainExt {
impl OffchainWorkerExt {
/// Create a new instance of `Self`.
pub fn new<O: Externalities + 'static>(offchain: O) -> Self {
Self(Box::new(offchain))
}
}
/// A externalities extension for accessing the Offchain DB.
pub trait DbExternalities: Send {
/// Sets a value in the local storage.
///
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]);
/// Removes a value in the local storage.
///
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]);
/// Sets a value in the local storage if it matches current value.
///
/// Since multiple offchain workers may be running concurrently, to prevent
/// data races use CAS to coordinate between them.
///
/// Returns `true` if the value has been set, `false` otherwise.
///
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool;
/// Gets a value from the local storage.
///
/// If the value does not exist in the storage `None` will be returned.
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It _is_ persisted between runs.
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>>;
}
impl<T: DbExternalities + ?Sized> DbExternalities for Box<T> {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
(&mut **self).local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
(&mut **self).local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
(&mut **self).local_storage_compare_and_set(kind, key, old_value, new_value)
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
(&mut **self).local_storage_get(kind, key)
}
}
impl<T: DbExternalities> DbExternalities for LimitedExternalities<T> {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
self.check(Capability::OffchainDbWrite, "local_storage_set");
self.externalities.local_storage_set(kind, key, value)
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
self.check(Capability::OffchainDbWrite, "local_storage_clear");
self.externalities.local_storage_clear(kind, key)
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8],
) -> bool {
self.check(Capability::OffchainDbWrite, "local_storage_compare_and_set");
self.externalities.local_storage_compare_and_set(kind, key, old_value, new_value)
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
self.check(Capability::OffchainDbRead, "local_storage_get");
self.externalities.local_storage_get(kind, key)
}
}
#[cfg(feature = "std")]
sp_externalities::decl_extension! {
/// The offchain database extension that will be registered at the Substrate externalities.
pub struct OffchainDbExt(Box<dyn DbExternalities>);
}
#[cfg(feature = "std")]
impl OffchainDbExt {
/// Create a new instance of `OffchainDbExt`.
pub fn new<O: DbExternalities + 'static>(offchain: O) -> Self {
Self(Box::new(offchain))
}
}
/// Abstraction over transaction pool.
///
/// This trait is currently used within the `ExternalitiesExtension`
@@ -244,44 +244,6 @@ impl offchain::Externalities for TestOffchainExt {
self.0.read().seed
}
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
let mut state = self.0.write();
match kind {
StorageKind::LOCAL => state.local_storage.set(b"", key, value),
StorageKind::PERSISTENT => state.persistent_storage.set(b"", key, value),
};
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
let mut state = self.0.write();
match kind {
StorageKind::LOCAL => state.local_storage.remove(b"", key),
StorageKind::PERSISTENT => state.persistent_storage.remove(b"", key),
};
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8]
) -> bool {
let mut state = self.0.write();
match kind {
StorageKind::LOCAL => state.local_storage.compare_and_set(b"", key, old_value, new_value),
StorageKind::PERSISTENT => state.persistent_storage.compare_and_set(b"", key, old_value, new_value),
}
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
let state = self.0.read();
match kind {
StorageKind::LOCAL => state.local_storage.get(TestPersistentOffchainDB::PREFIX, key),
StorageKind::PERSISTENT => state.persistent_storage.get(key),
}
}
fn http_request_start(&mut self, method: &str, uri: &str, meta: &[u8]) -> Result<RequestId, ()> {
let mut state = self.0.write();
let id = RequestId(state.requests.len() as u16);
@@ -393,6 +355,48 @@ impl offchain::Externalities for TestOffchainExt {
}
}
impl offchain::DbExternalities for TestOffchainExt {
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
let mut state = self.0.write();
match kind {
StorageKind::LOCAL => state.local_storage.set(b"", key, value),
StorageKind::PERSISTENT => state.persistent_storage.set(b"", key, value),
};
}
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
let mut state = self.0.write();
match kind {
StorageKind::LOCAL => state.local_storage.remove(b"", key),
StorageKind::PERSISTENT => state.persistent_storage.remove(b"", key),
};
}
fn local_storage_compare_and_set(
&mut self,
kind: StorageKind,
key: &[u8],
old_value: Option<&[u8]>,
new_value: &[u8]
) -> bool {
let mut state = self.0.write();
match kind {
StorageKind::LOCAL => state.local_storage
.compare_and_set(b"", key, old_value, new_value),
StorageKind::PERSISTENT => state.persistent_storage
.compare_and_set(b"", key, old_value, new_value),
}
}
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
let state = self.0.read();
match kind {
StorageKind::LOCAL => state.local_storage.get(TestPersistentOffchainDB::PREFIX, key),
StorageKind::PERSISTENT => state.persistent_storage.get(key),
}
}
}
/// The internal state of the fake transaction pool.
#[derive(Default)]
pub struct PoolState {
+31 -22
View File
@@ -39,7 +39,7 @@ use tracing;
use sp_core::{
crypto::Pair,
traits::{CallInWasmExt, TaskExecutorExt, RuntimeSpawnExt},
offchain::{OffchainExt, TransactionPoolExt},
offchain::{OffchainDbExt, OffchainWorkerExt, TransactionPoolExt},
hexdisplay::HexDisplay,
storage::ChildInfo,
};
@@ -843,7 +843,7 @@ pub trait Offchain {
/// Even if this function returns `true`, it does not mean that any keys are configured
/// and that the validator is registered in the chain.
fn is_validator(&mut self) -> bool {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("is_validator can be called only in the offchain worker context")
.is_validator()
}
@@ -860,21 +860,21 @@ pub trait Offchain {
/// Returns information about the local node's network state.
fn network_state(&mut self) -> Result<OpaqueNetworkState, ()> {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("network_state can be called only in the offchain worker context")
.network_state()
}
/// Returns current UNIX timestamp (in millis)
fn timestamp(&mut self) -> Timestamp {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("timestamp can be called only in the offchain worker context")
.timestamp()
}
/// Pause the execution until `deadline` is reached.
fn sleep_until(&mut self, deadline: Timestamp) {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("sleep_until can be called only in the offchain worker context")
.sleep_until(deadline)
}
@@ -884,7 +884,7 @@ pub trait Offchain {
/// This is a truly random, non-deterministic seed generated by host environment.
/// Obviously fine in the off-chain worker context.
fn random_seed(&mut self) -> [u8; 32] {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("random_seed can be called only in the offchain worker context")
.random_seed()
}
@@ -894,8 +894,9 @@ pub trait Offchain {
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It IS persisted between runs.
fn local_storage_set(&mut self, kind: StorageKind, key: &[u8], value: &[u8]) {
self.extension::<OffchainExt>()
.expect("local_storage_set can be called only in the offchain worker context")
self.extension::<OffchainDbExt>()
.expect("local_storage_set can be called only in the offchain call context with
OffchainDb extension")
.local_storage_set(kind, key, value)
}
@@ -904,8 +905,9 @@ pub trait Offchain {
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It IS persisted between runs.
fn local_storage_clear(&mut self, kind: StorageKind, key: &[u8]) {
self.extension::<OffchainExt>()
.expect("local_storage_clear can be called only in the offchain worker context")
self.extension::<OffchainDbExt>()
.expect("local_storage_clear can be called only in the offchain call context with
OffchainDb extension")
.local_storage_clear(kind, key)
}
@@ -925,9 +927,15 @@ pub trait Offchain {
old_value: Option<Vec<u8>>,
new_value: &[u8],
) -> bool {
self.extension::<OffchainExt>()
.expect("local_storage_compare_and_set can be called only in the offchain worker context")
.local_storage_compare_and_set(kind, key, old_value.as_ref().map(|v| v.deref()), new_value)
self.extension::<OffchainDbExt>()
.expect("local_storage_compare_and_set can be called only in the offchain call context
with OffchainDb extension")
.local_storage_compare_and_set(
kind,
key,
old_value.as_ref().map(|v| v.deref()),
new_value,
)
}
/// Gets a value from the local storage.
@@ -936,8 +944,9 @@ pub trait Offchain {
/// Note this storage is not part of the consensus, it's only accessible by
/// offchain worker tasks running on the same machine. It IS persisted between runs.
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
self.extension::<OffchainExt>()
.expect("local_storage_get can be called only in the offchain worker context")
self.extension::<OffchainDbExt>()
.expect("local_storage_get can be called only in the offchain call context with
OffchainDb extension")
.local_storage_get(kind, key)
}
@@ -951,7 +960,7 @@ pub trait Offchain {
uri: &str,
meta: &[u8],
) -> Result<HttpRequestId, ()> {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("http_request_start can be called only in the offchain worker context")
.http_request_start(method, uri, meta)
}
@@ -963,7 +972,7 @@ pub trait Offchain {
name: &str,
value: &str,
) -> Result<(), ()> {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("http_request_add_header can be called only in the offchain worker context")
.http_request_add_header(request_id, name, value)
}
@@ -980,7 +989,7 @@ pub trait Offchain {
chunk: &[u8],
deadline: Option<Timestamp>,
) -> Result<(), HttpError> {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("http_request_write_body can be called only in the offchain worker context")
.http_request_write_body(request_id, chunk, deadline)
}
@@ -997,7 +1006,7 @@ pub trait Offchain {
ids: &[HttpRequestId],
deadline: Option<Timestamp>,
) -> Vec<HttpRequestStatus> {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("http_response_wait can be called only in the offchain worker context")
.http_response_wait(ids, deadline)
}
@@ -1007,7 +1016,7 @@ pub trait Offchain {
/// Returns a vector of pairs `(HeaderKey, HeaderValue)`.
/// NOTE response headers have to be read before response body.
fn http_response_headers(&mut self, request_id: HttpRequestId) -> Vec<(Vec<u8>, Vec<u8>)> {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("http_response_headers can be called only in the offchain worker context")
.http_response_headers(request_id)
}
@@ -1026,7 +1035,7 @@ pub trait Offchain {
buffer: &mut [u8],
deadline: Option<Timestamp>,
) -> Result<u32, HttpError> {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("http_response_read_body can be called only in the offchain worker context")
.http_response_read_body(request_id, buffer, deadline)
.map(|r| r as u32)
@@ -1034,7 +1043,7 @@ pub trait Offchain {
/// Set the authorized nodes and authorized_only flag.
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
self.extension::<OffchainExt>()
self.extension::<OffchainWorkerExt>()
.expect("set_authorized_nodes can be called only in the offchain worker context")
.set_authorized_nodes(nodes, authorized_only)
}
@@ -518,7 +518,7 @@ mod tests {
use super::*;
use sp_io::TestExternalities;
use sp_core::offchain::{
OffchainExt,
OffchainWorkerExt,
testing,
};
@@ -526,7 +526,7 @@ mod tests {
fn should_send_a_basic_request_and_get_response() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let request: Request = Request::get("http://localhost:1234");
@@ -567,7 +567,7 @@ mod tests {
fn should_send_a_post_request() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let pending = Request::default()
@@ -104,7 +104,7 @@ mod tests {
use super::*;
use sp_io::TestExternalities;
use sp_core::offchain::{
OffchainExt,
OffchainDbExt,
testing,
};
@@ -112,7 +112,7 @@ mod tests {
fn should_set_and_get() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainDbExt::new(offchain));
t.execute_with(|| {
let val = StorageValue::persistent(b"testval");
@@ -134,7 +134,7 @@ mod tests {
fn should_mutate() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainDbExt::new(offchain));
t.execute_with(|| {
let val = StorageValue::persistent(b"testval");
@@ -453,7 +453,7 @@ pub trait BlockNumberProvider {
#[cfg(test)]
mod tests {
use super::*;
use sp_core::offchain::{testing, OffchainExt};
use sp_core::offchain::{testing, OffchainWorkerExt, OffchainDbExt};
use sp_io::TestExternalities;
const VAL_1: u32 = 0u32;
@@ -463,7 +463,8 @@ mod tests {
fn storage_lock_write_unlock_lock_read_unlock() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let mut lock = StorageLock::<'_, Time>::new(b"lock_1");
@@ -493,7 +494,8 @@ mod tests {
fn storage_lock_and_forget() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let mut lock = StorageLock::<'_, Time>::new(b"lock_2");
@@ -517,7 +519,8 @@ mod tests {
fn storage_lock_and_let_expire_and_lock_again() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let sleep_until = offchain::timestamp().add(Duration::from_millis(500));
@@ -549,7 +552,8 @@ mod tests {
fn extend_active_lock() {
let (offchain, state) = testing::TestOffchainExt::new();
let mut t = TestExternalities::default();
t.register_extension(OffchainExt::new(offchain));
t.register_extension(OffchainDbExt::new(offchain.clone()));
t.register_extension(OffchainWorkerExt::new(offchain));
t.execute_with(|| {
let lock_expiration = Duration::from_millis(300);
+1
View File
@@ -24,6 +24,7 @@ sc-client-db = { version = "0.9.0", features = ["test-helpers"], path = "../../c
sc-consensus = { version = "0.9.0", path = "../../client/consensus/common" }
sc-executor = { version = "0.9.0", path = "../../client/executor" }
sc-light = { version = "3.0.0", path = "../../client/light" }
sc-offchain = { version = "3.0.0", path = "../../client/offchain" }
sc-service = { version = "0.9.0", default-features = false, features = ["test-helpers"], path = "../../client/service" }
sp-blockchain = { version = "3.0.0", path = "../../primitives/blockchain" }
sp-consensus = { version = "0.9.0", path = "../../primitives/consensus/common" }
+2
View File
@@ -198,6 +198,7 @@ impl<Block: BlockT, Executor, Backend, G: GenesisInit> TestClientBuilder<Block,
) where
Executor: sc_client_api::CallExecutor<Block> + 'static,
Backend: sc_client_api::backend::Backend<Block>,
<Backend as sc_client_api::backend::Backend<Block>>::OffchainStorage: 'static,
{
let storage = {
let mut storage = self.genesis_init.genesis_storage();
@@ -225,6 +226,7 @@ impl<Block: BlockT, Executor, Backend, G: GenesisInit> TestClientBuilder<Block,
ExecutionExtensions::new(
self.execution_strategies,
self.keystore,
sc_offchain::OffchainDb::factory_from_backend(&*self.backend),
),
None,
ClientConfig {
@@ -26,7 +26,7 @@ use sp_state_machine::StateMachine;
use sp_externalities::Extensions;
use sc_service::{Configuration, NativeExecutionDispatch};
use sp_runtime::traits::{Block as BlockT, Header as HeaderT, NumberFor};
use sp_core::offchain::{OffchainExt, testing::TestOffchainExt};
use sp_core::offchain::{OffchainWorkerExt, testing::TestOffchainExt};
use sp_keystore::{
SyncCryptoStorePtr, KeystoreExt,
testing::KeyStore,
@@ -73,7 +73,7 @@ impl BenchmarkCmd {
let mut extensions = Extensions::default();
extensions.register(KeystoreExt(Arc::new(KeyStore::new()) as SyncCryptoStorePtr));
let (offchain, _) = TestOffchainExt::new();
extensions.register(OffchainExt::new(offchain));
extensions.register(OffchainWorkerExt::new(offchain));
let result = StateMachine::<_, _, NumberFor<BB>, _>::new(
&state,