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
+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);
}
}