mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 14:41:11 +00:00
Generic keystore (#3008)
* Add KeyTypeId. * Implement clone for sr25519::Pair. * Extend Pair with to_raw_vec. * Implement TypedKey for Signature and Pair. * Add trait Public. * Make keystore generic. * Fixup clone. * Fix tests. * Update service. * Fix imports. * Fix build. * Fix babe build. * Fix subkey build. * Make authority setup generic. * Update node-template. * Fix build. * Remove unsafe code. * Fix tests.
This commit is contained in:
Generated
+1
@@ -2222,6 +2222,7 @@ dependencies = [
|
||||
"substrate-consensus-aura-primitives 2.0.0",
|
||||
"substrate-consensus-common 2.0.0",
|
||||
"substrate-finality-grandpa 2.0.0",
|
||||
"substrate-finality-grandpa-primitives 2.0.0",
|
||||
"substrate-inherents 2.0.0",
|
||||
"substrate-keyring 2.0.0",
|
||||
"substrate-keystore 2.0.0",
|
||||
|
||||
@@ -45,7 +45,7 @@ use std::{sync::Arc, u64, fmt::{Debug, Display}, time::{Instant, Duration}};
|
||||
use runtime_support::serde::{Serialize, Deserialize};
|
||||
use parity_codec::{Decode, Encode};
|
||||
use parking_lot::Mutex;
|
||||
use primitives::{crypto::Pair, sr25519};
|
||||
use primitives::{Pair, Public, sr25519};
|
||||
use merlin::Transcript;
|
||||
use inherents::{InherentDataProviders, InherentData};
|
||||
use substrate_telemetry::{
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
use std::{collections::HashMap, ops::Deref};
|
||||
use lazy_static::lazy_static;
|
||||
use substrate_primitives::{ed25519::{Pair, Public, Signature}, Pair as PairT, H256};
|
||||
use substrate_primitives::{ed25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256};
|
||||
pub use substrate_primitives::ed25519;
|
||||
|
||||
/// Set of test accounts.
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::ops::Deref;
|
||||
use lazy_static::lazy_static;
|
||||
use substrate_primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, H256};
|
||||
use substrate_primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256};
|
||||
pub use substrate_primitives::sr25519;
|
||||
|
||||
/// Set of test accounts.
|
||||
@@ -68,7 +68,7 @@ impl Keyring {
|
||||
}
|
||||
|
||||
pub fn to_raw_public_vec(self) -> Vec<u8> {
|
||||
Public::from(self).into_raw_vec()
|
||||
Public::from(self).to_raw_vec()
|
||||
}
|
||||
|
||||
pub fn sign(self, msg: &[u8]) -> Signature {
|
||||
|
||||
@@ -23,7 +23,7 @@ use std::path::PathBuf;
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Write};
|
||||
|
||||
use substrate_primitives::{ed25519::{Pair, Public}, Pair as PairT};
|
||||
use substrate_primitives::crypto::{KeyTypeId, Pair, Public, TypedKey};
|
||||
|
||||
/// Keystore error.
|
||||
#[derive(Debug, derive_more::Display, derive_more::From)]
|
||||
@@ -59,7 +59,7 @@ impl std::error::Error for Error {
|
||||
/// Key store.
|
||||
pub struct Store {
|
||||
path: PathBuf,
|
||||
additional: HashMap<Public, Pair>,
|
||||
additional: HashMap<(KeyTypeId, Vec<u8>), Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Store {
|
||||
@@ -69,33 +69,49 @@ impl Store {
|
||||
Ok(Store { path, additional: HashMap::new() })
|
||||
}
|
||||
|
||||
fn get_pair<TPair: Pair + TypedKey>(&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)
|
||||
.map_err(|_| Error::InvalidSeed)?;
|
||||
return Ok(Some(pair));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn insert_pair<TPair: Pair + TypedKey>(&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(&self, password: &str) -> Result<Pair> {
|
||||
let (pair, phrase, _) = Pair::generate_with_phrase(Some(password));
|
||||
let mut file = File::create(self.key_file_path(&pair.public()))?;
|
||||
pub fn generate<TPair: Pair + TypedKey>(&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)?;
|
||||
file.flush()?;
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
/// Create a new key from seed. Do not place it into the store.
|
||||
pub fn generate_from_seed(&mut self, seed: &str) -> Result<Pair> {
|
||||
let pair = Pair::from_string(seed, None)
|
||||
pub fn generate_from_seed<TPair: Pair + TypedKey>(&mut self, seed: &str) -> Result<TPair> {
|
||||
let pair = TPair::from_string(seed, None)
|
||||
.ok().ok_or(Error::InvalidSeed)?;
|
||||
self.additional.insert(pair.public(), pair.clone());
|
||||
self.insert_pair(&pair);
|
||||
Ok(pair)
|
||||
}
|
||||
|
||||
/// Load a key file with given public key.
|
||||
pub fn load(&self, public: &Public, password: &str) -> Result<Pair> {
|
||||
if let Some(pair) = self.additional.get(public) {
|
||||
return Ok(pair.clone());
|
||||
pub fn load<TPair: Pair + TypedKey>(&self, public: &TPair::Public, password: &str) -> Result<TPair> {
|
||||
if let Some(pair) = self.get_pair(public)? {
|
||||
return Ok(pair)
|
||||
}
|
||||
let path = self.key_file_path(public);
|
||||
|
||||
let path = self.key_file_path::<TPair>(public);
|
||||
let file = File::open(path)?;
|
||||
|
||||
let phrase: String = ::serde_json::from_reader(&file)?;
|
||||
let (pair, _) = Pair::from_phrase(&phrase, Some(password))
|
||||
let (pair, _) = TPair::from_phrase(&phrase, Some(password))
|
||||
.ok().ok_or(Error::InvalidPhrase)?;
|
||||
if &pair.public() != public {
|
||||
return Err(Error::InvalidPassword);
|
||||
@@ -104,22 +120,28 @@ impl Store {
|
||||
}
|
||||
|
||||
/// Get public keys of all stored keys.
|
||||
pub fn contents(&self) -> Result<Vec<Public>> {
|
||||
let mut public_keys: Vec<Public> = self.additional.keys().cloned().collect();
|
||||
pub fn contents<TPublic: Public + TypedKey>(&self) -> Result<Vec<TPublic>> {
|
||||
let mut public_keys: Vec<TPublic> = self.additional.keys()
|
||||
.filter_map(|(ty, public)| {
|
||||
if *ty != TPublic::KEY_TYPE {
|
||||
return None
|
||||
}
|
||||
Some(TPublic::from_slice(public))
|
||||
})
|
||||
.collect();
|
||||
|
||||
let key_type: [u8; 4] = TPublic::KEY_TYPE.to_le_bytes();
|
||||
for entry in fs::read_dir(&self.path)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
// skip directories and non-unicode file names (hex is unicode)
|
||||
if let Some(name) = path.file_name().and_then(|n| n.to_str()) {
|
||||
if name.len() != 64 { continue }
|
||||
|
||||
match hex::decode(name) {
|
||||
Ok(ref hex) if hex.len() == 32 => {
|
||||
let mut buf = [0; 32];
|
||||
buf.copy_from_slice(&hex[..]);
|
||||
|
||||
public_keys.push(Public(buf));
|
||||
Ok(ref hex) => {
|
||||
if hex[0..4] != key_type { continue }
|
||||
let public = TPublic::from_slice(&hex[4..]);
|
||||
public_keys.push(public);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
@@ -129,9 +151,12 @@ impl Store {
|
||||
Ok(public_keys)
|
||||
}
|
||||
|
||||
fn key_file_path(&self, public: &Public) -> PathBuf {
|
||||
fn key_file_path<TPair: Pair + TypedKey>(&self, public: &TPair::Public) -> PathBuf {
|
||||
let mut buf = self.path.clone();
|
||||
buf.push(hex::encode(public.as_slice()));
|
||||
let bytes: [u8; 4] = TPair::KEY_TYPE.to_le_bytes();
|
||||
let key_type = hex::encode(bytes);
|
||||
let key = hex::encode(public.as_slice());
|
||||
buf.push(key_type + key.as_str());
|
||||
buf
|
||||
}
|
||||
}
|
||||
@@ -140,6 +165,7 @@ impl Store {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use tempdir::TempDir;
|
||||
use substrate_primitives::ed25519;
|
||||
use substrate_primitives::crypto::Ss58Codec;
|
||||
|
||||
#[test]
|
||||
@@ -147,16 +173,16 @@ mod tests {
|
||||
let temp_dir = TempDir::new("keystore").unwrap();
|
||||
let store = Store::open(temp_dir.path().to_owned()).unwrap();
|
||||
|
||||
assert!(store.contents().unwrap().is_empty());
|
||||
assert!(store.contents::<ed25519::Public>().unwrap().is_empty());
|
||||
|
||||
let key = store.generate("thepassword").unwrap();
|
||||
let key2 = store.load(&key.public(), "thepassword").unwrap();
|
||||
let key: ed25519::Pair = store.generate("thepassword").unwrap();
|
||||
let key2: ed25519::Pair = store.load(&key.public(), "thepassword").unwrap();
|
||||
|
||||
assert!(store.load(&key.public(), "notthepassword").is_err());
|
||||
assert!(store.load::<ed25519::Pair>(&key.public(), "notthepassword").is_err());
|
||||
|
||||
assert_eq!(key.public(), key2.public());
|
||||
|
||||
assert_eq!(store.contents().unwrap()[0], key.public());
|
||||
assert_eq!(store.contents::<ed25519::Public>().unwrap()[0], key.public());
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -164,7 +190,9 @@ mod tests {
|
||||
let temp_dir = TempDir::new("keystore").unwrap();
|
||||
let mut store = Store::open(temp_dir.path().to_owned()).unwrap();
|
||||
|
||||
let pair = store.generate_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc").unwrap();
|
||||
let pair: ed25519::Pair = store
|
||||
.generate_from_seed("0x3d97c819d68f9bafa7d6e79cb991eebcd77d966c5334c0b94d9e1fa7ad0869dc")
|
||||
.unwrap();
|
||||
assert_eq!("5DKUrgFqCPV8iAXx9sjy1nyBygQCeiUYRFWurZGhnrn3HJCA", pair.public().to_ss58check());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ use parity_codec::{Encode, Decode};
|
||||
use regex::Regex;
|
||||
#[cfg(feature = "std")]
|
||||
use base58::{FromBase58, ToBase58};
|
||||
#[cfg(feature = "std")]
|
||||
use std::hash::Hash;
|
||||
|
||||
/// 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";
|
||||
@@ -286,13 +288,30 @@ 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 {
|
||||
/// 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
|
||||
/// you are certain that the array actually is a pubkey. GIGO!
|
||||
fn from_slice(data: &[u8]) -> Self;
|
||||
|
||||
/// Return a `Vec<u8>` filled with raw data.
|
||||
#[cfg(feature = "std")]
|
||||
fn to_raw_vec(&self) -> Vec<u8>;
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
fn as_slice(&self) -> &[u8];
|
||||
}
|
||||
|
||||
/// Trait suitable for typical cryptographic PKI key pair type.
|
||||
///
|
||||
/// For now it just specifies how to create a key from a phrase and derivation path.
|
||||
#[cfg(feature = "std")]
|
||||
pub trait Pair: Sized + 'static {
|
||||
pub trait Pair: Sized + 'static
|
||||
{
|
||||
/// TThe type which is used to encode a public key.
|
||||
type Public;
|
||||
type Public: Public + Hash;
|
||||
|
||||
/// The type used to (minimally) encode the data required to securely create
|
||||
/// a new key pair.
|
||||
@@ -412,6 +431,31 @@ pub trait Pair: Sized + 'static {
|
||||
path,
|
||||
)
|
||||
}
|
||||
|
||||
/// Return a vec filled with raw data.
|
||||
fn to_raw_vec(&self) -> Vec<u8>;
|
||||
}
|
||||
|
||||
/// An identifier for a type of cryptographic key.
|
||||
///
|
||||
/// 0-1024 are reserved.
|
||||
pub type KeyTypeId = u32;
|
||||
|
||||
/// Constant key types.
|
||||
pub mod key_types {
|
||||
use super::KeyTypeId;
|
||||
|
||||
/// ED25519 public key.
|
||||
pub const ED25519: KeyTypeId = 10;
|
||||
|
||||
/// SR25519 public key.
|
||||
pub const SR25519: KeyTypeId = 20;
|
||||
}
|
||||
|
||||
/// A trait for something that has a key type ID.
|
||||
pub trait TypedKey {
|
||||
/// The type ID of this key.
|
||||
const KEY_TYPE: KeyTypeId;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -429,8 +473,21 @@ mod tests {
|
||||
Seed(Vec<u8>),
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
struct TestPublic;
|
||||
impl Public for TestPublic {
|
||||
fn from_slice(bytes: &[u8]) -> Self {
|
||||
Self
|
||||
}
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
&[]
|
||||
}
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
impl Pair for TestPair {
|
||||
type Public = ();
|
||||
type Public = TestPublic;
|
||||
type Seed = [u8; 0];
|
||||
type Signature = ();
|
||||
type DeriveError = ();
|
||||
@@ -464,7 +521,7 @@ mod tests {
|
||||
_message: M,
|
||||
_pubkey: P
|
||||
) -> bool { true }
|
||||
fn public(&self) -> Self::Public { () }
|
||||
fn public(&self) -> Self::Public { TestPublic }
|
||||
fn from_standard_components<I: Iterator<Item=DeriveJunction>>(
|
||||
phrase: &str,
|
||||
password: Option<&str>,
|
||||
@@ -481,6 +538,9 @@ mod tests {
|
||||
{
|
||||
Ok(TestPair::Seed(seed.to_owned()))
|
||||
}
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -32,7 +32,7 @@ use bip39::{Mnemonic, Language, MnemonicType};
|
||||
use crate::crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Derive, Ss58Codec};
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{de, Serializer, Serialize, Deserializer, Deserialize};
|
||||
use crate::crypto::UncheckedFrom;
|
||||
use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom};
|
||||
|
||||
/// A secret seed. It's not called a "secret key" because ring doesn't expose the secret keys
|
||||
/// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we
|
||||
@@ -282,16 +282,6 @@ impl Public {
|
||||
Public(data)
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// you are certain that the array actually is a pubkey. GIGO!
|
||||
pub fn from_slice(data: &[u8]) -> Self {
|
||||
let mut r = [0u8; 32];
|
||||
r.copy_from_slice(data);
|
||||
Public(r)
|
||||
}
|
||||
|
||||
/// A new instance from an H256.
|
||||
///
|
||||
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
|
||||
@@ -300,23 +290,35 @@ impl Public {
|
||||
Public(x.into())
|
||||
}
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
pub fn as_array_ref(&self) -> &[u8; 32] {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl TraitPublic for Public {
|
||||
/// 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
|
||||
/// you are certain that the array actually is a pubkey. GIGO!
|
||||
fn from_slice(data: &[u8]) -> Self {
|
||||
let mut r = [0u8; 32];
|
||||
r.copy_from_slice(data);
|
||||
Public(r)
|
||||
}
|
||||
|
||||
/// Return a `Vec<u8>` filled with raw data.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn to_raw_vec(self) -> Vec<u8> {
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
let r: &[u8; 32] = self.as_ref();
|
||||
r.to_vec()
|
||||
}
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
let r: &[u8; 32] = self.as_ref();
|
||||
&r[..]
|
||||
}
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
pub fn as_array_ref(&self) -> &[u8; 32] {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -460,6 +462,11 @@ impl TraitPair for Pair {
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a vec filled with raw data.
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
self.seed().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -481,6 +488,19 @@ impl Pair {
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedKey for Public {
|
||||
const KEY_TYPE: KeyTypeId = key_types::ED25519;
|
||||
}
|
||||
|
||||
impl TypedKey for Signature {
|
||||
const KEY_TYPE: KeyTypeId = key_types::ED25519;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl TypedKey for Pair {
|
||||
const KEY_TYPE: KeyTypeId = key_types::ED25519;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -69,7 +69,7 @@ pub use self::hash::{H160, H256, H512, convert_hash};
|
||||
pub use self::uint::U256;
|
||||
pub use changes_trie::ChangesTrieConfiguration;
|
||||
#[cfg(feature = "std")]
|
||||
pub use crypto::{DeriveJunction, Pair};
|
||||
pub use crypto::{DeriveJunction, Pair, Public};
|
||||
|
||||
pub use hash_db::Hasher;
|
||||
// Switch back to Blake after PoC-3 is out
|
||||
|
||||
@@ -31,13 +31,14 @@ use substrate_bip39::mini_secret_from_entropy;
|
||||
use bip39::{Mnemonic, Language, MnemonicType};
|
||||
#[cfg(feature = "std")]
|
||||
use crate::crypto::{Pair as TraitPair, DeriveJunction, Infallible, SecretStringError, Derive, Ss58Codec};
|
||||
use crate::{hash::{H256, H512}, crypto::UncheckedFrom};
|
||||
use crate::crypto::{key_types, KeyTypeId, Public as TraitPublic, TypedKey, UncheckedFrom};
|
||||
use crate::hash::{H256, H512};
|
||||
use parity_codec::{Encode, Decode};
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
||||
#[cfg(feature = "std")]
|
||||
use schnorrkel::keys::MINI_SECRET_KEY_LENGTH;
|
||||
use schnorrkel::keys::{MINI_SECRET_KEY_LENGTH, SECRET_KEY_LENGTH};
|
||||
|
||||
// signing context
|
||||
#[cfg(feature = "std")]
|
||||
@@ -51,6 +52,17 @@ pub struct Public(pub [u8; 32]);
|
||||
#[cfg(feature = "std")]
|
||||
pub struct Pair(Keypair);
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl Clone for Pair {
|
||||
fn clone(&self) -> Self {
|
||||
Pair(schnorrkel::Keypair {
|
||||
public: self.0.public.clone(),
|
||||
secret: schnorrkel::SecretKey::from_bytes(&self.0.secret.to_bytes()[..])
|
||||
.expect("key is always the correct size; qed")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Public> for Public {
|
||||
fn as_ref(&self) -> &Public {
|
||||
&self
|
||||
@@ -282,16 +294,6 @@ impl Public {
|
||||
Public(data)
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// you are certain that the array actually is a pubkey. GIGO!
|
||||
pub fn from_slice(data: &[u8]) -> Self {
|
||||
let mut r = [0u8; 32];
|
||||
r.copy_from_slice(data);
|
||||
Public(r)
|
||||
}
|
||||
|
||||
/// A new instance from an H256.
|
||||
///
|
||||
/// NOTE: No checking goes on to ensure this is a real public key. Only use it if
|
||||
@@ -300,21 +302,33 @@ impl Public {
|
||||
Public(x.into())
|
||||
}
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
pub fn as_array_ref(&self) -> &[u8; 32] {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl TraitPublic for Public {
|
||||
/// 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
|
||||
/// you are certain that the array actually is a pubkey. GIGO!
|
||||
fn from_slice(data: &[u8]) -> Self {
|
||||
let mut r = [0u8; 32];
|
||||
r.copy_from_slice(data);
|
||||
Public(r)
|
||||
}
|
||||
|
||||
/// Return a `Vec<u8>` filled with raw data.
|
||||
#[cfg(feature = "std")]
|
||||
pub fn into_raw_vec(self) -> Vec<u8> {
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
self.0.to_vec()
|
||||
}
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
pub fn as_slice(&self) -> &[u8] {
|
||||
fn as_slice(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Return a slice filled with raw data.
|
||||
pub fn as_array_ref(&self) -> &[u8; 32] {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -397,14 +411,22 @@ impl TraitPair for Pair {
|
||||
///
|
||||
/// You should never need to use this; generate(), generate_with_phrase(), from_phrase()
|
||||
fn from_seed_slice(seed: &[u8]) -> Result<Pair, SecretStringError> {
|
||||
if seed.len() != MINI_SECRET_KEY_LENGTH {
|
||||
Err(SecretStringError::InvalidSeedLength)
|
||||
} else {
|
||||
Ok(Pair(
|
||||
MiniSecretKey::from_bytes(seed)
|
||||
.map_err(|_| SecretStringError::InvalidSeed)?
|
||||
.expand_to_keypair()
|
||||
))
|
||||
match seed.len() {
|
||||
MINI_SECRET_KEY_LENGTH => {
|
||||
Ok(Pair(
|
||||
MiniSecretKey::from_bytes(seed)
|
||||
.map_err(|_| SecretStringError::InvalidSeed)?
|
||||
.expand_to_keypair()
|
||||
))
|
||||
}
|
||||
SECRET_KEY_LENGTH => {
|
||||
Ok(Pair(
|
||||
SecretKey::from_bytes(seed)
|
||||
.map_err(|_| SecretStringError::InvalidSeed)?
|
||||
.to_keypair()
|
||||
))
|
||||
}
|
||||
_ => Err(SecretStringError::InvalidSeedLength)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,6 +500,11 @@ impl TraitPair for Pair {
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a vec filled with raw data.
|
||||
fn to_raw_vec(&self) -> Vec<u8> {
|
||||
self.0.secret.to_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
@@ -495,6 +522,19 @@ impl Pair {
|
||||
}
|
||||
}
|
||||
|
||||
impl TypedKey for Public {
|
||||
const KEY_TYPE: KeyTypeId = key_types::SR25519;
|
||||
}
|
||||
|
||||
impl TypedKey for Signature {
|
||||
const KEY_TYPE: KeyTypeId = key_types::SR25519;
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl TypedKey for Pair {
|
||||
const KEY_TYPE: KeyTypeId = key_types::SR25519;
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -38,7 +38,7 @@ use futures::prelude::*;
|
||||
use keystore::Store as Keystore;
|
||||
use log::{info, warn, debug, error};
|
||||
use parity_codec::{Encode, Decode};
|
||||
use primitives::Pair;
|
||||
use primitives::{Pair, Public, crypto::TypedKey, ed25519};
|
||||
use runtime_primitives::generic::BlockId;
|
||||
use runtime_primitives::traits::{Header, NumberFor, SaturatedConversion};
|
||||
use substrate_executor::NativeExecutor;
|
||||
@@ -182,13 +182,13 @@ impl<Components: components::Components> Service<Components> {
|
||||
// FIXME #1063 remove this
|
||||
if let Some(keystore) = keystore.as_mut() {
|
||||
for seed in &config.keys {
|
||||
keystore.generate_from_seed(seed)?;
|
||||
keystore.generate_from_seed::<ed25519::Pair>(seed)?;
|
||||
}
|
||||
|
||||
public_key = match keystore.contents()?.get(0) {
|
||||
public_key = match keystore.contents::<ed25519::Public>()?.get(0) {
|
||||
Some(public_key) => public_key.to_string(),
|
||||
None => {
|
||||
let key = keystore.generate(&config.password)?;
|
||||
let key: ed25519::Pair = keystore.generate(&config.password)?;
|
||||
let public_key = key.public();
|
||||
info!("Generated a new keypair: {:?}", public_key);
|
||||
public_key.to_string()
|
||||
@@ -542,11 +542,15 @@ impl<Components: components::Components> Service<Components> {
|
||||
}
|
||||
|
||||
/// give the authority key, if we are an authority and have a key
|
||||
pub fn authority_key(&self) -> Option<primitives::ed25519::Pair> {
|
||||
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().map(|keys| keys.get(0)
|
||||
.map(|k| keystore.load(k, &self.config.password)))
|
||||
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 {
|
||||
@@ -856,7 +860,6 @@ fn build_system_rpc_handler<Components: components::Components>(
|
||||
/// # use transaction_pool::{self, txpool::{Pool as TransactionPool}};
|
||||
/// # use network::construct_simple_protocol;
|
||||
/// # use client::{self, LongestChain};
|
||||
/// # use primitives::{Pair as PairT, ed25519};
|
||||
/// # use consensus_common::import_queue::{BasicQueue, Verifier};
|
||||
/// # use consensus_common::{BlockOrigin, ImportBlock, well_known_cache_keys::Id as CacheKeyId};
|
||||
/// # use node_runtime::{GenesisConfig, RuntimeApi};
|
||||
@@ -903,7 +906,7 @@ fn build_system_rpc_handler<Components: components::Components>(
|
||||
/// { |config| <FullComponents<Factory>>::new(config) },
|
||||
/// // Setup as Consensus Authority (if the role and key are given)
|
||||
/// AuthoritySetup = {
|
||||
/// |service: Self::FullService, key: Option<Arc<ed25519::Pair>>| {
|
||||
/// |service: Self::FullService| {
|
||||
/// Ok(service)
|
||||
/// }},
|
||||
/// LightService = LightComponents<Self>
|
||||
@@ -1029,8 +1032,7 @@ macro_rules! construct_service_factory {
|
||||
) -> Result<Self::FullService, $crate::Error>
|
||||
{
|
||||
( $( $full_service_init )* ) (config).and_then(|service| {
|
||||
let key = (&service).authority_key().map(Arc::new);
|
||||
($( $authority_setup )*)(service, key)
|
||||
($( $authority_setup )*)(service)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,8 +65,8 @@ construct_service_factory! {
|
||||
FullComponents::<Factory>::new(config)
|
||||
},
|
||||
AuthoritySetup = {
|
||||
|service: Self::FullService, key: Option<Arc<Pair>>| {
|
||||
if let Some(key) = key {
|
||||
|service: Self::FullService| {
|
||||
if let Some(key) = service.authority_key::<Pair>() {
|
||||
info!("Using authority key {}", key.public());
|
||||
let proposer = Arc::new(ProposerFactory {
|
||||
client: service.client(),
|
||||
@@ -77,7 +77,7 @@ construct_service_factory! {
|
||||
.ok_or_else(|| ServiceError::SelectChainRequired)?;
|
||||
let aura = start_aura(
|
||||
SlotDuration::get_or_compute(&*client)?,
|
||||
key.clone(),
|
||||
Arc::new(key),
|
||||
client.clone(),
|
||||
select_chain,
|
||||
client,
|
||||
|
||||
@@ -24,10 +24,11 @@ substrate-basic-authorship = { path = "../../core/basic-authorship" }
|
||||
substrate-service = { path = "../../core/service" }
|
||||
transaction_pool = { package = "substrate-transaction-pool", path = "../../core/transaction-pool" }
|
||||
network = { package = "substrate-network", path = "../../core/network" }
|
||||
consensus = { package = "substrate-consensus-aura", path = "../../core/consensus/aura" }
|
||||
grandpa = { package = "substrate-finality-grandpa", path = "../../core/finality-grandpa" }
|
||||
sr-primitives = { path = "../../core/sr-primitives" }
|
||||
aura = { package = "substrate-consensus-aura", path = "../../core/consensus/aura" }
|
||||
aura_primitives = { package = "substrate-consensus-aura-primitives", path = "../../core/consensus/aura/primitives" }
|
||||
grandpa = { package = "substrate-finality-grandpa", path = "../../core/finality-grandpa" }
|
||||
grandpa_primitives = { package = "substrate-finality-grandpa-primitives", path = "../../core/finality-grandpa/primitives" }
|
||||
sr-primitives = { path = "../../core/sr-primitives" }
|
||||
node-executor = { path = "../executor" }
|
||||
substrate-keystore = { path = "../../core/keystore" }
|
||||
substrate-telemetry = { package = "substrate-telemetry", path = "../../core/telemetry" }
|
||||
|
||||
@@ -21,13 +21,13 @@
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use aura::{import_queue, start_aura, AuraImportQueue, SlotDuration};
|
||||
use client::{self, LongestChain};
|
||||
use consensus::{import_queue, start_aura, AuraImportQueue, SlotDuration};
|
||||
use grandpa::{self, FinalityProofProvider as GrandpaFinalityProofProvider};
|
||||
use node_executor;
|
||||
use primitives::{Pair as PairT, ed25519};
|
||||
use primitives::Pair;
|
||||
use futures::prelude::*;
|
||||
use node_primitives::Block;
|
||||
use node_primitives::{AuraPair, Block};
|
||||
use node_runtime::{GenesisConfig, RuntimeApi};
|
||||
use substrate_service::{
|
||||
FactoryFullConfiguration, LightComponents, FullComponents, FullBackend,
|
||||
@@ -79,12 +79,13 @@ construct_service_factory! {
|
||||
{ |config: FactoryFullConfiguration<Self>|
|
||||
FullComponents::<Factory>::new(config) },
|
||||
AuthoritySetup = {
|
||||
|mut service: Self::FullService, local_key: Option<Arc<ed25519::Pair>>| {
|
||||
|mut service: Self::FullService| {
|
||||
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(ref key) = local_key {
|
||||
info!("Using authority key {}", key.public());
|
||||
if let Some(aura_key) = service.authority_key::<AuraPair>() {
|
||||
info!("Using aura key {}", aura_key.public());
|
||||
|
||||
let proposer = Arc::new(substrate_basic_authorship::ProposerFactory {
|
||||
client: service.client(),
|
||||
transaction_pool: service.transaction_pool(),
|
||||
@@ -93,9 +94,10 @@ construct_service_factory! {
|
||||
let client = service.client();
|
||||
let select_chain = service.select_chain()
|
||||
.ok_or(ServiceError::SelectChainRequired)?;
|
||||
|
||||
let aura = start_aura(
|
||||
SlotDuration::get_or_compute(&*client)?,
|
||||
key.clone(),
|
||||
Arc::new(aura_key),
|
||||
client,
|
||||
select_chain,
|
||||
block_import.clone(),
|
||||
@@ -104,19 +106,18 @@ construct_service_factory! {
|
||||
service.config.custom.inherent_data_providers.clone(),
|
||||
service.config.force_authoring,
|
||||
)?;
|
||||
service.spawn_task(Box::new(aura.select(service.on_exit()).then(|_| Ok(()))));
|
||||
|
||||
info!("Running Grandpa session as Authority {}", key.public());
|
||||
let select = aura.select(service.on_exit()).then(|_| Ok(()));
|
||||
service.spawn_task(Box::new(select));
|
||||
}
|
||||
|
||||
let local_key = if service.config.disable_grandpa {
|
||||
let grandpa_key = if service.config.disable_grandpa {
|
||||
None
|
||||
} else {
|
||||
local_key
|
||||
service.authority_key::<grandpa_primitives::AuthorityPair>()
|
||||
};
|
||||
|
||||
let config = grandpa::Config {
|
||||
local_key,
|
||||
local_key: grandpa_key.map(Arc::new),
|
||||
// FIXME #1578 make this available through chainspec
|
||||
gossip_duration: Duration::from_millis(333),
|
||||
justification_period: 4096,
|
||||
@@ -165,7 +166,7 @@ construct_service_factory! {
|
||||
|
||||
config.custom.grandpa_import_setup = Some((block_import.clone(), link_half));
|
||||
|
||||
import_queue::<_, _, ed25519::Pair>(
|
||||
import_queue::<_, _, AuraPair>(
|
||||
slot_duration,
|
||||
block_import,
|
||||
Some(justification_import),
|
||||
@@ -189,7 +190,7 @@ construct_service_factory! {
|
||||
let finality_proof_import = block_import.clone();
|
||||
let finality_proof_request_builder = finality_proof_import.create_finality_proof_request_builder();
|
||||
|
||||
import_queue::<_, _, ed25519::Pair>(
|
||||
import_queue::<_, _, AuraPair>(
|
||||
SlotDuration::get_or_compute(&*client)?,
|
||||
block_import,
|
||||
None,
|
||||
@@ -215,7 +216,7 @@ construct_service_factory! {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Arc;
|
||||
use consensus::CompatibleDigestItem;
|
||||
use aura::CompatibleDigestItem;
|
||||
use consensus_common::{Environment, Proposer, ImportBlock, BlockOrigin, ForkChoiceStrategy};
|
||||
use node_primitives::DigestItem;
|
||||
use node_runtime::{BalancesCall, Call, CENTS, UncheckedExtrinsic};
|
||||
|
||||
@@ -44,12 +44,16 @@ pub type Balance = u128;
|
||||
/// Type used for expressing timestamp.
|
||||
pub type Moment = u64;
|
||||
|
||||
/// Alias to the signature scheme used for Aura authority signatures.
|
||||
pub type AuraSignature = primitives::ed25519::Signature;
|
||||
/// The aura crypto scheme defined via the keypair type.
|
||||
#[cfg(feature = "std")]
|
||||
pub type AuraPair = primitives::ed25519::Pair;
|
||||
|
||||
/// The Ed25519 pub key of an session that belongs to an Aura authority of the chain.
|
||||
/// Identity of an Aura authority.
|
||||
pub type AuraId = primitives::ed25519::Public;
|
||||
|
||||
/// Signature for an Aura authority.
|
||||
pub type AuraSignature = primitives::ed25519::Signature;
|
||||
|
||||
/// Index of a transaction in the chain.
|
||||
pub type Index = u64;
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ use std::{str::FromStr, io::{stdin, Read}};
|
||||
use hex_literal::hex;
|
||||
use clap::load_yaml;
|
||||
use bip39::{Mnemonic, Language, MnemonicType};
|
||||
use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, crypto::Ss58Codec, blake2_256};
|
||||
use substrate_primitives::{ed25519, sr25519, hexdisplay::HexDisplay, Pair, Public, crypto::Ss58Codec, blake2_256};
|
||||
use parity_codec::{Encode, Decode, Compact};
|
||||
use sr_primitives::generic::Era;
|
||||
use node_primitives::{Balance, Index, Hash};
|
||||
@@ -32,7 +32,7 @@ mod vanity;
|
||||
|
||||
trait Crypto {
|
||||
type Pair: Pair<Public=Self::Public>;
|
||||
type Public: Ss58Codec + AsRef<[u8]>;
|
||||
type Public: Public + Ss58Codec + AsRef<[u8]> + std::hash::Hash;
|
||||
fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair {
|
||||
Self::Pair::from_string(suri, password).expect("Invalid phrase")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user