Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

RPC api for offchain storage #4694

Merged
merged 4 commits into from
Jan 28, 2020
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/rpc-api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ pub use helpers::Receiver;

pub mod author;
pub mod chain;
pub mod offchain;
pub mod state;
pub mod system;
51 changes: 51 additions & 0 deletions client/rpc-api/src/offchain/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// Copyright 2020 Parity Technologies (UK) Ltd.

// This file is part of Substrate.

// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

//! Offchain RPC errors.

use jsonrpc_core as rpc;

/// Offchain RPC Result type.
pub type Result<T> = std::result::Result<T, Error>;

/// Offchain RPC errors.
#[derive(Debug, derive_more::Display, derive_more::From)]
pub enum Error {
/// Unavailable storage kind error.
#[display(fmt="This storage kind is not available yet.")]
UnavailableStorageKind,
}

impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
None
}
}

/// Base error code for all offchain errors.
const BASE_ERROR: i64 = 5000;

impl From<Error> for rpc::Error {
fn from(e: Error) -> Self {
match e {
Error::UnavailableStorageKind => rpc::Error {
code: rpc::ErrorCode::ServerError(BASE_ERROR + 1),
message: "This storage kind is not available yet" .into(),
data: None,
},
}
}
}
37 changes: 37 additions & 0 deletions client/rpc-api/src/offchain/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// Copyright 2020 Parity Technologies (UK) Ltd.

// This file is part of Substrate.

// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

//! Substrate offchain API.

pub mod error;

use jsonrpc_derive::rpc;
use self::error::Result;
use sp_core::{Bytes, offchain::StorageKind};

pub use self::gen_client::Client as OffchainClient;

/// Substrate offchain RPC API
#[rpc]
pub trait OffchainApi {
/// Set offchain local storage under given key and prefix.
#[rpc(name = "offchain_localStorageSet")]
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<()>;

/// Get offchain local storage under given key and prefix.
#[rpc(name = "offchain_localStorageGet")]
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result<Option<Bytes>>;
}
1 change: 1 addition & 0 deletions client/rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rpc = { package = "jsonrpc-core", version = "14.0.3" }
sp-version = { version = "2.0.0", path = "../../primitives/version" }
serde_json = "1.0.41"
sp-session = { version = "2.0.0", path = "../../primitives/session" }
sp-offchain = { version = "2.0.0", path = "../../primitives/offchain" }
sp-runtime = { version = "2.0.0", path = "../../primitives/runtime" }
sp-rpc = { version = "2.0.0", path = "../../primitives/rpc" }
sp-state-machine = { version = "0.8", path = "../../primitives/state-machine" }
Expand Down
1 change: 1 addition & 0 deletions client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ pub use rpc::IoHandlerExtension as RpcExtension;

pub mod author;
pub mod chain;
pub mod offchain;
pub mod state;
pub mod system;
67 changes: 67 additions & 0 deletions client/rpc/src/offchain/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// Copyright 2020 Parity Technologies (UK) Ltd.

// This file is part of Substrate.

// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

//! Substrate offchain API.

#[cfg(test)]
mod tests;

/// Re-export the API for backward compatibility.
pub use sc_rpc_api::offchain::*;
use self::error::{Error, Result};
use sp_core::{
Bytes,
offchain::{OffchainStorage, StorageKind},
};
use parking_lot::RwLock;
use std::sync::Arc;

/// Offchain API
#[derive(Debug)]
pub struct Offchain<T: OffchainStorage> {
/// Offchain storage
storage: Arc<RwLock<T>>,
}

impl<T: OffchainStorage> Offchain<T> {
/// Create new instance of Offchain API.
pub fn new(storage: T) -> Self {
Offchain {
storage: Arc::new(RwLock::new(storage)),
}
}
}

impl<T: OffchainStorage + 'static> OffchainApi for Offchain<T> {
/// Set offchain local storage under given key and prefix.
fn set_local_storage(&self, kind: StorageKind, key: Bytes, value: Bytes) -> Result<()> {
let prefix = match kind {
StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX,
StorageKind::LOCAL => return Err(Error::UnavailableStorageKind),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be supported in the future? If not, I don't understand why we take kind here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it mirrors the API available for offchain workers. Implementation could be done on top of #3263

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the difference between local and persistent?

Copy link
Contributor

@tomusdrw tomusdrw Jan 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fork awareness. LOCAL entries should be removed in case there is an reorg, while PERSISTENT should stay even if reorg happened (i.e. we see results produced by offchain workers that run on a different fork). The second one is useful if you want to make sure to avoid duplication of external "side effects".

};
self.storage.write().set(prefix, &*key, &*value);
Ok(())
}

