Refactors the offchain worker api (#3150)

* Update offchain primitives.

* Update offchain worker.

* Update im-online.

* Update service.

* Update node and node-template.

* Update runtime version.

* Fix build.

* Fix offchain worker tests.

* Generalize authority_pubkey.

* Add test.

* Update lib.rs
This commit is contained in:
David Craven
2019-07-22 11:20:57 +02:00
committed by Gavin Wood
parent 2edeef5825
commit a3d19baea3
17 changed files with 509 additions and 327 deletions
+2 -2
View File
@@ -28,7 +28,7 @@ use parking_lot::Mutex;
/// Offchain local storage
#[derive(Clone)]
pub struct LocalStorage {
db: Arc<KeyValueDB>,
db: Arc<dyn KeyValueDB>,
locks: Arc<Mutex<HashMap<Vec<u8>, Arc<Mutex<()>>>>>,
}
@@ -48,7 +48,7 @@ impl LocalStorage {
}
/// Create offchain local storage with given `KeyValueDB` backend.
pub fn new(db: Arc<KeyValueDB>) -> Self {
pub fn new(db: Arc<dyn KeyValueDB>) -> Self {
Self {
db,
locks: Default::default(),
+20 -43
View File
@@ -122,16 +122,6 @@ fn deadline_to_timestamp(deadline: u64) -> Option<offchain::Timestamp> {
}
}
fn u32_to_key(key: u32) -> std::result::Result<Option<offchain::CryptoKeyId>, ()> {
if key > u16::max_value() as u32 {
Err(())
} else if key == 0 {
Ok(None)
} else {
Ok(Some(offchain::CryptoKeyId(key as u16)))
}
}
impl_function_executor!(this: FunctionExecutor<'e, E>,
ext_print_utf8(utf8_data: *const u8, utf8_len: u32) => {
if let Ok(utf8) = this.memory.get(utf8_data, utf8_len as usize) {
@@ -721,7 +711,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(if res.is_ok() { 0 } else { 1 })
},
ext_new_crypto_key(crypto: u32) -> u32 => {
ext_new_crypto_key(crypto: u32) -> u64 => {
let kind = offchain::CryptoKind::try_from(crypto)
.map_err(|_| "crypto kind OOB while ext_new_crypto_key: wasm")?;
@@ -730,26 +720,23 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
.ok_or_else(|| "Calling unavailable API ext_new_crypto_key: wasm")?;
match res {
Ok(key_id) => Ok(key_id.into()),
Err(()) => Ok(u32::max_value()),
Ok(key) => Ok(key.into()),
Err(()) => Ok(u64::max_value()),
}
},
ext_encrypt(
key: u32,
kind: u32,
key: u64,
data: *const u8,
data_len: u32,
msg_len: *mut u32
) -> *mut u8 => {
let key = u32_to_key(key)
let key = offchain::CryptoKey::try_from(key)
.map_err(|_| "Key OOB while ext_encrypt: wasm")?;
let kind = offchain::CryptoKind::try_from(kind)
.map_err(|_| "crypto kind OOB while ext_encrypt: wasm")?;
let message = this.memory.get(data, data_len as usize)
.map_err(|_| "OOB while ext_encrypt: wasm")?;
let res = this.ext.offchain()
.map(|api| api.encrypt(key, kind, &*message))
.map(|api| api.encrypt(key, &*message))
.ok_or_else(|| "Calling unavailable API ext_encrypt: wasm")?;
let (offset,len) = match res {
@@ -784,15 +771,14 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(offset)
},
ext_authority_pubkey(
kind: u32,
ext_pubkey(
key: u64,
written_out: *mut u32
) -> *mut u8 => {
let kind = offchain::CryptoKind::try_from(kind)
.map_err(|_| "crypto kind OOB while ext_authority_pubkey: wasm")?;
let key = offchain::CryptoKey::try_from(key)
.map_err(|_| "Key OOB while ext_decrypt: wasm")?;
let res = this.ext.offchain()
.map(|api| api.authority_pubkey(kind))
.map(|api| api.pubkey(key))
.ok_or_else(|| "Calling unavailable API ext_authority_pubkey: wasm")?;
let encoded = res.encode();
@@ -805,21 +791,18 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(offset)
},
ext_decrypt(
key: u32,
kind: u32,
key: u64,
data: *const u8,
data_len: u32,
msg_len: *mut u32
) -> *mut u8 => {
let key = u32_to_key(key)
let key = offchain::CryptoKey::try_from(key)
.map_err(|_| "Key OOB while ext_decrypt: wasm")?;
let kind = offchain::CryptoKind::try_from(kind)
.map_err(|_| "crypto kind OOB while ext_decrypt: wasm")?;
let message = this.memory.get(data, data_len as usize)
.map_err(|_| "OOB while ext_decrypt: wasm")?;
let res = this.ext.offchain()
.map(|api| api.decrypt(key, kind, &*message))
.map(|api| api.decrypt(key, &*message))
.ok_or_else(|| "Calling unavailable API ext_decrypt: wasm")?;
let (offset,len) = match res {
@@ -839,21 +822,18 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(offset)
},
ext_sign(
key: u32,
kind: u32,
key: u64,
data: *const u8,
data_len: u32,
sig_data_len: *mut u32
) -> *mut u8 => {
let key = u32_to_key(key)
let key = offchain::CryptoKey::try_from(key)
.map_err(|_| "Key OOB while ext_sign: wasm")?;
let kind = offchain::CryptoKind::try_from(kind)
.map_err(|_| "crypto kind OOB while ext_sign: wasm")?;
let message = this.memory.get(data, data_len as usize)
.map_err(|_| "OOB while ext_sign: wasm")?;
let res = this.ext.offchain()
.map(|api| api.sign(key, kind, &*message))
.map(|api| api.sign(key, &*message))
.ok_or_else(|| "Calling unavailable API ext_sign: wasm")?;
let (offset,len) = match res {
@@ -873,24 +853,21 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
Ok(offset)
},
ext_verify(
key: u32,
kind: u32,
key: u64,
msg: *const u8,
msg_len: u32,
signature: *const u8,
signature_len: u32
) -> u32 => {
let key = u32_to_key(key)
let key = offchain::CryptoKey::try_from(key)
.map_err(|_| "Key OOB while ext_verify: wasm")?;
let kind = offchain::CryptoKind::try_from(kind)
.map_err(|_| "crypto kind OOB while ext_verify: wasm")?;
let message = this.memory.get(msg, msg_len as usize)
.map_err(|_| "OOB while ext_verify: wasm")?;
let signature = this.memory.get(signature, signature_len as usize)
.map_err(|_| "OOB while ext_verify: wasm")?;
let res = this.ext.offchain()
.map(|api| api.verify(key, kind, &*message, &*signature))
.map(|api| api.verify(key, &*message, &*signature))
.ok_or_else(|| "Calling unavailable API ext_verify: wasm")?;
match res {
+194 -107
View File
@@ -23,11 +23,11 @@ use parity_codec::{Encode, Decode};
use primitives::offchain::{
Timestamp, HttpRequestId, HttpRequestStatus, HttpError,
Externalities as OffchainExt,
CryptoKind, CryptoKeyId,
CryptoKind, CryptoKey,
StorageKind,
OpaqueNetworkState, OpaquePeerId, OpaqueMultiaddr,
};
use primitives::crypto::{Pair, Protected};
use primitives::crypto::{Pair, Public, Protected};
use primitives::{ed25519, sr25519};
use runtime_primitives::{
generic::BlockId,
@@ -44,25 +44,144 @@ enum ExtMessage {
/// A persisted key seed.
#[derive(Encode, Decode)]
struct CryptoKey {
struct StoredKey {
kind: CryptoKind,
phrase: String,
}
enum Key {
Sr25519(sr25519::Pair),
impl StoredKey {
fn generate_with_phrase(kind: CryptoKind, password: Option<&str>) -> Self {
match kind {
CryptoKind::Ed25519 => {
let phrase = ed25519::Pair::generate_with_phrase(password).1;
Self { kind, phrase }
}
CryptoKind::Sr25519 => {
let phrase = sr25519::Pair::generate_with_phrase(password).1;
Self { kind, phrase }
}
}
}
fn to_local_key(&self, password: Option<&str>) -> Result<LocalKey, ()> {
match self.kind {
CryptoKind::Ed25519 => {
ed25519::Pair::from_phrase(&self.phrase, password)
.map(|x| LocalKey::Ed25519(x.0))
}
CryptoKind::Sr25519 => {
sr25519::Pair::from_phrase(&self.phrase, password)
.map(|x| LocalKey::Sr25519(x.0))
}
}
.map_err(|e| {
warn!("Error recovering Offchain Worker key. Password invalid? {:?}", e);
()
})
}
}
enum LocalKey {
Ed25519(ed25519::Pair),
Sr25519(sr25519::Pair),
}
impl LocalKey {
fn public(&self) -> Result<Vec<u8>, ()> {
match self {
LocalKey::Ed25519(pair) => Ok(pair.public().to_raw_vec()),
LocalKey::Sr25519(pair) => Ok(pair.public().to_raw_vec()),
}
}
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, ()> {
match self {
LocalKey::Ed25519(pair) => {
let sig = pair.sign(data);
let bytes: &[u8] = sig.as_ref();
Ok(bytes.to_vec())
}
LocalKey::Sr25519(pair) => {
let sig = pair.sign(data);
let bytes: &[u8] = sig.as_ref();
Ok(bytes.to_vec())
}
}
}
fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
match self {
LocalKey::Ed25519(pair) => {
Ok(ed25519::Pair::verify_weak(signature, msg, pair.public()))
}
LocalKey::Sr25519(pair) => {
Ok(sr25519::Pair::verify_weak(signature, msg, pair.public()))
}
}
}
}
/// A key.
enum Key<ConsensusPair, FinalityPair> {
LocalKey(LocalKey),
AuthorityKey(ConsensusPair),
FgAuthorityKey(FinalityPair),
}
impl<ConsensusPair: Pair, FinalityPair: Pair> Key<ConsensusPair, FinalityPair> {
fn public(&self) -> Result<Vec<u8>, ()> {
match self {
Key::LocalKey(local) => {
local.public()
}
Key::AuthorityKey(pair) => {
Ok(pair.public().to_raw_vec())
}
Key::FgAuthorityKey(pair) => {
Ok(pair.public().to_raw_vec())
}
}
}
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, ()> {
match self {
Key::LocalKey(local) => {
local.sign(data)
}
Key::AuthorityKey(pair) => {
Ok(pair.sign(data).as_ref().to_vec())
}
Key::FgAuthorityKey(pair) => {
Ok(pair.sign(data).as_ref().to_vec())
}
}
}
fn verify(&self, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
match self {
Key::LocalKey(local) => {
local.verify(msg, signature)
}
Key::AuthorityKey(pair) => {
Ok(ConsensusPair::verify_weak(signature, msg, pair.public()))
}
Key::FgAuthorityKey(pair) => {
Ok(FinalityPair::verify_weak(signature, msg, pair.public()))
}
}
}
}
/// Asynchronous offchain API.
///
/// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently).
pub(crate) struct Api<Storage, KeyProvider> {
pub(crate) struct Api<Storage, KeyProvider, Block: traits::Block> {
sender: mpsc::UnboundedSender<ExtMessage>,
db: Storage,
keys_password: Protected<String>,
key_provider: KeyProvider,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
at: BlockId<Block>,
}
fn unavailable_yet<R: Default>(name: &str) -> R {
@@ -77,55 +196,56 @@ const KEYS_PREFIX: &[u8] = b"keys";
const NEXT_ID: &[u8] = b"crypto_key_id";
impl<Storage, KeyProvider> Api<Storage, KeyProvider> where
impl<Storage, KeyProvider, Block> Api<Storage, KeyProvider, Block> where
Storage: OffchainStorage,
KeyProvider: AuthorityKeyProvider,
KeyProvider: AuthorityKeyProvider<Block>,
Block: traits::Block,
{
fn keypair<P: Pair>(&self, phrase: &str) -> Result<P, ()> {
P::from_phrase(phrase, Some(self.keys_password.as_ref()))
.map_err(|e| {
warn!("Error recovering Offchain Worker key. Password invalid? {:?}", e);
()
})
.map(|x| x.0)
fn password(&self) -> Option<&str> {
Some(self.keys_password.as_ref().as_str())
}
fn read_key(&self, id: Option<CryptoKeyId>, kind: CryptoKind) -> Result<Key, ()> {
if let Some(id) = id {
let key = self.db.get(KEYS_PREFIX, &id.0.encode())
.and_then(|key| CryptoKey::decode(&mut &*key))
.ok_or(())?;
if key.kind != kind {
warn!(
"Invalid crypto kind (got: {:?}, expected: {:?}), when requesting key {:?}",
key.kind,
kind,
id
);
return Err(())
fn read_key(
&self,
key: CryptoKey,
) -> Result<Key<KeyProvider::ConsensusPair, KeyProvider::FinalityPair>, ()> {
match key {
CryptoKey::LocalKey { id, kind } => {
let key = self.db.get(KEYS_PREFIX, &id.encode())
.and_then(|key| StoredKey::decode(&mut &*key))
.ok_or(())?;
if key.kind != kind {
warn!(
"Invalid crypto kind (got: {:?}, expected: {:?}), when requesting key {:?}",
key.kind,
kind,
id
);
return Err(())
}
Ok(Key::LocalKey(key.to_local_key(self.password())?))
}
CryptoKey::AuthorityKey => {
let key = self.key_provider
.authority_key(&self.at)
.ok_or(())?;
Ok(Key::AuthorityKey(key))
}
CryptoKey::FgAuthorityKey => {
let key = self.key_provider
.fg_authority_key(&self.at)
.ok_or(())?;
Ok(Key::FgAuthorityKey(key))
}
Ok(match key.kind {
CryptoKind::Sr25519 => Key::Sr25519(self.keypair(&key.phrase)?),
CryptoKind::Ed25519 => Key::Ed25519(self.keypair(&key.phrase)?),
})
} else {
let key = match kind {
CryptoKind::Sr25519 => self.key_provider.authority_key().map(Key::Sr25519),
CryptoKind::Ed25519 => self.key_provider.authority_key().map(Key::Ed25519),
};
key.ok_or_else(|| {
warn!("AuthorityKey is not configured, yet offchain worker tried to access it.");
()
})
}
}
}
impl<Storage, KeyProvider> OffchainExt for Api<Storage, KeyProvider> where
impl<Storage, KeyProvider, Block> OffchainExt for Api<Storage, KeyProvider, Block>
where
Storage: OffchainStorage,
KeyProvider: AuthorityKeyProvider,
KeyProvider: AuthorityKeyProvider<Block>,
Block: traits::Block,
{
fn submit_transaction(&mut self, ext: Vec<u8>) -> Result<(), ()> {
self.sender
@@ -134,16 +254,8 @@ impl<Storage, KeyProvider> OffchainExt for Api<Storage, KeyProvider> where
.map_err(|_| ())
}
fn new_crypto_key(&mut self, kind: CryptoKind) -> Result<CryptoKeyId, ()> {
let phrase = match kind {
CryptoKind::Ed25519 => {
ed25519::Pair::generate_with_phrase(Some(self.keys_password.as_ref())).1
},
CryptoKind::Sr25519 => {
sr25519::Pair::generate_with_phrase(Some(self.keys_password.as_ref())).1
},
};
fn new_crypto_key(&mut self, kind: CryptoKind) -> Result<CryptoKey, ()> {
let key = StoredKey::generate_with_phrase(kind, self.password());
let (id, id_encoded) = loop {
let encoded = self.db.get(KEYS_PREFIX, NEXT_ID);
let encoded_slice = encoded.as_ref().map(|x| x.as_slice());
@@ -157,19 +269,13 @@ impl<Storage, KeyProvider> OffchainExt for Api<Storage, KeyProvider> where
}
};
self.db.set(KEYS_PREFIX, &id_encoded, &CryptoKey { phrase, kind } .encode());
self.db.set(KEYS_PREFIX, &id_encoded, &key.encode());
Ok(CryptoKeyId(id))
Ok(CryptoKey::LocalKey { id, kind })
}
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 pubkey(&self, key: CryptoKey) -> Result<Vec<u8>, ()> {
self.read_key(key)?.public()
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
@@ -182,33 +288,23 @@ impl<Storage, KeyProvider> OffchainExt for Api<Storage, KeyProvider> where
Ok(OpaqueNetworkState::from(state))
}
fn encrypt(&mut self, _key: Option<CryptoKeyId>, _kind: CryptoKind, _data: &[u8]) -> Result<Vec<u8>, ()> {
fn encrypt(&mut self, _key: CryptoKey, _data: &[u8]) -> Result<Vec<u8>, ()> {
unavailable_yet::<()>("encrypt");
Err(())
}
fn decrypt(&mut self, _key: Option<CryptoKeyId>, _kind: CryptoKind, _data: &[u8]) -> Result<Vec<u8>, ()> {
fn decrypt(&mut self, _key: CryptoKey, _data: &[u8]) -> Result<Vec<u8>, ()> {
unavailable_yet::<()>("decrypt");
Err(())
}
fn sign(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
let key = self.read_key(key, kind)?;
Ok(match key {
Key::Sr25519(pair) => pair.sign(data).0.to_vec(),
Key::Ed25519(pair) => pair.sign(data).0.to_vec(),
})
fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
self.read_key(key)?.sign(data)
}
fn verify(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
let key = self.read_key(key, kind)?;
Ok(match key {
Key::Sr25519(pair) => sr25519::Pair::verify_weak(signature, msg, pair.public()),
Key::Ed25519(pair) => ed25519::Pair::verify_weak(signature, msg, pair.public()),
})
fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
self.read_key(key)?.verify(msg, signature)
}
fn timestamp(&mut self) -> Timestamp {
@@ -385,14 +481,14 @@ pub(crate) struct AsyncApi<A: ChainApi> {
impl<A: ChainApi> AsyncApi<A> {
/// Creates new Offchain extensions API implementation an the asynchronous processing part.
pub fn new<S: OffchainStorage, P: AuthorityKeyProvider>(
pub fn new<S: OffchainStorage, P: AuthorityKeyProvider<A::Block>>(
transaction_pool: Arc<Pool<A>>,
db: S,
keys_password: Protected<String>,
key_provider: P,
at: BlockId<A::Block>,
network_state: Arc<dyn NetworkStateInfo + Send + Sync>,
) -> (Api<S, P>, AsyncApi<A>) {
) -> (Api<S, P, A::Block>, AsyncApi<A>) {
let (sender, rx) = mpsc::unbounded();
let api = Api {
@@ -401,6 +497,7 @@ impl<A: ChainApi> AsyncApi<A> {
keys_password,
key_provider,
network_state,
at,
};
let async_api = AsyncApi {
@@ -446,10 +543,12 @@ impl<A: ChainApi> AsyncApi<A> {
#[cfg(test)]
mod tests {
use super::*;
use std::{collections::HashSet, convert::TryFrom};
use std::convert::TryFrom;
use runtime_primitives::traits::Zero;
use client_db::offchain::LocalStorage;
use crate::tests::TestProvider;
use network::PeerId;
use test_client::runtime::Block;
struct MockNetworkStateInfo();
@@ -463,7 +562,7 @@ mod tests {
}
}
fn offchain_api() -> (Api<LocalStorage, TestProvider>, AsyncApi<impl ChainApi>) {
fn offchain_api() -> (Api<LocalStorage, TestProvider<Block>, Block>, AsyncApi<impl ChainApi>) {
let _ = env_logger::try_init();
let db = LocalStorage::new_test();
let client = Arc::new(test_client::new());
@@ -472,7 +571,7 @@ mod tests {
);
let mock = Arc::new(MockNetworkStateInfo());
AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(0), mock)
AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(Zero::zero()), mock)
}
#[test]
@@ -515,22 +614,16 @@ mod tests {
let msg = b"Hello world!";
// when
let key_id = api.new_crypto_key(kind).unwrap();
let signature = api.sign(Some(key_id), kind, msg).unwrap();
let key = api.new_crypto_key(kind).unwrap();
let signature = api.sign(key, msg).unwrap();
// then
let res = api.verify(Some(key_id), kind, msg, &signature).unwrap();
let res = api.verify(key, msg, &signature).unwrap();
assert_eq!(res, true);
let res = api.verify(Some(key_id), kind, msg, &[]).unwrap();
let res = api.verify(key, msg, &[]).unwrap();
assert_eq!(res, false);
let res = api.verify(Some(key_id), kind, b"Different msg", &signature).unwrap();
let res = api.verify(key, b"Different msg", &signature).unwrap();
assert_eq!(res, false);
assert_eq!(
api.verify(Some(key_id), CryptoKind::Sr25519, msg, &signature).is_err(),
kind != CryptoKind::Sr25519
);
};
test(CryptoKind::Ed25519);
@@ -543,23 +636,17 @@ mod tests {
let mut api = offchain_api().0;
api.key_provider.ed_key = Some(ed25519::Pair::generate().0);
let msg = b"Hello world!";
let kind = CryptoKind::Ed25519;
// when
let signature = api.sign(None, kind, msg).unwrap();
let signature = api.sign(CryptoKey::AuthorityKey, msg).unwrap();
// then
let res = api.verify(None, kind, msg, &signature).unwrap();
let res = api.verify(CryptoKey::AuthorityKey, msg, &signature).unwrap();
assert_eq!(res, true);
let res = api.verify(None, kind, msg, &[]).unwrap();
let res = api.verify(CryptoKey::AuthorityKey, msg, &[]).unwrap();
assert_eq!(res, false);
let res = api.verify(None, kind, b"Different msg", &signature).unwrap();
let res = api.verify(CryptoKey::AuthorityKey, b"Different msg", &signature).unwrap();
assert_eq!(res, false);
assert!(
api.verify(None, CryptoKind::Sr25519, msg, &signature).is_err(),
"Invalid kind should trigger a missing key error."
);
}
#[test]
+35 -14
View File
@@ -60,9 +60,17 @@ pub mod testing;
pub use offchain_primitives::OffchainWorkerApi;
/// Provides currently configured authority key.
pub trait AuthorityKeyProvider: Clone + 'static {
pub trait AuthorityKeyProvider<Block: traits::Block>: Clone + 'static {
/// The crypto used by the block authoring algorithm.
type ConsensusPair: crypto::Pair;
/// The crypto used by the finality gadget.
type FinalityPair: crypto::Pair;
/// Returns currently configured authority key.
fn authority_key<TPair: crypto::Pair>(&self) -> Option<TPair>;
fn authority_key(&self, block_id: &BlockId<Block>) -> Option<Self::ConsensusPair>;
/// Returns currently configured finality gadget authority key.
fn fg_authority_key(&self, block_id: &BlockId<Block>) -> Option<Self::FinalityPair>;
}
/// An offchain workers manager.
@@ -122,7 +130,7 @@ impl<Client, Storage, KeyProvider, Block> OffchainWorkers<
Block: traits::Block,
Client: ProvideRuntimeApi,
Client::Api: OffchainWorkerApi<Block>,
KeyProvider: AuthorityKeyProvider,
KeyProvider: AuthorityKeyProvider<Block>,
Storage: client::backend::OffchainStorage + 'static,
{
/// Start the offchain workers after given block.
@@ -163,8 +171,7 @@ impl<Client, Storage, KeyProvider, Block> OffchainWorkers<
mod tests {
use super::*;
use futures::Future;
use primitives::{ed25519, sr25519, crypto::{TypedKey, Pair}};
use std::collections::HashSet;
use primitives::{ed25519, sr25519};
use network::{Multiaddr, PeerId};
struct MockNetworkStateInfo();
@@ -179,19 +186,33 @@ mod tests {
}
}
#[derive(Clone, Default)]
pub(crate) struct TestProvider {
#[derive(Clone)]
pub(crate) struct TestProvider<Block> {
_marker: PhantomData<Block>,
pub(crate) sr_key: Option<sr25519::Pair>,
pub(crate) ed_key: Option<ed25519::Pair>,
}
impl AuthorityKeyProvider for TestProvider {
fn authority_key<TPair: crypto::Pair>(&self) -> Option<TPair> {
TPair::from_seed_slice(&match TPair::KEY_TYPE {
sr25519::Pair::KEY_TYPE => self.sr_key.as_ref().map(|key| key.to_raw_vec()),
ed25519::Pair::KEY_TYPE => self.ed_key.as_ref().map(|key| key.to_raw_vec()),
_ => None,
}?).ok()
impl<Block: traits::Block> Default for TestProvider<Block> {
fn default() -> Self {
Self {
_marker: PhantomData,
sr_key: None,
ed_key: None,
}
}
}
impl<Block: traits::Block> AuthorityKeyProvider<Block> for TestProvider<Block> {
type ConsensusPair = ed25519::Pair;
type FinalityPair = sr25519::Pair;
fn authority_key(&self, _: &BlockId<Block>) -> Option<Self::ConsensusPair> {
self.ed_key.clone()
}
fn fg_authority_key(&self, _: &BlockId<Block>) -> Option<Self::FinalityPair> {
self.sr_key.clone()
}
}
+7 -12
View File
@@ -29,7 +29,7 @@ use primitives::offchain::{
HttpRequestStatus as RequestStatus,
Timestamp,
CryptoKind,
CryptoKeyId,
CryptoKey,
StorageKind,
OpaqueNetworkState,
};
@@ -144,18 +144,17 @@ impl offchain::Externalities for TestOffchainExt {
unimplemented!("not needed in tests so far")
}
fn authority_pubkey(&self, _kind: CryptoKind) -> Result<Vec<u8>, ()> {
fn pubkey(&self, _key: CryptoKey) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
}
fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result<CryptoKeyId, ()> {
fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result<CryptoKey, ()> {
unimplemented!("not needed in tests so far")
}
fn encrypt(
&mut self,
_key: Option<CryptoKeyId>,
_kind: CryptoKind,
_key: CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
@@ -163,8 +162,7 @@ impl offchain::Externalities for TestOffchainExt {
fn decrypt(
&mut self,
_key: Option<CryptoKeyId>,
_kind: CryptoKind,
_key: CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
@@ -172,8 +170,7 @@ impl offchain::Externalities for TestOffchainExt {
fn sign(
&mut self,
_key: Option<CryptoKeyId>,
_kind: CryptoKind,
_key: CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unimplemented!("not needed in tests so far")
@@ -181,8 +178,7 @@ impl offchain::Externalities for TestOffchainExt {
fn verify(
&mut self,
_key: Option<CryptoKeyId>,
_kind: CryptoKind,
_key: CryptoKey,
_msg: &[u8],
_signature: &[u8],
) -> Result<bool, ()> {
@@ -332,4 +328,3 @@ impl offchain::Externalities for TestOffchainExt {
}
}
}
+9 -4
View File
@@ -457,7 +457,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
}
/// Trait suitable for typical cryptographic PKI key public type.
pub trait Public: TypedKey + PartialEq + Eq {
pub trait Public: AsRef<[u8]> + TypedKey + PartialEq + Eq + Clone + Send + Sync {
/// A new instance from the given slice that should be 32 bytes long.
///
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
@@ -476,7 +476,7 @@ pub trait Public: TypedKey + PartialEq + Eq {
///
/// For now it just specifies how to create a key from a phrase and derivation path.
#[cfg(feature = "std")]
pub trait Pair: TypedKey + Sized + 'static {
pub trait Pair: TypedKey + Sized + Clone + Send + Sync + 'static {
/// The type which is used to encode a public key.
type Public: Public + Hash;
@@ -631,7 +631,7 @@ mod tests {
use hex_literal::hex;
use super::*;
#[derive(Eq, PartialEq, Debug)]
#[derive(Clone, Eq, PartialEq, Debug)]
enum TestPair {
Generated,
GeneratedWithPhrase,
@@ -640,8 +640,13 @@ mod tests {
Seed(Vec<u8>),
}
#[derive(PartialEq, Eq, Hash)]
#[derive(Clone, PartialEq, Eq, Hash)]
struct TestPublic;
impl AsRef<[u8]> for TestPublic {
fn as_ref(&self) -> &[u8] {
&[]
}
}
impl Public for TestPublic {
fn from_slice(_bytes: &[u8]) -> Self {
Self
+83 -25
View File
@@ -87,14 +87,49 @@ impl From<CryptoKind> for u32 {
}
}
/// Opaque type for created crypto keys.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
/// Key to use in the offchain worker crypto api.
#[derive(Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct CryptoKeyId(pub u16);
pub enum CryptoKey {
/// Use a key from the offchain workers local storage.
LocalKey {
/// The id of the key.
id: u16,
/// The kind of the key.
kind: CryptoKind,
},
/// Use the key the block authoring algorithm uses.
AuthorityKey,
/// Use the key the finality gadget uses.
FgAuthorityKey,
}
impl From<CryptoKeyId> for u32 {
fn from(c: CryptoKeyId) -> Self {
c.0 as u32
impl TryFrom<u64> for CryptoKey {
type Error = ();
fn try_from(key: u64) -> Result<Self, Self::Error> {
match key & 0xFF {
0 => {
let id = (key >> 8 & 0xFFFF) as u16;
let kind = CryptoKind::try_from((key >> 32) as u32)?;
Ok(CryptoKey::LocalKey { id, kind })
}
1 => Ok(CryptoKey::AuthorityKey),
2 => Ok(CryptoKey::FgAuthorityKey),
_ => Err(()),
}
}
}
impl From<CryptoKey> for u64 {
fn from(key: CryptoKey) -> u64 {
match key {
CryptoKey::LocalKey { id, kind } => {
((kind as u64) << 32) | ((id as u64) << 8)
}
CryptoKey::AuthorityKey => 1,
CryptoKey::FgAuthorityKey => 2,
}
}
}
@@ -279,13 +314,13 @@ pub trait Externalities {
/// 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.
fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result<CryptoKeyId, ()>;
fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result<CryptoKey, ()>;
/// Returns the locally configured authority public key, if available.
fn pubkey(&self, key: CryptoKey) -> Result<Vec<u8>, ()>;
/// Encrypt a piece of data using given crypto key.
///
@@ -293,7 +328,7 @@ pub trait Externalities {
///
/// Returns an error if `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn encrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()>;
fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Decrypt a piece of data using given crypto key.
///
@@ -301,7 +336,7 @@ pub trait Externalities {
///
/// Returns an error if data cannot be decrypted or the `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn decrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()>;
fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Sign a piece of data using given crypto key.
///
@@ -309,14 +344,14 @@ pub trait Externalities {
///
/// Returns an error if `key` is not available or does not exist,
/// or the expected `CryptoKind` does not match.
fn sign(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()>;
fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Verifies that `signature` for `msg` matches given `key`.
///
/// Returns an `Ok` with `true` in case it does, `false` in case it doesn't.
/// Returns an error in case the key is not available or does not exist or the parameters
/// lengths are incorrect or `CryptoKind` does not match.
fn verify(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
/// Returns current UNIX timestamp (in millis)
fn timestamp(&mut self) -> Timestamp;
@@ -431,32 +466,32 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
(&mut **self).submit_transaction(ex)
}
fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result<CryptoKeyId, ()> {
fn new_crypto_key(&mut self, crypto: CryptoKind) -> Result<CryptoKey, ()> {
(&mut **self).new_crypto_key(crypto)
}
fn encrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).encrypt(key, kind, data)
fn encrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).encrypt(key, data)
}
fn network_state(&self) -> Result<OpaqueNetworkState, ()> {
(& **self).network_state()
}
fn authority_pubkey(&self, key:CryptoKind) -> Result<Vec<u8>, ()> {
(&**self).authority_pubkey(key)
fn pubkey(&self, key: CryptoKey) -> Result<Vec<u8>, ()> {
(&**self).pubkey(key)
}
fn decrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).decrypt(key, kind, data)
fn decrypt(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).decrypt(key, data)
}
fn sign(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).sign(key, kind, data)
fn sign(&mut self, key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()> {
(&mut **self).sign(key, data)
}
fn verify(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
(&mut **self).verify(key, kind, msg, signature)
fn verify(&mut self, key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
(&mut **self).verify(key, msg, signature)
}
fn timestamp(&mut self) -> Timestamp {
@@ -536,4 +571,27 @@ mod tests {
assert_eq!(t.sub(Duration::from_millis(10)), Timestamp(0));
assert_eq!(t.diff(&Timestamp(3)), Duration(2));
}
#[test]
fn crypto_key_to_from_u64() {
let key = CryptoKey::AuthorityKey;
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
let key = CryptoKey::FgAuthorityKey;
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
let key = CryptoKey::LocalKey { id: 0, kind: CryptoKind::Ed25519 };
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
let key = CryptoKey::LocalKey { id: 10, kind: CryptoKind::Sr25519 };
let uint: u64 = key.clone().into();
let key2 = CryptoKey::try_from(uint).unwrap();
assert_eq!(key, key2);
}
}
+17 -3
View File
@@ -30,7 +30,7 @@ use runtime_primitives::{
BuildStorage, traits::{Block as BlockT, Header as HeaderT, ProvideRuntimeApi}, generic::BlockId
};
use crate::config::Configuration;
use primitives::{Blake2Hasher, H256};
use primitives::{Blake2Hasher, H256, Pair};
use rpc::{self, apis::system::SystemInfo};
use futures::{prelude::*, future::Executor, sync::mpsc};
@@ -128,6 +128,16 @@ pub type ComponentOffchainStorage<C> = <
/// Block type for `Components`
pub type ComponentBlock<C> = <<C as Components>::Factory as ServiceFactory>::Block;
/// ConsensusPair type for `Components`
pub type ComponentConsensusPair<C> = <<C as Components>::Factory as ServiceFactory>::ConsensusPair;
/// FinalityPair type for `Components`
pub type ComponentFinalityPair<C> = <<C as Components>::Factory as ServiceFactory>::FinalityPair;
/// AuthorityKeyProvider type for `Components`
pub type ComponentAuthorityKeyProvider<C> =
AuthorityKeyProvider<ComponentBlock<C>, ComponentConsensusPair<C>, ComponentFinalityPair<C>>;
/// Extrinsic hash type for `Components`
pub type ComponentExHash<C> = <<C as Components>::TransactionPoolApi as txpool::ChainApi>::Hash;
@@ -231,7 +241,7 @@ pub trait OffchainWorker<C: Components> {
offchain: &offchain::OffchainWorkers<
ComponentClient<C>,
ComponentOffchainStorage<C>,
AuthorityKeyProvider,
ComponentAuthorityKeyProvider<C>,
ComponentBlock<C>
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
@@ -248,7 +258,7 @@ impl<C: Components> OffchainWorker<Self> for C where
offchain: &offchain::OffchainWorkers<
ComponentClient<C>,
ComponentOffchainStorage<C>,
AuthorityKeyProvider,
ComponentAuthorityKeyProvider<C>,
ComponentBlock<C>
>,
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
@@ -283,6 +293,10 @@ pub type TaskExecutor = Arc<dyn Executor<Box<dyn Future<Item = (), Error = ()> +
pub trait ServiceFactory: 'static + Sized {
/// Block type.
type Block: BlockT<Hash=H256>;
/// Consensus crypto type.
type ConsensusPair: Pair;
/// Finality crypto type.
type FinalityPair: Pair;
/// The type that implements the runtime API.
type RuntimeApi: Send + Sync;
/// Network protocol extensions.
+69 -17
View File
@@ -26,6 +26,7 @@ pub mod chain_ops;
pub mod error;
use std::io;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::collections::HashMap;
use std::time::Duration;
@@ -42,7 +43,7 @@ use log::{info, warn, debug, error};
use parity_codec::{Encode, Decode};
use primitives::{Pair, ed25519, crypto};
use runtime_primitives::generic::BlockId;
use runtime_primitives::traits::{Header, NumberFor, SaturatedConversion};
use runtime_primitives::traits::{Header, NumberFor, SaturatedConversion, Zero};
use substrate_executor::NativeExecutor;
use sysinfo::{get_current_pid, ProcessExt, System, SystemExt};
use tel::{telemetry, SUBSTRATE_INFO};
@@ -55,12 +56,14 @@ pub use transaction_pool::txpool::{
};
pub use client::FinalityNotifications;
pub use components::{ServiceFactory, FullBackend, FullExecutor, LightBackend,
pub use components::{
ServiceFactory, FullBackend, FullExecutor, LightBackend, ComponentAuthorityKeyProvider,
LightExecutor, Components, PoolApi, ComponentClient, ComponentOffchainStorage,
ComponentBlock, FullClient, LightClient, FullComponents, LightComponents,
CodeExecutor, NetworkService, FactoryChainSpec, FactoryBlock,
FactoryFullConfiguration, RuntimeGenesis, FactoryGenesis,
ComponentExHash, ComponentExtrinsic, FactoryExtrinsic
ComponentExHash, ComponentExtrinsic, FactoryExtrinsic,
ComponentConsensusPair, ComponentFinalityPair,
};
use components::{StartRPC, MaintainTransactionPool, OffchainWorker};
#[doc(hidden)]
@@ -82,7 +85,7 @@ pub struct Service<Components: components::Components> {
NetworkStatus<ComponentBlock<Components>>, NetworkState
)>>>>,
transaction_pool: Arc<TransactionPool<Components::TransactionPoolApi>>,
keystore: AuthorityKeyProvider,
keystore: ComponentAuthorityKeyProvider<Components>,
exit: ::exit_future::Exit,
signal: Option<Signal>,
/// Sender for futures that must be spawned as background tasks.
@@ -102,7 +105,7 @@ pub struct Service<Components: components::Components> {
_offchain_workers: Option<Arc<offchain::OffchainWorkers<
ComponentClient<Components>,
ComponentOffchainStorage<Components>,
AuthorityKeyProvider,
ComponentAuthorityKeyProvider<Components>,
ComponentBlock<Components>>
>>,
}
@@ -264,6 +267,7 @@ impl<Components: components::Components> Service<Components> {
let network_status_sinks = Arc::new(Mutex::new(Vec::new()));
let keystore_authority_key = AuthorityKeyProvider {
_marker: PhantomData,
roles: config.roles,
password: config.password.clone(),
keystore: keystore.map(Arc::new),
@@ -498,10 +502,17 @@ impl<Components: components::Components> Service<Components> {
}
/// give the authority key, if we are an authority and have a key
pub fn authority_key<TPair: Pair>(&self) -> Option<TPair> {
pub fn authority_key(&self) -> Option<ComponentConsensusPair<Components>> {
use offchain::AuthorityKeyProvider;
self.keystore.authority_key()
self.keystore.authority_key(&BlockId::Number(Zero::zero()))
}
/// give the authority key, if we are an authority and have a key
pub fn fg_authority_key(&self) -> Option<ComponentFinalityPair<Components>> {
use offchain::AuthorityKeyProvider;
self.keystore.fg_authority_key(&BlockId::Number(Zero::zero()))
}
/// return a shared instance of Telemetry (if enabled)
@@ -531,7 +542,8 @@ impl<Components: components::Components> Service<Components> {
/// If the request subscribes you to events, the `Sender` in the `RpcSession` object is used to
/// send back spontaneous events.
pub fn rpc_query(&self, mem: &RpcSession, request: &str)
-> impl Future<Item = Option<String>, Error = ()> {
-> impl Future<Item = Option<String>, Error = ()>
{
self.rpc_handlers.handle_request(request, mem.metadata.clone())
}
@@ -733,7 +745,7 @@ impl<Components> Drop for Service<Components> where Components: components::Comp
fn start_rpc_servers<F: ServiceFactory, H: FnMut() -> rpc::RpcHandler>(
config: &FactoryFullConfiguration<F>,
mut gen_handler: H
) -> Result<Box<std::any::Any + Send + Sync>, error::Error> {
) -> Result<Box<dyn std::any::Any + Send + Sync>, error::Error> {
fn maybe_start_server<T, F>(address: Option<SocketAddr>, mut start: F) -> Result<Option<T>, io::Error>
where F: FnMut(&SocketAddr) -> Result<T, io::Error>,
{
@@ -876,16 +888,27 @@ impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<
}
}
/// A provider of current authority key.
#[derive(Clone)]
pub struct AuthorityKeyProvider {
/// A provider of current authority key.
pub struct AuthorityKeyProvider<Block, ConsensusPair, FinalityPair> {
_marker: PhantomData<(Block, ConsensusPair, FinalityPair)>,
roles: Roles,
keystore: Option<Arc<Keystore>>,
password: crypto::Protected<String>,
}
impl offchain::AuthorityKeyProvider for AuthorityKeyProvider {
fn authority_key<TPair: Pair>(&self) -> Option<TPair> {
impl<Block, ConsensusPair, FinalityPair>
offchain::AuthorityKeyProvider<Block>
for AuthorityKeyProvider<Block, ConsensusPair, FinalityPair>
where
Block: runtime_primitives::traits::Block,
ConsensusPair: Pair,
FinalityPair: Pair,
{
type ConsensusPair = ConsensusPair;
type FinalityPair = FinalityPair;
fn authority_key(&self, _at: &BlockId<Block>) -> Option<Self::ConsensusPair> {
if self.roles != Roles::AUTHORITY {
return None
}
@@ -896,10 +919,33 @@ impl offchain::AuthorityKeyProvider for AuthorityKeyProvider {
};
let loaded_key = keystore
.contents()
.map(|keys| keys.get(0)
.map(|k| keystore.load(k, self.password.as_ref()))
);
.contents()
.map(|keys| keys.get(0)
.map(|k| keystore.load(k, self.password.as_ref()))
);
if let Ok(Some(Ok(key))) = loaded_key {
Some(key)
} else {
None
}
}
fn fg_authority_key(&self, _at: &BlockId<Block>) -> Option<Self::FinalityPair> {
if self.roles != Roles::AUTHORITY {
return None
}
let keystore = match self.keystore {
Some(ref keystore) => keystore,
None => return None
};
let loaded_key = keystore
.contents()
.map(|keys| keys.get(0)
.map(|k| keystore.load(k, self.password.as_ref()))
);
if let Ok(Some(Ok(key))) = loaded_key {
Some(key)
@@ -957,6 +1003,8 @@ impl offchain::AuthorityKeyProvider for AuthorityKeyProvider {
/// struct Factory {
/// // Declare the block type
/// Block = Block,
/// ConsensusPair = primitives::ed25519::Pair,
/// FinalityPair = primitives::ed25519::Pair,
/// RuntimeApi = RuntimeApi,
/// // Declare the network protocol and give an initializer.
/// NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) },
@@ -1000,6 +1048,8 @@ macro_rules! construct_service_factory {
$(#[$attr:meta])*
struct $name:ident {
Block = $block:ty,
ConsensusPair = $consensus_pair:ty,
FinalityPair = $finality_pair:ty,
RuntimeApi = $runtime_api:ty,
NetworkProtocol = $protocol:ty { $( $protocol_init:tt )* },
RuntimeDispatch = $dispatch:ty,
@@ -1025,6 +1075,8 @@ macro_rules! construct_service_factory {
#[allow(unused_variables)]
impl $crate::ServiceFactory for $name {
type Block = $block;
type ConsensusPair = $consensus_pair;
type FinalityPair = $finality_pair;
type RuntimeApi = $runtime_api;
type NetworkProtocol = $protocol;
type RuntimeDispatch = $dispatch;
+7 -13
View File
@@ -36,7 +36,7 @@ pub use primitives::Blake2Hasher;
use primitives::offchain::{
Timestamp,
HttpRequestId, HttpRequestStatus, HttpError,
CryptoKind, CryptoKeyId,
CryptoKind, CryptoKey,
StorageKind,
OpaqueNetworkState,
};
@@ -244,46 +244,40 @@ export_api! {
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>, ()>;
fn pubkey(key: CryptoKey) -> Result<Vec<u8>, ()>;
/// Create new key(pair) for signing/encryption/decryption.
///
/// Returns an error if given crypto kind is not supported.
fn new_crypto_key(crypto: CryptoKind) -> Result<CryptoKeyId, ()>;
fn new_crypto_key(crypto: CryptoKind) -> Result<CryptoKey, ()>;
/// Encrypt a piece of data using given crypto key.
///
/// If `key` is `None`, it will attempt to use current authority key.
///
/// Returns an error if `key` is not available or does not exist.
fn encrypt(key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()>;
fn encrypt(key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Decrypt a piece of data using given crypto key.
///
/// If `key` is `None`, it will attempt to use current authority key.
///
/// Returns an error if data cannot be decrypted or the `key` is not available or does not exist.
fn decrypt(key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()>;
fn decrypt(key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Sign a piece of data using given crypto key.
///
/// If `key` is `None`, it will attempt to use current authority key.
///
/// Returns an error if `key` is not available or does not exist.
fn sign(key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()>;
fn sign(key: CryptoKey, data: &[u8]) -> Result<Vec<u8>, ()>;
/// Verifies that `signature` for `msg` matches given `key`.
///
/// Returns an `Ok` with `true` in case it does, `false` in case it doesn't.
/// Returns an error in case the key is not available or does not exist or the parameters
/// lengths are incorrect.
fn verify(
key: Option<CryptoKeyId>,
kind: CryptoKind,
msg: &[u8],
signature: &[u8]
) -> Result<bool, ()>;
fn verify(key: CryptoKey, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
/// Returns current UNIX timestamp (in millis)
fn timestamp() -> Timestamp;
+11 -15
View File
@@ -275,56 +275,52 @@ impl OffchainApi for () {
}, "network_state can be called only in the offchain worker context")
}
fn authority_pubkey(crypto: offchain::CryptoKind) -> Result<Vec<u8>, ()> {
fn pubkey(key: offchain::CryptoKey) -> Result<Vec<u8>, ()> {
with_offchain(|ext| {
ext.authority_pubkey(crypto)
ext.pubkey(key)
}, "authority_pubkey can be called only in the offchain worker context")
}
fn new_crypto_key(crypto: offchain::CryptoKind) -> Result<offchain::CryptoKeyId, ()> {
fn new_crypto_key(crypto: offchain::CryptoKind) -> Result<offchain::CryptoKey, ()> {
with_offchain(|ext| {
ext.new_crypto_key(crypto)
}, "new_crypto_key can be called only in the offchain worker context")
}
fn encrypt(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
data: &[u8],
) -> Result<Vec<u8>, ()> {
with_offchain(|ext| {
ext.encrypt(key, kind, data)
ext.encrypt(key, data)
}, "encrypt can be called only in the offchain worker context")
}
fn decrypt(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
data: &[u8],
) -> Result<Vec<u8>, ()> {
with_offchain(|ext| {
ext.decrypt(key, kind, data)
ext.decrypt(key, data)
}, "decrypt can be called only in the offchain worker context")
}
fn sign(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
data: &[u8],
) -> Result<Vec<u8>, ()> {
with_offchain(|ext| {
ext.sign(key, kind, data)
ext.sign(key, data)
}, "sign can be called only in the offchain worker context")
}
fn verify(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
msg: &[u8],
signature: &[u8],
) -> Result<bool, ()> {
with_offchain(|ext| {
ext.verify(key, kind, msg, signature)
ext.verify(key, msg, signature)
}, "verify can be called only in the offchain worker context")
}
+35 -44
View File
@@ -19,7 +19,7 @@ pub use rstd;
pub use rstd::{mem, slice};
use core::{intrinsics, panic::PanicInfo};
use rstd::{vec::Vec, cell::Cell, convert::TryInto};
use rstd::{vec::Vec, cell::Cell, convert::TryInto, convert::TryFrom};
use primitives::{offchain, Blake2Hasher};
#[cfg(not(feature = "no_panic_handler"))]
@@ -410,7 +410,7 @@ pub mod ext {
/// 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;
fn ext_pubkey(key: u64, written_out: *mut u32) -> *mut u8;
/// Create new key(pair) for signing/encryption/decryption.
///
@@ -418,7 +418,7 @@ pub mod ext {
///
/// - A crypto key id (if the value is less than u16::max_value)
/// - `u32::max_value` in case the crypto is not supported
fn ext_new_crypto_key(crypto: u32) -> u32;
fn ext_new_crypto_key(crypto: u32) -> u64;
/// Encrypt a piece of data using given crypto key.
///
@@ -430,8 +430,7 @@ pub mod ext {
/// - Otherwise, pointer to the encrypted message in memory,
/// `msg_len` contains the length of the message.
fn ext_encrypt(
key: u32,
kind: u32,
key: u64,
data: *const u8,
data_len: u32,
msg_len: *mut u32
@@ -448,8 +447,7 @@ pub mod ext {
/// - Otherwise, pointer to the decrypted message in memory,
/// `msg_len` contains the length of the message.
fn ext_decrypt(
key: u32,
kind: u32,
key: u64,
data: *const u8,
data_len: u32,
msg_len: *mut u32
@@ -466,8 +464,7 @@ pub mod ext {
/// - Otherwise, pointer to the signature in memory,
/// `sig_data_len` contains the length of the signature.
fn ext_sign(
key: u32,
kind: u32,
key: u64,
data: *const u8,
data_len: u32,
sig_data_len: *mut u32
@@ -482,8 +479,7 @@ pub mod ext {
/// - `1` in case it doesn't match the key
/// - `u32::max_value` if the key is invalid.
fn ext_verify(
key: u32,
kind: u32,
key: u64,
msg: *const u8,
msg_len: u32,
signature: *const u8,
@@ -929,13 +925,11 @@ impl OffchainApi for () {
}
}
fn authority_pubkey(kind: offchain::CryptoKind) -> Result<Vec<u8>, ()> {
let kind = kind as isize as u32;
fn pubkey(key: CryptoKey) -> Result<Vec<u8>, ()> {
let mut len = 0u32;
let raw_result = unsafe {
let ptr = ext_authority_pubkey.get()(
kind,
let ptr = ext_pubkey.get()(
key.into(),
&mut len,
);
@@ -948,76 +942,73 @@ impl OffchainApi for () {
}
}
fn new_crypto_key(crypto: offchain::CryptoKind) -> Result<offchain::CryptoKeyId, ()> {
fn new_crypto_key(crypto: offchain::CryptoKind) -> Result<offchain::CryptoKey, ()> {
let crypto = crypto.into();
let ret = unsafe {
ext_new_crypto_key.get()(crypto)
};
if ret > u16::max_value() as u32 {
Err(())
} else {
Ok(offchain::CryptoKeyId(ret as u16))
}
offchain::CryptoKey::try_from(ret)
}
fn encrypt(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
data: &[u8],
) -> Result<Vec<u8>, ()> {
let key = key.map(Into::into).unwrap_or(0);
let kind = kind.into();
let mut len = 0_u32;
unsafe {
let ptr = ext_encrypt.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len);
let ptr = ext_encrypt.get()(
key.into(),
data.as_ptr(),
data.len() as u32,
&mut len
);
from_raw_parts(ptr, len).ok_or(())
}
}
fn decrypt(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
data: &[u8],
) -> Result<Vec<u8>, ()> {
let key = key.map(Into::into).unwrap_or(0);
let kind = kind.into();
let mut len = 0_u32;
unsafe {
let ptr = ext_decrypt.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len);
let ptr = ext_decrypt.get()(
key.into(),
data.as_ptr(),
data.len() as u32,
&mut len
);
from_raw_parts(ptr, len).ok_or(())
}
}
fn sign(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
data: &[u8],
) -> Result<Vec<u8>, ()> {
let key = key.map(Into::into).unwrap_or(0);
let kind = kind.into();
let mut len = 0_u32;
unsafe {
let ptr = ext_sign.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len);
let ptr = ext_sign.get()(
key.into(),
data.as_ptr(),
data.len() as u32,
&mut len
);
from_raw_parts(ptr, len).ok_or(())
}
}
fn verify(
key: Option<offchain::CryptoKeyId>,
kind: offchain::CryptoKind,
key: offchain::CryptoKey,
msg: &[u8],
signature: &[u8],
) -> Result<bool, ()> {
let key = key.map(Into::into).unwrap_or(0);
let kind = kind.into();
let val = unsafe {
ext_verify.get()(
key,
kind,
key.into(),
msg.as_ptr(),
msg.len() as u32,
signature.as_ptr(),
+7 -11
View File
@@ -246,9 +246,9 @@ impl offchain::Externalities for NeverOffchainExt {
unreachable!()
}
fn authority_pubkey(
fn pubkey(
&self,
_crypto: offchain::CryptoKind,
_key: offchain::CryptoKey,
) -> Result<Vec<u8>, ()> {
unreachable!()
}
@@ -256,14 +256,13 @@ impl offchain::Externalities for NeverOffchainExt {
fn new_crypto_key(
&mut self,
_crypto: offchain::CryptoKind,
) -> Result<offchain::CryptoKeyId, ()> {
) -> Result<offchain::CryptoKey, ()> {
unreachable!()
}
fn encrypt(
&mut self,
_key: Option<offchain::CryptoKeyId>,
_kind: offchain::CryptoKind,
_key: offchain::CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unreachable!()
@@ -271,8 +270,7 @@ impl offchain::Externalities for NeverOffchainExt {
fn decrypt(
&mut self,
_key: Option<offchain::CryptoKeyId>,
_kind: offchain::CryptoKind,
_key: offchain::CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unreachable!()
@@ -280,8 +278,7 @@ impl offchain::Externalities for NeverOffchainExt {
fn sign(
&mut self,
_key: Option<offchain::CryptoKeyId>,
_kind: offchain::CryptoKind,
_key: offchain::CryptoKey,
_data: &[u8],
) -> Result<Vec<u8>, ()> {
unreachable!()
@@ -289,8 +286,7 @@ impl offchain::Externalities for NeverOffchainExt {
fn verify(
&mut self,
_key: Option<offchain::CryptoKeyId>,
_kind: offchain::CryptoKind,
_key: offchain::CryptoKey,
_msg: &[u8],
_signature: &[u8],
) -> Result<bool, ()> {
+3 -1
View File
@@ -43,6 +43,8 @@ construct_simple_protocol! {
construct_service_factory! {
struct Factory {
Block = Block,
ConsensusPair = Pair,
FinalityPair = Pair,
RuntimeApi = RuntimeApi,
NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) },
RuntimeDispatch = Executor,
@@ -66,7 +68,7 @@ construct_service_factory! {
},
AuthoritySetup = {
|service: Self::FullService| {
if let Some(key) = service.authority_key::<Pair>() {
if let Some(key) = service.authority_key() {
info!("Using authority key {}", key.public());
let proposer = Arc::new(ProposerFactory {
client: service.client(),
+5 -2
View File
@@ -26,6 +26,7 @@ use client::{self, LongestChain};
use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
use node_executor;
use primitives::Pair;
use grandpa_primitives::AuthorityPair as GrandpaPair;
use futures::prelude::*;
use node_primitives::{AuraPair, Block};
use node_runtime::{GenesisConfig, RuntimeApi};
@@ -66,6 +67,8 @@ impl<F> Default for NodeConfig<F> where F: substrate_service::ServiceFactory {
construct_service_factory! {
struct Factory {
Block = Block,
ConsensusPair = AuraPair,
FinalityPair = GrandpaPair,
RuntimeApi = RuntimeApi,
NetworkProtocol = NodeProtocol { |config| Ok(NodeProtocol::new()) },
RuntimeDispatch = node_executor::Executor,
@@ -83,7 +86,7 @@ construct_service_factory! {
let (block_import, link_half) = service.config.custom.grandpa_import_setup.take()
.expect("Link Half and Block Import are present for Full Services or setup failed before. qed");
if let Some(aura_key) = service.authority_key::<AuraPair>() {
if let Some(aura_key) = service.authority_key() {
info!("Using aura key {}", aura_key.public());
let proposer = Arc::new(substrate_basic_authorship::ProposerFactory {
@@ -113,7 +116,7 @@ construct_service_factory! {
let grandpa_key = if service.config.disable_grandpa {
None
} else {
service.authority_key::<grandpa_primitives::AuthorityPair>()
service.fg_authority_key()
};
let config = grandpa::Config {
+2 -2
View File
@@ -74,8 +74,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 115,
impl_version: 115,
spec_version: 116,
impl_version: 116,
apis: RUNTIME_API_VERSIONS,
};
+3 -12
View File
@@ -69,8 +69,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
use substrate_primitives::{
crypto::TypedKey, offchain::CryptoKind,
crypto::key_types,
crypto::TypedKey, offchain::CryptoKey,
offchain::OpaqueNetworkState,
offchain::StorageKind,
sr25519, ed25519,
@@ -113,7 +112,6 @@ enum OffchainErr {
FailedSigning,
NetworkState,
SubmitTransaction,
UnknownCryptoKind,
}
impl Printable for OffchainErr {
@@ -125,7 +123,6 @@ impl Printable for OffchainErr {
OffchainErr::FailedSigning => print("Offchain error: signing failed!"),
OffchainErr::NetworkState => print("Offchain error: fetching network state failed!"),
OffchainErr::SubmitTransaction => print("Offchain error: submitting transaction failed!"),
OffchainErr::UnknownCryptoKind => print("Offchain error: the CryptoKind is unknown!"),
}
}
}
@@ -216,14 +213,8 @@ decl_module! {
// Runs after every block.
fn offchain_worker(now: T::BlockNumber) {
fn gossip_at<T: Trait>(block_number: T::BlockNumber) -> Result<(), OffchainErr> {
let kind = match <T::AuthorityId as TypedKey>::KEY_TYPE {
key_types::SR25519 => CryptoKind::Sr25519,
key_types::ED25519 => CryptoKind::Ed25519,
_ => return Err(OffchainErr::UnknownCryptoKind),
};
// we run only when a local authority key is configured
if let Ok(key) = sr_io::authority_pubkey(kind) {
if let Ok(key) = sr_io::pubkey(CryptoKey::AuthorityKey) {
let authority_id = <T as Trait>::AuthorityId::decode(&mut &key[..])
.ok_or(OffchainErr::DecodeAuthorityId)?;
let network_state =
@@ -235,7 +226,7 @@ decl_module! {
authority_id,
};
let signature = sr_io::sign(None, kind, &heartbeat_data.encode())
let signature = sr_io::sign(CryptoKey::AuthorityKey, &heartbeat_data.encode())
.map_err(|_| OffchainErr::FailedSigning)?;
let call = Call::heartbeat(heartbeat_data, signature);
let ex = T::UncheckedExtrinsic::new_unsigned(call.into())