Introduce srml/im-online (#3079)

* Fix grammar and typo

* Extend network service

* Extend offchain API

* Support creating unsigned UncheckedExtrinsic

* Introduce srml/im-online

* Bump impl and spec version

* Fix web-wasm test

* Apply suggestions from code review

Remove parity-multiaddr dependency

Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>

* Replace transmute with from_raw_parts

* Replace PeerId.to_string() with .to_base58()

Co-Authored-By: Pierre Krieger <pierre.krieger1708@gmail.com>

* Update Cargo.lock

* Bump impl and spec version (again)

It was updated in master in the meantime.

* Apply suggestions from code review

Co-Authored-By: Sergei Pepyakin <sergei@parity.io>

* Address comments

* Add public function is_online_in_current_session()

* Bump spec_version

* Fix doc tests

* Improve comments

* Remove superfluous line

* Name parameters consistently

* Implement comments

* Switch From to TryFrom

* Use Vec instead of HashSet

* Fix tests

* Revert me: local testing

* Fix check if already sent during session

We gossip each session, hence we need to check
if already sent in this session (not era).

* Fix typos

* Consistent terminology

* Revert "Revert me: local testing"

This reverts commit 73fbc29ff3e5ed71d99436318260b4f007e837f4.

* Introduce IsMember trait

* Implement misc comments

* Remove unused function

* Fix test

* Fix external_addresses being written

* Fix test

* Add necessary trait bound

* Do not increment version

* Update lib.rs
This commit is contained in:
Michael Müller
2019-07-20 03:19:44 +02:00
committed by Gavin Wood
parent a757dfb222
commit c70b81444a
34 changed files with 981 additions and 22 deletions
@@ -26,6 +26,7 @@ use wasmi::{
};
use state_machine::{Externalities, ChildStorageKey};
use crate::error::{Error, Result};
use parity_codec::Encode;
use primitives::{blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair};
use primitives::offchain;
use primitives::hexdisplay::HexDisplay;
@@ -767,6 +768,42 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(offset)
},
ext_network_state(written_out: *mut u32) -> *mut u8 => {
let res = this.ext.offchain()
.map(|api| api.network_state())
.ok_or_else(|| "Calling unavailable API ext_network_state: wasm")?;
let encoded = res.encode();
let len = encoded.len() as u32;
let offset = this.heap.allocate(len)? as u32;
this.memory.set(offset, &encoded)
.map_err(|_| "Invalid attempt to set memory in ext_network_state")?;
this.memory.write_primitive(written_out, len)
.map_err(|_| "Invalid attempt to write written_out in ext_network_state")?;
Ok(offset)
},
ext_authority_pubkey(
kind: u32,
written_out: *mut u32
) -> *mut u8 => {
let kind = offchain::CryptoKind::try_from(kind)
.map_err(|_| "crypto kind OOB while ext_authority_pubkey: wasm")?;
let res = this.ext.offchain()
.map(|api| api.authority_pubkey(kind))
.ok_or_else(|| "Calling unavailable API ext_authority_pubkey: wasm")?;
let encoded = res.encode();
let len = encoded.len() as u32;
let offset = this.heap.allocate(len)? as u32;
this.memory.set(offset, &encoded)
.map_err(|_| "Invalid attempt to set memory in ext_authority_pubkey")?;
this.memory.write_primitive(written_out, len)
.map_err(|_| "Invalid attempt to write written_out in ext_authority_pubkey")?;
Ok(offset)
},
ext_decrypt(
key: u32,
kind: u32,
+1
View File
@@ -189,6 +189,7 @@ pub mod test;
pub use chain::{Client as ClientHandle, FinalityProofProvider};
pub use service::{
NetworkService, NetworkWorker, TransactionPool, ExHashT, ReportHandle,
NetworkStateInfo,
};
pub use protocol::{PeerInfo, Context, consensus_gossip, message, specialization};
pub use protocol::sync::SyncState;
+45 -1
View File
@@ -34,6 +34,7 @@ use futures::{prelude::*, sync::mpsc};
use log::{warn, error, info};
use libp2p::core::{swarm::NetworkBehaviour, transport::boxed::Boxed, muxing::StreamMuxerBox};
use libp2p::{PeerId, Multiaddr, multihash::Multihash};
use parking_lot::Mutex;
use peerset::PeersetHandle;
use runtime_primitives::{traits::{Block as BlockT, NumberFor}, ConsensusEngineId};
@@ -86,6 +87,8 @@ impl ReportHandle {
pub struct NetworkService<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> {
/// Number of peers we're connected to.
num_connected: Arc<AtomicUsize>,
/// The local external addresses.
external_addresses: Arc<Mutex<Vec<Multiaddr>>>,
/// Are we actively catching up with the chain?
is_major_syncing: Arc<AtomicBool>,
/// Local copy of the `PeerId` of the local node.
@@ -215,8 +218,11 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> NetworkWorker
Swarm::<B, S, H>::add_external_address(&mut swarm, addr.clone());
}
let external_addresses = Arc::new(Mutex::new(Vec::new()));
let service = Arc::new(NetworkService {
bandwidth,
external_addresses: external_addresses.clone(),
num_connected: num_connected.clone(),
is_major_syncing: is_major_syncing.clone(),
peerset: peerset_handle,
@@ -226,6 +232,7 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> NetworkWorker
});
Ok(NetworkWorker {
external_addresses,
num_connected,
is_major_syncing,
network_service: swarm,
@@ -295,7 +302,7 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> NetworkWorker
/// Get network state.
///
/// **Note**: Use this only for debugging. This API is unstable. There are warnings literaly
/// everywhere about this. Please don't use this function to retreive actual information.
/// everywhere about this. Please don't use this function to retrieve actual information.
pub fn network_state(&mut self) -> NetworkState {
let swarm = &mut self.network_service;
let open = swarm.user_protocol().open_peers().cloned().collect::<Vec<_>>();
@@ -487,6 +494,11 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> NetworkServic
pub fn num_connected(&self) -> usize {
self.num_connected.load(Ordering::Relaxed)
}
/// Returns the local external addresses.
pub fn external_addresses(&self) -> Vec<Multiaddr> {
self.external_addresses.lock().clone()
}
}
impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT>
@@ -500,6 +512,32 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT>
}
}
/// Trait for providing information about the local network state
pub trait NetworkStateInfo {
/// Returns the local external addresses.
fn external_addresses(&self) -> Vec<Multiaddr>;
/// Returns the local Peer ID.
fn peer_id(&self) -> PeerId;
}
impl<B, S, H> NetworkStateInfo for NetworkService<B, S, H>
where
B: runtime_primitives::traits::Block,
S: NetworkSpecialization<B>,
H: ExHashT,
{
/// Returns the local external addresses.
fn external_addresses(&self) -> Vec<Multiaddr> {
self.external_addresses.lock().clone()
}
/// Returns the local Peer ID.
fn peer_id(&self) -> PeerId {
self.local_peer_id.clone()
}
}
/// Messages sent from the `NetworkService` to the `NetworkWorker`.
///
/// Each entry corresponds to a method of `NetworkService`.
@@ -520,6 +558,8 @@ enum ServerToWorkerMsg<B: BlockT, S: NetworkSpecialization<B>> {
/// You are encouraged to poll this in a separate background thread or task.
#[must_use = "The NetworkWorker must be polled in order for the network to work"]
pub struct NetworkWorker<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> {
/// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
external_addresses: Arc<Mutex<Vec<Multiaddr>>>,
/// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
num_connected: Arc<AtomicUsize>,
/// Updated by the `NetworkWorker` and loaded by the `NetworkService`.
@@ -621,6 +661,10 @@ impl<B: BlockT + 'static, S: NetworkSpecialization<B>, H: ExHashT> Future for Ne
// Update the variables shared with the `NetworkService`.
self.num_connected.store(self.network_service.user_protocol_mut().num_connected_peers(), Ordering::Relaxed);
{
let external_addresses = Swarm::<B, S, H>::external_addresses(&self.network_service).cloned().collect();
*self.external_addresses.lock() = external_addresses;
}
self.is_major_syncing.store(match self.network_service.user_protocol_mut().sync_state() {
SyncState::Idle => false,
SyncState::Downloading => true,
+1
View File
@@ -16,6 +16,7 @@ parking_lot = "0.8.0"
primitives = { package = "substrate-primitives", path = "../../core/primitives" }
runtime_primitives = { package = "sr-primitives", path = "../../core/sr-primitives" }
transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" }
network = { package = "substrate-network", path = "../../core/network" }
[dev-dependencies]
env_logger = "0.6"
+127 -2
View File
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use std::{str::FromStr, sync::Arc, convert::TryFrom};
use client::backend::OffchainStorage;
use crate::AuthorityKeyProvider;
use futures::{Stream, Future, sync::mpsc};
@@ -25,6 +25,7 @@ use primitives::offchain::{
Externalities as OffchainExt,
CryptoKind, CryptoKeyId,
StorageKind,
OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr,
};
use primitives::crypto::{Pair, Protected};
use primitives::{ed25519, sr25519};
@@ -33,6 +34,8 @@ use runtime_primitives::{
traits::{self, Extrinsic},
};
use transaction_pool::txpool::{Pool, ChainApi};
use network::NetworkStateInfo;
use network::{PeerId, Multiaddr};
/// A message between the offchain extension and the processing thread.
enum ExtMessage {
@@ -59,6 +62,7 @@ pub(crate) struct Api<Storage, KeyProvider> {
db: Storage,
keys_password: Protected<String>,
key_provider: KeyProvider,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
}
fn unavailable_yet<R: Default>(name: &str) -> R {
@@ -158,6 +162,26 @@ impl<Storage, KeyProvider> OffchainExt for Api<Storage, KeyProvider> where
Ok(CryptoKeyId(id))
}
fn authority_pubkey(&self, kind: CryptoKind) -> Result<Vec<u8>, ()> {
let key = self.read_key(None, kind)?;
let public = match key {
Key::Sr25519(pair) => pair.public().encode(),
Key::Ed25519(pair) => pair.public().encode(),
};
Ok(public)
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
let external_addresses = self.network_state.external_addresses();
let state = NetworkState::new(
self.network_state.peer_id(),
external_addresses,
);
Ok(OpaqueNetworkState::from(state))
}
fn encrypt(&mut self, _key: Option<CryptoKeyId>, _kind: CryptoKind, _data: &[u8]) -> Result<Vec<u8>, ()> {
unavailable_yet::<()>("encrypt");
Err(())
@@ -285,6 +309,71 @@ impl<Storage, KeyProvider> OffchainExt for Api<Storage, KeyProvider> where
}
}
/// Information about the local node's network state.
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct NetworkState {
peer_id: PeerId,
external_addresses: Vec<Multiaddr>,
}
impl NetworkState {
fn new(peer_id: PeerId, external_addresses: Vec<Multiaddr>) -> Self {
NetworkState {
peer_id,
external_addresses,
}
}
}
impl From<NetworkState> for OpaqueNetworkState {
fn from(state: NetworkState) -> OpaqueNetworkState {
let enc = Encode::encode(&state.peer_id.into_bytes());
let peer_id = OpaquePeerId::new(enc);
let external_addresses: Vec<OpaqueMultiaddr> = state
.external_addresses
.iter()
.map(|multiaddr| {
let e = Encode::encode(&multiaddr.to_string());
OpaqueMultiaddr::new(e)
})
.collect();
OpaqueNetworkState {
peer_id,
external_addresses,
}
}
}
impl TryFrom<OpaqueNetworkState> for NetworkState {
type Error = ();
fn try_from(state: OpaqueNetworkState) -> Result<Self, Self::Error> {
let inner_vec = state.peer_id.0;
let bytes: Vec<u8> = Decode::decode(&mut &inner_vec[..]).ok_or(())?;
let peer_id = PeerId::from_bytes(bytes).map_err(|_| ())?;
let external_addresses: Result<Vec<Multiaddr>, Self::Error> = state.external_addresses
.iter()
.map(|enc_multiaddr| -> Result<Multiaddr, Self::Error> {
let inner_vec = &enc_multiaddr.0;
let bytes = <Vec<u8>>::decode(&mut &inner_vec[..]).ok_or(())?;
let multiaddr_str = String::from_utf8(bytes).map_err(|_| ())?;
let multiaddr = Multiaddr::from_str(&multiaddr_str).map_err(|_| ())?;
Ok(multiaddr)
})
.collect();
let external_addresses = external_addresses?;
Ok(NetworkState {
peer_id,
external_addresses,
})
}
}
/// Offchain extensions implementation API
///
/// This is the asynchronous processing part of the API.
@@ -302,6 +391,7 @@ impl<A: ChainApi> AsyncApi<A> {
keys_password: Protected<String>,
key_provider: P,
at: BlockId<A::Block>,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
) -> (Api<S, P>, AsyncApi<A>) {
let (sender, rx) = mpsc::unbounded();
@@ -310,6 +400,7 @@ impl<A: ChainApi> AsyncApi<A> {
db,
keys_password,
key_provider,
network_state,
};
let async_api = AsyncApi {
@@ -355,8 +446,22 @@ impl<A: ChainApi> AsyncApi<A> {
#[cfg(test)]
mod tests {
use super::*;
use std::{collections::HashSet, convert::TryFrom};
use client_db::offchain::LocalStorage;
use crate::tests::TestProvider;
use network::PeerId;
struct MockNetworkStateInfo();
impl NetworkStateInfo for MockNetworkStateInfo {
fn external_addresses(&self) -> Vec<Multiaddr> {
Vec::new()
}
fn peer_id(&self) -> PeerId {
PeerId::random()
}
}
fn offchain_api() -> (Api<LocalStorage, TestProvider>, AsyncApi<impl ChainApi>) {
let _ = env_logger::try_init();
@@ -366,7 +471,8 @@ mod tests {
Pool::new(Default::default(), transaction_pool::ChainApi::new(client.clone()))
);
AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(0))
let mock = Arc::new(MockNetworkStateInfo());
AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(0), mock)
}
#[test]
@@ -455,4 +561,23 @@ mod tests {
"Invalid kind should trigger a missing key error."
);
}
#[test]
fn should_convert_network_states() {
// given
let state = NetworkState::new(
PeerId::random(),
vec![
Multiaddr::try_from("/ip4/127.0.0.1/tcp/1234".to_string()).unwrap(),
Multiaddr::try_from("/ip6/2601:9:4f81:9700:803e:ca65:66e8:c21").unwrap(),
],
);
// when
let opaque_state = OpaqueNetworkState::from(state.clone());
let converted_back_state = NetworkState::try_from(opaque_state).unwrap();
// then
assert_eq!(state, converted_back_state);
}
}
+19 -1
View File
@@ -51,6 +51,7 @@ use runtime_primitives::{
};
use futures::future::Future;
use transaction_pool::txpool::{Pool, ChainApi};
use network::NetworkStateInfo;
mod api;
@@ -130,6 +131,7 @@ impl<Client, Storage, KeyProvider, Block> OffchainWorkers<
&self,
number: &<Block::Header as traits::Header>::Number,
pool: &Arc<Pool<A>>,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
) -> impl Future<Item = (), Error = ()> where
A: ChainApi<Block=Block> + 'static,
{
@@ -145,6 +147,7 @@ impl<Client, Storage, KeyProvider, Block> OffchainWorkers<
self.keys_password.clone(),
self.authority_key.clone(),
at.clone(),
network_state.clone(),
);
debug!("Running offchain workers at {:?}", at);
let api = Box::new(api);
@@ -161,6 +164,20 @@ mod tests {
use super::*;
use futures::Future;
use primitives::{ed25519, sr25519, crypto::{TypedKey, Pair}};
use std::collections::HashSet;
use network::{Multiaddr, PeerId};
struct MockNetworkStateInfo();
impl NetworkStateInfo for MockNetworkStateInfo {
fn external_addresses(&self) -> Vec<Multiaddr> {
Vec::new()
}
fn peer_id(&self) -> PeerId {
PeerId::random()
}
}
#[derive(Clone, Default)]
pub(crate) struct TestProvider {
@@ -186,10 +203,11 @@ mod tests {
let client = Arc::new(test_client::new());
let pool = Arc::new(Pool::new(Default::default(), ::transaction_pool::ChainApi::new(client.clone())));
let db = client_db::offchain::LocalStorage::new_test();
let mock = Arc::new(MockNetworkStateInfo());
// when
let offchain = OffchainWorkers::new(client, db, TestProvider::default(), "".to_owned().into());
runtime.executor().spawn(offchain.on_block_imported(&0u64, &pool));
runtime.executor().spawn(offchain.on_block_imported(&0u64, &pool, mock.clone()));
// then
runtime.shutdown_on_idle().wait().unwrap();
+9
View File
@@ -31,6 +31,7 @@ use primitives::offchain::{
CryptoKind,
CryptoKeyId,
StorageKind,
OpaqueNetworkState,
};
/// Pending request.
@@ -139,6 +140,14 @@ impl offchain::Externalities for TestOffchainExt {
unimplemented!("not needed in tests so far")
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
unimplemented!("not needed in tests so far")
}
fn authority_pubkey(&self, _kind: CryptoKind) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
}
fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result<CryptoKeyId, ()> {
unimplemented!("not needed in tests so far")
}
+50 -1
View File
@@ -185,6 +185,41 @@ impl TryFrom<u32> for HttpRequestStatus {
}
}
/// A blob to hold information about the local node's network state
/// without committing to its format.
#[derive(Clone, Eq, PartialEq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct OpaqueNetworkState {
/// PeerId of the local node.
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(Clone, Eq, PartialEq, Encode, Decode)]
#[cfg_attr(feature = "std", derive(Debug))]
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)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct OpaqueMultiaddr(pub Vec<u8>);
impl OpaqueMultiaddr {
/// Create new `OpaqueMultiaddr`
pub fn new(vec: Vec<u8>) -> Self {
OpaqueMultiaddr(vec)
}
}
/// Opaque timestamp type
#[derive(Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Default)]
#[cfg_attr(feature = "std", derive(Debug))]
@@ -241,6 +276,12 @@ pub trait Externalities {
/// The transaction will end up in the pool and be propagated to others.
fn submit_transaction(&mut self, extrinsic: Vec<u8>) -> Result<(), ()>;
/// Returns information about the local node's network state.
fn network_state(&self) -> Result<OpaqueNetworkState, ()>;
/// Returns the locally configured authority public key, if available.
fn authority_pubkey(&self, crypto: CryptoKind) -> Result<Vec<u8>, ()>;
/// Create new key(pair) for signing/encryption/decryption.
///
/// Returns an error if given crypto kind is not supported.
@@ -319,7 +360,7 @@ pub trait Externalities {
/// 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>>;
/// Initiaties a http request given HTTP verb and the URL.
/// Initiates a http request given HTTP verb and the URL.
///
/// Meta is a future-reserved field containing additional, parity-codec encoded parameters.
/// Returns the id of newly started request.
@@ -398,6 +439,14 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
(&mut **self).encrypt(key, kind, data)
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
(& **self).network_state()
}
fn authority_pubkey(&self, key:CryptoKind) -> Result<Vec<u8>, ()> {
(&**self).authority_pubkey(key)
}
fn decrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).decrypt(key, kind, data)
}
+1
View File
@@ -33,6 +33,7 @@ transaction_pool = { package = "substrate-transaction-pool", path = "../../core/
rpc = { package = "substrate-rpc-servers", path = "../../core/rpc-servers" }
tel = { package = "substrate-telemetry", path = "../../core/telemetry" }
offchain = { package = "substrate-offchain", path = "../../core/offchain" }
parity-multiaddr = { package = "parity-multiaddr", version = "0.5.0" }
[dev-dependencies]
substrate-test-runtime-client = { path = "../test-runtime/client" }
+4 -2
View File
@@ -23,7 +23,7 @@ use client_db;
use client::{self, Client, runtime_api};
use crate::{error, Service, AuthorityKeyProvider};
use consensus_common::{import_queue::ImportQueue, SelectChain};
use network::{self, OnDemand, FinalityProofProvider, config::BoxFinalityProofRequestBuilder};
use network::{self, OnDemand, FinalityProofProvider, NetworkStateInfo, config::BoxFinalityProofRequestBuilder};
use substrate_executor::{NativeExecutor, NativeExecutionDispatch};
use transaction_pool::txpool::{self, Options as TransactionPoolOptions, Pool as TransactionPool};
use runtime_primitives::{
@@ -235,6 +235,7 @@ pub trait OffchainWorker<C: Components> {
ComponentBlock<C>
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
network_state: &Arc<dyn NetworkStateInfo + Send + Sync>,
) -> error::Result<Box<dyn Future<Item = (), Error = ()> + Send>>;
}
@@ -251,8 +252,9 @@ impl<C: Components> OffchainWorker<Self> for C where
ComponentBlock<C>
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
network_state: &Arc<dyn NetworkStateInfo + Send + Sync>,
) -> error::Result<Box<dyn Future<Item = (), Error = ()> + Send>> {
Ok(Box::new(offchain.on_block_imported(number, pool)))
Ok(Box::new(offchain.on_block_imported(number, pool, network_state.clone())))
}
}
+3 -1
View File
@@ -37,7 +37,7 @@ use exit_future::Signal;
use futures::prelude::*;
use futures03::stream::{StreamExt as _, TryStreamExt as _};
use keystore::Store as Keystore;
use network::NetworkState;
use network::{NetworkState, NetworkStateInfo};
use log::{info, warn, debug, error};
use parity_codec::{Encode, Decode};
use primitives::{Pair, ed25519, crypto};
@@ -293,6 +293,7 @@ impl<Components: components::Components> Service<Components> {
let wclient = Arc::downgrade(&client);
let offchain = offchain_workers.as_ref().map(Arc::downgrade);
let to_spawn_tx_ = to_spawn_tx.clone();
let network_state_info: Arc<dyn NetworkStateInfo + Send + Sync> = network.clone();
let events = client.import_notification_stream()
.map(|v| Ok::<_, ()>(v)).compat()
@@ -312,6 +313,7 @@ impl<Components: components::Components> Service<Components> {
&number,
&offchain,
&txpool,
&network_state_info,
).map_err(|e| warn!("Offchain workers error processing new block: {:?}", e))?;
let _ = to_spawn_tx_.unbounded_send(future);
}
+9 -1
View File
@@ -38,6 +38,7 @@ use primitives::offchain::{
HttpRequestId, HttpRequestStatus, HttpError,
CryptoKind, CryptoKeyId,
StorageKind,
OpaqueNetworkState,
};
/// Error verifying ECDSA signature
@@ -239,6 +240,13 @@ export_api! {
/// The transaction will end up in the pool.
fn submit_transaction<T: codec::Encode>(data: &T) -> Result<(), ()>;
/// Returns information about the local node's network state.
fn network_state() -> Result<OpaqueNetworkState, ()>;
/// Returns the currently configured authority public key, if available.
// TODO [#3139] change into crypto_pubkey(&self, key: Option<CryptoKeyId>, kind: CryptoKind)
fn authority_pubkey(crypto: CryptoKind) -> Result<Vec<u8>, ()>;
/// Create new key(pair) for signing/encryption/decryption.
///
/// Returns an error if given crypto kind is not supported.
@@ -313,7 +321,7 @@ export_api! {
/// offchain worker tasks running on the same machine. It IS persisted between runs.
fn local_storage_get(kind: StorageKind, key: &[u8]) -> Option<Vec<u8>>;
/// Initiaties a http request given HTTP verb and the URL.
/// Initiates a http request given HTTP verb and the URL.
///
/// Meta is a future-reserved field containing additional, parity-codec encoded parameters.
/// Returns the id of newly started request.
+12
View File
@@ -269,6 +269,18 @@ impl OffchainApi for () {
}, "submit_transaction can be called only in the offchain worker context")
}
fn network_state() -> Result<OpaqueNetworkState, ()> {
with_offchain(|ext| {
ext.network_state()
}, "network_state can be called only in the offchain worker context")
}
fn authority_pubkey(crypto: offchain::CryptoKind) -> Result<Vec<u8>, ()> {
with_offchain(|ext| {
ext.authority_pubkey(crypto)
}, "authority_pubkey can be called only in the offchain worker context")
}
fn new_crypto_key(crypto: offchain::CryptoKind) -> Result<offchain::CryptoKeyId, ()> {
with_offchain(|ext| {
ext.new_crypto_key(crypto)
+61 -1
View File
@@ -385,6 +385,33 @@ pub mod ext {
/// - nonzero otherwise.
fn ext_submit_transaction(data: *const u8, len: u32) -> u32;
/// Returns information about the local node's network state.
///
/// # Returns
///
/// The encoded `Result<offchain::OpaqueNetworkState, ()>`.
/// `written_out` contains the length of the message.
///
/// The ownership of the returned buffer is transferred to the runtime
/// code and the runtime is responsible for freeing it. This is always
/// a properly allocated pointer (which cannot be NULL), hence the
/// runtime code can always rely on it.
fn ext_network_state(written_out: *mut u32) -> *mut u8;
/// Returns the locally configured authority public key, if available.
/// The `crypto` argument is `offchain::CryptoKind` converted to `u32`.
///
/// # Returns
///
/// The encoded `Result<PublicKey encoded to Vec<u8>, ()>`.
/// `written_out` contains the length of the message.
///
/// The ownership of the returned buffer is transferred to the runtime
/// code and the runtime is responsible for freeing it. This is always
/// a properly allocated pointer (which cannot be NULL), hence the
/// runtime code can always rely on it.
fn ext_authority_pubkey(crypto: u32, written_out: *mut u32) -> *mut u8;
/// Create new key(pair) for signing/encryption/decryption.
///
/// # Returns
@@ -504,7 +531,7 @@ pub mod ext {
/// - Otherwise, pointer to the value in memory. `value_len` contains the length of the value.
fn ext_local_storage_get(kind: u32, key: *const u8, key_len: u32, value_len: *mut u32) -> *mut u8;
/// Initiaties a http request.
/// Initiates a http request.
///
/// `meta` is parity-codec encoded additional parameters to the request (like redirection policy,
/// timeouts, certificates policy, etc). The format is not yet specified and the field is currently
@@ -888,6 +915,39 @@ impl OffchainApi for () {
}
}
fn network_state() -> Result<offchain::OpaqueNetworkState, ()> {
let mut len = 0_u32;
let raw_result = unsafe {
let ptr = ext_network_state.get()(&mut len);
from_raw_parts(ptr, len)
};
match raw_result {
Some(raw_result) => codec::Decode::decode(&mut &*raw_result).unwrap_or(Err(())),
None => Err(())
}
}
fn authority_pubkey(kind: offchain::CryptoKind) -> Result<Vec<u8>, ()> {
let kind = kind as isize as u32;
let mut len = 0u32;
let raw_result = unsafe {
let ptr = ext_authority_pubkey.get()(
kind,
&mut len,
);
from_raw_parts(ptr, len)
};
match raw_result {
Some(raw_result) => codec::Decode::decode(&mut &*raw_result).unwrap_or(Err(())),
None => Err(())
}
}
fn new_crypto_key(crypto: offchain::CryptoKind) -> Result<offchain::CryptoKeyId, ()> {
let crypto = crypto.into();
let ret = unsafe {
@@ -114,9 +114,15 @@ impl<
Signature: Codec,
Call,
> Extrinsic for UncheckedExtrinsic<Address, Index, Call, Signature> {
type Call = Call;
fn is_signed(&self) -> Option<bool> {
Some(self.signature.is_some())
}
fn new_unsigned(call: Self::Call) -> Option<Self> {
Some(UncheckedExtrinsic::new_unsigned(call))
}
}
impl<Address: Codec, Index: HasCompact + Codec, Signature: Codec, Call: Decode> Decode
@@ -59,9 +59,15 @@ impl<Address, Index, Call, Signature> UncheckedMortalCompactExtrinsic<Address, I
}
impl<Address: Encode, Index: Encode, Call: Encode, Signature: Encode> Extrinsic for UncheckedMortalCompactExtrinsic<Address, Index, Call, Signature> {
type Call = Call;
fn is_signed(&self) -> Option<bool> {
Some(self.signature.is_some())
}
fn new_unsigned(call: Self::Call) -> Option<Self> {
Some(UncheckedMortalCompactExtrinsic::new_unsigned(call))
}
}
impl<Address, AccountId, Index, Call, Signature, Context, Hash, BlockNumber> Checkable<Context>
@@ -61,9 +61,15 @@ impl<Address, Index, Call, Signature> UncheckedMortalExtrinsic<Address, Index, C
}
impl<Address: Encode, Index: Encode, Call: Encode, Signature: Encode> Extrinsic for UncheckedMortalExtrinsic<Address, Index, Call, Signature> {
type Call = Call;
fn is_signed(&self) -> Option<bool> {
Some(self.signature.is_some())
}
fn new_unsigned(call: Self::Call) -> Option<Self> {
Some(UncheckedMortalExtrinsic::new_unsigned(call))
}
}
impl<Address, AccountId, Index, Call, Signature, Context, Hash, BlockNumber> Checkable<Context>
+4
View File
@@ -869,9 +869,13 @@ impl ::serde::Serialize for OpaqueExtrinsic {
}
impl traits::Extrinsic for OpaqueExtrinsic {
type Call = ();
fn is_signed(&self) -> Option<bool> {
None
}
fn new_unsigned(_call: Self::Call) -> Option<Self> { None }
}
#[cfg(test)]
@@ -44,6 +44,14 @@ impl TypedKey for UintAuthorityId {
const KEY_TYPE: KeyTypeId = UINT_DUMMY_KEY;
}
impl AsRef<[u8]> for UintAuthorityId {
fn as_ref(&self) -> &[u8] {
let ptr = self.0 as *const _;
// It's safe to do this here since `UintAuthorityId` is `u64`.
unsafe { std::slice::from_raw_parts(ptr, 8) }
}
}
impl OpaqueKeys for UintAuthorityId {
type KeyTypeIds = std::iter::Cloned<std::slice::Iter<'static, KeyTypeId>>;
@@ -133,6 +141,8 @@ impl<'a> Deserialize<'a> for Header {
pub struct ExtrinsicWrapper<Xt>(Xt);
impl<Xt> traits::Extrinsic for ExtrinsicWrapper<Xt> {
type Call = ();
fn is_signed(&self) -> Option<bool> {
None
}
@@ -219,6 +229,8 @@ impl<Call: Codec + Sync + Send, Context> Checkable<Context> for TestXt<Call> {
fn check(self, _: &Context) -> Result<Self::Checked, &'static str> { Ok(self) }
}
impl<Call: Codec + Sync + Send> traits::Extrinsic for TestXt<Call> {
type Call = Call;
fn is_signed(&self) -> Option<bool> {
Some(self.0.is_some())
}
+13 -1
View File
@@ -622,6 +622,12 @@ pub trait RandomnessBeacon {
pub trait Member: Send + Sync + Sized + MaybeDebug + Eq + PartialEq + Clone + 'static {}
impl<T: Send + Sync + Sized + MaybeDebug + Eq + PartialEq + Clone + 'static> Member for T {}
/// Determine if a `MemberId` is a valid member.
pub trait IsMember<MemberId> {
/// Is the given `MemberId` a valid member?
fn is_member(member_id: &MemberId) -> bool;
}
/// Something which fulfills the abstract idea of a Substrate header. It has types for a `Number`,
/// a `Hash` and a `Digest`. It provides access to an `extrinsics_root`, `state_root` and
/// `parent_hash`, as well as a `digest` and a block `number`.
@@ -702,10 +708,16 @@ pub trait Block: Clone + Send + Sync + Codec + Eq + MaybeSerializeDebugButNotDes
}
/// Something that acts like an `Extrinsic`.
pub trait Extrinsic {
pub trait Extrinsic: Sized {
/// The function call.
type Call;
/// Is this `Extrinsic` signed?
/// If no information are available about signed/unsigned, `None` should be returned.
fn is_signed(&self) -> Option<bool> { None }
/// New instance of an unsigned extrinsic aka "inherent".
fn new_unsigned(_call: Self::Call) -> Option<Self> { None }
}
/// Extract the hashing type for a block.
+1 -1
View File
@@ -81,7 +81,7 @@ where
changes_trie_transaction: Option<(MemoryDB<H>, H::Out)>,
/// Additional externalities for offchain workers.
///
/// If None, some methods from the trait might not supported.
/// If None, some methods from the trait might not be supported.
offchain_externalities: Option<&'a mut O>,
/// Dummy usage of N arg.
_phantom: ::std::marker::PhantomData<N>,
+14 -1
View File
@@ -24,7 +24,7 @@ use log::warn;
use hash_db::Hasher;
use parity_codec::{Decode, Encode};
use primitives::{
storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain
storage::well_known_keys, NativeOrEncoded, NeverNativeValue, offchain,
};
pub mod backend;
@@ -240,6 +240,19 @@ impl offchain::Externalities for NeverOffchainExt {
unreachable!()
}
fn network_state(
&self,
) -> Result<offchain::OpaqueNetworkState, ()> {
unreachable!()
}
fn authority_pubkey(
&self,
_crypto: offchain::CryptoKind,
) -> Result<Vec<u8>, ()> {
unreachable!()
}
fn new_crypto_key(
&mut self,
_crypto: offchain::CryptoKind,
+6
View File
@@ -140,6 +140,8 @@ impl BlindCheckable for Extrinsic {
}
impl ExtrinsicT for Extrinsic {
type Call = ();
fn is_signed(&self) -> Option<bool> {
if let Extrinsic::IncludeData(_) = *self {
Some(false)
@@ -147,6 +149,10 @@ impl ExtrinsicT for Extrinsic {
Some(true)
}
}
fn new_unsigned(_call: Self::Call) -> Option<Self> {
None
}
}
impl Extrinsic {