mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 17:01:09 +00:00
[offchain] Support for sign & verify for crypto keys (#3023)
* Implement sign & verify. * Use phrases and password. * Sign & verify with authority keys. * Fix tests. * WiP * WiP * Allow the caller to decide on 'CryptoKind'. * Remove TODO. * Make seed private back. * Fix non-std build and bump version. * Use Into<u32> instead of asses. * Add missing typedef.
This commit is contained in:
committed by
Gavin Wood
parent
ed630e5eda
commit
e729dbabbe
Generated
+1
@@ -4544,6 +4544,7 @@ dependencies = [
|
||||
"tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"twox-hash 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"wasmi 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"zeroize 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -376,7 +376,7 @@ where
|
||||
let spec = load_spec(&cli.shared_params, spec_factory)?;
|
||||
let mut config = service::Configuration::default_with_spec(spec.clone());
|
||||
if cli.interactive_password {
|
||||
config.password = input_keystore_password()?
|
||||
config.password = input_keystore_password()?.into()
|
||||
}
|
||||
|
||||
config.impl_name = impl_name;
|
||||
|
||||
@@ -79,7 +79,7 @@ impl client::backend::OffchainStorage for LocalStorage {
|
||||
&mut self,
|
||||
prefix: &[u8],
|
||||
item_key: &[u8],
|
||||
old_value: &[u8],
|
||||
old_value: Option<&[u8]>,
|
||||
new_value: &[u8],
|
||||
) -> bool {
|
||||
let key: Vec<u8> = prefix.iter().chain(item_key).cloned().collect();
|
||||
@@ -91,11 +91,10 @@ impl client::backend::OffchainStorage for LocalStorage {
|
||||
let is_set;
|
||||
{
|
||||
let _key_guard = key_lock.lock();
|
||||
is_set = self.db.get(columns::OFFCHAIN, &key)
|
||||
let val = self.db.get(columns::OFFCHAIN, &key)
|
||||
.ok()
|
||||
.and_then(|x| x)
|
||||
.map(|v| &*v == old_value)
|
||||
.unwrap_or(true);
|
||||
.and_then(|x| x);
|
||||
is_set = val.as_ref().map(|x| &**x) == old_value;
|
||||
|
||||
if is_set {
|
||||
self.set(prefix, item_key, new_value)
|
||||
@@ -130,8 +129,20 @@ mod tests {
|
||||
storage.set(prefix, key, value);
|
||||
assert_eq!(storage.get(prefix, key), Some(value.to_vec()));
|
||||
|
||||
assert_eq!(storage.compare_and_set(prefix, key, value, b"asd"), true);
|
||||
assert_eq!(storage.compare_and_set(prefix, key, Some(value), b"asd"), true);
|
||||
assert_eq!(storage.get(prefix, key), Some(b"asd".to_vec()));
|
||||
assert!(storage.locks.lock().is_empty(), "Locks map should be empty!");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_compare_and_set_on_empty_field() {
|
||||
let mut storage = LocalStorage::new_test();
|
||||
let prefix = b"prefix";
|
||||
let key = b"key";
|
||||
|
||||
assert_eq!(storage.compare_and_set(prefix, key, None, b"asd"), true);
|
||||
assert_eq!(storage.get(prefix, key), Some(b"asd".to_vec()));
|
||||
assert!(storage.locks.lock().is_empty(), "Locks map should be empty!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ pub trait OffchainStorage: Clone + Send + Sync {
|
||||
&mut self,
|
||||
prefix: &[u8],
|
||||
key: &[u8],
|
||||
old_value: &[u8],
|
||||
old_value: Option<&[u8]>,
|
||||
new_value: &[u8],
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
@@ -794,19 +794,23 @@ impl backend::OffchainStorage for OffchainStorage {
|
||||
&mut self,
|
||||
prefix: &[u8],
|
||||
key: &[u8],
|
||||
old_value: &[u8],
|
||||
old_value: Option<&[u8]>,
|
||||
new_value: &[u8],
|
||||
) -> bool {
|
||||
use std::collections::hash_map::Entry;
|
||||
let key = prefix.iter().chain(key).cloned().collect();
|
||||
|
||||
let mut set = false;
|
||||
self.storage.entry(key).and_modify(|val| {
|
||||
if val.as_slice() == old_value {
|
||||
*val = new_value.to_vec();
|
||||
set = true;
|
||||
}
|
||||
});
|
||||
set
|
||||
match self.storage.entry(key) {
|
||||
Entry::Vacant(entry) => if old_value.is_none() {
|
||||
entry.insert(new_value.to_vec());
|
||||
true
|
||||
} else { false },
|
||||
Entry::Occupied(ref mut entry) if Some(entry.get().as_slice()) == old_value => {
|
||||
entry.insert(new_value.to_vec());
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -845,9 +849,13 @@ mod tests {
|
||||
assert_eq!(storage.get(b"A", b"B"), Some(b"C".to_vec()));
|
||||
assert_eq!(storage.get(b"B", b"A"), None);
|
||||
|
||||
storage.compare_and_set(b"A", b"B", b"X", b"D");
|
||||
storage.compare_and_set(b"A", b"B", Some(b"X"), b"D");
|
||||
assert_eq!(storage.get(b"A", b"B"), Some(b"C".to_vec()));
|
||||
storage.compare_and_set(b"A", b"B", b"C", b"D");
|
||||
storage.compare_and_set(b"A", b"B", Some(b"C"), b"D");
|
||||
assert_eq!(storage.get(b"A", b"B"), Some(b"D".to_vec()));
|
||||
|
||||
assert!(!storage.compare_and_set(b"B", b"A", Some(b""), b"Y"));
|
||||
assert!(storage.compare_and_set(b"B", b"A", None, b"X"));
|
||||
assert_eq!(storage.get(b"B", b"A"), Some(b"X".to_vec()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,18 +729,26 @@ 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.0 as u32),
|
||||
Ok(key_id) => Ok(key_id.into()),
|
||||
Err(()) => Ok(u32::max_value()),
|
||||
}
|
||||
},
|
||||
ext_encrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8 => {
|
||||
ext_encrypt(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
data: *const u8,
|
||||
data_len: u32,
|
||||
msg_len: *mut u32
|
||||
) -> *mut u8 => {
|
||||
let key = u32_to_key(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, &*message))
|
||||
.map(|api| api.encrypt(key, kind, &*message))
|
||||
.ok_or_else(|| "Calling unavailable API ext_encrypt: wasm")?;
|
||||
|
||||
let (offset,len) = match res {
|
||||
@@ -759,14 +767,22 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
|
||||
Ok(offset)
|
||||
},
|
||||
ext_decrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8 => {
|
||||
ext_decrypt(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
data: *const u8,
|
||||
data_len: u32,
|
||||
msg_len: *mut u32
|
||||
) -> *mut u8 => {
|
||||
let key = u32_to_key(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, &*message))
|
||||
.map(|api| api.decrypt(key, kind, &*message))
|
||||
.ok_or_else(|| "Calling unavailable API ext_decrypt: wasm")?;
|
||||
|
||||
let (offset,len) = match res {
|
||||
@@ -785,14 +801,22 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
|
||||
Ok(offset)
|
||||
},
|
||||
ext_sign(key: u32, data: *const u8, data_len: u32, sig_data_len: *mut u32) -> *mut u8 => {
|
||||
ext_sign(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
data: *const u8,
|
||||
data_len: u32,
|
||||
sig_data_len: *mut u32
|
||||
) -> *mut u8 => {
|
||||
let key = u32_to_key(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, &*message))
|
||||
.map(|api| api.sign(key, kind, &*message))
|
||||
.ok_or_else(|| "Calling unavailable API ext_sign: wasm")?;
|
||||
|
||||
let (offset,len) = match res {
|
||||
@@ -813,6 +837,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
},
|
||||
ext_verify(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
msg: *const u8,
|
||||
msg_len: u32,
|
||||
signature: *const u8,
|
||||
@@ -820,13 +845,15 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
) -> u32 => {
|
||||
let key = u32_to_key(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, &*message, &*signature))
|
||||
.map(|api| api.verify(key, kind, &*message, &*signature))
|
||||
.ok_or_else(|| "Calling unavailable API ext_verify: wasm")?;
|
||||
|
||||
match res {
|
||||
@@ -945,7 +972,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
.ok_or_else(|| "Calling unavailable API ext_http_request_start: wasm")?;
|
||||
|
||||
if let Ok(id) = id {
|
||||
Ok(id.0 as u32)
|
||||
Ok(id.into())
|
||||
} else {
|
||||
Ok(u32::max_value())
|
||||
}
|
||||
@@ -996,7 +1023,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
|
||||
Ok(match res {
|
||||
Ok(()) => 0,
|
||||
Err(e) => e as u8 as u32,
|
||||
Err(e) => e.into(),
|
||||
})
|
||||
},
|
||||
ext_http_response_wait(
|
||||
@@ -1074,7 +1101,7 @@ impl_function_executor!(this: FunctionExecutor<'e, E>,
|
||||
read as u32
|
||||
},
|
||||
Err(err) => {
|
||||
u32::max_value() - err as u8 as u32 + 1
|
||||
u32::max_value() - u32::from(err) + 1
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
@@ -23,7 +23,7 @@ use std::path::PathBuf;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Write};
|
||||
|
||||
use substrate_primitives::crypto::{KeyTypeId, Pair, Public, TypedKey};
|
||||
use substrate_primitives::crypto::{KeyTypeId, Pair, Public};
|
||||
|
||||
/// Keystore error.
|
||||
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||
@@ -69,7 +69,7 @@ impl Store {
|
||||
Ok(Store { path, additional: HashMap::new() })
|
||||
}
|
||||
|
||||
fn get_pair<TPair: Pair + TypedKey>(&self, public: &TPair::Public) -> Result<Option<TPair>> {
|
||||
fn get_pair<TPair: Pair>(&self, public: &TPair::Public) -> Result<Option<TPair>> {
|
||||
let key = (TPair::KEY_TYPE, public.to_raw_vec());
|
||||
if let Some(bytes) = self.additional.get(&key) {
|
||||
let pair = TPair::from_seed_slice(bytes)
|
||||
@@ -79,13 +79,13 @@ impl Store {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn insert_pair<TPair: Pair + TypedKey>(&mut self, pair: &TPair) {
|
||||
fn insert_pair<TPair: Pair>(&mut self, pair: &TPair) {
|
||||
let key = (TPair::KEY_TYPE, pair.public().to_raw_vec());
|
||||
self.additional.insert(key, pair.to_raw_vec());
|
||||
}
|
||||
|
||||
/// Generate a new key, placing it into the store.
|
||||
pub fn generate<TPair: Pair + TypedKey>(&self, password: &str) -> Result<TPair> {
|
||||
pub fn generate<TPair: Pair>(&self, password: &str) -> Result<TPair> {
|
||||
let (pair, phrase, _) = TPair::generate_with_phrase(Some(password));
|
||||
let mut file = File::create(self.key_file_path::<TPair>(&pair.public()))?;
|
||||
::serde_json::to_writer(&file, &phrase)?;
|
||||
@@ -94,7 +94,7 @@ impl Store {
|
||||
}
|
||||
|
||||
/// Create a new key from seed. Do not place it into the store.
|
||||
pub fn generate_from_seed<TPair: Pair + TypedKey>(&mut self, seed: &str) -> Result<TPair> {
|
||||
pub fn generate_from_seed<TPair: Pair>(&mut self, seed: &str) -> Result<TPair> {
|
||||
let pair = TPair::from_string(seed, None)
|
||||
.ok().ok_or(Error::InvalidSeed)?;
|
||||
self.insert_pair(&pair);
|
||||
@@ -102,7 +102,7 @@ impl Store {
|
||||
}
|
||||
|
||||
/// Load a key file with given public key.
|
||||
pub fn load<TPair: Pair + TypedKey>(&self, public: &TPair::Public, password: &str) -> Result<TPair> {
|
||||
pub fn load<TPair: Pair>(&self, public: &TPair::Public, password: &str) -> Result<TPair> {
|
||||
if let Some(pair) = self.get_pair(public)? {
|
||||
return Ok(pair)
|
||||
}
|
||||
@@ -120,7 +120,7 @@ impl Store {
|
||||
}
|
||||
|
||||
/// Get public keys of all stored keys.
|
||||
pub fn contents<TPublic: Public + TypedKey>(&self) -> Result<Vec<TPublic>> {
|
||||
pub fn contents<TPublic: Public>(&self) -> Result<Vec<TPublic>> {
|
||||
let mut public_keys: Vec<TPublic> = self.additional.keys()
|
||||
.filter_map(|(ty, public)| {
|
||||
if *ty != TPublic::KEY_TYPE {
|
||||
@@ -151,7 +151,7 @@ impl Store {
|
||||
Ok(public_keys)
|
||||
}
|
||||
|
||||
fn key_file_path<TPair: Pair + TypedKey>(&self, public: &TPair::Public) -> PathBuf {
|
||||
fn key_file_path<TPair: Pair>(&self, public: &TPair::Public) -> PathBuf {
|
||||
let mut buf = self.path.clone();
|
||||
let bytes: [u8; 4] = TPair::KEY_TYPE.to_le_bytes();
|
||||
let key_type = hex::encode(bytes);
|
||||
|
||||
@@ -16,15 +16,18 @@
|
||||
|
||||
use std::sync::Arc;
|
||||
use client::backend::OffchainStorage;
|
||||
use crate::AuthorityKeyProvider;
|
||||
use futures::{Stream, Future, sync::mpsc};
|
||||
use log::{info, debug, warn, error};
|
||||
use parity_codec::Decode;
|
||||
use parity_codec::{Encode, Decode};
|
||||
use primitives::offchain::{
|
||||
Timestamp, HttpRequestId, HttpRequestStatus, HttpError,
|
||||
Externalities as OffchainExt,
|
||||
CryptoKind, CryptoKeyId,
|
||||
StorageKind,
|
||||
};
|
||||
use primitives::crypto::{Pair, Protected};
|
||||
use primitives::{ed25519, sr25519};
|
||||
use runtime_primitives::{
|
||||
generic::BlockId,
|
||||
traits::{self, Extrinsic},
|
||||
@@ -36,12 +39,26 @@ enum ExtMessage {
|
||||
SubmitExtrinsic(Vec<u8>),
|
||||
}
|
||||
|
||||
/// A persisted key seed.
|
||||
#[derive(Encode, Decode)]
|
||||
struct CryptoKey {
|
||||
kind: CryptoKind,
|
||||
phrase: String,
|
||||
}
|
||||
|
||||
enum Key {
|
||||
Sr25519(sr25519::Pair),
|
||||
Ed25519(ed25519::Pair),
|
||||
}
|
||||
|
||||
/// Asynchronous offchain API.
|
||||
///
|
||||
/// NOTE this is done to prevent recursive calls into the runtime (which are not supported currently).
|
||||
pub(crate) struct Api<S> {
|
||||
pub(crate) struct Api<Storage, KeyProvider> {
|
||||
sender: mpsc::UnboundedSender<ExtMessage>,
|
||||
db: S,
|
||||
db: Storage,
|
||||
keys_password: Protected<String>,
|
||||
key_provider: KeyProvider,
|
||||
}
|
||||
|
||||
fn unavailable_yet<R: Default>(name: &str) -> R {
|
||||
@@ -52,8 +69,60 @@ fn unavailable_yet<R: Default>(name: &str) -> R {
|
||||
|
||||
const LOCAL_DB: &str = "LOCAL (fork-aware) DB";
|
||||
const STORAGE_PREFIX: &[u8] = b"storage";
|
||||
const KEYS_PREFIX: &[u8] = b"keys";
|
||||
|
||||
impl<S: OffchainStorage> OffchainExt for Api<S> {
|
||||
const NEXT_ID: &[u8] = b"crypto_key_id";
|
||||
|
||||
impl<Storage, KeyProvider> Api<Storage, KeyProvider> where
|
||||
Storage: OffchainStorage,
|
||||
KeyProvider: AuthorityKeyProvider,
|
||||
{
|
||||
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 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(())
|
||||
}
|
||||
|
||||
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
|
||||
Storage: OffchainStorage,
|
||||
KeyProvider: AuthorityKeyProvider,
|
||||
{
|
||||
fn submit_transaction(&mut self, ext: Vec<u8>) -> Result<(), ()> {
|
||||
self.sender
|
||||
.unbounded_send(ExtMessage::SubmitExtrinsic(ext))
|
||||
@@ -61,29 +130,61 @@ impl<S: OffchainStorage> OffchainExt for Api<S> {
|
||||
.map_err(|_| ())
|
||||
}
|
||||
|
||||
fn new_crypto_key(&mut self, _crypto: CryptoKind) -> Result<CryptoKeyId, ()> {
|
||||
unavailable_yet::<()>("new_crypto_key");
|
||||
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
|
||||
},
|
||||
};
|
||||
|
||||
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());
|
||||
let new_id = encoded_slice.and_then(|mut x| u16::decode(&mut x)).unwrap_or_default()
|
||||
.checked_add(1)
|
||||
.ok_or(())?;
|
||||
let new_id_encoded = new_id.encode();
|
||||
|
||||
if self.db.compare_and_set(KEYS_PREFIX, NEXT_ID, encoded_slice, &new_id_encoded) {
|
||||
break (new_id, new_id_encoded);
|
||||
}
|
||||
};
|
||||
|
||||
self.db.set(KEYS_PREFIX, &id_encoded, &CryptoKey { phrase, kind } .encode());
|
||||
|
||||
Ok(CryptoKeyId(id))
|
||||
}
|
||||
|
||||
fn encrypt(&mut self, _key: Option<CryptoKeyId>, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn encrypt(&mut self, _key: Option<CryptoKeyId>, _kind: CryptoKind, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
unavailable_yet::<()>("encrypt");
|
||||
Err(())
|
||||
}
|
||||
|
||||
fn decrypt(&mut self, _key: Option<CryptoKeyId>, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn decrypt(&mut self, _key: Option<CryptoKeyId>, _kind: CryptoKind, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
unavailable_yet::<()>("decrypt");
|
||||
Err(())
|
||||
|
||||
}
|
||||
|
||||
fn sign(&mut self, _key: Option<CryptoKeyId>, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
unavailable_yet::<()>("sign");
|
||||
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 verify(&mut self, _key: Option<CryptoKeyId>, _msg: &[u8], _signature: &[u8]) -> Result<bool, ()> {
|
||||
unavailable_yet::<()>("verify");
|
||||
Err(())
|
||||
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 timestamp(&mut self) -> Timestamp {
|
||||
@@ -114,7 +215,7 @@ impl<S: OffchainStorage> OffchainExt for Api<S> {
|
||||
) -> bool {
|
||||
match kind {
|
||||
StorageKind::PERSISTENT => {
|
||||
self.db.compare_and_set(STORAGE_PREFIX, key, old_value, new_value)
|
||||
self.db.compare_and_set(STORAGE_PREFIX, key, Some(old_value), new_value)
|
||||
},
|
||||
StorageKind::LOCAL => unavailable_yet(LOCAL_DB),
|
||||
}
|
||||
@@ -195,16 +296,20 @@ 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>(
|
||||
pub fn new<S: OffchainStorage, P: AuthorityKeyProvider>(
|
||||
transaction_pool: Arc<Pool<A>>,
|
||||
db: S,
|
||||
keys_password: Protected<String>,
|
||||
key_provider: P,
|
||||
at: BlockId<A::Block>,
|
||||
) -> (Api<S>, AsyncApi<A>) {
|
||||
) -> (Api<S, P>, AsyncApi<A>) {
|
||||
let (sender, rx) = mpsc::unbounded();
|
||||
|
||||
let api = Api {
|
||||
sender,
|
||||
db,
|
||||
keys_password,
|
||||
key_provider,
|
||||
};
|
||||
|
||||
let async_api = AsyncApi {
|
||||
@@ -251,8 +356,9 @@ impl<A: ChainApi> AsyncApi<A> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use client_db::offchain::LocalStorage;
|
||||
use crate::tests::TestProvider;
|
||||
|
||||
fn offchain_api() -> (Api<LocalStorage>, AsyncApi<impl ChainApi>) {
|
||||
fn offchain_api() -> (Api<LocalStorage, TestProvider>, AsyncApi<impl ChainApi>) {
|
||||
let _ = env_logger::try_init();
|
||||
let db = LocalStorage::new_test();
|
||||
let client = Arc::new(test_client::new());
|
||||
@@ -260,7 +366,7 @@ mod tests {
|
||||
Pool::new(Default::default(), transaction_pool::ChainApi::new(client.clone()))
|
||||
);
|
||||
|
||||
AsyncApi::new(pool, db, BlockId::Number(0))
|
||||
AsyncApi::new(pool, db, "pass".to_owned().into(), TestProvider::default(), BlockId::Number(0))
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -294,4 +400,59 @@ mod tests {
|
||||
assert_eq!(api.local_storage_compare_and_set(kind, key, b"value", b"xxx"), true);
|
||||
assert_eq!(api.local_storage_get(kind, key), Some(b"xxx".to_vec()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_create_a_new_key_and_sign_and_verify_stuff() {
|
||||
let test = |kind: CryptoKind| {
|
||||
// given
|
||||
let mut api = offchain_api().0;
|
||||
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();
|
||||
|
||||
// then
|
||||
let res = api.verify(Some(key_id), kind, msg, &signature).unwrap();
|
||||
assert_eq!(res, true);
|
||||
let res = api.verify(Some(key_id), kind, msg, &[]).unwrap();
|
||||
assert_eq!(res, false);
|
||||
let res = api.verify(Some(key_id), kind, 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);
|
||||
test(CryptoKind::Sr25519);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_sign_and_verify_with_authority_key() {
|
||||
// given
|
||||
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();
|
||||
|
||||
// then
|
||||
let res = api.verify(None, kind, msg, &signature).unwrap();
|
||||
assert_eq!(res, true);
|
||||
let res = api.verify(None, kind, msg, &[]).unwrap();
|
||||
assert_eq!(res, false);
|
||||
let res = api.verify(None, kind, 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."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,10 @@ use std::{
|
||||
|
||||
use client::runtime_api::ApiExt;
|
||||
use log::{debug, warn};
|
||||
use primitives::ExecutionContext;
|
||||
use primitives::{
|
||||
ExecutionContext,
|
||||
crypto,
|
||||
};
|
||||
use runtime_primitives::{
|
||||
generic::BlockId,
|
||||
traits::{self, ProvideRuntimeApi},
|
||||
@@ -55,38 +58,71 @@ pub mod testing;
|
||||
|
||||
pub use offchain_primitives::OffchainWorkerApi;
|
||||
|
||||
/// Provides currently configured authority key.
|
||||
pub trait AuthorityKeyProvider: Clone + 'static {
|
||||
/// Returns currently configured authority key.
|
||||
fn authority_key<TPair: crypto::Pair>(&self) -> Option<TPair>;
|
||||
}
|
||||
|
||||
/// An offchain workers manager.
|
||||
pub struct OffchainWorkers<C, S, Block: traits::Block> {
|
||||
client: Arc<C>,
|
||||
db: S,
|
||||
pub struct OffchainWorkers<
|
||||
Client,
|
||||
Storage,
|
||||
KeyProvider,
|
||||
Block: traits::Block,
|
||||
> {
|
||||
client: Arc<Client>,
|
||||
db: Storage,
|
||||
authority_key: KeyProvider,
|
||||
keys_password: crypto::Protected<String>,
|
||||
_block: PhantomData<Block>,
|
||||
}
|
||||
|
||||
impl<C, S, Block: traits::Block> OffchainWorkers<C, S, Block> {
|
||||
impl<Client, Storage, KeyProvider, Block: traits::Block> OffchainWorkers<
|
||||
Client,
|
||||
Storage,
|
||||
KeyProvider,
|
||||
Block,
|
||||
> {
|
||||
/// Creates new `OffchainWorkers`.
|
||||
pub fn new(
|
||||
client: Arc<C>,
|
||||
db: S,
|
||||
client: Arc<Client>,
|
||||
db: Storage,
|
||||
authority_key: KeyProvider,
|
||||
keys_password: crypto::Protected<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
client,
|
||||
db,
|
||||
authority_key,
|
||||
keys_password,
|
||||
_block: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, S, Block: traits::Block> fmt::Debug for OffchainWorkers<C, S, Block> {
|
||||
impl<Client, Storage, KeyProvider, Block: traits::Block> fmt::Debug for OffchainWorkers<
|
||||
Client,
|
||||
Storage,
|
||||
KeyProvider,
|
||||
Block,
|
||||
> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_tuple("OffchainWorkers").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, S, Block> OffchainWorkers<C, S, Block> where
|
||||
impl<Client, Storage, KeyProvider, Block> OffchainWorkers<
|
||||
Client,
|
||||
Storage,
|
||||
KeyProvider,
|
||||
Block,
|
||||
> where
|
||||
Block: traits::Block,
|
||||
S: client::backend::OffchainStorage + 'static,
|
||||
C: ProvideRuntimeApi,
|
||||
C::Api: OffchainWorkerApi<Block>,
|
||||
Client: ProvideRuntimeApi,
|
||||
Client::Api: OffchainWorkerApi<Block>,
|
||||
KeyProvider: AuthorityKeyProvider,
|
||||
Storage: client::backend::OffchainStorage + 'static,
|
||||
{
|
||||
/// Start the offchain workers after given block.
|
||||
#[must_use]
|
||||
@@ -106,6 +142,8 @@ impl<C, S, Block> OffchainWorkers<C, S, Block> where
|
||||
let (api, runner) = api::AsyncApi::new(
|
||||
pool.clone(),
|
||||
self.db.clone(),
|
||||
self.keys_password.clone(),
|
||||
self.authority_key.clone(),
|
||||
at.clone(),
|
||||
);
|
||||
debug!("Running offchain workers at {:?}", at);
|
||||
@@ -122,6 +160,23 @@ impl<C, S, Block> OffchainWorkers<C, S, Block> where
|
||||
mod tests {
|
||||
use super::*;
|
||||
use futures::Future;
|
||||
use primitives::{ed25519, sr25519, crypto::{TypedKey, Pair}};
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
pub(crate) struct TestProvider {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_call_into_runtime_and_produce_extrinsic() {
|
||||
@@ -133,7 +188,7 @@ mod tests {
|
||||
let db = client_db::offchain::LocalStorage::new_test();
|
||||
|
||||
// when
|
||||
let offchain = OffchainWorkers::new(client, db);
|
||||
let offchain = OffchainWorkers::new(client, db, TestProvider::default(), "".to_owned().into());
|
||||
runtime.executor().spawn(offchain.on_block_imported(&0u64, &pool));
|
||||
|
||||
// then
|
||||
|
||||
@@ -143,19 +143,40 @@ impl offchain::Externalities for TestOffchainExt {
|
||||
unimplemented!("not needed in tests so far")
|
||||
}
|
||||
|
||||
fn encrypt(&mut self, _key: Option<CryptoKeyId>, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn encrypt(
|
||||
&mut self,
|
||||
_key: Option<CryptoKeyId>,
|
||||
_kind: CryptoKind,
|
||||
_data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
unimplemented!("not needed in tests so far")
|
||||
}
|
||||
|
||||
fn decrypt(&mut self, _key: Option<CryptoKeyId>, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn decrypt(
|
||||
&mut self,
|
||||
_key: Option<CryptoKeyId>,
|
||||
_kind: CryptoKind,
|
||||
_data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
unimplemented!("not needed in tests so far")
|
||||
}
|
||||
|
||||
fn sign(&mut self, _key: Option<CryptoKeyId>, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn sign(
|
||||
&mut self,
|
||||
_key: Option<CryptoKeyId>,
|
||||
_kind: CryptoKind,
|
||||
_data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
unimplemented!("not needed in tests so far")
|
||||
}
|
||||
|
||||
fn verify(&mut self, _key: Option<CryptoKeyId>, _msg: &[u8], _signature: &[u8]) -> Result<bool, ()> {
|
||||
fn verify(
|
||||
&mut self,
|
||||
_key: Option<CryptoKeyId>,
|
||||
_kind: CryptoKind,
|
||||
_msg: &[u8],
|
||||
_signature: &[u8],
|
||||
) -> Result<bool, ()> {
|
||||
unimplemented!("not needed in tests so far")
|
||||
}
|
||||
|
||||
@@ -190,7 +211,7 @@ impl offchain::Externalities for TestOffchainExt {
|
||||
match kind {
|
||||
StorageKind::LOCAL => &mut state.local_storage,
|
||||
StorageKind::PERSISTENT => &mut state.persistent_storage,
|
||||
}.compare_and_set(b"", key, old_value, new_value)
|
||||
}.compare_and_set(b"", key, Some(old_value), new_value)
|
||||
}
|
||||
|
||||
fn local_storage_get(&mut self, kind: StorageKind, key: &[u8]) -> Option<Vec<u8>> {
|
||||
|
||||
@@ -27,6 +27,7 @@ tiny-bip39 = { version = "0.6.1", optional = true }
|
||||
hex = { version = "0.3", optional = true }
|
||||
regex = { version = "1.1", optional = true }
|
||||
num-traits = { version = "0.2", default-features = false }
|
||||
zeroize = { version = "0.9.2", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
substrate-serializer = { path = "../serializer" }
|
||||
@@ -72,4 +73,5 @@ std = [
|
||||
"schnorrkel",
|
||||
"regex",
|
||||
"num-traits/std",
|
||||
"zeroize/std"
|
||||
]
|
||||
|
||||
@@ -28,6 +28,7 @@ use regex::Regex;
|
||||
use base58::{FromBase58, ToBase58};
|
||||
#[cfg(feature = "std")]
|
||||
use std::hash::Hash;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// The root phrase for our publicly known keys.
|
||||
pub const DEV_PHRASE: &str = "bottom drive obey lake curtain smoke basket hold race lonely fit walk";
|
||||
@@ -66,6 +67,43 @@ impl<S, T: UncheckedFrom<S>> UncheckedInto<T> for S {
|
||||
}
|
||||
}
|
||||
|
||||
/// A store for sensitive data.
|
||||
///
|
||||
/// Calls `Zeroize::zeroize` upon `Drop`.
|
||||
#[derive(Clone)]
|
||||
pub struct Protected<T: Zeroize>(T);
|
||||
|
||||
impl<T: Zeroize> AsRef<T> for Protected<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl<T: Zeroize> std::fmt::Debug for Protected<T> {
|
||||
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(fmt, "<protected>")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zeroize> From<T> for Protected<T> {
|
||||
fn from(t: T) -> Self {
|
||||
Protected(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zeroize> Zeroize for Protected<T> {
|
||||
fn zeroize(&mut self) {
|
||||
self.0.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Zeroize> Drop for Protected<T> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize()
|
||||
}
|
||||
}
|
||||
|
||||
/// An error with the interpretation of a secret.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
#[cfg(feature = "std")]
|
||||
@@ -289,7 +327,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
|
||||
}
|
||||
|
||||
/// Trait suitable for typical cryptographic PKI key public type.
|
||||
pub trait Public: PartialEq + Eq {
|
||||
pub trait Public: TypedKey + PartialEq + Eq {
|
||||
/// 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
|
||||
@@ -308,9 +346,8 @@ pub trait Public: PartialEq + Eq {
|
||||
///
|
||||
/// For now it just specifies how to create a key from a phrase and derivation path.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Pair: Sized + 'static
|
||||
{
|
||||
/// TThe type which is used to encode a public key.
|
||||
pub trait Pair: TypedKey + Sized + 'static {
|
||||
/// The type which is used to encode a public key.
|
||||
type Public: Public + Hash;
|
||||
|
||||
/// The type used to (minimally) encode the data required to securely create
|
||||
@@ -319,7 +356,7 @@ pub trait Pair: Sized + 'static
|
||||
|
||||
/// The type used to represent a signature. Can be created from a key pair and a message
|
||||
/// and verified with the message and a public key.
|
||||
type Signature;
|
||||
type Signature: AsRef<[u8]>;
|
||||
|
||||
/// Error returned from the `derive` function.
|
||||
type DeriveError;
|
||||
@@ -476,7 +513,7 @@ mod tests {
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
struct TestPublic;
|
||||
impl Public for TestPublic {
|
||||
fn from_slice(bytes: &[u8]) -> Self {
|
||||
fn from_slice(_bytes: &[u8]) -> Self {
|
||||
Self
|
||||
}
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
@@ -486,10 +523,13 @@ mod tests {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
impl TypedKey for TestPublic {
|
||||
const KEY_TYPE: u32 = 4242;
|
||||
}
|
||||
impl Pair for TestPair {
|
||||
type Public = TestPublic;
|
||||
type Seed = [u8; 0];
|
||||
type Signature = ();
|
||||
type Signature = [u8; 0];
|
||||
type DeriveError = ();
|
||||
|
||||
fn generate() -> (Self, <Self as Pair>::Seed) { (TestPair::Generated, []) }
|
||||
@@ -510,7 +550,7 @@ mod tests {
|
||||
Err(())
|
||||
}
|
||||
fn from_seed(_seed: &<TestPair as Pair>::Seed) -> Self { TestPair::Seed(vec![]) }
|
||||
fn sign(&self, _message: &[u8]) -> Self::Signature { () }
|
||||
fn sign(&self, _message: &[u8]) -> Self::Signature { [] }
|
||||
fn verify<P: AsRef<Self::Public>, M: AsRef<[u8]>>(
|
||||
_sig: &Self::Signature,
|
||||
_message: M,
|
||||
@@ -542,6 +582,9 @@ mod tests {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
impl TypedKey for TestPair {
|
||||
const KEY_TYPE: u32 = 4242;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn interpret_std_seed_should_work() {
|
||||
|
||||
@@ -16,11 +16,13 @@
|
||||
|
||||
//! Offchain workers types
|
||||
|
||||
use crate::crypto;
|
||||
use parity_codec::{Encode, Decode};
|
||||
use rstd::prelude::{Vec, Box};
|
||||
use rstd::convert::TryFrom;
|
||||
|
||||
/// A type of supported crypto.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
#[repr(C)]
|
||||
pub enum StorageKind {
|
||||
@@ -50,15 +52,21 @@ impl TryFrom<u32> for StorageKind {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<StorageKind> for u32 {
|
||||
fn from(c: StorageKind) -> Self {
|
||||
c as u8 as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// A type of supported crypto.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
#[repr(C)]
|
||||
pub enum CryptoKind {
|
||||
/// SR25519 crypto (Schnorrkel)
|
||||
Sr25519 = 1,
|
||||
Sr25519 = crypto::key_types::SR25519 as isize,
|
||||
/// ED25519 crypto (Edwards)
|
||||
Ed25519 = 2,
|
||||
Ed25519 = crypto::key_types::ED25519 as isize,
|
||||
}
|
||||
|
||||
impl TryFrom<u32> for CryptoKind {
|
||||
@@ -66,23 +74,41 @@ impl TryFrom<u32> for CryptoKind {
|
||||
|
||||
fn try_from(kind: u32) -> Result<Self, Self::Error> {
|
||||
match kind {
|
||||
e if e == u32::from(CryptoKind::Sr25519 as u8) => Ok(CryptoKind::Sr25519),
|
||||
e if e == u32::from(CryptoKind::Ed25519 as u8) => Ok(CryptoKind::Ed25519),
|
||||
e if e == CryptoKind::Sr25519 as isize as u32 => Ok(CryptoKind::Sr25519),
|
||||
e if e == CryptoKind::Ed25519 as isize as u32 => Ok(CryptoKind::Ed25519),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CryptoKind> for u32 {
|
||||
fn from(c: CryptoKind) -> Self {
|
||||
c as isize as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque type for created crypto keys.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct CryptoKeyId(pub u16);
|
||||
|
||||
impl From<CryptoKeyId> for u32 {
|
||||
fn from(c: CryptoKeyId) -> Self {
|
||||
c.0 as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Opaque type for offchain http requests.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
pub struct HttpRequestId(pub u16);
|
||||
|
||||
impl From<HttpRequestId> for u32 {
|
||||
fn from(c: HttpRequestId) -> Self {
|
||||
c.0 as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// An error enum returned by some http methods.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
@@ -106,6 +132,12 @@ impl TryFrom<u32> for HttpError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HttpError> for u32 {
|
||||
fn from(c: HttpError) -> Self {
|
||||
c as u8 as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Status of the HTTP request
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "std", derive(Debug))]
|
||||
@@ -216,31 +248,34 @@ pub trait Externalities {
|
||||
|
||||
/// Encrypt a piece of data using given crypto key.
|
||||
///
|
||||
/// If `key` is `None`, it will attempt to use current authority key.
|
||||
/// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`.
|
||||
///
|
||||
/// Returns an error if `key` is not available or does not exist.
|
||||
fn encrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()>;
|
||||
/// 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>, ()>;
|
||||
|
||||
/// Decrypt a piece of data using given crypto key.
|
||||
///
|
||||
/// If `key` is `None`, it will attempt to use current authority key.
|
||||
/// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`.
|
||||
///
|
||||
/// Returns an error if data cannot be decrypted or the `key` is not available or does not exist.
|
||||
fn decrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()>;
|
||||
/// 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>, ()>;
|
||||
|
||||
/// Sign a piece of data using given crypto key.
|
||||
///
|
||||
/// If `key` is `None`, it will attempt to use current authority key.
|
||||
/// If `key` is `None`, it will attempt to use current authority key of `CryptoKind`.
|
||||
///
|
||||
/// Returns an error if `key` is not available or does not exist.
|
||||
fn sign(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()>;
|
||||
/// 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>, ()>;
|
||||
|
||||
/// 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(&mut self, key: Option<CryptoKeyId>, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
|
||||
/// lengths are incorrect or `CryptoKind` does not match.
|
||||
fn verify(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
|
||||
|
||||
/// Returns current UNIX timestamp (in millis)
|
||||
fn timestamp(&mut self) -> Timestamp;
|
||||
@@ -359,20 +394,20 @@ impl<T: Externalities + ?Sized> Externalities for Box<T> {
|
||||
(&mut **self).new_crypto_key(crypto)
|
||||
}
|
||||
|
||||
fn encrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
(&mut **self).encrypt(key, data)
|
||||
fn encrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
(&mut **self).encrypt(key, kind, data)
|
||||
}
|
||||
|
||||
fn decrypt(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
(&mut **self).decrypt(key, data)
|
||||
fn decrypt(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
(&mut **self).decrypt(key, kind, data)
|
||||
}
|
||||
|
||||
fn sign(&mut self, key: Option<CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
(&mut **self).sign(key, data)
|
||||
fn sign(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
(&mut **self).sign(key, kind, data)
|
||||
}
|
||||
|
||||
fn verify(&mut self, key: Option<CryptoKeyId>, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
|
||||
(&mut **self).verify(key, msg, signature)
|
||||
fn verify(&mut self, key: Option<CryptoKeyId>, kind: CryptoKind, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
|
||||
(&mut **self).verify(key, kind, msg, signature)
|
||||
}
|
||||
|
||||
fn timestamp(&mut self) -> Timestamp {
|
||||
|
||||
@@ -379,6 +379,7 @@ fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> Sec
|
||||
secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0.expand()
|
||||
}
|
||||
|
||||
/// The raw secret seed, which can be used to recreate the `Pair`.
|
||||
#[cfg(feature = "std")]
|
||||
type Seed = [u8; MINI_SECRET_KEY_LENGTH];
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use serde::{Serialize, de::DeserializeOwned};
|
||||
use crate::chain_spec::ChainSpec;
|
||||
use client_db;
|
||||
use client::{self, Client, runtime_api};
|
||||
use crate::{error, Service};
|
||||
use crate::{error, Service, AuthorityKeyProvider};
|
||||
use consensus_common::{import_queue::ImportQueue, SelectChain};
|
||||
use network::{self, OnDemand, FinalityProofProvider};
|
||||
use substrate_executor::{NativeExecutor, NativeExecutionDispatch};
|
||||
@@ -191,7 +191,7 @@ fn maintain_transaction_pool<Api, Backend, Block, Executor, PoolApi>(
|
||||
client: &Client<Backend, Executor, Block, Api>,
|
||||
transaction_pool: &TransactionPool<PoolApi>,
|
||||
) -> error::Result<()> where
|
||||
Block: BlockT<Hash = <Blake2Hasher as ::primitives::Hasher>::Out>,
|
||||
Block: BlockT<Hash = <Blake2Hasher as primitives::Hasher>::Out>,
|
||||
Backend: client::backend::Backend<Block, Blake2Hasher>,
|
||||
Client<Backend, Executor, Block, Api>: ProvideRuntimeApi,
|
||||
<Client<Backend, Executor, Block, Api> as ProvideRuntimeApi>::Api: runtime_api::TaggedTransactionQueue<Block>,
|
||||
@@ -231,6 +231,7 @@ pub trait OffchainWorker<C: Components> {
|
||||
offchain: &offchain::OffchainWorkers<
|
||||
ComponentClient<C>,
|
||||
ComponentOffchainStorage<C>,
|
||||
AuthorityKeyProvider,
|
||||
ComponentBlock<C>
|
||||
>,
|
||||
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
|
||||
@@ -246,6 +247,7 @@ impl<C: Components> OffchainWorker<Self> for C where
|
||||
offchain: &offchain::OffchainWorkers<
|
||||
ComponentClient<C>,
|
||||
ComponentOffchainStorage<C>,
|
||||
AuthorityKeyProvider,
|
||||
ComponentBlock<C>
|
||||
>,
|
||||
pool: &Arc<TransactionPool<C::TransactionPoolApi>>,
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
//! Service configuration.
|
||||
|
||||
use std::{path::PathBuf, net::SocketAddr};
|
||||
use transaction_pool;
|
||||
use crate::chain_spec::ChainSpec;
|
||||
pub use client::ExecutionStrategies;
|
||||
pub use client_db::PruningMode;
|
||||
pub use network::config::{ExtTransport, NetworkConfiguration, Roles};
|
||||
|
||||
use std::{path::PathBuf, net::SocketAddr};
|
||||
use transaction_pool;
|
||||
use crate::chain_spec::ChainSpec;
|
||||
use primitives::crypto::Protected;
|
||||
use runtime_primitives::BuildStorage;
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use target_info::Target;
|
||||
@@ -86,7 +88,7 @@ pub struct Configuration<C, G: Serialize + DeserializeOwned + BuildStorage> {
|
||||
/// Disable GRANDPA when running in validator mode
|
||||
pub disable_grandpa: bool,
|
||||
/// Node keystore's password
|
||||
pub password: String,
|
||||
pub password: Protected<String>,
|
||||
}
|
||||
|
||||
impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C, G> {
|
||||
@@ -120,7 +122,7 @@ impl<C: Default, G: Serialize + DeserializeOwned + BuildStorage> Configuration<C
|
||||
offchain_worker: Default::default(),
|
||||
force_authoring: false,
|
||||
disable_grandpa: false,
|
||||
password: "".to_string(),
|
||||
password: "".to_string().into(),
|
||||
};
|
||||
configuration.network.boot_nodes = configuration.chain_spec.boot_nodes().to_vec();
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ use keystore::Store as Keystore;
|
||||
use network::NetworkState;
|
||||
use log::{info, warn, debug, error};
|
||||
use parity_codec::{Encode, Decode};
|
||||
use primitives::{Pair, Public, crypto::TypedKey, ed25519};
|
||||
use primitives::{Pair, ed25519, crypto};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{Header, NumberFor, SaturatedConversion};
|
||||
use substrate_executor::NativeExecutor;
|
||||
@@ -74,14 +74,14 @@ const DEFAULT_PROTOCOL_ID: &str = "sup";
|
||||
/// Substrate service.
|
||||
pub struct Service<Components: components::Components> {
|
||||
client: Arc<ComponentClient<Components>>,
|
||||
select_chain: Option<<Components as components::Components>::SelectChain>,
|
||||
select_chain: Option<Components::SelectChain>,
|
||||
network: Arc<components::NetworkService<Components>>,
|
||||
/// Sinks to propagate network status updates.
|
||||
network_status_sinks: Arc<Mutex<Vec<mpsc::UnboundedSender<(
|
||||
NetworkStatus<ComponentBlock<Components>>, NetworkState
|
||||
)>>>>,
|
||||
transaction_pool: Arc<TransactionPool<Components::TransactionPoolApi>>,
|
||||
keystore: Option<Keystore>,
|
||||
keystore: AuthorityKeyProvider,
|
||||
exit: ::exit_future::Exit,
|
||||
signal: Option<Signal>,
|
||||
/// Sender for futures that must be spawned as background tasks.
|
||||
@@ -101,6 +101,7 @@ pub struct Service<Components: components::Components> {
|
||||
_offchain_workers: Option<Arc<offchain::OffchainWorkers<
|
||||
ComponentClient<Components>,
|
||||
ComponentOffchainStorage<Components>,
|
||||
AuthorityKeyProvider,
|
||||
ComponentBlock<Components>>
|
||||
>>,
|
||||
}
|
||||
@@ -192,7 +193,7 @@ impl<Components: components::Components> Service<Components> {
|
||||
public_key = match keystore.contents::<ed25519::Public>()?.get(0) {
|
||||
Some(public_key) => public_key.to_string(),
|
||||
None => {
|
||||
let key: ed25519::Pair = keystore.generate(&config.password)?;
|
||||
let key: ed25519::Pair = keystore.generate(&config.password.as_ref())?;
|
||||
let public_key = key.public();
|
||||
info!("Generated a new keypair: {:?}", public_key);
|
||||
public_key.to_string()
|
||||
@@ -259,6 +260,12 @@ impl<Components: components::Components> Service<Components> {
|
||||
let network = network_mut.service().clone();
|
||||
let network_status_sinks = Arc::new(Mutex::new(Vec::new()));
|
||||
|
||||
let keystore_authority_key = AuthorityKeyProvider {
|
||||
roles: config.roles,
|
||||
password: config.password.clone(),
|
||||
keystore: keystore.map(Arc::new),
|
||||
};
|
||||
|
||||
#[allow(deprecated)]
|
||||
let offchain_storage = client.backend().offchain_storage();
|
||||
let offchain_workers = match (config.offchain_worker, offchain_storage) {
|
||||
@@ -266,6 +273,8 @@ impl<Components: components::Components> Service<Components> {
|
||||
Some(Arc::new(offchain::OffchainWorkers::new(
|
||||
client.clone(),
|
||||
db,
|
||||
keystore_authority_key.clone(),
|
||||
config.password.clone(),
|
||||
)))
|
||||
},
|
||||
(true, None) => {
|
||||
@@ -469,7 +478,7 @@ impl<Components: components::Components> Service<Components> {
|
||||
to_spawn_tx,
|
||||
to_spawn_rx,
|
||||
to_poll: Vec::new(),
|
||||
keystore,
|
||||
keystore: keystore_authority_key,
|
||||
config,
|
||||
exit,
|
||||
rpc_handlers,
|
||||
@@ -481,23 +490,10 @@ impl<Components: components::Components> Service<Components> {
|
||||
}
|
||||
|
||||
/// give the authority key, if we are an authority and have a key
|
||||
pub fn authority_key<TPair>(&self) -> Option<TPair>
|
||||
where
|
||||
TPair: Pair + TypedKey,
|
||||
<TPair as Pair>::Public: Public + TypedKey,
|
||||
{
|
||||
if self.config.roles != Roles::AUTHORITY { return None }
|
||||
if let Some(keystore) = &self.keystore {
|
||||
if let Ok(Some(Ok(key))) = keystore.contents::<TPair::Public>().map(|keys| keys.get(0)
|
||||
.map(|k| keystore.load::<TPair>(k, &self.config.password)))
|
||||
{
|
||||
Some(key)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
pub fn authority_key<TPair: Pair>(&self) -> Option<TPair> {
|
||||
use offchain::AuthorityKeyProvider;
|
||||
|
||||
self.keystore.authority_key()
|
||||
}
|
||||
|
||||
/// return a shared instance of Telemetry (if enabled)
|
||||
@@ -870,6 +866,39 @@ impl<C: Components> network::TransactionPool<ComponentExHash<C>, ComponentBlock<
|
||||
}
|
||||
}
|
||||
|
||||
/// A provider of current authority key.
|
||||
#[derive(Clone)]
|
||||
pub struct AuthorityKeyProvider {
|
||||
roles: Roles,
|
||||
keystore: Option<Arc<Keystore>>,
|
||||
password: crypto::Protected<String>,
|
||||
}
|
||||
|
||||
impl offchain::AuthorityKeyProvider for AuthorityKeyProvider {
|
||||
fn authority_key<TPair: Pair>(&self) -> Option<TPair> {
|
||||
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)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructs a service factory with the given name that implements the `ServiceFactory` trait.
|
||||
/// The required parameters are required to be given in the exact order. Some parameters are followed
|
||||
/// by `{}` blocks. These blocks are required and used to initialize the given parameter.
|
||||
|
||||
@@ -194,7 +194,7 @@ fn node_config<F: ServiceFactory> (
|
||||
offchain_worker: false,
|
||||
force_authoring: false,
|
||||
disable_grandpa: false,
|
||||
password: "".to_string(),
|
||||
password: "".to_string().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -249,28 +249,33 @@ export_api! {
|
||||
/// 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>, data: &[u8]) -> Result<Vec<u8>, ()>;
|
||||
fn encrypt(key: Option<CryptoKeyId>, kind: CryptoKind, 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>, data: &[u8]) -> Result<Vec<u8>, ()>;
|
||||
fn decrypt(key: Option<CryptoKeyId>, kind: CryptoKind, 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>, data: &[u8]) -> Result<Vec<u8>, ()>;
|
||||
fn sign(key: Option<CryptoKeyId>, kind: CryptoKind, 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>, msg: &[u8], signature: &[u8]) -> Result<bool, ()>;
|
||||
fn verify(
|
||||
key: Option<CryptoKeyId>,
|
||||
kind: CryptoKind,
|
||||
msg: &[u8],
|
||||
signature: &[u8]
|
||||
) -> Result<bool, ()>;
|
||||
|
||||
/// Returns current UNIX timestamp (in millis)
|
||||
fn timestamp() -> Timestamp;
|
||||
|
||||
@@ -275,27 +275,44 @@ impl OffchainApi for () {
|
||||
}, "new_crypto_key can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
fn encrypt(key: Option<offchain::CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn encrypt(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
with_offchain(|ext| {
|
||||
ext.encrypt(key, data)
|
||||
ext.encrypt(key, kind, data)
|
||||
}, "encrypt can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
fn decrypt(key: Option<offchain::CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn decrypt(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
with_offchain(|ext| {
|
||||
ext.decrypt(key, data)
|
||||
ext.decrypt(key, kind, data)
|
||||
}, "decrypt can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
fn sign(key: Option<offchain::CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn sign(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
with_offchain(|ext| {
|
||||
ext.sign(key, data)
|
||||
ext.sign(key, kind, data)
|
||||
}, "sign can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
fn verify(key: Option<offchain::CryptoKeyId>, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
|
||||
fn verify(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
msg: &[u8],
|
||||
signature: &[u8],
|
||||
) -> Result<bool, ()> {
|
||||
with_offchain(|ext| {
|
||||
ext.verify(key, msg, signature)
|
||||
ext.verify(key, kind, msg, signature)
|
||||
}, "verify can be called only in the offchain worker context")
|
||||
}
|
||||
|
||||
|
||||
@@ -395,18 +395,24 @@ pub mod ext {
|
||||
|
||||
/// Encrypt a piece of data using given crypto key.
|
||||
///
|
||||
/// If `key` is `0`, it will attempt to use current authority key.
|
||||
/// If `key` is `0`, it will attempt to use current authority key of given `kind`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// - `0` in case the key is invalid, `msg_len` is set to `u32::max_value`
|
||||
/// - Otherwise, pointer to the encrypted message in memory,
|
||||
/// `msg_len` contains the length of the message.
|
||||
fn ext_encrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8;
|
||||
fn ext_encrypt(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
data: *const u8,
|
||||
data_len: u32,
|
||||
msg_len: *mut u32
|
||||
) -> *mut u8;
|
||||
|
||||
/// Decrypt a piece of data using given crypto key.
|
||||
///
|
||||
/// If `key `is `0`, it will attempt to use current authority key.
|
||||
/// If `key` is `0`, it will attempt to use current authority key of given `kind`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@@ -414,11 +420,17 @@ pub mod ext {
|
||||
/// `msg_len` is set to `u32::max_value`
|
||||
/// - Otherwise, pointer to the decrypted message in memory,
|
||||
/// `msg_len` contains the length of the message.
|
||||
fn ext_decrypt(key: u32, data: *const u8, data_len: u32, msg_len: *mut u32) -> *mut u8;
|
||||
fn ext_decrypt(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
data: *const u8,
|
||||
data_len: u32,
|
||||
msg_len: *mut u32
|
||||
) -> *mut u8;
|
||||
|
||||
/// Sign a piece of data using given crypto key.
|
||||
///
|
||||
/// If `key` is `0`, it will attempt to use current authority key.
|
||||
/// If `key` is `0`, it will attempt to use current authority key of given `kind`.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
@@ -426,11 +438,17 @@ pub mod ext {
|
||||
/// `sig_data_len` is set to `u32::max_value`
|
||||
/// - Otherwise, pointer to the signature in memory,
|
||||
/// `sig_data_len` contains the length of the signature.
|
||||
fn ext_sign(key: u32, data: *const u8, data_len: u32, sig_data_len: *mut u32) -> *mut u8;
|
||||
fn ext_sign(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
data: *const u8,
|
||||
data_len: u32,
|
||||
sig_data_len: *mut u32
|
||||
) -> *mut u8;
|
||||
|
||||
/// Verifies that `signature` for `msg` matches given `key`.
|
||||
///
|
||||
/// If `key` is `0`, it will attempt to use current authority key.
|
||||
/// If `key` is `0`, it will attempt to use current authority key of given `kind`.
|
||||
///
|
||||
/// # Returns
|
||||
/// - `0` in case the signature is correct
|
||||
@@ -438,6 +456,7 @@ pub mod ext {
|
||||
/// - `u32::max_value` if the key is invalid.
|
||||
fn ext_verify(
|
||||
key: u32,
|
||||
kind: u32,
|
||||
msg: *const u8,
|
||||
msg_len: u32,
|
||||
signature: *const u8,
|
||||
@@ -870,7 +889,7 @@ impl OffchainApi for () {
|
||||
}
|
||||
|
||||
fn new_crypto_key(crypto: offchain::CryptoKind) -> Result<offchain::CryptoKeyId, ()> {
|
||||
let crypto = crypto as u8 as u32;
|
||||
let crypto = crypto.into();
|
||||
let ret = unsafe {
|
||||
ext_new_crypto_key.get()(crypto)
|
||||
};
|
||||
@@ -882,41 +901,63 @@ impl OffchainApi for () {
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt(key: Option<offchain::CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
let key = key.map(|x| x.0 as u32).unwrap_or(0);
|
||||
fn encrypt(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
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, data.as_ptr(), data.len() as u32, &mut len);
|
||||
let ptr = ext_encrypt.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len);
|
||||
|
||||
from_raw_parts(ptr, len).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
fn decrypt(key: Option<offchain::CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
let key = key.map(|x| x.0 as u32).unwrap_or(0);
|
||||
fn decrypt(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
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, data.as_ptr(), data.len() as u32, &mut len);
|
||||
let ptr = ext_decrypt.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len);
|
||||
|
||||
from_raw_parts(ptr, len).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(key: Option<offchain::CryptoKeyId>, data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
let key = key.map(|x| x.0 as u32).unwrap_or(0);
|
||||
fn sign(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
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, data.as_ptr(), data.len() as u32, &mut len);
|
||||
let ptr = ext_sign.get()(key, kind, data.as_ptr(), data.len() as u32, &mut len);
|
||||
|
||||
from_raw_parts(ptr, len).ok_or(())
|
||||
}
|
||||
}
|
||||
|
||||
fn verify(key: Option<offchain::CryptoKeyId>, msg: &[u8], signature: &[u8]) -> Result<bool, ()> {
|
||||
let key = key.map(|x| x.0 as u32).unwrap_or(0);
|
||||
fn verify(
|
||||
key: Option<offchain::CryptoKeyId>,
|
||||
kind: offchain::CryptoKind,
|
||||
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,
|
||||
msg.as_ptr(),
|
||||
msg.len() as u32,
|
||||
signature.as_ptr(),
|
||||
@@ -954,7 +995,7 @@ impl OffchainApi for () {
|
||||
fn local_storage_set(kind: offchain::StorageKind, key: &[u8], value: &[u8]) {
|
||||
unsafe {
|
||||
ext_local_storage_set.get()(
|
||||
kind as u8 as u32,
|
||||
kind.into(),
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
value.as_ptr(),
|
||||
@@ -966,7 +1007,7 @@ impl OffchainApi for () {
|
||||
fn local_storage_compare_and_set(kind: offchain::StorageKind, key: &[u8], old_value: &[u8], new_value: &[u8]) -> bool {
|
||||
unsafe {
|
||||
ext_local_storage_compare_and_set.get()(
|
||||
kind as u8 as u32,
|
||||
kind.into(),
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
old_value.as_ptr(),
|
||||
@@ -981,7 +1022,7 @@ impl OffchainApi for () {
|
||||
let mut len = 0u32;
|
||||
unsafe {
|
||||
let ptr = ext_local_storage_get.get()(
|
||||
kind as u8 as u32,
|
||||
kind.into(),
|
||||
key.as_ptr(),
|
||||
key.len() as u32,
|
||||
&mut len,
|
||||
@@ -1019,7 +1060,7 @@ impl OffchainApi for () {
|
||||
|
||||
let result = unsafe {
|
||||
ext_http_request_add_header.get()(
|
||||
request_id.0 as u32,
|
||||
request_id.into(),
|
||||
name.as_ptr(),
|
||||
name.len() as u32,
|
||||
value.as_ptr(),
|
||||
@@ -1041,7 +1082,7 @@ impl OffchainApi for () {
|
||||
) -> Result<(), offchain::HttpError> {
|
||||
let res = unsafe {
|
||||
ext_http_request_write_body.get()(
|
||||
request_id.0 as u32,
|
||||
request_id.into(),
|
||||
chunk.as_ptr(),
|
||||
chunk.len() as u32,
|
||||
deadline.map_or(0, |x| x.unix_millis()),
|
||||
@@ -1084,7 +1125,7 @@ impl OffchainApi for () {
|
||||
let mut len = 0u32;
|
||||
let raw_result = unsafe {
|
||||
let ptr = ext_http_response_headers.get()(
|
||||
request_id.0 as u32,
|
||||
request_id.into(),
|
||||
&mut len,
|
||||
);
|
||||
|
||||
@@ -1101,7 +1142,7 @@ impl OffchainApi for () {
|
||||
) -> Result<usize, offchain::HttpError> {
|
||||
let res = unsafe {
|
||||
ext_http_response_read_body.get()(
|
||||
request_id.0 as u32,
|
||||
request_id.into(),
|
||||
buffer.as_mut_ptr(),
|
||||
buffer.len() as u32,
|
||||
deadline.map_or(0, |x| x.unix_millis()),
|
||||
|
||||
@@ -250,6 +250,7 @@ impl offchain::Externalities for NeverOffchainExt {
|
||||
fn encrypt(
|
||||
&mut self,
|
||||
_key: Option<offchain::CryptoKeyId>,
|
||||
_kind: offchain::CryptoKind,
|
||||
_data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
unreachable!()
|
||||
@@ -258,18 +259,25 @@ impl offchain::Externalities for NeverOffchainExt {
|
||||
fn decrypt(
|
||||
&mut self,
|
||||
_key: Option<offchain::CryptoKeyId>,
|
||||
_kind: offchain::CryptoKind,
|
||||
_data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn sign(&mut self, _key: Option<offchain::CryptoKeyId>, _data: &[u8]) -> Result<Vec<u8>, ()> {
|
||||
fn sign(
|
||||
&mut self,
|
||||
_key: Option<offchain::CryptoKeyId>,
|
||||
_kind: offchain::CryptoKind,
|
||||
_data: &[u8],
|
||||
) -> Result<Vec<u8>, ()> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn verify(
|
||||
&mut self,
|
||||
_key: Option<offchain::CryptoKeyId>,
|
||||
_kind: offchain::CryptoKind,
|
||||
_msg: &[u8],
|
||||
_signature: &[u8],
|
||||
) -> Result<bool, ()> {
|
||||
|
||||
Reference in New Issue
Block a user