Set reserved nodes with offchain worker. (#6996)

* add offchain worker api to set reserved nodes.

* new offchain api to get node public key.

* node public key from converter

* refactor set reserved nodes ocw api.

* new ndoe authorization pallet

* remove unnecessary clone and more.

* more

* tests for node authorization pallet

* remove dependency

* fix build

* more tests.

* refactor

* Update primitives/core/src/offchain/testing.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update frame/node-authorization/src/lib.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update frame/node-authorization/src/lib.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* Update frame/node-authorization/src/lib.rs

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>

* format code

* expose NetworkService

* remove NetworkStateInfo in offchain

* replace NodePublicKey with PeerId.

* set max length of peer id.

* clear more

* use BTreeSet for set of peers.

* decode opaque peer id.

* extract NetworkProvider for client offchain.

* use OpaquePeerId in node authorization pallet.

* fix test

* better documentation

* fix test

* doc

* more fix

* Update primitives/core/src/offchain/mod.rs

Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>

* Update client/offchain/src/api.rs

Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>

* derive serialize and deserialize

Co-authored-by: Tomasz Drwięga <tomusdrw@users.noreply.github.com>
Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>
This commit is contained in:
kaichao
2020-09-10 23:26:09 +08:00
committed by GitHub
parent 56ce689be2
commit 91c0213413
15 changed files with 1080 additions and 44 deletions
+14
View File
@@ -4738,6 +4738,20 @@ dependencies = [
"sp-std",
]
[[package]]
name = "pallet-node-authorization"
version = "2.0.0-rc6"
dependencies = [
"frame-support",
"frame-system",
"parity-scale-codec",
"serde",
"sp-core",
"sp-io",
"sp-runtime",
"sp-std",
]
[[package]]
name = "pallet-offences"
version = "2.0.0-rc6"
+1
View File
@@ -88,6 +88,7 @@ members = [
"frame/metadata",
"frame/multisig",
"frame/nicks",
"frame/node-authorization",
"frame/offences",
"frame/proxy",
"frame/randomness-collective-flip",
+17 -1
View File
@@ -20,7 +20,7 @@
//!
//! There are two main structs in this module: [`NetworkWorker`] and [`NetworkService`].
//! The [`NetworkWorker`] *is* the network and implements the `Future` trait. It must be polled in
//! order fo the network to advance.
//! order for the network to advance.
//! The [`NetworkService`] is merely a shared version of the [`NetworkWorker`]. You can obtain an
//! `Arc<NetworkService>` by calling [`NetworkWorker::service`].
//!
@@ -605,6 +605,22 @@ impl<B: BlockT + 'static, H: ExHashT> NetworkService<B, H> {
&self.local_peer_id
}
/// Set authorized peers.
///
/// Need a better solution to manage authorized peers, but now just use reserved peers for
/// prototyping.
pub fn set_authorized_peers(&self, peers: HashSet<PeerId>) {
self.peerset.set_reserved_peers(peers)
}
/// Set authorized_only flag.
///
/// Need a better solution to decide authorized_only, but now just use reserved_only flag for
/// prototyping.
pub fn set_authorized_only(&self, reserved_only: bool) {
self.peerset.set_reserved_only(reserved_only)
}
/// Appends a notification to the buffer of pending outgoing notifications with the given peer.
/// Has no effect if the notifications channel with this protocol name is not open.
///
+35 -15
View File
@@ -19,16 +19,18 @@ use std::{
sync::Arc,
convert::TryFrom,
thread::sleep,
collections::HashSet,
};
use sp_core::offchain::OffchainStorage;
use crate::NetworkProvider;
use futures::Future;
use log::error;
use sc_network::{PeerId, Multiaddr, NetworkStateInfo};
use sc_network::{PeerId, Multiaddr};
use codec::{Encode, Decode};
use sp_core::OpaquePeerId;
use sp_core::offchain::{
Externalities as OffchainExt, HttpRequestId, Timestamp, HttpRequestStatus, HttpError,
OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr, StorageKind,
OffchainStorage, OpaqueNetworkState, OpaqueMultiaddr, StorageKind,
};
pub use sp_offchain::STORAGE_PREFIX;
pub use http::SharedClient;
@@ -49,8 +51,8 @@ mod timestamp;
pub(crate) struct Api<Storage> {
/// Offchain Workers database.
db: Storage,
/// A NetworkState provider.
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
/// 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.
@@ -73,10 +75,10 @@ impl<Storage: OffchainStorage> OffchainExt for Api<Storage> {
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
let external_addresses = self.network_state.external_addresses();
let external_addresses = self.network_provider.external_addresses();
let state = NetworkState::new(
self.network_state.local_peer_id(),
self.network_provider.local_peer_id(),
external_addresses,
);
Ok(OpaqueNetworkState::from(state))
@@ -180,6 +182,15 @@ impl<Storage: OffchainStorage> OffchainExt for Api<Storage> {
) -> Result<usize, HttpError> {
self.http.response_read_body(request_id, buffer, deadline)
}
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
let peer_ids: HashSet<PeerId> = nodes.into_iter()
.filter_map(|node| PeerId::from_bytes(node.0).ok())
.collect();
self.network_provider.set_authorized_peers(peer_ids);
self.network_provider.set_authorized_only(authorized_only);
}
}
/// Information about the local node's network state.
@@ -256,10 +267,10 @@ pub(crate) struct AsyncApi {
}
impl AsyncApi {
/// Creates new Offchain extensions API implementation an the asynchronous processing part.
/// Creates new Offchain extensions API implementation an the asynchronous processing part.
pub fn new<S: OffchainStorage>(
db: S,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
network_provider: Arc<dyn NetworkProvider + Send + Sync>,
is_validator: bool,
shared_client: SharedClient,
) -> (Api<S>, Self) {
@@ -267,7 +278,7 @@ impl AsyncApi {
let api = Api {
db,
network_state,
network_provider,
is_validator,
http: http_api,
};
@@ -292,11 +303,21 @@ mod tests {
use super::*;
use std::{convert::{TryFrom, TryInto}, time::SystemTime};
use sc_client_db::offchain::LocalStorage;
use sc_network::PeerId;
use sc_network::{NetworkStateInfo, PeerId};
struct MockNetworkStateInfo();
struct TestNetwork();
impl NetworkStateInfo for MockNetworkStateInfo {
impl NetworkProvider for TestNetwork {
fn set_authorized_peers(&self, _peers: HashSet<PeerId>) {
unimplemented!()
}
fn set_authorized_only(&self, _reserved_only: bool) {
unimplemented!()
}
}
impl NetworkStateInfo for TestNetwork {
fn external_addresses(&self) -> Vec<Multiaddr> {
Vec::new()
}
@@ -309,10 +330,9 @@ mod tests {
fn offchain_api() -> (Api<LocalStorage>, AsyncApi) {
let _ = env_logger::try_init();
let db = LocalStorage::new_test();
let mock = Arc::new(MockNetworkStateInfo());
let mock = Arc::new(TestNetwork());
let shared_client = SharedClient::new();
AsyncApi::new(
db,
mock,
+49 -10
View File
@@ -33,14 +33,17 @@
#![warn(missing_docs)]
use std::{fmt, marker::PhantomData, sync::Arc};
use std::{
fmt, marker::PhantomData, sync::Arc,
collections::HashSet,
};
use parking_lot::Mutex;
use threadpool::ThreadPool;
use sp_api::{ApiExt, ProvideRuntimeApi};
use futures::future::Future;
use log::{debug, warn};
use sc_network::NetworkStateInfo;
use sc_network::{ExHashT, NetworkService, NetworkStateInfo, PeerId};
use sp_core::{offchain::{self, OffchainStorage}, ExecutionContext, traits::SpawnNamed};
use sp_runtime::{generic::BlockId, traits::{self, Header}};
use futures::{prelude::*, future::ready};
@@ -50,6 +53,30 @@ use api::SharedClient;
pub use sp_offchain::{OffchainWorkerApi, STORAGE_PREFIX};
/// NetworkProvider provides [`OffchainWorkers`] with all necessary hooks into the
/// underlying Substrate networking.
pub trait NetworkProvider: NetworkStateInfo {
/// Set the authorized peers.
fn set_authorized_peers(&self, peers: HashSet<PeerId>);
/// Set the authorized only flag.
fn set_authorized_only(&self, reserved_only: bool);
}
impl<B, H> NetworkProvider for NetworkService<B, H>
where
B: traits::Block + 'static,
H: ExHashT,
{
fn set_authorized_peers(&self, peers: HashSet<PeerId>) {
self.set_authorized_peers(peers)
}
fn set_authorized_only(&self, reserved_only: bool) {
self.set_authorized_only(reserved_only)
}
}
/// An offchain workers manager.
pub struct OffchainWorkers<Client, Storage, Block: traits::Block> {
client: Arc<Client>,
@@ -98,7 +125,7 @@ impl<Client, Storage, Block> OffchainWorkers<
pub fn on_block_imported(
&self,
header: &Block::Header,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
network_provider: Arc<dyn NetworkProvider + Send + Sync>,
is_validator: bool,
) -> impl Future<Output = ()> {
let runtime = self.client.runtime_api();
@@ -122,7 +149,7 @@ impl<Client, Storage, Block> OffchainWorkers<
if version > 0 {
let (api, runner) = api::AsyncApi::new(
self.db.clone(),
network_state.clone(),
network_provider,
is_validator,
self.shared_client.clone(),
);
@@ -173,7 +200,7 @@ pub async fn notification_future<Client, Storage, Block, Spawner>(
client: Arc<Client>,
offchain: Arc<OffchainWorkers<Client, Storage, Block>>,
spawner: Spawner,
network_state_info: Arc<dyn NetworkStateInfo + Send + Sync>,
network_provider: Arc<dyn NetworkProvider + Send + Sync>,
)
where
Block: traits::Block,
@@ -188,7 +215,7 @@ pub async fn notification_future<Client, Storage, Block, Spawner>(
"offchain-on-block",
offchain.on_block_imported(
&n.header,
network_state_info.clone(),
network_provider.clone(),
is_validator,
).boxed(),
);
@@ -213,9 +240,9 @@ mod tests {
use sc_transaction_pool::{BasicPool, FullChainApi};
use sp_transaction_pool::{TransactionPool, InPoolTransaction};
struct MockNetworkStateInfo();
struct TestNetwork();
impl NetworkStateInfo for MockNetworkStateInfo {
impl NetworkStateInfo for TestNetwork {
fn external_addresses(&self) -> Vec<Multiaddr> {
Vec::new()
}
@@ -225,6 +252,16 @@ mod tests {
}
}
impl NetworkProvider for TestNetwork {
fn set_authorized_peers(&self, _peers: HashSet<PeerId>) {
unimplemented!()
}
fn set_authorized_only(&self, _reserved_only: bool) {
unimplemented!()
}
}
struct TestPool(
Arc<BasicPool<FullChainApi<TestClient, Block>, Block>>
);
@@ -255,12 +292,14 @@ mod tests {
client.clone(),
));
let db = sc_client_db::offchain::LocalStorage::new_test();
let network_state = Arc::new(MockNetworkStateInfo());
let network = Arc::new(TestNetwork());
let header = client.header(&BlockId::number(0)).unwrap().unwrap();
// when
let offchain = OffchainWorkers::new(client, db);
futures::executor::block_on(offchain.on_block_imported(&header, network_state, false));
futures::executor::block_on(
offchain.on_block_imported(&header, network, false)
);
// then
assert_eq!(pool.0.status().ready, 1);
+12
View File
@@ -45,6 +45,7 @@ const FORGET_AFTER: Duration = Duration::from_secs(3600);
enum Action {
AddReservedPeer(PeerId),
RemoveReservedPeer(PeerId),
SetReservedPeers(HashSet<PeerId>),
SetReservedOnly(bool),
ReportPeer(PeerId, ReputationChange),
SetPriorityGroup(String, HashSet<PeerId>),
@@ -102,6 +103,11 @@ impl PeersetHandle {
pub fn set_reserved_only(&self, reserved: bool) {
let _ = self.tx.unbounded_send(Action::SetReservedOnly(reserved));
}
/// Set reserved peers to the new set.
pub fn set_reserved_peers(&self, peer_ids: HashSet<PeerId>) {
let _ = self.tx.unbounded_send(Action::SetReservedPeers(peer_ids));
}
/// Reports an adjustment to the reputation of the given peer.
pub fn report_peer(&self, peer_id: PeerId, score_diff: ReputationChange) {
@@ -246,6 +252,10 @@ impl Peerset {
fn on_remove_reserved_peer(&mut self, peer_id: PeerId) {
self.on_remove_from_priority_group(RESERVED_NODES, peer_id);
}
fn on_set_reserved_peers(&mut self, peer_ids: HashSet<PeerId>) {
self.on_set_priority_group(RESERVED_NODES, peer_ids);
}
fn on_set_reserved_only(&mut self, reserved_only: bool) {
self.reserved_only = reserved_only;
@@ -655,6 +665,8 @@ impl Stream for Peerset {
self.on_add_reserved_peer(peer_id),
Action::RemoveReservedPeer(peer_id) =>
self.on_remove_reserved_peer(peer_id),
Action::SetReservedPeers(peer_ids) =>
self.on_set_reserved_peers(peer_ids),
Action::SetReservedOnly(reserved) =>
self.on_set_reserved_only(reserved),
Action::ReportPeer(peer_id, score_diff) =>
+1 -1
View File
@@ -431,7 +431,7 @@ pub fn build_offchain_workers<TBl, TBackend, TCl>(
client.clone(),
offchain,
Clone::clone(&spawn_handle),
network.clone()
network.clone(),
)
);
}
@@ -23,7 +23,8 @@ use super::*;
use frame_system::RawOrigin;
use frame_benchmarking::benchmarks;
use sp_core::offchain::{OpaquePeerId, OpaqueMultiaddr};
use sp_core::OpaquePeerId;
use sp_core::offchain::OpaqueMultiaddr;
use sp_runtime::traits::{ValidateUnsigned, Zero};
use sp_runtime::transaction_validity::TransactionSource;
use frame_support::traits::UnfilteredDispatchable;
+1 -1
View File
@@ -21,8 +21,8 @@
use super::*;
use crate::mock::*;
use sp_core::OpaquePeerId;
use sp_core::offchain::{
OpaquePeerId,
OffchainExt,
TransactionPoolExt,
testing::{TestOffchainExt, TestTransactionPoolExt},
@@ -0,0 +1,35 @@
[package]
name = "pallet-node-authorization"
version = "2.0.0-rc6"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2018"
license = "Apache-2.0"
homepage = "https://substrate.dev"
repository = "https://github.com/paritytech/substrate/"
description = "FRAME pallet for node authorization"
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
serde = { version = "1.0.101", optional = true }
codec = { package = "parity-scale-codec", version = "1.3.4", default-features = false, features = ["derive"] }
frame-support = { version = "2.0.0-rc6", default-features = false, path = "../support" }
frame-system = { version = "2.0.0-rc6", default-features = false, path = "../system" }
sp-core = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/core" }
sp-io = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/io" }
sp-runtime = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "2.0.0-rc6", default-features = false, path = "../../primitives/std" }
[features]
default = ["std"]
std = [
"serde",
"codec/std",
"frame-support/std",
"frame-system/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
]
@@ -0,0 +1,861 @@
// This file is part of Substrate.
// Copyright (C) 2019-2020 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.
//! # Node authorization pallet
//!
//! This pallet manages a configurable set of nodes for a permissioned network.
//! Each node is dentified by a PeerId (i.e. Vec<u8>). It provides two ways to
//! authorize a node,
//!
//! - a set of well known nodes across different organizations in which the
//! connections are allowed.
//! - users can claim the ownership for each node, then manage the connections of
//! the node.
//!
//! A node must have an owner. The owner can additionally change the connections
//! for the node. Only one user is allowed to claim a specific node. To eliminate
//! false claim, the maintainer of the node should claim it before even starting the
//! node. This pallet uses offchain worker to set reserved nodes, if the node is not
//! an authority, make sure to enable offchain worker with the right CLI flag. The
//! node can be lagged with the latest block, in this case you need to disable offchain
//! worker and manually set reserved nodes when starting it.
// Ensure we're `no_std` when compiling for Wasm.
#![cfg_attr(not(feature = "std"), no_std)]
use sp_core::OpaquePeerId as PeerId;
use sp_std::{
collections::btree_set::BTreeSet,
iter::FromIterator,
prelude::*,
};
use codec::Decode;
use frame_support::{
decl_module, decl_storage, decl_event, decl_error,
debug, ensure,
weights::{DispatchClass, Weight},
traits::{Get, EnsureOrigin},
};
use frame_system::ensure_signed;
pub trait WeightInfo {
fn add_well_known_node() -> Weight;
fn remove_well_known_node() -> Weight;
fn swap_well_known_node() -> Weight;
fn reset_well_known_nodes() -> Weight;
fn claim_node() -> Weight;
fn remove_claim() -> Weight;
fn transfer_node() -> Weight;
fn add_connections() -> Weight;
fn remove_connections() -> Weight;
}
impl WeightInfo for () {
fn add_well_known_node() -> Weight { 50_000_000 }
fn remove_well_known_node() -> Weight { 50_000_000 }
fn swap_well_known_node() -> Weight { 50_000_000 }
fn reset_well_known_nodes() -> Weight { 50_000_000 }
fn claim_node() -> Weight { 50_000_000 }
fn remove_claim() -> Weight { 50_000_000 }
fn transfer_node() -> Weight { 50_000_000 }
fn add_connections() -> Weight { 50_000_000 }
fn remove_connections() -> Weight { 50_000_000 }
}
pub trait Trait: frame_system::Trait {
/// The event type of this module.
type Event: From<Event<Self>> + Into<<Self as frame_system::Trait>::Event>;
/// The maximum number of well known nodes that are allowed to set
type MaxWellKnownNodes: Get<u32>;
/// The maximum length in bytes of PeerId
type MaxPeerIdLength: Get<u32>;
/// The origin which can add a well known node.
type AddOrigin: EnsureOrigin<Self::Origin>;
/// The origin which can remove a well known node.
type RemoveOrigin: EnsureOrigin<Self::Origin>;
/// The origin which can swap the well known nodes.
type SwapOrigin: EnsureOrigin<Self::Origin>;
/// The origin which can reset the well known nodes.
type ResetOrigin: EnsureOrigin<Self::Origin>;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}
decl_storage! {
trait Store for Module<T: Trait> as NodeAuthorization {
/// The set of well known nodes. This is stored sorted (just by value).
pub WellKnownNodes get(fn well_known_nodes): BTreeSet<PeerId>;
/// A map that maintains the ownership of each node.
pub Owners get(fn owners):
map hasher(blake2_128_concat) PeerId => T::AccountId;
/// The additional adapative connections of each node.
pub AdditionalConnections get(fn additional_connection):
map hasher(blake2_128_concat) PeerId => BTreeSet<PeerId>;
}
add_extra_genesis {
config(nodes): Vec<(PeerId, T::AccountId)>;
build(|config: &GenesisConfig<T>| {
<Module<T>>::initialize_nodes(&config.nodes)
})
}
}
decl_event! {
pub enum Event<T> where
<T as frame_system::Trait>::AccountId,
{
/// The given well known node was added.
NodeAdded(PeerId, AccountId),
/// The given well known node was removed.
NodeRemoved(PeerId),
/// The given well known node was swapped; first item was removed,
/// the latter was added.
NodeSwapped(PeerId, PeerId),
/// The given well known nodes were reset.
NodesReset(Vec<(PeerId, AccountId)>),
/// The given node was claimed by a user.
NodeClaimed(PeerId, AccountId),
/// The given claim was removed by its owner.
ClaimRemoved(PeerId, AccountId),
/// The node was transferred to another account.
NodeTransferred(PeerId, AccountId),
/// The allowed connections were added to a node.
ConnectionsAdded(PeerId, Vec<PeerId>),
/// The allowed connections were removed from a node.
ConnectionsRemoved(PeerId, Vec<PeerId>),
}
}
decl_error! {
/// Error for the node authorization module.
pub enum Error for Module<T: Trait> {
/// The PeerId is too long.
PeerIdTooLong,
/// Too many well known nodes.
TooManyNodes,
/// The node is already joined in the list.
AlreadyJoined,
/// The node doesn't exist in the list.
NotExist,
/// The node is already claimed by a user.
AlreadyClaimed,
/// The node hasn't been claimed yet.
NotClaimed,
/// You are not the owner of the node.
NotOwner,
/// No permisson to perform specific operation.
PermissionDenied,
}
}
decl_module! {
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
/// The maximum number of authorized well known nodes
const MaxWellKnownNodes: u32 = T::MaxWellKnownNodes::get();
/// The maximum length in bytes of PeerId
const MaxPeerIdLength: u32 = T::MaxPeerIdLength::get();
type Error = Error<T>;
fn deposit_event() = default;
/// Add a node to the set of well known nodes. If the node is already claimed, the owner
/// will be updated and keep the existing additional connection unchanged.
///
/// May only be called from `T::AddOrigin`.
///
/// - `node`: identifier of the node.
#[weight = (T::WeightInfo::add_well_known_node(), DispatchClass::Operational)]
pub fn add_well_known_node(origin, node: PeerId, owner: T::AccountId) {
T::AddOrigin::ensure_origin(origin)?;
ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
let mut nodes = WellKnownNodes::get();
ensure!(nodes.len() < T::MaxWellKnownNodes::get() as usize, Error::<T>::TooManyNodes);
ensure!(!nodes.contains(&node), Error::<T>::AlreadyJoined);
nodes.insert(node.clone());
WellKnownNodes::put(&nodes);
<Owners<T>>::insert(&node, &owner);
Self::deposit_event(RawEvent::NodeAdded(node, owner));
}
/// Remove a node from the set of well known nodes. The ownership and additional
/// connections of the node will also be removed.
///
/// May only be called from `T::RemoveOrigin`.
///
/// - `node`: identifier of the node.
#[weight = (T::WeightInfo::remove_well_known_node(), DispatchClass::Operational)]
pub fn remove_well_known_node(origin, node: PeerId) {
T::RemoveOrigin::ensure_origin(origin)?;
ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
let mut nodes = WellKnownNodes::get();
ensure!(nodes.contains(&node), Error::<T>::NotExist);
nodes.remove(&node);
WellKnownNodes::put(&nodes);
<Owners<T>>::remove(&node);
AdditionalConnections::remove(&node);
Self::deposit_event(RawEvent::NodeRemoved(node));
}
/// Swap a well known node to another. Both the ownership and additional connections
/// stay untouched.
///
/// May only be called from `T::SwapOrigin`.
///
/// - `remove`: the node which will be moved out from the list.
/// - `add`: the node which will be put in the list.
#[weight = (T::WeightInfo::swap_well_known_node(), DispatchClass::Operational)]
pub fn swap_well_known_node(origin, remove: PeerId, add: PeerId) {
T::SwapOrigin::ensure_origin(origin)?;
ensure!(remove.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
ensure!(add.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
if remove == add { return Ok(()) }
let mut nodes = WellKnownNodes::get();
ensure!(nodes.contains(&remove), Error::<T>::NotExist);
ensure!(!nodes.contains(&add), Error::<T>::AlreadyJoined);
nodes.remove(&remove);
nodes.insert(add.clone());
WellKnownNodes::put(&nodes);
Owners::<T>::swap(&remove, &add);
AdditionalConnections::swap(&remove, &add);
Self::deposit_event(RawEvent::NodeSwapped(remove, add));
}
/// Reset all the well known nodes. This will not remove the ownership and additional
/// connections for the removed nodes. The node owner can perform further cleaning if
/// they decide to leave the network.
///
/// May only be called from `T::ResetOrigin`.
///
/// - `nodes`: the new nodes for the allow list.
#[weight = (T::WeightInfo::reset_well_known_nodes(), DispatchClass::Operational)]
pub fn reset_well_known_nodes(origin, nodes: Vec<(PeerId, T::AccountId)>) {
T::ResetOrigin::ensure_origin(origin)?;
ensure!(nodes.len() < T::MaxWellKnownNodes::get() as usize, Error::<T>::TooManyNodes);
Self::initialize_nodes(&nodes);
Self::deposit_event(RawEvent::NodesReset(nodes));
}
/// A given node can be claimed by anyone. The owner should be the first to know its
/// PeerId, so claim it right away!
///
/// - `node`: identifier of the node.
#[weight = T::WeightInfo::claim_node()]
pub fn claim_node(origin, node: PeerId) {
let sender = ensure_signed(origin)?;
ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
ensure!(!Owners::<T>::contains_key(&node),Error::<T>::AlreadyClaimed);
Owners::<T>::insert(&node, &sender);
Self::deposit_event(RawEvent::NodeClaimed(node, sender));
}
/// A claim can be removed by its owner and get back the reservation. The additional
/// connections are also removed. You can't remove a claim on well known nodes, as it
/// needs to reach consensus among the network participants.
///
/// - `node`: identifier of the node.
#[weight = T::WeightInfo::remove_claim()]
pub fn remove_claim(origin, node: PeerId) {
let sender = ensure_signed(origin)?;
ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
ensure!(Owners::<T>::contains_key(&node), Error::<T>::NotClaimed);
ensure!(Owners::<T>::get(&node) == sender, Error::<T>::NotOwner);
ensure!(!WellKnownNodes::get().contains(&node), Error::<T>::PermissionDenied);
Owners::<T>::remove(&node);
AdditionalConnections::remove(&node);
Self::deposit_event(RawEvent::ClaimRemoved(node, sender));
}
/// A node can be transferred to a new owner.
///
/// - `node`: identifier of the node.
/// - `owner`: new owner of the node.
#[weight = T::WeightInfo::transfer_node()]
pub fn transfer_node(origin, node: PeerId, owner: T::AccountId) {
let sender = ensure_signed(origin)?;
ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
ensure!(Owners::<T>::contains_key(&node), Error::<T>::NotClaimed);
ensure!(Owners::<T>::get(&node) == sender, Error::<T>::NotOwner);
Owners::<T>::insert(&node, &owner);
Self::deposit_event(RawEvent::NodeTransferred(node, owner));
}
/// Add additional connections to a given node.
///
/// - `node`: identifier of the node.
/// - `connections`: additonal nodes from which the connections are allowed.
#[weight = T::WeightInfo::add_connections()]
pub fn add_connections(
origin,
node: PeerId,
connections: Vec<PeerId>
) {
let sender = ensure_signed(origin)?;
ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
ensure!(Owners::<T>::contains_key(&node), Error::<T>::NotClaimed);
ensure!(Owners::<T>::get(&node) == sender, Error::<T>::NotOwner);
let mut nodes = AdditionalConnections::get(&node);
for add_node in connections.iter() {
if *add_node == node {
continue;
}
nodes.insert(add_node.clone());
}
AdditionalConnections::insert(&node, nodes);
Self::deposit_event(RawEvent::ConnectionsAdded(node, connections));
}
/// Remove additional connections of a given node.
///
/// - `node`: identifier of the node.
/// - `connections`: additonal nodes from which the connections are not allowed anymore.
#[weight = T::WeightInfo::remove_connections()]
pub fn remove_connections(
origin,
node: PeerId,
connections: Vec<PeerId>
) {
let sender = ensure_signed(origin)?;
ensure!(node.0.len() < T::MaxPeerIdLength::get() as usize, Error::<T>::PeerIdTooLong);
ensure!(Owners::<T>::contains_key(&node), Error::<T>::NotClaimed);
ensure!(Owners::<T>::get(&node) == sender, Error::<T>::NotOwner);
let mut nodes = AdditionalConnections::get(&node);
for remove_node in connections.iter() {
nodes.remove(remove_node);
}
AdditionalConnections::insert(&node, nodes);
Self::deposit_event(RawEvent::ConnectionsRemoved(node, connections));
}
/// Set reserved node every block. It may not be enabled depends on the offchain
/// worker settings when starting the node.
fn offchain_worker(now: T::BlockNumber) {
let network_state = sp_io::offchain::network_state();
match network_state {
Err(_) => debug::error!("Error: failed to get network state of node at {:?}", now),
Ok(state) => {
let encoded_peer = state.peer_id.0;
match Decode::decode(&mut &encoded_peer[..]) {
Err(_) => debug::error!("Error: failed to decode PeerId at {:?}", now),
Ok(node) => sp_io::offchain::set_authorized_nodes(
Self::get_authorized_nodes(&PeerId(node)),
true
)
}
}
}
}
}
}
impl<T: Trait> Module<T> {
fn initialize_nodes(nodes: &Vec<(PeerId, T::AccountId)>) {
let peer_ids = nodes.iter()
.map(|item| item.0.clone())
.collect::<BTreeSet<PeerId>>();
WellKnownNodes::put(&peer_ids);
for (node, who) in nodes.iter() {
Owners::<T>::insert(node, who);
}
}
fn get_authorized_nodes(node: &PeerId) -> Vec<PeerId> {
let mut nodes = AdditionalConnections::get(node);
let mut well_known_nodes = WellKnownNodes::get();
if well_known_nodes.contains(node) {
well_known_nodes.remove(node);
nodes.extend(well_known_nodes);
}
Vec::from_iter(nodes)
}
}
#[cfg(test)]
mod tests {
use super::*;
use frame_support::{
assert_ok, assert_noop, impl_outer_origin, weights::Weight,
parameter_types, ord_parameter_types,
};
use frame_system::EnsureSignedBy;
use sp_core::H256;
use sp_runtime::{Perbill, traits::{BlakeTwo256, IdentityLookup, BadOrigin}, testing::Header};
impl_outer_origin! {
pub enum Origin for Test where system = frame_system {}
}
#[derive(Clone, Eq, PartialEq)]
pub struct Test;
parameter_types! {
pub const BlockHashCount: u64 = 250;
pub const MaximumBlockWeight: Weight = 1024;
pub const MaximumBlockLength: u32 = 2 * 1024;
pub const AvailableBlockRatio: Perbill = Perbill::one();
}
impl frame_system::Trait for Test {
type BaseCallFilter = ();
type Origin = Origin;
type Index = u64;
type BlockNumber = u64;
type Hash = H256;
type Call = ();
type Hashing = BlakeTwo256;
type AccountId = u64;
type Lookup = IdentityLookup<Self::AccountId>;
type Header = Header;
type Event = ();
type BlockHashCount = BlockHashCount;
type MaximumBlockWeight = MaximumBlockWeight;
type DbWeight = ();
type BlockExecutionWeight = ();
type ExtrinsicBaseWeight = ();
type MaximumExtrinsicWeight = MaximumBlockWeight;
type MaximumBlockLength = MaximumBlockLength;
type AvailableBlockRatio = AvailableBlockRatio;
type Version = ();
type ModuleToIndex = ();
type AccountData = ();
type OnNewAccount = ();
type OnKilledAccount = ();
type SystemWeightInfo = ();
}
ord_parameter_types! {
pub const One: u64 = 1;
pub const Two: u64 = 2;
pub const Three: u64 = 3;
pub const Four: u64 = 4;
}
parameter_types! {
pub const MaxWellKnownNodes: u32 = 4;
pub const MaxPeerIdLength: u32 = 2;
}
impl Trait for Test {
type Event = ();
type MaxWellKnownNodes = MaxWellKnownNodes;
type MaxPeerIdLength = MaxPeerIdLength;
type AddOrigin = EnsureSignedBy<One, u64>;
type RemoveOrigin = EnsureSignedBy<Two, u64>;
type SwapOrigin = EnsureSignedBy<Three, u64>;
type ResetOrigin = EnsureSignedBy<Four, u64>;
type WeightInfo = ();
}
type NodeAuthorization = Module<Test>;
fn test_node(id: u8) -> PeerId {
PeerId(vec![id])
}
fn new_test_ext() -> sp_io::TestExternalities {
let mut t = frame_system::GenesisConfig::default().build_storage::<Test>().unwrap();
GenesisConfig::<Test> {
nodes: vec![(test_node(10), 10), (test_node(20), 20), (test_node(30), 30)],
}.assimilate_storage(&mut t).unwrap();
t.into()
}
#[test]
fn add_well_known_node_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::add_well_known_node(Origin::signed(2), test_node(15), 15),
BadOrigin
);
assert_noop!(
NodeAuthorization::add_well_known_node(Origin::signed(1), PeerId(vec![1, 2, 3]), 15),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::add_well_known_node(Origin::signed(1), test_node(20), 20),
Error::<Test>::AlreadyJoined
);
assert_ok!(
NodeAuthorization::add_well_known_node(Origin::signed(1), test_node(15), 15)
);
assert_eq!(
WellKnownNodes::get(),
BTreeSet::from_iter(vec![test_node(10), test_node(15), test_node(20), test_node(30)])
);
assert_eq!(Owners::<Test>::get(test_node(10)), 10);
assert_eq!(Owners::<Test>::get(test_node(20)), 20);
assert_eq!(Owners::<Test>::get(test_node(30)), 30);
assert_eq!(Owners::<Test>::get(test_node(15)), 15);
assert_noop!(
NodeAuthorization::add_well_known_node(Origin::signed(1), test_node(25), 25),
Error::<Test>::TooManyNodes
);
});
}
#[test]
fn remove_well_known_node_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::remove_well_known_node(Origin::signed(3), test_node(20)),
BadOrigin
);
assert_noop!(
NodeAuthorization::remove_well_known_node(Origin::signed(2), PeerId(vec![1, 2, 3])),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::remove_well_known_node(Origin::signed(2), test_node(40)),
Error::<Test>::NotExist
);
AdditionalConnections::insert(
test_node(20),
BTreeSet::from_iter(vec![test_node(40)])
);
assert!(AdditionalConnections::contains_key(test_node(20)));
assert_ok!(
NodeAuthorization::remove_well_known_node(Origin::signed(2), test_node(20))
);
assert_eq!(
WellKnownNodes::get(),
BTreeSet::from_iter(vec![test_node(10), test_node(30)])
);
assert!(!Owners::<Test>::contains_key(test_node(20)));
assert!(!AdditionalConnections::contains_key(test_node(20)));
});
}
#[test]
fn swap_well_known_node_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::swap_well_known_node(
Origin::signed(4), test_node(20), test_node(5)
),
BadOrigin
);
assert_noop!(
NodeAuthorization::swap_well_known_node(
Origin::signed(3), PeerId(vec![1, 2, 3]), test_node(20)
),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::swap_well_known_node(
Origin::signed(3), test_node(20), PeerId(vec![1, 2, 3])
),
Error::<Test>::PeerIdTooLong
);
assert_ok!(
NodeAuthorization::swap_well_known_node(
Origin::signed(3), test_node(20), test_node(20)
)
);
assert_eq!(
WellKnownNodes::get(),
BTreeSet::from_iter(vec![test_node(10), test_node(20), test_node(30)])
);
assert_noop!(
NodeAuthorization::swap_well_known_node(
Origin::signed(3), test_node(15), test_node(5)
),
Error::<Test>::NotExist
);
assert_noop!(
NodeAuthorization::swap_well_known_node(
Origin::signed(3), test_node(20), test_node(30)
),
Error::<Test>::AlreadyJoined
);
AdditionalConnections::insert(
test_node(20),
BTreeSet::from_iter(vec![test_node(15)])
);
assert_ok!(
NodeAuthorization::swap_well_known_node(
Origin::signed(3), test_node(20), test_node(5)
)
);
assert_eq!(
WellKnownNodes::get(),
BTreeSet::from_iter(vec![test_node(5), test_node(10), test_node(30)])
);
assert!(!Owners::<Test>::contains_key(test_node(20)));
assert_eq!(Owners::<Test>::get(test_node(5)), 20);
assert!(!AdditionalConnections::contains_key(test_node(20)));
assert_eq!(
AdditionalConnections::get(test_node(5)),
BTreeSet::from_iter(vec![test_node(15)])
);
});
}
#[test]
fn reset_well_known_nodes_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::reset_well_known_nodes(
Origin::signed(3),
vec![(test_node(15), 15), (test_node(5), 5), (test_node(20), 20)]
),
BadOrigin
);
assert_noop!(
NodeAuthorization::reset_well_known_nodes(
Origin::signed(4),
vec![
(test_node(15), 15),
(test_node(5), 5),
(test_node(20), 20),
(test_node(25), 25),
]
),
Error::<Test>::TooManyNodes
);
assert_ok!(
NodeAuthorization::reset_well_known_nodes(
Origin::signed(4),
vec![(test_node(15), 15), (test_node(5), 5), (test_node(20), 20)]
)
);
assert_eq!(
WellKnownNodes::get(),
BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(20)])
);
assert_eq!(Owners::<Test>::get(test_node(5)), 5);
assert_eq!(Owners::<Test>::get(test_node(15)), 15);
assert_eq!(Owners::<Test>::get(test_node(20)), 20);
});
}
#[test]
fn claim_node_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::claim_node(Origin::signed(1), PeerId(vec![1, 2, 3])),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::claim_node(Origin::signed(1), test_node(20)),
Error::<Test>::AlreadyClaimed
);
assert_ok!(NodeAuthorization::claim_node(Origin::signed(15), test_node(15)));
assert_eq!(Owners::<Test>::get(test_node(15)), 15);
});
}
#[test]
fn remove_claim_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::remove_claim(Origin::signed(15), PeerId(vec![1, 2, 3])),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::remove_claim(Origin::signed(15), test_node(15)),
Error::<Test>::NotClaimed
);
assert_noop!(
NodeAuthorization::remove_claim(Origin::signed(15), test_node(20)),
Error::<Test>::NotOwner
);
assert_noop!(
NodeAuthorization::remove_claim(Origin::signed(20), test_node(20)),
Error::<Test>::PermissionDenied
);
Owners::<Test>::insert(test_node(15), 15);
AdditionalConnections::insert(
test_node(15),
BTreeSet::from_iter(vec![test_node(20)])
);
assert_ok!(NodeAuthorization::remove_claim(Origin::signed(15), test_node(15)));
assert!(!Owners::<Test>::contains_key(test_node(15)));
assert!(!AdditionalConnections::contains_key(test_node(15)));
});
}
#[test]
fn transfer_node_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::transfer_node(Origin::signed(15), PeerId(vec![1, 2, 3]), 10),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::transfer_node(Origin::signed(15), test_node(15), 10),
Error::<Test>::NotClaimed
);
assert_noop!(
NodeAuthorization::transfer_node(Origin::signed(15), test_node(20), 10),
Error::<Test>::NotOwner
);
assert_ok!(NodeAuthorization::transfer_node(Origin::signed(20), test_node(20), 15));
assert_eq!(Owners::<Test>::get(test_node(20)), 15);
});
}
#[test]
fn add_connections_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::add_connections(
Origin::signed(15), PeerId(vec![1, 2, 3]), vec![test_node(5)]
),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::add_connections(
Origin::signed(15), test_node(15), vec![test_node(5)]
),
Error::<Test>::NotClaimed
);
assert_noop!(
NodeAuthorization::add_connections(
Origin::signed(15), test_node(20), vec![test_node(5)]
),
Error::<Test>::NotOwner
);
assert_ok!(
NodeAuthorization::add_connections(
Origin::signed(20),
test_node(20),
vec![test_node(15), test_node(5), test_node(25), test_node(20)]
)
);
assert_eq!(
AdditionalConnections::get(test_node(20)),
BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(25)])
);
});
}
#[test]
fn remove_connections_works() {
new_test_ext().execute_with(|| {
assert_noop!(
NodeAuthorization::remove_connections(
Origin::signed(15), PeerId(vec![1, 2, 3]), vec![test_node(5)]
),
Error::<Test>::PeerIdTooLong
);
assert_noop!(
NodeAuthorization::remove_connections(
Origin::signed(15), test_node(15), vec![test_node(5)]
),
Error::<Test>::NotClaimed
);
assert_noop!(
NodeAuthorization::remove_connections(
Origin::signed(15), test_node(20), vec![test_node(5)]
),
Error::<Test>::NotOwner
);
AdditionalConnections::insert(
test_node(20),
BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(25)])
);
assert_ok!(
NodeAuthorization::remove_connections(
Origin::signed(20),
test_node(20),
vec![test_node(15), test_node(5)]
)
);
assert_eq!(
AdditionalConnections::get(test_node(20)),
BTreeSet::from_iter(vec![test_node(25)])
);
});
}
#[test]
fn get_authorized_nodes_works() {
new_test_ext().execute_with(|| {
AdditionalConnections::insert(
test_node(20),
BTreeSet::from_iter(vec![test_node(5), test_node(15), test_node(25)])
);
let mut authorized_nodes = Module::<Test>::get_authorized_nodes(&test_node(20));
authorized_nodes.sort();
assert_eq!(
authorized_nodes,
vec![test_node(5), test_node(10), test_node(15), test_node(25), test_node(30)]
);
});
}
}
+14 -1
View File
@@ -32,6 +32,7 @@ macro_rules! map {
);
}
use sp_runtime_interface::pass_by::{PassByEnum, PassByInner};
use sp_std::prelude::*;
use sp_std::ops::Deref;
#[cfg(feature = "std")]
@@ -176,6 +177,18 @@ impl sp_std::ops::Deref for OpaqueMetadata {
}
}
/// Simple blob to hold a `PeerId` without committing to its format.
#[derive(Default, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, RuntimeDebug, PassByInner)]
#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]
pub struct OpaquePeerId(pub Vec<u8>);
impl OpaquePeerId {
/// Create new `OpaquePeerId`
pub fn new(vec: Vec<u8>) -> Self {
OpaquePeerId(vec)
}
}
/// Something that is either a native or an encoded value.
#[cfg(feature = "std")]
pub enum NativeOrEncoded<R> {
@@ -257,7 +270,7 @@ pub trait TypeId {
/// A log level matching the one from `log` crate.
///
/// Used internally by `sp_io::log` method.
#[derive(Encode, Decode, sp_runtime_interface::pass_by::PassByEnum, Copy, Clone)]
#[derive(Encode, Decode, PassByEnum, Copy, Clone)]
pub enum LogLevel {
/// `Error` log level.
Error = 1,
+25 -13
View File
@@ -19,7 +19,7 @@
use codec::{Encode, Decode};
use sp_std::{prelude::{Vec, Box}, convert::TryFrom};
use crate::RuntimeDebug;
use crate::{OpaquePeerId, RuntimeDebug};
use sp_runtime_interface::pass_by::{PassByCodec, PassByInner, PassByEnum};
pub use crate::crypto::KeyTypeId;
@@ -184,23 +184,12 @@ impl TryFrom<u32> for HttpRequestStatus {
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByCodec)]
#[cfg_attr(feature = "std", derive(Default))]
pub struct OpaqueNetworkState {
/// PeerId of the local node.
/// PeerId of the local node in SCALE encoded.
pub peer_id: OpaquePeerId,
/// List of addresses the node knows it can be reached as.
pub external_addresses: Vec<OpaqueMultiaddr>,
}
/// Simple blob to hold a `PeerId` without committing to its format.
#[derive(Default, Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner)]
pub struct OpaquePeerId(pub Vec<u8>);
impl OpaquePeerId {
/// Create new `OpaquePeerId`
pub fn new(vec: Vec<u8>) -> Self {
OpaquePeerId(vec)
}
}
/// Simple blob to hold a `Multiaddr` without committing to its format.
#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, PassByInner)]
pub struct OpaqueMultiaddr(pub Vec<u8>);
@@ -277,6 +266,8 @@ pub enum Capability {
OffchainWorkerDbRead = 32,
/// Access to offchain worker DB (writes).
OffchainWorkerDbWrite = 64,
/// Manage the authorized nodes
NodeAuthorization = 128,
}
/// A set of capabilities
@@ -495,6 +486,18 @@ pub trait Externalities: Send {
buffer: &mut [u8],
deadline: Option<Timestamp>
) -> Result<usize, HttpError>;
/// Set the authorized nodes from runtime.
///
/// In a permissioned network, the connections between nodes need to reach a
/// consensus between participants.
///
/// - `nodes`: a set of nodes which are allowed to connect for the local node.
/// each one is identified with an `OpaquePeerId`, here it just use plain bytes
/// without any encoding. Invalid `OpaquePeerId`s are silently ignored.
/// - `authorized_only`: if true, only the authorized nodes are allowed to connect,
/// otherwise unauthorized nodes can also be connected through other mechanism.
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool);
}
impl<T: Externalities + ?Sized> Externalities for Box<T> {
@@ -573,6 +576,10 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
) -> Result<usize, HttpError> {
(&mut **self).http_response_read_body(request_id, buffer, deadline)
}
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
(&mut **self).set_authorized_nodes(nodes, authorized_only)
}
}
/// An `OffchainExternalities` implementation with limited capabilities.
@@ -691,6 +698,11 @@ impl<T: Externalities> Externalities for LimitedExternalities<T> {
self.check(Capability::Http, "http_response_read_body");
self.externalities.http_response_read_body(request_id, buffer, deadline)
}
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
self.check(Capability::NodeAuthorization, "set_authorized_nodes");
self.externalities.set_authorized_nodes(nodes, authorized_only)
}
}
#[cfg(feature = "std")]
@@ -24,6 +24,7 @@ use std::{
collections::{BTreeMap, VecDeque},
sync::Arc,
};
use crate::OpaquePeerId;
use crate::offchain::{
self,
storage::{InMemOffchainStorage, OffchainOverlayedChange, OffchainOverlayedChanges},
@@ -375,6 +376,10 @@ impl offchain::Externalities for TestOffchainExt {
Err(HttpError::IoError)
}
}
fn set_authorized_nodes(&mut self, _nodes: Vec<OpaquePeerId>, _authorized_only: bool) {
unimplemented!()
}
}
/// The internal state of the fake transaction pool.
+8 -1
View File
@@ -42,7 +42,7 @@ use sp_core::{
};
use sp_core::{
crypto::KeyTypeId, ed25519, sr25519, ecdsa, H256, LogLevel,
OpaquePeerId, crypto::KeyTypeId, ed25519, sr25519, ecdsa, H256, LogLevel,
offchain::{
Timestamp, HttpRequestId, HttpRequestStatus, HttpError, StorageKind, OpaqueNetworkState,
},
@@ -960,6 +960,13 @@ pub trait Offchain {
.http_response_read_body(request_id, buffer, deadline)
.map(|r| r as u32)
}
/// Set the authorized nodes and authorized_only flag.
fn set_authorized_nodes(&mut self, nodes: Vec<OpaquePeerId>, authorized_only: bool) {
self.extension::<OffchainExt>()
.expect("set_authorized_nodes can be called only in the offchain worker context")
.set_authorized_nodes(nodes, authorized_only)
}
}
/// Wasm only interface that provides functions for calling into the allocator.