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
+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 {