/// Get offchain local storage under given key and prefix.
fn get_local_storage(&self, kind: StorageKind, key: Bytes) -> Result<Option<Bytes>> {
let prefix = match kind {
bkchr marked this conversation as resolved.
Show resolved Hide resolved
StorageKind::PERSISTENT => sp_offchain::STORAGE_PREFIX,
StorageKind::LOCAL => return Err(Error::UnavailableStorageKind),
};
Ok(self.storage.read().get(prefix, &*key).map(Into::into))
}
}
36 changes: 36 additions & 0 deletions client/rpc/src/offchain/tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright 2017-2020 Parity Technologies (UK) Ltd.
// Copyright 2020 Parity Technologies (UK) Ltd.

// This file is part of Substrate.

// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.

use super::*;
use assert_matches::assert_matches;
use sp_core::{Bytes, offchain::storage::InMemOffchainStorage};

#[test]
fn local_storage_should_work() {
let storage = InMemOffchainStorage::default();
let offchain = Offchain::new(storage);
let key = Bytes(b"offchain_storage".to_vec());
let value = Bytes(b"offchain_value".to_vec());

assert_matches!(
offchain.set_local_storage(StorageKind::PERSISTENT, key.clone(), value.clone()),
Ok(())
);
assert_matches!(
offchain.get_local_storage(StorageKind::PERSISTENT, key),
Ok(Some(ref v)) if *v == value
);
}
31 changes: 22 additions & 9 deletions client/service/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -855,7 +855,7 @@ ServiceBuilder<
let network_status_sinks = Arc::new(Mutex::new(status_sinks::StatusSinks::new()));

let offchain_storage = backend.offchain_storage();
let offchain_workers = match (config.offchain_worker, offchain_storage) {
let offchain_workers = match (config.offchain_worker, offchain_storage.clone()) {
(true, Some(db)) => {
Some(Arc::new(sc_offchain::OffchainWorkers::new(client.clone(), db)))
},
Expand Down Expand Up @@ -1000,7 +1000,7 @@ ServiceBuilder<
// RPC
let (system_rpc_tx, system_rpc_rx) = mpsc::unbounded();
let gen_handler = || {
use sc_rpc::{chain, state, author, system};
use sc_rpc::{chain, state, author, system, offchain};

let system_info = sc_rpc::system::SystemInfo {
chain_name: config.chain_spec.name().into(),
Expand Down Expand Up @@ -1046,13 +1046,26 @@ ServiceBuilder<
);
let system = system::System::new(system_info, system_rpc_tx.clone());

sc_rpc_server::rpc_handler((
state::StateApi::to_delegate(state),
chain::ChainApi::to_delegate(chain),
author::AuthorApi::to_delegate(author),
system::SystemApi::to_delegate(system),
rpc_extensions.clone(),
))
match offchain_storage.clone() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
match offchain_storage.clone() {
match offchain_storage {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ark930 please apply this as well.

Some(storage) => {
let offchain = sc_rpc::offchain::Offchain::new(storage);
sc_rpc_server::rpc_handler((
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@tomusdrw it would be nice if rpc_handlers would support Option<>, so we don't have this repetition here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

state::StateApi::to_delegate(state),
chain::ChainApi::to_delegate(chain),
offchain::OffchainApi::to_delegate(offchain),
author::AuthorApi::to_delegate(author),
system::SystemApi::to_delegate(system),
rpc_extensions.clone(),
))
},
None => sc_rpc_server::rpc_handler((
state::StateApi::to_delegate(state),
chain::ChainApi::to_delegate(chain),
author::AuthorApi::to_delegate(author),
system::SystemApi::to_delegate(system),
rpc_extensions.clone(),
))
}
};
let rpc_handlers = gen_handler();
let rpc = start_rpc_servers(&config, gen_handler)?;
Expand Down
1 change: 1 addition & 0 deletions primitives/core/src/offchain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub trait OffchainStorage: Clone + Send + Sync {

/// A type of supported crypto.
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, RuntimeDebug, PassByEnum)]
#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub enum StorageKind {
/// Persistent storage is non-revertible and not fork-aware. It means that any value
Expand Down