Add SECP256k1/ECDSA support for transaction signing (#3861)

* Add SECP256k1/ECDSA support for transaction signing.

* Refactoring and fixes

* Fix for contracts

* Avoid breaking runtime host function

* Build fixes, make subkey work more generaically.

* Fix tests

* Dedpulicate a bit of code, remove unneeded code, docs

* Bump runtime version

* Fix a test and clean up some code.

* Derivation can derive seed.

* Whitespace

* Bump runtime again.

* Update core/primitives/src/crypto.rs

Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Update core/primitives/src/ecdsa.rs

Co-Authored-By: Kian Paimani <5588131+kianenigma@users.noreply.github.com>

* Fix AppVerify
This commit is contained in:
Gavin Wood
2019-10-24 10:59:09 +02:00
committed by GitHub
parent 62a238a81b
commit d97775542a
30 changed files with 1286 additions and 419 deletions
+2
View File
@@ -5366,6 +5366,7 @@ dependencies = [
"hex-literal 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"impl-serde 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libsecp256k1 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-scale-codec 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -5385,6 +5386,7 @@ dependencies = [
"substrate-primitives-storage 2.0.0",
"substrate-serializer 2.0.0",
"tiny-bip39 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
"tiny-keccak 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"twox-hash 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"zeroize 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)",
+2 -11
View File
@@ -88,22 +88,13 @@ macro_rules! app_crypto {
}
fn derive<
Iter: Iterator<Item=$crate::DeriveJunction>
>(&self, path: Iter) -> Result<Self, Self::DeriveError> {
self.0.derive(path).map(Self)
>(&self, path: Iter, seed: Option<Self::Seed>) -> Result<(Self, Option<Self::Seed>), Self::DeriveError> {
self.0.derive(path, seed).map(|x| (Self(x.0), x.1))
}
fn from_seed(seed: &Self::Seed) -> Self { Self(<$pair>::from_seed(seed)) }
fn from_seed_slice(seed: &[u8]) -> Result<Self, $crate::SecretStringError> {
<$pair>::from_seed_slice(seed).map(Self)
}
fn from_standard_components<
I: Iterator<Item=$crate::DeriveJunction>
>(
seed: &str,
password: Option<&str>,
path: I,
) -> Result<Self, $crate::SecretStringError> {
<$pair>::from_standard_components::<I>(seed, password, path).map(Self)
}
fn sign(&self, msg: &[u8]) -> Self::Signature {
Signature(self.0.sign(msg))
}
@@ -126,3 +126,4 @@ pub trait RuntimeAppPublic: Sized {
/// Verify that the given signature matches the given message using this public key.
fn verify<M: AsRef<[u8]>>(&self, msg: &M, signature: &Self::Signature) -> bool;
}
+56 -26
View File
@@ -41,6 +41,40 @@ macro_rules! debug_trace {
pub struct SubstrateExternals;
enum RecoverResult {
Invalid(u32),
Valid(secp256k1::PublicKey),
}
fn secp256k1_recover(
context: &mut dyn FunctionContext,
msg_data: Pointer<u8>,
sig_data: Pointer<u8>,
) -> WResult<RecoverResult> {
let mut sig = [0u8; 65];
context.read_memory_into(sig_data, &mut sig[..])
.map_err(|_| "Invalid attempt to get signature in ext_secp256k1_ecdsa_recover")?;
let rs = match secp256k1::Signature::parse_slice(&sig[0..64]) {
Ok(rs) => rs,
_ => return Ok(RecoverResult::Invalid(1)),
};
let recovery_id = if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8;
let v = match secp256k1::RecoveryId::parse(recovery_id) {
Ok(v) => v,
_ => return Ok(RecoverResult::Invalid(2)),
};
let mut msg = [0u8; 32];
context.read_memory_into(msg_data, &mut msg[..])
.map_err(|_| "Invalid attempt to get message in ext_secp256k1_ecdsa_recover")?;
Ok(match secp256k1::recover(&secp256k1::Message::parse(&msg), &rs, &v) {
Ok(pubkey) => RecoverResult::Valid(pubkey),
Err(_) => RecoverResult::Invalid(3),
})
}
impl_wasm_host_interface! {
impl SubstrateExternals where context {
ext_malloc(size: WordSize) -> Pointer<u8> {
@@ -781,33 +815,29 @@ impl_wasm_host_interface! {
sig_data: Pointer<u8>,
pubkey_data: Pointer<u8>,
) -> u32 {
let mut sig = [0u8; 65];
context.read_memory_into(sig_data, &mut sig[..])
.map_err(|_| "Invalid attempt to get signature in ext_secp256k1_ecdsa_recover")?;
let rs = match secp256k1::Signature::parse_slice(&sig[0..64]) {
Ok(rs) => rs,
_ => return Ok(1),
};
match secp256k1_recover(context, msg_data, sig_data)? {
RecoverResult::Invalid(c) => Ok(c),
RecoverResult::Valid(pubkey) => {
context.write_memory(pubkey_data, &pubkey.serialize()[1..65])
.map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?;
Ok(0)
}
}
}
let recovery_id = if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8;
let v = match secp256k1::RecoveryId::parse(recovery_id) {
Ok(v) => v,
_ => return Ok(2),
};
let mut msg = [0u8; 32];
context.read_memory_into(msg_data, &mut msg[..])
.map_err(|_| "Invalid attempt to get message in ext_secp256k1_ecdsa_recover")?;
let pubkey = match secp256k1::recover(&secp256k1::Message::parse(&msg), &rs, &v) {
Ok(pk) => pk,
_ => return Ok(3),
};
context.write_memory(pubkey_data, &pubkey.serialize()[1..65])
.map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?;
Ok(0)
ext_secp256k1_ecdsa_recover_compressed(
msg_data: Pointer<u8>,
sig_data: Pointer<u8>,
pubkey_data: Pointer<u8>,
) -> u32 {
match secp256k1_recover(context, msg_data, sig_data)? {
RecoverResult::Invalid(c) => Ok(c),
RecoverResult::Valid(pubkey) => {
context.write_memory(pubkey_data, &pubkey.serialize_compressed()[..])
.map_err(|_| "Invalid attempt to set pubkey in ext_secp256k1_ecdsa_recover")?;
Ok(0)
}
}
}
ext_is_validator() -> u32 {
+15
View File
@@ -20,6 +20,7 @@ use std::{collections::HashMap, ops::Deref};
use lazy_static::lazy_static;
use primitives::{ed25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256};
pub use primitives::ed25519;
use sr_primitives::AccountId32;
/// Set of test accounts.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::Display, strum_macros::EnumIter)]
@@ -39,6 +40,10 @@ impl Keyring {
Self::iter().find(|&k| &Public::from(k) == who)
}
pub fn from_account_id(who: &AccountId32) -> Option<Keyring> {
Self::iter().find(|&k| &k.to_account_id() == who)
}
pub fn from_raw_public(who: [u8; 32]) -> Option<Keyring> {
Self::from_public(&Public::from_raw(who))
}
@@ -59,6 +64,10 @@ impl Keyring {
Public::from(self).to_raw_vec()
}
pub fn to_account_id(self) -> AccountId32 {
self.to_raw_public().into()
}
pub fn sign(self, msg: &[u8]) -> Signature {
Pair::from(self).sign(msg)
}
@@ -119,6 +128,12 @@ impl From<Keyring> for Public {
}
}
impl From<Keyring> for AccountId32 {
fn from(k: Keyring) -> Self {
k.to_account_id()
}
}
impl From<Keyring> for Pair {
fn from(k: Keyring) -> Self {
k.pair()
+15
View File
@@ -21,6 +21,7 @@ use std::ops::Deref;
use lazy_static::lazy_static;
use primitives::{sr25519::{Pair, Public, Signature}, Pair as PairT, Public as PublicT, H256};
pub use primitives::sr25519;
use sr_primitives::AccountId32;
/// Set of test accounts.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, strum_macros::Display, strum_macros::EnumIter)]
@@ -40,6 +41,10 @@ impl Keyring {
Self::iter().find(|&k| &Public::from(k) == who)
}
pub fn from_account_id(who: &AccountId32) -> Option<Keyring> {
Self::iter().find(|&k| &k.to_account_id() == who)
}
pub fn from_raw_public(who: [u8; 32]) -> Option<Keyring> {
Self::from_public(&Public::from_raw(who))
}
@@ -60,6 +65,10 @@ impl Keyring {
Public::from(self).to_raw_vec()
}
pub fn to_account_id(self) -> AccountId32 {
self.to_raw_public().into()
}
pub fn sign(self, msg: &[u8]) -> Signature {
Pair::from(self).sign(msg)
}
@@ -114,6 +123,12 @@ lazy_static! {
};
}
impl From<Keyring> for AccountId32 {
fn from(k: Keyring) -> Self {
k.to_account_id()
}
}
impl From<Keyring> for Public {
fn from(k: Keyring) -> Self {
(*PUBLIC_KEYS).get(&k).unwrap().clone()
+4
View File
@@ -31,6 +31,8 @@ num-traits = { version = "0.2.8", default-features = false }
zeroize = "0.10.1"
lazy_static = { version = "1.4.0", optional = true }
parking_lot = { version = "0.9.0", optional = true }
libsecp256k1 = { version = "0.3.0", optional = true }
tiny-keccak = { version = "1.5.0", optional = true }
substrate-debug-derive = { version = "2.0.0", path = "./debug-derive" }
externalities = { package = "substrate-externalities", path = "../externalities", optional = true }
primitives-storage = { package = "substrate-primitives-storage", path = "storage", default-features = false }
@@ -82,6 +84,8 @@ std = [
"schnorrkel",
"regex",
"num-traits/std",
"libsecp256k1",
"tiny-keccak",
"substrate-debug-derive/std",
"externalities",
"primitives-storage/std",
+191 -95
View File
@@ -255,7 +255,7 @@ pub enum PublicError {
/// Key that can be encoded to/from SS58.
#[cfg(feature = "std")]
pub trait Ss58Codec: Sized {
pub trait Ss58Codec: Sized + AsMut<[u8]> + AsRef<[u8]> + Default {
/// Some if the string is a properly encoded SS58Check address.
fn from_ss58check(s: &str) -> Result<Self, PublicError> {
Self::from_ss58check_with_version(s)
@@ -269,7 +269,23 @@ pub trait Ss58Codec: Sized {
})
}
/// Some if the string is a properly encoded SS58Check address.
fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError>;
fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> {
let mut res = Self::default();
let len = res.as_mut().len();
let d = s.from_base58().map_err(|_| PublicError::BadBase58)?; // failure here would be invalid encoding.
if d.len() != len + 3 {
// Invalid length.
return Err(PublicError::BadLength);
}
let ver = d[0].try_into().map_err(|_: ()| PublicError::UnknownVersion)?;
if d[len + 1..len + 3] != ss58hash(&d[0..len + 1]).as_bytes()[0..2] {
// Invalid checksum.
return Err(PublicError::InvalidChecksum);
}
res.as_mut().copy_from_slice(&d[1..len + 1]);
Ok((res, ver))
}
/// Some if the string is a properly encoded SS58Check address, optionally with
/// a derivation path following.
fn from_string(s: &str) -> Result<Self, PublicError> {
@@ -285,7 +301,13 @@ pub trait Ss58Codec: Sized {
}
/// Return the ss58-check string for this key.
fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String;
fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String {
let mut v = vec![version.into()];
v.extend(self.as_ref());
let r = ss58hash(&v);
v.extend(&r.as_bytes()[0..2]);
v.to_base58()
}
/// Return the ss58-check string for this key.
fn to_ss58check(&self) -> String { self.to_ss58check_with_version(*DEFAULT_VERSION.lock()) }
/// Some if the string is a properly encoded SS58Check address, optionally with
@@ -408,44 +430,28 @@ pub fn set_default_ss58_version(version: Ss58AddressFormat) {
}
#[cfg(feature = "std")]
impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
fn from_ss58check_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> {
let mut res = T::default();
let len = res.as_mut().len();
let d = s.from_base58().map_err(|_| PublicError::BadBase58)?; // failure here would be invalid encoding.
if d.len() != len + 3 {
// Invalid length.
return Err(PublicError::BadLength);
}
let ver = d[0].try_into().map_err(|_: ()| PublicError::UnknownVersion)?;
if d[len+1..len+3] != ss58hash(&d[0..len+1]).as_bytes()[0..2] {
// Invalid checksum.
return Err(PublicError::InvalidChecksum);
}
res.as_mut().copy_from_slice(&d[1..len+1]);
Ok((res, ver))
}
fn to_ss58check_with_version(&self, version: Ss58AddressFormat) -> String {
let mut v = vec![version.into()];
v.extend(self.as_ref());
let r = ss58hash(&v);
v.extend(&r.as_bytes()[0..2]);
v.to_base58()
}
impl<T: Sized + AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
fn from_string(s: &str) -> Result<Self, PublicError> {
let re = Regex::new(r"^(?P<ss58>[\w\d]+)?(?P<path>(//?[^/]+)*)$")
let re = Regex::new(r"^(?P<ss58>[\w\d ]+)?(?P<path>(//?[^/]+)*)$")
.expect("constructed from known-good static value; qed");
let cap = re.captures(s).ok_or(PublicError::InvalidFormat)?;
let re_junction = Regex::new(r"/(/?[^/]+)")
.expect("constructed from known-good static value; qed");
let addr = Self::from_ss58check(
cap.name("ss58")
.map(|r| r.as_str())
.unwrap_or(DEV_ADDRESS)
)?;
let s = cap.name("ss58")
.map(|r| r.as_str())
.unwrap_or(DEV_ADDRESS);
let addr = if s.starts_with("0x") {
let d = hex::decode(&s[2..]).map_err(|_| PublicError::InvalidFormat)?;
let mut r = Self::default();
if d.len() == r.as_ref().len() {
r.as_mut().copy_from_slice(&d);
r
} else {
Err(PublicError::BadLength)?
}
} else {
Self::from_ss58check(s)?
};
if cap["path"].is_empty() {
Ok(addr)
} else {
@@ -457,7 +463,7 @@ impl<T: AsMut<[u8]> + AsRef<[u8]> + Default + Derive> Ss58Codec for T {
}
fn from_string_with_version(s: &str) -> Result<(Self, Ss58AddressFormat), PublicError> {
let re = Regex::new(r"^(?P<ss58>[\w\d]+)?(?P<path>(//?[^/]+)*)$")
let re = Regex::new(r"^(?P<ss58>[\w\d ]+)?(?P<path>(//?[^/]+)*)$")
.expect("constructed from known-good static value; qed");
let cap = re.captures(s).ok_or(PublicError::InvalidFormat)?;
let re_junction = Regex::new(r"/(/?[^/]+)")
@@ -495,6 +501,103 @@ pub trait Public: AsRef<[u8]> + AsMut<[u8]> + Default + Derive + CryptoType + Pa
fn as_slice(&self) -> &[u8] { self.as_ref() }
}
/// An opaque 32-byte cryptographic identifier.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Default, Encode, Decode)]
pub struct AccountId32([u8; 32]);
impl UncheckedFrom<crate::hash::H256> for AccountId32 {
fn unchecked_from(h: crate::hash::H256) -> Self {
AccountId32(h.into())
}
}
#[cfg(feature = "std")]
impl Ss58Codec for AccountId32 {}
impl AsRef<[u8]> for AccountId32 {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl AsMut<[u8]> for AccountId32 {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0[..]
}
}
impl AsRef<[u8; 32]> for AccountId32 {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl AsMut<[u8; 32]> for AccountId32 {
fn as_mut(&mut self) -> &mut [u8; 32] {
&mut self.0
}
}
impl From<[u8; 32]> for AccountId32 {
fn from(x: [u8; 32]) -> AccountId32 {
AccountId32(x)
}
}
impl<'a> rstd::convert::TryFrom<&'a [u8]> for AccountId32 {
type Error = ();
fn try_from(x: &'a [u8]) -> Result<AccountId32, ()> {
if x.len() == 32 {
let mut r = AccountId32::default();
r.0.copy_from_slice(x);
Ok(r)
} else {
Err(())
}
}
}
impl From<AccountId32> for [u8; 32] {
fn from(x: AccountId32) -> [u8; 32] {
x.0
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for AccountId32 {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_ss58check())
}
}
impl rstd::fmt::Debug for AccountId32 {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
let s = self.to_ss58check();
write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&self.0), &s[0..8])
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut rstd::fmt::Formatter) -> rstd::fmt::Result {
Ok(())
}
}
#[cfg(feature = "std")]
impl serde::Serialize for AccountId32 {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer {
serializer.serialize_str(&self.to_ss58check())
}
}
#[cfg(feature = "std")]
impl<'de> serde::Deserialize<'de> for AccountId32 {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de> {
Ss58Codec::from_ss58check(&String::deserialize(deserializer)?)
.map_err(|e| serde::de::Error::custom(format!("{:?}", e)))
}
}
#[cfg(feature = "std")]
pub use self::dummy::*;
@@ -544,17 +647,10 @@ mod dummy {
Ok(Default::default())
}
fn derive<
Iter: Iterator<Item=DeriveJunction>
>(&self, _: Iter) -> Result<Self, Self::DeriveError> { Ok(Self) }
Iter: Iterator<Item=DeriveJunction>,
>(&self, _: Iter, _: Option<Dummy>) -> Result<(Self, Option<Dummy>), Self::DeriveError> { Ok((Self, None)) }
fn from_seed(_: &Self::Seed) -> Self { Self }
fn from_seed_slice(_: &[u8]) -> Result<Self, SecretStringError> { Ok(Self) }
fn from_standard_components<
I: Iterator<Item=DeriveJunction>
>(
_: &str,
_: Option<&str>,
_: I
) -> Result<Self, SecretStringError> { Ok(Self) }
fn sign(&self, _: &[u8]) -> Self::Signature { Self }
fn verify<M: AsRef<[u8]>>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true }
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(_: &[u8], _: M, _: P) -> bool { true }
@@ -604,7 +700,10 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static {
fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Self, Self::Seed), SecretStringError>;
/// Derive a child key from a series of given junctions.
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, path: Iter) -> Result<Self, Self::DeriveError>;
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self,
path: Iter,
seed: Option<Self::Seed>,
) -> Result<(Self, Option<Self::Seed>), Self::DeriveError>;
/// Generate new key pair from the provided `seed`.
///
@@ -619,11 +718,6 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static {
/// by an attacker then they can also derive your key.
fn from_seed_slice(seed: &[u8]) -> Result<Self, SecretStringError>;
/// Construct a key from a phrase, password and path.
fn from_standard_components<
I: Iterator<Item=DeriveJunction>
>(phrase: &str, password: Option<&str>, path: I) -> Result<Self, SecretStringError>;
/// Sign a message.
fn sign(&self, message: &[u8]) -> Self::Signature;
@@ -636,7 +730,9 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static {
/// Get the public key.
fn public(&self) -> Self::Public;
/// Interprets the string `s` in order to generate a key Pair.
/// Interprets the string `s` in order to generate a key Pair. Returns both the pair and an optional seed, in the
/// case that the pair can be expressed as a direct derivation from a seed (some cases, such as Sr25519 derivations
/// with path components, cannot).
///
/// This takes a helper function to do the key generation from a phrase, password and
/// junction iterator.
@@ -662,31 +758,40 @@ pub trait Pair: CryptoType + Sized + Clone + Send + Sync + 'static {
/// be equivalent to no password at all.
///
/// `None` is returned if no matches are found.
fn from_string(s: &str, password_override: Option<&str>) -> Result<Self, SecretStringError> {
let hex_seed = if s.starts_with("0x") {
&s[2..]
} else {
s
};
if let Ok(d) = hex::decode(hex_seed) {
if let Ok(r) = Self::from_seed_slice(&d) {
return Ok(r)
}
}
let re = Regex::new(r"^(?P<phrase>\w+( \w+)*)?(?P<path>(//?[^/]+)*)(///(?P<password>.*))?$")
fn from_string_with_seed(s: &str, password_override: Option<&str>) -> Result<(Self, Option<Self::Seed>), SecretStringError> {
let re = Regex::new(r"^(?P<phrase>[\d\w ]+)?(?P<path>(//?[^/]+)*)(///(?P<password>.*))?$")
.expect("constructed from known-good static value; qed");
let cap = re.captures(s).ok_or(SecretStringError::InvalidFormat)?;
let re_junction = Regex::new(r"/(/?[^/]+)")
.expect("constructed from known-good static value; qed");
let path = re_junction.captures_iter(&cap["path"])
.map(|f| DeriveJunction::from(&f[1]));
Self::from_standard_components(
cap.name("phrase").map(|r| r.as_str()).unwrap_or(DEV_PHRASE),
password_override.or_else(|| cap.name("password").map(|m| m.as_str())),
path,
)
let phrase = cap.name("phrase").map(|r| r.as_str()).unwrap_or(DEV_PHRASE);
let password = password_override.or_else(|| cap.name("password").map(|m| m.as_str()));
let (root, seed) = if phrase.starts_with("0x") {
hex::decode(&phrase[2..]).ok()
.and_then(|seed_vec| {
let mut seed = Self::Seed::default();
if seed.as_ref().len() == seed_vec.len() {
seed.as_mut().copy_from_slice(&seed_vec);
Some((Self::from_seed(&seed), seed))
} else {
None
}
})
.ok_or(SecretStringError::InvalidSeed)?
} else {
Self::from_phrase(phrase, password)
.map_err(|_| SecretStringError::InvalidPhrase)?
};
root.derive(path, Some(seed)).map_err(|_| SecretStringError::InvalidPath)
}
fn from_string(s: &str, password_override: Option<&str>) -> Result<Self, SecretStringError> {
Self::from_string_with_seed(s, password_override).map(|x| x.0)
}
/// Return a vec filled with raw data.
@@ -846,13 +951,13 @@ mod tests {
}
impl Pair for TestPair {
type Public = TestPublic;
type Seed = [u8; 0];
type Seed = [u8; 8];
type Signature = [u8; 0];
type DeriveError = ();
fn generate() -> (Self, <Self as Pair>::Seed) { (TestPair::Generated, []) }
fn generate() -> (Self, <Self as Pair>::Seed) { (TestPair::Generated, [0u8; 8]) }
fn generate_with_phrase(_password: Option<&str>) -> (Self, String, <Self as Pair>::Seed) {
(TestPair::GeneratedWithPhrase, "".into(), [])
(TestPair::GeneratedWithPhrase, "".into(), [0u8; 8])
}
fn from_phrase(phrase: &str, password: Option<&str>)
-> Result<(Self, <Self as Pair>::Seed), SecretStringError>
@@ -860,14 +965,20 @@ mod tests {
Ok((TestPair::GeneratedFromPhrase {
phrase: phrase.to_owned(),
password: password.map(Into::into)
}, []))
}, [0u8; 8]))
}
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, _path: Iter)
-> Result<Self, Self::DeriveError>
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, path_iter: Iter, _: Option<[u8; 8]>)
-> Result<(Self, Option<[u8; 8]>), Self::DeriveError>
{
Err(())
Ok((match self.clone() {
TestPair::Standard {phrase, password, path} =>
TestPair::Standard { phrase, password, path: path.into_iter().chain(path_iter).collect() },
TestPair::GeneratedFromPhrase {phrase, password} =>
TestPair::Standard { phrase, password, path: path_iter.collect() },
x => if path_iter.count() == 0 { x } else { return Err(()) },
}, None))
}
fn from_seed(_seed: &<TestPair as Pair>::Seed) -> Self { TestPair::Seed(vec![]) }
fn from_seed(_seed: &<TestPair as Pair>::Seed) -> Self { TestPair::Seed(_seed.as_ref().to_owned()) }
fn sign(&self, _message: &[u8]) -> Self::Signature { [] }
fn verify<M: AsRef<[u8]>>(_: &Self::Signature, _: M, _: &Self::Public) -> bool { true }
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(
@@ -876,17 +987,6 @@ mod tests {
_pubkey: P
) -> bool { true }
fn public(&self) -> Self::Public { TestPublic }
fn from_standard_components<I: Iterator<Item=DeriveJunction>>(
phrase: &str,
password: Option<&str>,
path: I
) -> Result<Self, SecretStringError> {
Ok(TestPair::Standard {
phrase: phrase.to_owned(),
password: password.map(ToOwned::to_owned),
path: path.collect()
})
}
fn from_seed_slice(seed: &[u8])
-> Result<Self, SecretStringError>
{
@@ -903,10 +1003,6 @@ mod tests {
TestPair::from_string("0x0123456789abcdef", None),
Ok(TestPair::Seed(hex!["0123456789abcdef"][..].to_owned()))
);
assert_eq!(
TestPair::from_string("0123456789abcdef", None),
Ok(TestPair::Seed(hex!["0123456789abcdef"][..].to_owned()))
);
}
#[test]
+608
View File
@@ -0,0 +1,608 @@
// Copyright 2017-2019 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
// tag::description[]
//! Simple ECDSA API.
// end::description[]
use rstd::cmp::Ordering;
use codec::{Encode, Decode};
#[cfg(feature = "std")]
use std::convert::{TryFrom, TryInto};
#[cfg(feature = "std")]
use substrate_bip39::seed_from_entropy;
#[cfg(feature = "std")]
use bip39::{Mnemonic, Language, MnemonicType};
#[cfg(feature = "std")]
use crate::{hashing::blake2_256, crypto::{Pair as TraitPair, DeriveJunction, SecretStringError, Ss58Codec}};
#[cfg(feature = "std")]
use serde::{de, Serializer, Serialize, Deserializer, Deserialize};
use crate::crypto::{Public as TraitPublic, UncheckedFrom, CryptoType, Derive};
#[cfg(feature = "std")]
use secp256k1::{PublicKey, SecretKey};
/// A secret seed (which is bytewise essentially equivalent to a SecretKey).
///
/// We need it as a different type because `Seed` is expected to be AsRef<[u8]>.
#[cfg(feature = "std")]
type Seed = [u8; 32];
/// The ECDSA 33-byte compressed public key.
#[derive(Clone, Encode, Decode)]
pub struct Public(pub [u8; 33]);
impl PartialOrd for Public {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Public {
fn cmp(&self, other: &Self) -> Ordering {
self.0[..].cmp(&other.0[..])
}
}
impl PartialEq for Public {
fn eq(&self, other: &Self) -> bool {
&self.0[..] == &other.0[..]
}
}
impl Eq for Public {}
impl Default for Public {
fn default() -> Self {
Public([0u8; 33])
}
}
/// A key pair.
#[cfg(feature = "std")]
#[derive(Clone)]
pub struct Pair {
public: PublicKey,
secret: SecretKey,
}
impl AsRef<[u8; 33]> for Public {
fn as_ref(&self) -> &[u8; 33] {
&self.0
}
}
impl AsRef<[u8]> for Public {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl AsMut<[u8]> for Public {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0[..]
}
}
impl rstd::convert::TryFrom<&[u8]> for Public {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() == 33 {
let mut inner = [0u8; 33];
inner.copy_from_slice(data);
Ok(Public(inner))
} else {
Err(())
}
}
}
impl From<Public> for [u8; 33] {
fn from(x: Public) -> Self {
x.0
}
}
#[cfg(feature = "std")]
impl From<Pair> for Public {
fn from(x: Pair) -> Self {
x.public()
}
}
impl UncheckedFrom<[u8; 33]> for Public {
fn unchecked_from(x: [u8; 33]) -> Self {
Public(x)
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for Public {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.to_ss58check())
}
}
#[cfg(feature = "std")]
impl std::fmt::Debug for Public {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let s = self.to_ss58check();
write!(f, "{} ({}...)", crate::hexdisplay::HexDisplay::from(&&self.0[..]), &s[0..8])
}
}
#[cfg(feature = "std")]
impl Serialize for Public {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
serializer.serialize_str(&self.to_ss58check())
}
}
#[cfg(feature = "std")]
impl<'de> Deserialize<'de> for Public {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de> {
Public::from_ss58check(&String::deserialize(deserializer)?)
.map_err(|e| de::Error::custom(format!("{:?}", e)))
}
}
#[cfg(feature = "std")]
impl std::hash::Hash for Public {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}
/// A signature (a 512-bit value).
#[derive(Encode, Decode)]
pub struct Signature([u8; 65]);
impl rstd::convert::TryFrom<&[u8]> for Signature {
type Error = ();
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() == 65 {
let mut inner = [0u8; 65];
inner.copy_from_slice(data);
Ok(Signature(inner))
} else {
Err(())
}
}
}
impl Clone for Signature {
fn clone(&self) -> Self {
let mut r = [0u8; 65];
r.copy_from_slice(&self.0[..]);
Signature(r)
}
}
impl Default for Signature {
fn default() -> Self {
Signature([0u8; 65])
}
}
impl PartialEq for Signature {
fn eq(&self, b: &Self) -> bool {
self.0[..] == b.0[..]
}
}
impl Eq for Signature {}
impl From<Signature> for [u8; 65] {
fn from(v: Signature) -> [u8; 65] {
v.0
}
}
impl AsRef<[u8; 65]> for Signature {
fn as_ref(&self) -> &[u8; 65] {
&self.0
}
}
impl AsRef<[u8]> for Signature {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
impl AsMut<[u8]> for Signature {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0[..]
}
}
#[cfg(feature = "std")]
impl std::fmt::Debug for Signature {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0))
}
}
#[cfg(feature = "std")]
impl std::hash::Hash for Signature {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::hash::Hash::hash(&self.0[..], state);
}
}
impl Signature {
/// A new instance from the given 65-byte `data`.
///
/// NOTE: No checking goes on to ensure this is a real signature. Only use it if
/// you are certain that the array actually is a signature. GIGO!
pub fn from_raw(data: [u8; 65]) -> Signature {
Signature(data)
}
/// Recover the public key from this signature and a message.
#[cfg(feature = "std")]
pub fn recover<M: AsRef<[u8]>>(&self, message: M) -> Option<Public> {
let message = secp256k1::Message::parse(&blake2_256(message.as_ref()));
let sig: (_, _) = self.try_into().ok()?;
secp256k1::recover(&message, &sig.0, &sig.1).ok()
.map(|recovered| Public(recovered.serialize_compressed()))
}
}
#[cfg(feature = "std")]
impl From<(secp256k1::Signature, secp256k1::RecoveryId)> for Signature {
fn from(x: (secp256k1::Signature, secp256k1::RecoveryId)) -> Signature {
let mut r = Self::default();
r.0[0..64].copy_from_slice(&x.0.serialize()[..]);
r.0[64] = x.1.serialize();
r
}
}
#[cfg(feature = "std")]
impl<'a> TryFrom<&'a Signature> for (secp256k1::Signature, secp256k1::RecoveryId) {
type Error = ();
fn try_from(x: &'a Signature) -> Result<(secp256k1::Signature, secp256k1::RecoveryId), Self::Error> {
Ok((
secp256k1::Signature::parse_slice(&x.0[0..64]).expect("hardcoded to 64 bytes; qed"),
secp256k1::RecoveryId::parse(x.0[64]).map_err(|_| ())?,
))
}
}
/// An error type for SS58 decoding.
#[cfg(feature = "std")]
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub enum PublicError {
/// Bad alphabet.
BadBase58,
/// Bad length.
BadLength,
/// Unknown version.
UnknownVersion,
/// Invalid checksum.
InvalidChecksum,
}
impl Public {
/// A new instance from the given 33-byte `data`.
///
/// 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_raw(data: [u8; 33]) -> Self {
Public(data)
}
/// Return a slice filled with raw data.
pub fn as_array_ref(&self) -> &[u8; 33] {
self.as_ref()
}
}
impl TraitPublic for Public {
/// A new instance from the given slice that should be 33 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; 33];
r.copy_from_slice(data);
Public(r)
}
}
impl Derive for Public {}
/// Derive a single hard junction.
#[cfg(feature = "std")]
fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed {
("Secp256k1HDKD", secret_seed, cc).using_encoded(|data| {
let mut res = [0u8; 32];
res.copy_from_slice(blake2_rfc::blake2b::blake2b(32, &[], data).as_bytes());
res
})
}
/// An error when deriving a key.
#[cfg(feature = "std")]
pub enum DeriveError {
/// A soft key was found in the path (and is unsupported).
SoftKeyInPath,
}
#[cfg(feature = "std")]
impl TraitPair for Pair {
type Public = Public;
type Seed = Seed;
type Signature = Signature;
type DeriveError = DeriveError;
/// Generate new secure (random) key pair and provide the recovery phrase.
///
/// You can recover the same key later with `from_phrase`.
fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) {
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
let phrase = mnemonic.phrase();
let (pair, seed) = Self::from_phrase(phrase, password)
.expect("All phrases generated by Mnemonic are valid; qed");
(
pair,
phrase.to_owned(),
seed,
)
}
/// Generate key pair from given recovery phrase and password.
fn from_phrase(phrase: &str, password: Option<&str>) -> Result<(Pair, Seed), SecretStringError> {
let big_seed = seed_from_entropy(
Mnemonic::from_phrase(phrase, Language::English)
.map_err(|_| SecretStringError::InvalidPhrase)?.entropy(),
password.unwrap_or(""),
).map_err(|_| SecretStringError::InvalidSeed)?;
let mut seed = Seed::default();
seed.copy_from_slice(&big_seed[0..32]);
Self::from_seed_slice(&big_seed[0..32]).map(|x| (x, seed))
}
/// Make a new key pair from secret seed material.
///
/// You should never need to use this; generate(), generate_with_phrase
fn from_seed(seed: &Seed) -> Pair {
Self::from_seed_slice(&seed[..]).expect("seed has valid length; qed")
}
/// Make a new key pair from secret seed material. The slice must be 32 bytes long or it
/// will return `None`.
///
/// You should never need to use this; generate(), generate_with_phrase
fn from_seed_slice(seed_slice: &[u8]) -> Result<Pair, SecretStringError> {
let secret = SecretKey::parse_slice(seed_slice)
.map_err(|_| SecretStringError::InvalidSeedLength)?;
let public = PublicKey::from_secret_key(&secret);
Ok(Pair{ secret, public })
}
/// Derive a child key from a series of given junctions.
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self,
path: Iter,
_seed: Option<Seed>
) -> Result<(Pair, Option<Seed>), DeriveError> {
let mut acc = self.secret.serialize();
for j in path {
match j {
DeriveJunction::Soft(_cc) => return Err(DeriveError::SoftKeyInPath),
DeriveJunction::Hard(cc) => acc = derive_hard_junction(&acc, &cc),
}
}
Ok((Self::from_seed(&acc), Some(acc)))
}
/// Get the public key.
fn public(&self) -> Public {
Public(self.public.serialize_compressed())
}
/// Sign a message.
fn sign(&self, message: &[u8]) -> Signature {
let message = secp256k1::Message::parse(&blake2_256(message));
secp256k1::sign(&message, &self.secret).into()
}
/// Verify a signature on a message. Returns true if the signature is good.
fn verify<M: AsRef<[u8]>>(sig: &Self::Signature, message: M, pubkey: &Self::Public) -> bool {
let message = secp256k1::Message::parse(&blake2_256(message.as_ref()));
let sig: (_, _) = match sig.try_into() { Ok(x) => x, _ => return false };
match secp256k1::recover(&message, &sig.0, &sig.1) {
Ok(actual) => &pubkey.0[..] == &actual.serialize_compressed()[..],
_ => false,
}
}
/// Verify a signature on a message. Returns true if the signature is good.
///
/// This doesn't use the type system to ensure that `sig` and `pubkey` are the correct
/// size. Use it only if you're coming from byte buffers and need the speed.
fn verify_weak<P: AsRef<[u8]>, M: AsRef<[u8]>>(sig: &[u8], message: M, pubkey: P) -> bool {
let message = secp256k1::Message::parse(&blake2_256(message.as_ref()));
if sig.len() != 65 { return false }
let ri = match secp256k1::RecoveryId::parse(sig[64]) { Ok(x) => x, _ => return false };
let sig = match secp256k1::Signature::parse_slice(&sig[0..64]) { Ok(x) => x, _ => return false };
match secp256k1::recover(&message, &sig, &ri) {
Ok(actual) => pubkey.as_ref() == &actual.serialize_compressed()[..],
_ => false,
}
}
/// Return a vec filled with raw data.
fn to_raw_vec(&self) -> Vec<u8> {
self.seed().to_vec()
}
}
#[cfg(feature = "std")]
impl Pair {
/// Get the seed for this key.
pub fn seed(&self) -> Seed {
self.secret.serialize()
}
/// Exactly as `from_string` except that if no matches are found then, the the first 32
/// characters are taken (padded with spaces as necessary) and used as the MiniSecretKey.
pub fn from_legacy_string(s: &str, password_override: Option<&str>) -> Pair {
Self::from_string(s, password_override).unwrap_or_else(|_| {
let mut padded_seed: Seed = [' ' as u8; 32];
let len = s.len().min(32);
padded_seed[..len].copy_from_slice(&s.as_bytes()[..len]);
Self::from_seed(&padded_seed)
})
}
}
impl CryptoType for Public {
#[cfg(feature="std")]
type Pair = Pair;
}
impl CryptoType for Signature {
#[cfg(feature="std")]
type Pair = Pair;
}
#[cfg(feature = "std")]
impl CryptoType for Pair {
type Pair = Pair;
}
#[cfg(test)]
mod test {
use super::*;
use hex_literal::hex;
use crate::crypto::DEV_PHRASE;
#[test]
fn default_phrase_should_be_used() {
assert_eq!(
Pair::from_string("//Alice///password", None).unwrap().public(),
Pair::from_string(&format!("{}//Alice", DEV_PHRASE), Some("password")).unwrap().public(),
);
}
#[test]
fn seed_and_derive_should_work() {
let seed = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
let pair = Pair::from_seed(&seed);
assert_eq!(pair.seed(), seed);
let path = vec![DeriveJunction::Hard([0u8; 32])];
let derived = pair.derive(path.into_iter(), None).ok().unwrap();
assert_eq!(
derived.0.seed(),
hex!("b8eefc4937200a8382d00050e050ced2d4ab72cc2ef1b061477afb51564fdd61")
);
}
#[test]
fn test_vector_should_work() {
let pair = Pair::from_seed(
&hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60")
);
let public = pair.public();
assert_eq!(public, Public::from_raw(
hex!("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91")
));
let message = b"";
let signature = hex!("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00");
let signature = Signature::from_raw(signature);
assert!(&pair.sign(&message[..]) == &signature);
assert!(Pair::verify(&signature, &message[..], &public));
}
#[test]
fn test_vector_by_string_should_work() {
let pair = Pair::from_string(
"0x9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
None
).unwrap();
let public = pair.public();
assert_eq!(public, Public::from_raw(
hex!("028db55b05db86c0b1786ca49f095d76344c9e6056b2f02701a7e7f3c20aabfd91")
));
let message = b"";
let signature = hex!("3dde91174bd9359027be59a428b8146513df80a2a3c7eda2194f64de04a69ab97b753169e94db6ffd50921a2668a48b94ca11e3d32c1ff19cfe88890aa7e8f3c00");
let signature = Signature::from_raw(signature);
assert!(&pair.sign(&message[..]) == &signature);
assert!(Pair::verify(&signature, &message[..], &public));
}
#[test]
fn generated_pair_should_work() {
let (pair, _) = Pair::generate();
let public = pair.public();
let message = b"Something important";
let signature = pair.sign(&message[..]);
assert!(Pair::verify(&signature, &message[..], &public));
assert!(!Pair::verify(&signature, b"Something else", &public));
}
#[test]
fn seeded_pair_should_work() {
let pair = Pair::from_seed(b"12345678901234567890123456789012");
let public = pair.public();
assert_eq!(public, Public::from_raw(
hex!("035676109c54b9a16d271abeb4954316a40a32bcce023ac14c8e26e958aa68fba9")
));
let message = hex!("2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee00000000000000000200d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a4500000000000000");
let signature = pair.sign(&message[..]);
println!("Correct signature: {:?}", signature);
assert!(Pair::verify(&signature, &message[..], &public));
assert!(!Pair::verify(&signature, "Other message", &public));
}
#[test]
fn generate_with_phrase_recovery_possible() {
let (pair1, phrase, _) = Pair::generate_with_phrase(None);
let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
assert_eq!(pair1.public(), pair2.public());
}
#[test]
fn generate_with_password_phrase_recovery_possible() {
let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password"));
let (pair2, _) = Pair::from_phrase(&phrase, Some("password")).unwrap();
assert_eq!(pair1.public(), pair2.public());
}
#[test]
fn password_does_something() {
let (pair1, phrase, _) = Pair::generate_with_phrase(Some("password"));
let (pair2, _) = Pair::from_phrase(&phrase, None).unwrap();
assert_ne!(pair1.public(), pair2.public());
}
#[test]
fn ss58check_roundtrip_works() {
let pair = Pair::from_seed(b"12345678901234567890123456789012");
let public = pair.public();
let s = public.to_ss58check();
println!("Correct: {}", s);
let cmp = Public::from_ss58check(&s).unwrap();
assert_eq!(cmp, public);
}
}
+6 -14
View File
@@ -406,7 +406,10 @@ impl TraitPair for Pair {
}
/// Derive a child key from a series of given junctions.
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, path: Iter) -> Result<Pair, DeriveError> {
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self,
path: Iter,
_seed: Option<Seed>,
) -> Result<(Pair, Option<Seed>), DeriveError> {
let mut acc = self.0.secret.to_bytes();
for j in path {
match j {
@@ -414,18 +417,7 @@ impl TraitPair for Pair {
DeriveJunction::Hard(cc) => acc = derive_hard_junction(&acc, &cc),
}
}
Ok(Self::from_seed(&acc))
}
/// Generate a key from the phrase, password and derivation path.
fn from_standard_components<I: Iterator<Item=DeriveJunction>>(
phrase: &str,
password: Option<&str>,
path: I
) -> Result<Pair, SecretStringError> {
Self::from_phrase(phrase, password)?.0
.derive(path)
.map_err(|_| SecretStringError::InvalidPath)
Ok((Self::from_seed(&acc), Some(acc)))
}
/// Get the public key.
@@ -528,7 +520,7 @@ mod test {
let pair = Pair::from_seed(&seed);
assert_eq!(pair.seed(), &seed);
let path = vec![DeriveJunction::Hard([0u8; 32])];
let derived = pair.derive(path.into_iter()).ok().unwrap();
let derived = pair.derive(path.into_iter(), None).ok().unwrap().0;
assert_eq!(
derived.seed(),
&hex!("ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c")
+1 -1
View File
@@ -80,7 +80,7 @@ macro_rules! impl_non_endians {
impl_non_endians!([u8; 1], [u8; 2], [u8; 3], [u8; 4], [u8; 5], [u8; 6], [u8; 7], [u8; 8],
[u8; 10], [u8; 12], [u8; 14], [u8; 16], [u8; 20], [u8; 24], [u8; 28], [u8; 32], [u8; 40],
[u8; 48], [u8; 56], [u8; 64], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);
[u8; 48], [u8; 56], [u8; 64], [u8; 65], [u8; 80], [u8; 96], [u8; 112], [u8; 128]);
/// Format into ASCII + # + hex, suitable for storage key preimages.
pub fn ascii_format(asciish: &[u8]) -> String {
+1
View File
@@ -59,6 +59,7 @@ pub mod u32_trait;
pub mod ed25519;
pub mod sr25519;
pub mod ecdsa;
pub mod hash;
mod hasher;
pub mod offchain;
+28 -25
View File
@@ -388,8 +388,8 @@ impl AsRef<schnorrkel::Keypair> for Pair {
/// Derive a single hard junction.
#[cfg(feature = "std")]
fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> SecretKey {
secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0.expand(ExpansionMode::Ed25519)
fn derive_hard_junction(secret: &SecretKey, cc: &[u8; CHAIN_CODE_LENGTH]) -> MiniSecretKey {
secret.hard_derive_mini_secret_key(Some(ChainCode(cc.clone())), b"").0
}
/// The raw secret seed, which can be used to recreate the `Pair`.
@@ -444,17 +444,6 @@ impl TraitPair for Pair {
}
}
/// Generate a key from the phrase, password and derivation path.
fn from_standard_components<I: Iterator<Item=DeriveJunction>>(
phrase: &str,
password: Option<&str>,
path: I
) -> Result<Pair, SecretStringError> {
Self::from_phrase(phrase, password)?.0
.derive(path)
.map_err(|_| SecretStringError::InvalidPath)
}
fn generate_with_phrase(password: Option<&str>) -> (Pair, String, Seed) {
let mnemonic = Mnemonic::new(MnemonicType::Words12, Language::English);
let phrase = mnemonic.phrase();
@@ -473,13 +462,27 @@ impl TraitPair for Pair {
.map(|m| Self::from_entropy(m.entropy(), password))
}
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self, path: Iter) -> Result<Pair, Self::DeriveError> {
fn derive<Iter: Iterator<Item=DeriveJunction>>(&self,
path: Iter,
seed: Option<Seed>,
) -> Result<(Pair, Option<Seed>), Self::DeriveError> {
let seed = if let Some(s) = seed {
if let Ok(msk) = MiniSecretKey::from_bytes(&s) {
if msk.expand(ExpansionMode::Ed25519) == self.0.secret {
Some(msk)
} else { None }
} else { None }
} else { None };
let init = self.0.secret.clone();
let result = path.fold(init, |acc, j| match j {
DeriveJunction::Soft(cc) => acc.derived_key_simple(ChainCode(cc), &[]).0,
DeriveJunction::Hard(cc) => derive_hard_junction(&acc, &cc),
let (result, seed) = path.fold((init, seed), |(acc, acc_seed), j| match (j, acc_seed) {
(DeriveJunction::Soft(cc), _) =>
(acc.derived_key_simple(ChainCode(cc), &[]).0, None),
(DeriveJunction::Hard(cc), maybe_seed) => {
let seed = derive_hard_junction(&acc, &cc);
(seed.expand(ExpansionMode::Ed25519), maybe_seed.map(|_| seed))
}
});
Ok(Self(result.into()))
Ok((Self(result.into()), seed.map(|s| MiniSecretKey::to_bytes(&s))))
}
fn sign(&self, message: &[u8]) -> Signature {
@@ -621,9 +624,9 @@ mod test {
let pair = Pair::from_seed(&hex!(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"
));
let derive_1 = pair.derive(Some(DeriveJunction::soft(1)).into_iter()).unwrap();
let derive_1b = pair.derive(Some(DeriveJunction::soft(1)).into_iter()).unwrap();
let derive_2 = pair.derive(Some(DeriveJunction::soft(2)).into_iter()).unwrap();
let derive_1 = pair.derive(Some(DeriveJunction::soft(1)).into_iter(), None).unwrap().0;
let derive_1b = pair.derive(Some(DeriveJunction::soft(1)).into_iter(), None).unwrap().0;
let derive_2 = pair.derive(Some(DeriveJunction::soft(2)).into_iter(), None).unwrap().0;
assert_eq!(derive_1.public(), derive_1b.public());
assert_ne!(derive_1.public(), derive_2.public());
}
@@ -633,9 +636,9 @@ mod test {
let pair = Pair::from_seed(&hex!(
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"
));
let derive_1 = pair.derive(Some(DeriveJunction::hard(1)).into_iter()).unwrap();
let derive_1b = pair.derive(Some(DeriveJunction::hard(1)).into_iter()).unwrap();
let derive_2 = pair.derive(Some(DeriveJunction::hard(2)).into_iter()).unwrap();
let derive_1 = pair.derive(Some(DeriveJunction::hard(1)).into_iter(), None).unwrap().0;
let derive_1b = pair.derive(Some(DeriveJunction::hard(1)).into_iter(), None).unwrap().0;
let derive_2 = pair.derive(Some(DeriveJunction::hard(2)).into_iter(), None).unwrap().0;
assert_eq!(derive_1.public(), derive_1b.public());
assert_ne!(derive_1.public(), derive_2.public());
}
@@ -646,7 +649,7 @@ mod test {
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"
));
let path = Some(DeriveJunction::soft(1));
let pair_1 = pair.derive(path.clone().into_iter()).unwrap();
let pair_1 = pair.derive(path.clone().into_iter(), None).unwrap().0;
let public_1 = pair.public().derive(path.into_iter()).unwrap();
assert_eq!(pair_1.public(), public_1);
}
+7 -2
View File
@@ -220,15 +220,20 @@ export_api! {
/// Verify and recover a SECP256k1 ECDSA signature.
/// - `sig` is passed in RSV format. V should be either 0/1 or 27/28.
/// - returns `Err` if the signature is bad, otherwise the 64-byte pubkey (doesn't include the 0x04 prefix).
/// - returns `Err` if the signature is bad, otherwise the 64-byte raw pubkey (doesn't include the 0x04 prefix).
fn secp256k1_ecdsa_recover(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 64], EcdsaVerifyError>;
/// Verify and recover a SECP256k1 ECDSA signature.
/// - `sig` is passed in RSV format. V should be either 0/1 or 27/28.
/// - returns `Err` if the signature is bad, otherwise the 33-byte compressed pubkey.
fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError>;
}
}
export_api! {
pub(crate) trait HashingApi {
/// Conduct a 256-bit Keccak hash.
fn keccak_256(data: &[u8]) -> [u8; 32] ;
fn keccak_256(data: &[u8]) -> [u8; 32];
/// Conduct a 128-bit Blake2 hash.
fn blake2_128(data: &[u8]) -> [u8; 16];
+10
View File
@@ -300,6 +300,16 @@ impl CryptoApi for () {
res.copy_from_slice(&pubkey.serialize()[1..65]);
Ok(res)
}
fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError> {
let rs = secp256k1::Signature::parse_slice(&sig[0..64])
.map_err(|_| EcdsaVerifyError::BadRS)?;
let v = secp256k1::RecoveryId::parse(if sig[64] > 26 { sig[64] - 27 } else { sig[64] } as u8)
.map_err(|_| EcdsaVerifyError::BadV)?;
let pubkey = secp256k1::recover(&secp256k1::Message::parse(msg), &rs, &v)
.map_err(|_| EcdsaVerifyError::BadSignature)?;
Ok(pubkey.serialize_compressed())
}
}
impl HashingApi for () {
+24
View File
@@ -394,12 +394,23 @@ pub mod ext {
) -> u32;
/// Note: ext_secp256k1_ecdsa_recover returns 0 if the signature is correct, nonzero otherwise.
///
/// pubkey_data must point to 64 bytes.
fn ext_secp256k1_ecdsa_recover(
msg_data: *const u8,
sig_data: *const u8,
pubkey_data: *mut u8,
) -> u32;
/// Note: ext_secp256k1_ecdsa_recover_compressed returns 0 if the signature is correct, nonzero otherwise.
///
/// pubkey_data must point to 33 bytes.
fn ext_secp256k1_ecdsa_recover_compressed(
msg_data: *const u8,
sig_data: *const u8,
pubkey_data: *mut u8,
) -> u32;
//================================
// Offchain-worker Context
//================================
@@ -971,6 +982,19 @@ impl CryptoApi for () {
_ => unreachable!("`ext_secp256k1_ecdsa_recover` only returns 0, 1, 2 or 3; qed"),
}
}
fn secp256k1_ecdsa_recover_compressed(sig: &[u8; 65], msg: &[u8; 32]) -> Result<[u8; 33], EcdsaVerifyError> {
let mut pubkey = [0u8; 33];
match unsafe {
ext_secp256k1_ecdsa_recover_compressed.get()(msg.as_ptr(), sig.as_ptr(), pubkey.as_mut_ptr())
} {
0 => Ok(pubkey),
1 => Err(EcdsaVerifyError::BadRS),
2 => Err(EcdsaVerifyError::BadV),
3 => Err(EcdsaVerifyError::BadSignature),
_ => unreachable!("`ext_secp256k1_ecdsa_recover_compressed` only returns 0, 1, 2 or 3; qed"),
}
}
}
impl OffchainApi for () {
@@ -21,7 +21,7 @@ use rstd::fmt;
use runtime_io::blake2_256;
use codec::{Decode, Encode, EncodeLike, Input, Error};
use crate::{
traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic},
traits::{self, Member, MaybeDisplay, SignedExtension, Checkable, Extrinsic, IdentifyAccount},
generic::CheckedExtrinsic, transaction_validity::{TransactionValidityError, InvalidTransaction},
};
@@ -98,7 +98,8 @@ for
where
Address: Member + MaybeDisplay,
Call: Encode + Member,
Signature: Member + traits::Verify<Signer=AccountId>,
Signature: Member + traits::Verify,
<Signature as traits::Verify>::Signer: IdentifyAccount<AccountId=AccountId>,
Extra: SignedExtension<AccountId=AccountId>,
AccountId: Member + MaybeDisplay,
Lookup: traits::Lookup<Source=Address, Target=AccountId>,
@@ -284,17 +285,26 @@ mod tests {
use super::*;
use runtime_io::blake2_256;
use crate::codec::{Encode, Decode};
use crate::traits::{SignedExtension, IdentityLookup};
use crate::traits::{SignedExtension, IdentifyAccount, IdentityLookup};
use serde::{Serialize, Deserialize};
type TestContext = IdentityLookup<u64>;
#[derive(Eq, PartialEq, Clone, Copy, Debug, Serialize, Deserialize, Encode, Decode)]
pub struct TestSigner(pub u64);
impl From<u64> for TestSigner { fn from(x: u64) -> Self { Self(x) } }
impl From<TestSigner> for u64 { fn from(x: TestSigner) -> Self { x.0 } }
impl IdentifyAccount for TestSigner {
type AccountId = u64;
fn into_account(self) -> u64 { self.into() }
}
#[derive(Eq, PartialEq, Clone, Debug, Serialize, Deserialize, Encode, Decode)]
struct TestSig(u64, Vec<u8>);
impl traits::Verify for TestSig {
type Signer = u64;
fn verify<L: traits::Lazy<[u8]>>(&self, mut msg: L, signer: &Self::Signer) -> bool {
*signer == self.0 && msg.get() == &self.1[..]
type Signer = TestSigner;
fn verify<L: traits::Lazy<[u8]>>(&self, mut msg: L, signer: &u64) -> bool {
signer == &self.0 && msg.get() == &self.1[..]
}
}
+49 -11
View File
@@ -42,7 +42,7 @@ pub use runtime_io::{StorageOverlay, ChildrenStorageOverlay};
use rstd::prelude::*;
use rstd::convert::TryFrom;
use primitives::{crypto, ed25519, sr25519, hash::{H256, H512}};
use primitives::{crypto, ed25519, sr25519, ecdsa, hash::{H256, H512}};
use codec::{Encode, Decode};
#[cfg(feature = "std")]
@@ -59,7 +59,7 @@ pub mod weights;
pub use generic::{DigestItem, Digest};
/// Re-export this since it's part of the API of this crate.
pub use primitives::{TypeId, crypto::{key_types, KeyTypeId, CryptoType}};
pub use primitives::{TypeId, crypto::{key_types, KeyTypeId, CryptoType, AccountId32}};
pub use app_crypto::RuntimeAppPublic;
/// Re-export `RuntimeDebug`, to avoid dependency clutter.
@@ -117,6 +117,7 @@ macro_rules! create_runtime_str {
#[cfg(feature = "std")]
pub use serde::{Serialize, Deserialize, de::DeserializeOwned};
use crate::traits::IdentifyAccount;
/// Complex storage builder stuff.
#[cfg(feature = "std")]
@@ -175,6 +176,8 @@ pub enum MultiSignature {
Ed25519(ed25519::Signature),
/// An Sr25519 signature.
Sr25519(sr25519::Signature),
/// An ECDSA/SECP256k1 signature.
Ecdsa(ecdsa::Signature),
}
impl From<ed25519::Signature> for MultiSignature {
@@ -189,6 +192,12 @@ impl From<sr25519::Signature> for MultiSignature {
}
}
impl From<ecdsa::Signature> for MultiSignature {
fn from(x: ecdsa::Signature) -> Self {
MultiSignature::Ecdsa(x)
}
}
impl Default for MultiSignature {
fn default() -> Self {
MultiSignature::Ed25519(Default::default())
@@ -203,6 +212,8 @@ pub enum MultiSigner {
Ed25519(ed25519::Public),
/// An Sr25519 identity.
Sr25519(sr25519::Public),
/// An SECP256k1/ECDSA identity (actually, the Blake2 hash of the pub key).
Ecdsa(ecdsa::Public),
}
impl Default for MultiSigner {
@@ -224,6 +235,18 @@ impl AsRef<[u8]> for MultiSigner {
match *self {
MultiSigner::Ed25519(ref who) => who.as_ref(),
MultiSigner::Sr25519(ref who) => who.as_ref(),
MultiSigner::Ecdsa(ref who) => who.as_ref(),
}
}
}
impl traits::IdentifyAccount for MultiSigner {
type AccountId = AccountId32;
fn into_account(self) -> AccountId32 {
match self {
MultiSigner::Ed25519(who) => <[u8; 32]>::from(who).into(),
MultiSigner::Sr25519(who) => <[u8; 32]>::from(who).into(),
MultiSigner::Ecdsa(who) => runtime_io::blake2_256(who.as_ref()).into(),
}
}
}
@@ -240,23 +263,37 @@ impl From<sr25519::Public> for MultiSigner {
}
}
#[cfg(feature = "std")]
impl From<ecdsa::Public> for MultiSigner {
fn from(x: ecdsa::Public) -> Self {
MultiSigner::Ecdsa(x)
}
}
#[cfg(feature = "std")]
impl std::fmt::Display for MultiSigner {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
match *self {
MultiSigner::Ed25519(ref who) => write!(fmt, "ed25519: {}", who),
MultiSigner::Sr25519(ref who) => write!(fmt, "sr25519: {}", who),
MultiSigner::Ecdsa(ref who) => write!(fmt, "ecdsa: {}", who),
}
}
}
impl Verify for MultiSignature {
type Signer = MultiSigner;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool {
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &AccountId32) -> bool {
use primitives::crypto::Public;
match (self, signer) {
(MultiSignature::Ed25519(ref sig), &MultiSigner::Ed25519(ref who)) => sig.verify(msg, who),
(MultiSignature::Sr25519(ref sig), &MultiSigner::Sr25519(ref who)) => sig.verify(msg, who),
_ => false,
(MultiSignature::Ed25519(ref sig), who) => sig.verify(msg, &ed25519::Public::from_slice(who.as_ref())),
(MultiSignature::Sr25519(ref sig), who) => sig.verify(msg, &sr25519::Public::from_slice(who.as_ref())),
(MultiSignature::Ecdsa(ref sig), who) => {
let m = runtime_io::blake2_256(msg.get());
match runtime_io::secp256k1_ecdsa_recover_compressed(sig.as_ref(), &m) {
Ok(pubkey) => &runtime_io::blake2_256(pubkey.as_ref()) == <dyn AsRef<[u8; 32]>>::as_ref(who),
_ => false,
}
}
}
}
}
@@ -269,12 +306,13 @@ pub struct AnySignature(H512);
impl Verify for AnySignature {
type Signer = sr25519::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &sr25519::Public) -> bool {
use primitives::crypto::Public;
let msg = msg.get();
sr25519::Signature::try_from(self.0.as_fixed_bytes().as_ref())
.map(|s| runtime_io::sr25519_verify(&s, msg.get(), &signer))
.map(|s| s.verify(msg, signer))
.unwrap_or(false)
|| ed25519::Signature::try_from(self.0.as_fixed_bytes().as_ref())
.and_then(|s| ed25519::Public::try_from(signer.0.as_ref()).map(|p| (s, p)))
.map(|(s, p)| runtime_io::ed25519_verify(&s, msg.get(), &p))
.map(|s| s.verify(msg, &ed25519::Public::from_slice(signer.as_ref())))
.unwrap_or(false)
}
}
@@ -398,7 +436,7 @@ impl From<&'static str> for DispatchError {
/// Verify a signature on an encoded value in a lazy manner. This can be
/// an optimization if the signature scheme has an "unsigned" escape hash.
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signer: &V::Signer) -> bool {
pub fn verify_encoded_lazy<V: Verify, T: codec::Encode>(sig: &V, item: &T, signer: &<V::Signer as IdentifyAccount>::AccountId) -> bool {
// The `Lazy<T>` trait expresses something like `X: FnMut<Output = for<'a> &'a T>`.
// unfortunately this is a lifetime relationship that can't
// be expressed without generic associated types, better unification of HRTBs in type position,
+63 -10
View File
@@ -50,46 +50,84 @@ impl<'a> Lazy<[u8]> for &'a [u8] {
fn get(&mut self) -> &[u8] { &**self }
}
/// Some type that is able to be collapsed into an account ID. It is not possible to recreate the original value from
/// the account ID.
pub trait IdentifyAccount {
/// The account ID that this can be transformed into.
type AccountId;
/// Transform into an account.
fn into_account(self) -> Self::AccountId;
}
impl IdentifyAccount for primitives::ed25519::Public {
type AccountId = Self;
fn into_account(self) -> Self { self }
}
impl IdentifyAccount for primitives::sr25519::Public {
type AccountId = Self;
fn into_account(self) -> Self { self }
}
impl IdentifyAccount for primitives::ecdsa::Public {
type AccountId = Self;
fn into_account(self) -> Self { self }
}
/// Means of signature verification.
pub trait Verify {
/// Type of the signer.
type Signer;
type Signer: IdentifyAccount;
/// Verify a signature. Return `true` if signature is valid for the value.
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &<Self::Signer as IdentifyAccount>::AccountId) -> bool;
}
impl Verify for primitives::ed25519::Signature {
type Signer = primitives::ed25519::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &Self::Signer) -> bool {
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &primitives::ed25519::Public) -> bool {
runtime_io::ed25519_verify(self, msg.get(), signer)
}
}
impl Verify for primitives::sr25519::Signature {
type Signer = primitives::sr25519::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &Self::Signer) -> bool {
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &primitives::sr25519::Public) -> bool {
runtime_io::sr25519_verify(self, msg.get(), signer)
}
}
impl Verify for primitives::ecdsa::Signature {
type Signer = primitives::ecdsa::Public;
fn verify<L: Lazy<[u8]>>(&self, mut msg: L, signer: &primitives::ecdsa::Public) -> bool {
match runtime_io::secp256k1_ecdsa_recover_compressed(self.as_ref(), &runtime_io::blake2_256(msg.get())) {
Ok(pubkey) => <dyn AsRef<[u8]>>::as_ref(signer) == &pubkey[..],
_ => false,
}
}
}
/// Means of signature verification of an application key.
pub trait AppVerify {
/// Type of the signer.
type Signer;
type AccountId;
/// Verify a signature. Return `true` if signature is valid for the value.
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::AccountId) -> bool;
}
impl<
S: Verify<Signer=<<T as AppKey>::Public as app_crypto::AppPublic>::Generic> + From<T>,
T: app_crypto::Wraps<Inner=S> + app_crypto::AppKey + app_crypto::AppSignature +
AsRef<S> + AsMut<S> + From<S>,
> AppVerify for T {
type Signer = <T as AppKey>::Public;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &Self::Signer) -> bool {
> AppVerify for T where
<S as Verify>::Signer: IdentifyAccount<AccountId = <S as Verify>::Signer>,
<<T as AppKey>::Public as app_crypto::AppPublic>::Generic:
IdentifyAccount<AccountId = <<T as AppKey>::Public as app_crypto::AppPublic>::Generic>,
{
type AccountId = <T as AppKey>::Public;
fn verify<L: Lazy<[u8]>>(&self, msg: L, signer: &<T as AppKey>::Public) -> bool {
use app_crypto::IsWrappedBy;
let inner: &S = self.as_ref();
let inner_pubkey = <Self::Signer as app_crypto::AppPublic>::Generic::from_ref(&signer);
let inner_pubkey = <<T as AppKey>::Public as app_crypto::AppPublic>::Generic::from_ref(&signer);
Verify::verify(inner, msg, inner_pubkey)
}
}
@@ -1179,6 +1217,21 @@ mod tests {
use super::AccountIdConversion;
use crate::codec::{Encode, Decode, Input};
mod t {
use primitives::crypto::KeyTypeId;
use app_crypto::{app_crypto, sr25519};
app_crypto!(sr25519, KeyTypeId(*b"test"));
}
#[test]
fn app_verify_works() {
use t::*;
use super::AppVerify;
let s = Signature::default();
let _ = s.verify(&[0u8; 100][..], &Public::default());
}
#[derive(Encode, Decode, Default, PartialEq, Debug)]
struct U32Value(u32);
impl super::TypeId for U32Value {
+38 -29
View File
@@ -17,7 +17,7 @@
//! Substrate chain configurations.
use chain_spec::ChainSpecExtension;
use primitives::{Pair, Public, crypto::UncheckedInto};
use primitives::{Pair, Public, crypto::UncheckedInto, sr25519};
use serde::{Serialize, Deserialize};
use node_runtime::{
AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig,
@@ -32,10 +32,13 @@ use substrate_telemetry::TelemetryEndpoints;
use grandpa_primitives::{AuthorityId as GrandpaId};
use babe_primitives::{AuthorityId as BabeId};
use im_online::sr25519::{AuthorityId as ImOnlineId};
use sr_primitives::Perbill;
use sr_primitives::{traits::Verify, Perbill};
pub use node_primitives::{AccountId, Balance};
pub use node_primitives::{AccountId, Balance, Signature};
pub use node_runtime::GenesisConfig;
use sr_primitives::traits::IdentifyAccount;
type AccountPublic = <Signature as Verify>::Signer;
const STAGING_TELEMETRY_URL: &str = "wss://telemetry.polkadot.io/submit/";
@@ -72,9 +75,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
let initial_authorities: Vec<(AccountId, AccountId, GrandpaId, BabeId, ImOnlineId)> = vec![(
// 5Fbsd6WXDGiLTxunqeK5BATNiocfCqu9bS1yArVjCgeBLkVy
hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].unchecked_into(),
hex!["9c7a2ee14e565db0c69f78c7b4cd839fbf52b607d867e9e9c5a79042898a0d12"].into(),
// 5EnCiV7wSHeNhjW3FSUwiJNkcc2SBkPLn5Nj93FmbLtBjQUq
hex!["781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"].unchecked_into(),
hex!["781ead1e2fa9ccb74b44c19d29cb2a7a4b5be3972927ae98cd3877523976a276"].into(),
// 5Fb9ayurnxnaXj56CjmyQLBiadfRCqUbL2VWNbbe1nZU6wiC
hex!["9becad03e6dcac03cee07edebca5475314861492cdfc96a2144a67bbe9699332"].unchecked_into(),
// 5EZaeQ8djPcq9pheJUhgerXQZt9YaHnMJpiHMRhwQeinqUW8
@@ -83,9 +86,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
hex!["6e7e4eb42cbd2e0ab4cae8708ce5509580b8c04d11f6758dbf686d50fe9f9106"].unchecked_into(),
),(
// 5ERawXCzCWkjVq3xz1W5KGNtVx2VdefvZ62Bw1FEuZW4Vny2
hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].unchecked_into(),
hex!["68655684472b743e456907b398d3a44c113f189e56d1bbfd55e889e295dfde78"].into(),
// 5Gc4vr42hH1uDZc93Nayk5G7i687bAQdHHc9unLuyeawHipF
hex!["c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"].unchecked_into(),
hex!["c8dc79e36b29395413399edaec3e20fcca7205fb19776ed8ddb25d6f427ec40e"].into(),
// 5EockCXN6YkiNCDjpqqnbcqd4ad35nU4RmA1ikM4YeRN4WcE
hex!["7932cff431e748892fa48e10c63c17d30f80ca42e4de3921e641249cd7fa3c2f"].unchecked_into(),
// 5DhLtiaQd1L1LU9jaNeeu9HJkP6eyg3BwXA7iNMzKm7qqruQ
@@ -94,9 +97,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
hex!["482dbd7297a39fa145c570552249c2ca9dd47e281f0c500c971b59c9dcdcd82e"].unchecked_into(),
),(
// 5DyVtKWPidondEu8iHZgi6Ffv9yrJJ1NDNLom3X9cTDi98qp
hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].unchecked_into(),
hex!["547ff0ab649283a7ae01dbc2eb73932eba2fb09075e9485ff369082a2ff38d65"].into(),
// 5FeD54vGVNpFX3PndHPXJ2MDakc462vBCD5mgtWRnWYCpZU9
hex!["9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"].unchecked_into(),
hex!["9e42241d7cd91d001773b0b616d523dd80e13c6c2cab860b1234ef1b9ffc1526"].into(),
// 5E1jLYfLdUQKrFrtqoKgFrRvxM3oQPMbf6DfcsrugZZ5Bn8d
hex!["5633b70b80a6c8bb16270f82cca6d56b27ed7b76c8fd5af2986a25a4788ce440"].unchecked_into(),
// 5DhKqkHRkndJu8vq7pi2Q5S3DfftWJHGxbEUNH43b46qNspH
@@ -105,9 +108,9 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
hex!["482a3389a6cf42d8ed83888cfd920fec738ea30f97e44699ada7323f08c3380a"].unchecked_into(),
),(
// 5HYZnKWe5FVZQ33ZRJK1rG3WaLMztxWrrNDb1JRwaHHVWyP9
hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].unchecked_into(),
hex!["f26cdb14b5aec7b2789fd5ca80f979cef3761897ae1f37ffb3e154cbcc1c2663"].into(),
// 5EPQdAQ39WQNLCRjWsCk5jErsCitHiY5ZmjfWzzbXDoAoYbn
hex!["66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"].unchecked_into(),
hex!["66bc1e5d275da50b72b15de072a2468a5ad414919ca9054d2695767cf650012f"].into(),
// 5DMa31Hd5u1dwoRKgC4uvqyrdK45RHv3CpwvpUC1EzuwDit4
hex!["3919132b851ef0fd2dae42a7e734fe547af5a6b809006100f48944d7fae8e8ef"].unchecked_into(),
// 5C4vDQxA8LTck2xJEy4Yg1hM9qjDt4LvTQaMo4Y8ne43aU6x
@@ -120,7 +123,7 @@ fn staging_testnet_config_genesis() -> GenesisConfig {
let root_key: AccountId = hex![
// 5Ff3iXP75ruzroPWRP2FYBHWnmGGBSb63857BgnzCoXNxfPo
"9ee5e5bdc0ec239eb164f865ecc345ce4c88e76ee002e0f7e318097347471809"
].unchecked_into();
].into();
let endowed_accounts: Vec<AccountId> = vec![root_key.clone()];
@@ -154,12 +157,18 @@ pub fn get_from_seed<TPublic: Public>(seed: &str) -> <TPublic::Pair as Pair>::Pu
.public()
}
/// Helper function to generate an account ID from seed
pub fn get_account_id_from_seed<TPublic: Public>(seed: &str) -> AccountId where
AccountPublic: From<<TPublic::Pair as Pair>::Public>
{
AccountPublic::from(get_from_seed::<TPublic>(seed)).into_account()
}
/// Helper function to generate stash, controller and session key from seed
pub fn get_authority_keys_from_seed(seed: &str) -> (AccountId, AccountId, GrandpaId, BabeId, ImOnlineId) {
(
get_from_seed::<AccountId>(&format!("{}//stash", seed)),
get_from_seed::<AccountId>(seed),
get_account_id_from_seed::<sr25519::Public>(&format!("{}//stash", seed)),
get_account_id_from_seed::<sr25519::Public>(seed),
get_from_seed::<GrandpaId>(seed),
get_from_seed::<BabeId>(seed),
get_from_seed::<ImOnlineId>(seed),
@@ -175,18 +184,18 @@ pub fn testnet_genesis(
) -> GenesisConfig {
let endowed_accounts: Vec<AccountId> = endowed_accounts.unwrap_or_else(|| {
vec![
get_from_seed::<AccountId>("Alice"),
get_from_seed::<AccountId>("Bob"),
get_from_seed::<AccountId>("Charlie"),
get_from_seed::<AccountId>("Dave"),
get_from_seed::<AccountId>("Eve"),
get_from_seed::<AccountId>("Ferdie"),
get_from_seed::<AccountId>("Alice//stash"),
get_from_seed::<AccountId>("Bob//stash"),
get_from_seed::<AccountId>("Charlie//stash"),
get_from_seed::<AccountId>("Dave//stash"),
get_from_seed::<AccountId>("Eve//stash"),
get_from_seed::<AccountId>("Ferdie//stash"),
get_account_id_from_seed::<sr25519::Public>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Bob"),
get_account_id_from_seed::<sr25519::Public>("Charlie"),
get_account_id_from_seed::<sr25519::Public>("Dave"),
get_account_id_from_seed::<sr25519::Public>("Eve"),
get_account_id_from_seed::<sr25519::Public>("Ferdie"),
get_account_id_from_seed::<sr25519::Public>("Alice//stash"),
get_account_id_from_seed::<sr25519::Public>("Bob//stash"),
get_account_id_from_seed::<sr25519::Public>("Charlie//stash"),
get_account_id_from_seed::<sr25519::Public>("Dave//stash"),
get_account_id_from_seed::<sr25519::Public>("Eve//stash"),
get_account_id_from_seed::<sr25519::Public>("Ferdie//stash"),
]
});
@@ -272,7 +281,7 @@ fn development_config_genesis() -> GenesisConfig {
vec![
get_authority_keys_from_seed("Alice"),
],
get_from_seed::<AccountId>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Alice"),
None,
true,
)
@@ -298,7 +307,7 @@ fn local_testnet_genesis() -> GenesisConfig {
get_authority_keys_from_seed("Alice"),
get_authority_keys_from_seed("Bob"),
],
get_from_seed::<AccountId>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Alice"),
None,
false,
)
@@ -330,7 +339,7 @@ pub(crate) mod tests {
vec![
get_authority_keys_from_seed("Alice"),
],
get_from_seed::<AccountId>("Alice"),
get_account_id_from_seed::<sr25519::Public>("Alice"),
None,
false,
)
+9 -4
View File
@@ -25,16 +25,21 @@ use codec::{Encode, Decode};
use keyring::sr25519::Keyring;
use node_runtime::{
Call, CheckedExtrinsic, UncheckedExtrinsic, SignedExtra, BalancesCall, ExistentialDeposit,
MinimumPeriod,
MinimumPeriod
};
use node_primitives::Signature;
use primitives::{sr25519, crypto::Pair};
use sr_primitives::{generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension}};
use sr_primitives::{
generic::Era, traits::{Block as BlockT, Header as HeaderT, SignedExtension, Verify, IdentifyAccount}
};
use transaction_factory::RuntimeAdapter;
use transaction_factory::modes::Mode;
use inherents::InherentData;
use timestamp;
use finality_tracker;
type AccountPublic = <Signature as Verify>::Signer;
pub struct FactoryState<N> {
block_no: N,
@@ -167,7 +172,7 @@ impl RuntimeAdapter for FactoryState<Number> {
}
fn master_account_id() -> Self::AccountId {
Keyring::Alice.pair().public()
Keyring::Alice.to_account_id()
}
fn master_account_secret() -> Self::Secret {
@@ -177,7 +182,7 @@ impl RuntimeAdapter for FactoryState<Number> {
/// Generates a random `AccountId` from `seed`.
fn gen_random_account_id(seed: &Self::Number) -> Self::AccountId {
let pair: sr25519::Pair = sr25519::Pair::from_seed(&gen_seed_bytes(*seed));
pair.public().into()
AccountPublic::from(pair.public()).into_account()
}
/// Generates a random `Secret` from `seed`.
+9 -8
View File
@@ -330,17 +330,15 @@ mod tests {
use consensus_common::{
Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy, BlockImport,
};
use node_primitives::{Block, DigestItem};
use node_runtime::{BalancesCall, Call, UncheckedExtrinsic};
use node_primitives::{Block, DigestItem, Signature};
use node_runtime::{BalancesCall, Call, UncheckedExtrinsic, Address};
use node_runtime::constants::{currency::CENTS, time::SLOT_DURATION};
use codec::{Encode, Decode};
use primitives::{
crypto::Pair as CryptoPair,
sr25519::Public as AddressPublic, H256,
};
use primitives::{crypto::Pair as CryptoPair, H256};
use sr_primitives::{
generic::{BlockId, Era, Digest, SignedPayload},
traits::Block as BlockT,
traits::Verify,
OpaqueExtrinsic,
};
use timestamp;
@@ -348,6 +346,9 @@ mod tests {
use keyring::AccountKeyring;
use substrate_service::{AbstractService, Roles};
use crate::service::new_full;
use sr_primitives::traits::IdentifyAccount;
type AccountPublic = <Signature as Verify>::Signer;
#[cfg(feature = "rhd")]
fn test_sync() {
@@ -518,8 +519,8 @@ mod tests {
},
|service, _| {
let amount = 5 * CENTS;
let to = AddressPublic::from_raw(bob.public().0);
let from = AddressPublic::from_raw(charlie.public().0);
let to: Address = AccountPublic::from(bob.public()).into_account().into();
let from: Address = AccountPublic::from(charlie.public()).into_account().into();
let genesis_hash = service.client().block_hash(0).unwrap().unwrap();
let best_block_id = BlockId::number(service.client().info().chain.best_number);
let version = service.client().runtime_version_at(&best_block_id).unwrap().spec_version;
+4 -5
View File
@@ -21,21 +21,20 @@
#![cfg_attr(not(feature = "std"), no_std)]
use sr_primitives::{
generic, traits::{Verify, BlakeTwo256}, OpaqueExtrinsic, AnySignature
generic, traits::{Verify, BlakeTwo256, IdentifyAccount}, OpaqueExtrinsic, MultiSignature
};
/// An index to a block.
pub type BlockNumber = u32;
/// Alias to 512-bit hash when used in the context of a transaction signature on the chain.
pub type Signature = AnySignature;
pub type Signature = MultiSignature;
/// Some way of identifying an account on the chain. We intentionally make it equivalent
/// to the public key of our transaction signing scheme.
pub type AccountId = <Signature as Verify>::Signer;
pub type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
/// The type for looking up accounts. We don't expect more than 4 billion of them, but you
/// never know...
/// The type for looking up accounts. We don't expect more than 4 billion of them.
pub type AccountIndex = u32;
/// Balance of an account.
+3 -56
View File
@@ -42,7 +42,7 @@ use sr_primitives::curve::PiecewiseLinear;
use sr_primitives::transaction_validity::TransactionValidity;
use sr_primitives::weights::Weight;
use sr_primitives::traits::{
self, BlakeTwo256, Block as BlockT, NumberFor, StaticLookup, SaturatedConversion,
BlakeTwo256, Block as BlockT, NumberFor, StaticLookup,
};
use version::RuntimeVersion;
#[cfg(any(feature = "std", test))]
@@ -83,8 +83,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 182,
impl_version: 182,
spec_version: 183,
impl_version: 183,
apis: RUNTIME_API_VERSIONS,
};
@@ -455,34 +455,6 @@ impl finality_tracker::Trait for Runtime {
type ReportLatency = ReportLatency;
}
impl system::offchain::CreateTransaction<Runtime, UncheckedExtrinsic> for Runtime {
type Signature = Signature;
fn create_transaction<F: system::offchain::Signer<AccountId, Self::Signature>>(
call: Call,
account: AccountId,
index: Index,
) -> Option<(Call, <UncheckedExtrinsic as traits::Extrinsic>::SignaturePayload)> {
let period = 1 << 8;
let current_block = System::block_number().saturated_into::<u64>();
let tip = 0;
let extra: SignedExtra = (
system::CheckVersion::<Runtime>::new(),
system::CheckGenesis::<Runtime>::new(),
system::CheckEra::<Runtime>::from(generic::Era::mortal(period, current_block)),
system::CheckNonce::<Runtime>::from(index),
system::CheckWeight::<Runtime>::new(),
transaction_payment::ChargeTransactionPayment::<Runtime>::from(tip),
Default::default(),
);
let raw_payload = SignedPayload::new(call, extra).ok()?;
let signature = F::sign(account.clone(), &raw_payload)?;
let address = Indices::unlookup(account);
let (call, extra, _) = raw_payload.deconstruct();
Some((call, (address, signature, extra)))
}
}
construct_runtime!(
pub enum Runtime where
Block = Block,
@@ -693,28 +665,3 @@ impl_runtime_apis! {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use sr_primitives::app_crypto::RuntimeAppPublic;
use system::offchain::SubmitSignedTransaction;
fn is_submit_signed_transaction<T, Signer>(_arg: T) where
T: SubmitSignedTransaction<
Runtime,
Call,
Extrinsic=UncheckedExtrinsic,
CreateTransaction=Runtime,
Signer=Signer,
>,
Signer: RuntimeAppPublic + From<AccountId>,
Signer::Signature: Into<Signature>,
{}
#[test]
fn validate_bounds() {
let x = SubmitTransaction::default();
is_submit_signed_transaction(x);
}
}
+2 -4
View File
@@ -23,9 +23,7 @@ use sr_primitives::generic::Era;
use codec::Encode;
/// Alice's account id.
pub fn alice() -> AccountId {
AccountKeyring::Alice.into()
}
pub fn alice() -> AccountId { AccountKeyring::Alice.into() }
/// Bob's account id.
pub fn bob() -> AccountId {
@@ -82,7 +80,7 @@ pub fn sign(xt: CheckedExtrinsic, version: u32, genesis_hash: [u8; 32]) -> Unche
match xt.signed {
Some((signed, extra)) => {
let payload = (xt.function, extra.clone(), version, genesis_hash, genesis_hash);
let key = AccountKeyring::from_public(&signed).unwrap();
let key = AccountKeyring::from_account_id(&signed).unwrap();
let signature = payload.using_encoded(|b| {
if b.len() > 256 {
key.sign(&sr_io::blake2_256(b))
+6 -65
View File
@@ -21,71 +21,24 @@ use sr_primitives::app_crypto::RuntimeAppPublic;
use sr_primitives::traits::Extrinsic as ExtrinsicT;
/// A trait responsible for signing a payload using given account.
pub trait Signer<Account, Signature> {
pub trait Signer<Public, Signature> {
/// Sign any encodable payload with given account and produce a signature.
///
/// Returns `Some` if signing succeeded and `None` in case the `account` couldn't be used.
fn sign<Payload: Encode>(account: Account, payload: &Payload) -> Option<Signature>;
fn sign<Payload: Encode>(public: Public, payload: &Payload) -> Option<Signature>;
}
impl<Account, Signature, AppPublic> Signer<Account, Signature> for AppPublic where
AppPublic: RuntimeAppPublic + From<Account>,
impl<Public, Signature, AppPublic> Signer<Public, Signature> for AppPublic where
AppPublic: RuntimeAppPublic + From<Public>,
AppPublic::Signature: Into<Signature>,
{
fn sign<Payload: Encode>(account: Account, raw_payload: &Payload) -> Option<Signature> {
fn sign<Payload: Encode>(public: Public, raw_payload: &Payload) -> Option<Signature> {
raw_payload.using_encoded(|payload| {
AppPublic::from(account).sign(&payload).map(Into::into)
AppPublic::from(public).sign(&payload).map(Into::into)
})
}
}
/// Creates runtime-specific signed transaction.
pub trait CreateTransaction<T: crate::Trait, Extrinsic: ExtrinsicT> {
type Signature;
/// Attempt to create signed extrinsic data that encodes call from given account.
///
/// Runtime implementation is free to construct the payload to sign and the signature
/// in any way it wants.
/// Returns `None` if signed extrinsic could not be created (either because signing failed
/// or because of any other runtime-specific reason).
fn create_transaction<F: Signer<T::AccountId, Self::Signature>>(
call: Extrinsic::Call,
account: T::AccountId,
nonce: T::Index,
) -> Option<(Extrinsic::Call, Extrinsic::SignaturePayload)>;
}
/// A trait to sign and submit transactions in offchain calls.
pub trait SubmitSignedTransaction<T: crate::Trait, Call> {
/// Unchecked extrinsic type.
type Extrinsic: ExtrinsicT<Call=Call> + codec::Encode;
/// A runtime-specific type to produce signed data for the extrinsic.
type CreateTransaction: CreateTransaction<T, Self::Extrinsic>;
/// A type used to sign transactions created using `CreateTransaction`.
type Signer: Signer<
T::AccountId,
<Self::CreateTransaction as CreateTransaction<T, Self::Extrinsic>>::Signature,
>;
/// Sign given call and submit it to the transaction pool.
///
/// Returns `Ok` if the transaction was submitted correctly
/// and `Err` if the key for given `id` was not found or the
/// transaction was rejected from the pool.
fn sign_and_submit(call: impl Into<Call>, id: T::AccountId) -> Result<(), ()> {
let call = call.into();
let expected = <crate::Module<T>>::account_nonce(&id);
let (call, signature_data) = Self::CreateTransaction
::create_transaction::<Self::Signer>(call, id, expected)
.ok_or(())?;
let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?;
runtime_io::submit_transaction(xt.encode())
}
}
/// A trait to submit unsigned transactions in offchain calls.
pub trait SubmitUnsignedTransaction<T: crate::Trait, Call> {
/// Unchecked extrinsic type.
@@ -114,18 +67,6 @@ impl<S, C, E> Default for TransactionSubmitter<S, C, E> {
}
}
/// A blanket implementation to simplify creation of transaction signer & submitter in the runtime.
impl<T, E, S, C, Call> SubmitSignedTransaction<T, Call> for TransactionSubmitter<S, C, E> where
T: crate::Trait,
C: CreateTransaction<T, E>,
S: Signer<T::AccountId, <C as CreateTransaction<T, E>>::Signature>,
E: ExtrinsicT<Call=Call> + codec::Encode,
{
type Extrinsic = E;
type CreateTransaction = C;
type Signer = S;
}
/// A blanket impl to use the same submitter for usigned transactions as well.
impl<T, E, S, C, Call> SubmitUnsignedTransaction<T, Call> for TransactionSubmitter<S, C, E> where
T: crate::Trait,
+5
View File
@@ -12,6 +12,11 @@ args:
long: sr25519
help: Use Schnorr/Ristretto x25519/BIP39 cryptography
takes_value: false
- secp256k1:
short: k
long: secp256k1
help: Use SECP256k1/ECDSA/BIP39 cryptography
takes_value: false
- password:
short: p
long: password
+104 -42
View File
@@ -22,15 +22,15 @@ use bip39::{Language, Mnemonic, MnemonicType};
use clap::{load_yaml, App, ArgMatches};
use codec::{Decode, Encode};
use hex_literal::hex;
use node_primitives::{Balance, Hash, Index};
use node_primitives::{Balance, Hash, Index, AccountId, Signature};
use node_runtime::{BalancesCall, Call, Runtime, SignedPayload, UncheckedExtrinsic, VERSION};
use primitives::{
crypto::{set_default_ss58_version, Ss58AddressFormat, Ss58Codec},
ed25519, sr25519, Pair, Public, H256, hexdisplay::HexDisplay,
ed25519, sr25519, ecdsa, Pair, Public, H256, hexdisplay::HexDisplay,
};
use sr_primitives::generic::Era;
use sr_primitives::{traits::{IdentifyAccount, Verify}, generic::Era};
use std::{
convert::TryInto,
convert::{TryInto, TryFrom},
io::{stdin, Read},
str::FromStr,
};
@@ -43,8 +43,10 @@ trait Crypto: Sized {
fn pair_from_suri(suri: &str, password: Option<&str>) -> Self::Pair {
Self::Pair::from_string(suri, password).expect("Invalid phrase")
}
fn ss58_from_pair(pair: &Self::Pair) -> String {
pair.public().to_ss58check()
fn ss58_from_pair(pair: &Self::Pair) -> String where
<Self::Pair as Pair>::Public: PublicT,
{
pair.public().into_runtime().into_account().to_ss58check()
}
fn public_from_pair(pair: &Self::Pair) -> Self::Public {
pair.public()
@@ -58,28 +60,43 @@ trait Crypto: Sized {
{
if let Ok((pair, seed)) = Self::Pair::from_phrase(uri, password) {
let public_key = Self::public_from_pair(&pair);
println!("Secret phrase `{}` is account:\n Secret seed: {}\n Public key (hex): {}\n Address (SS58): {}",
println!("Secret phrase `{}` is account:\n \
Secret seed: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
format_seed::<Self>(seed),
format_public_key::<Self>(public_key),
format_public_key::<Self>(public_key.clone()),
format_account_id::<Self>(public_key),
Self::ss58_from_pair(&pair)
);
} else if let Ok(pair) = Self::Pair::from_string(uri, password) {
} else if let Ok((pair, seed)) = Self::Pair::from_string_with_seed(uri, password) {
let public_key = Self::public_from_pair(&pair);
println!(
"Secret Key URI `{}` is account:\n Public key (hex): {}\n Address (SS58): {}",
println!("Secret Key URI `{}` is account:\n \
Secret seed: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
format_public_key::<Self>(public_key),
if let Some(seed) = seed { format_seed::<Self>(seed) } else { "n/a".into() },
format_public_key::<Self>(public_key.clone()),
format_account_id::<Self>(public_key),
Self::ss58_from_pair(&pair)
);
} else if let Ok((public_key, v)) =
<Self::Pair as Pair>::Public::from_string_with_version(uri)
{
let v = network_override.unwrap_or(v);
println!("Public Key URI `{}` is account:\n Network ID/version: {}\n Public key (hex): {}\n Address (SS58): {}",
println!("Public Key URI `{}` is account:\n \
Network ID/version: {}\n \
Public key (hex): {}\n \
Account ID: {}\n \
SS58 Address: {}",
uri,
String::from(v),
format_public_key::<Self>(public_key.clone()),
format_account_id::<Self>(public_key.clone()),
public_key.to_ss58check_with_version(v)
);
} else {
@@ -106,17 +123,37 @@ impl Crypto for Sr25519 {
type Public = sr25519::Public;
}
struct Ecdsa;
impl Crypto for Ecdsa {
type Pair = ecdsa::Pair;
type Public = ecdsa::Public;
}
type SignatureOf<C> = <<C as Crypto>::Pair as Pair>::Signature;
type PublicOf<C> = <<C as Crypto>::Pair as Pair>::Public;
type SeedOf<C> = <<C as Crypto>::Pair as Pair>::Seed;
type AccountPublic = <Signature as Verify>::Signer;
trait SignatureT: AsRef<[u8]> + AsMut<[u8]> + Default {}
trait PublicT: Sized + AsRef<[u8]> + Ss58Codec {}
trait SignatureT: AsRef<[u8]> + AsMut<[u8]> + Default {
/// Converts the signature into a runtime account signature, if possible. If not possible, bombs out.
fn into_runtime(self) -> Signature {
panic!("This cryptography isn't supported for this runtime.")
}
}
trait PublicT: Sized + AsRef<[u8]> + Ss58Codec {
/// Converts the public key into a runtime account public key, if possible. If not possible, bombs out.
fn into_runtime(self) -> AccountPublic {
panic!("This cryptography isn't supported for this runtime.")
}
}
impl SignatureT for sr25519::Signature {}
impl SignatureT for ed25519::Signature {}
impl PublicT for sr25519::Public {}
impl PublicT for ed25519::Public {}
impl SignatureT for sr25519::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl SignatureT for ed25519::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl SignatureT for ecdsa::Signature { fn into_runtime(self) -> Signature { self.into() } }
impl PublicT for sr25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
impl PublicT for ed25519::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
impl PublicT for ecdsa::Public { fn into_runtime(self) -> AccountPublic { self.into() } }
fn main() {
let yaml = load_yaml!("cli.yml");
@@ -125,10 +162,12 @@ fn main() {
.get_matches();
if matches.is_present("ed25519") {
execute::<Ed25519>(matches)
} else {
execute::<Sr25519>(matches)
return execute::<Ed25519>(matches)
}
if matches.is_present("secp256k1") {
return execute::<Ecdsa>(matches)
}
return execute::<Sr25519>(matches)
}
fn execute<C: Crypto>(matches: ArgMatches)
@@ -165,7 +204,7 @@ where
("verify", Some(matches)) => {
let should_decode = matches.is_present("hex");
let message = read_message_from_stdin(should_decode);
let is_valid_signature = do_verify::<C>(matches, message, password);
let is_valid_signature = do_verify::<C>(matches, message);
if is_valid_signature {
println!("Signature verifies correctly.");
} else {
@@ -182,20 +221,20 @@ where
C::print_from_uri(&formated_seed, None, maybe_network);
}
("transfer", Some(matches)) => {
let signer = read_pair::<Sr25519>(matches.value_of("from"), password);
let signer = read_pair::<C>(matches.value_of("from"), password);
let index = read_required_parameter::<Index>(matches, "index");
let genesis_hash = read_genesis_hash(matches);
let to = read_public_key::<Sr25519>(matches.value_of("to"), password);
let to: AccountId = read_account_id(matches.value_of("to"));
let amount = read_required_parameter::<Balance>(matches, "amount");
let function = Call::Balances(BalancesCall::transfer(to.into(), amount));
let extrinsic = create_extrinsic(function, index, signer, genesis_hash);
let extrinsic = create_extrinsic::<C>(function, index, signer, genesis_hash);
print_extrinsic(extrinsic);
}
("sign-transaction", Some(matches)) => {
let signer = read_pair::<Sr25519>(matches.value_of("suri"), password);
let signer = read_pair::<C>(matches.value_of("suri"), password);
let index = read_required_parameter::<Index>(matches, "nonce");
let genesis_hash = read_genesis_hash(matches);
@@ -205,7 +244,7 @@ where
.and_then(|x| Decode::decode(&mut &x[..]).ok())
.unwrap();
let extrinsic = create_extrinsic(function, index, signer, genesis_hash);
let extrinsic = create_extrinsic::<C>(function, index, signer, genesis_hash);
print_extrinsic(extrinsic);
}
@@ -236,13 +275,13 @@ where
format_signature::<C>(&signature)
}
fn do_verify<C: Crypto>(matches: &ArgMatches, message: Vec<u8>, password: Option<&str>) -> bool
fn do_verify<C: Crypto>(matches: &ArgMatches, message: Vec<u8>) -> bool
where
SignatureOf<C>: SignatureT,
PublicOf<C>: PublicT,
{
let signature = read_signature::<C>(matches);
let pubkey = read_public_key::<C>(matches.value_of("uri"), password);
let pubkey = read_public_key::<C>(matches.value_of("uri"));
<<C as Crypto>::Pair as Pair>::verify(&signature, &message, &pubkey)
}
@@ -305,9 +344,7 @@ where
signature
}
fn read_public_key<C: Crypto>(matched_uri: Option<&str>, password: Option<&str>) -> PublicOf<C>
where
SignatureOf<C>: SignatureT,
fn read_public_key<C: Crypto>(matched_uri: Option<&str>) -> PublicOf<C> where
PublicOf<C>: PublicT,
{
let uri = matched_uri.expect("parameter is required; thus it can't be None; qed");
@@ -319,13 +356,28 @@ where
if let Ok(pubkey_vec) = hex::decode(uri) {
<C as Crypto>::Public::from_slice(pubkey_vec.as_slice())
} else {
<C as Crypto>::Pair::from_string(uri, password)
<C as Crypto>::Public::from_string(uri)
.ok()
.map(|p| p.public())
.expect("Invalid URI; expecting either a secret URI or a public URI.")
}
}
fn read_account_id(matched_uri: Option<&str>) -> AccountId {
let uri = matched_uri.expect("parameter is required; thus it can't be None; qed");
let uri = if uri.starts_with("0x") {
&uri[2..]
} else {
uri
};
if let Ok(data_vec) = hex::decode(uri) {
AccountId::try_from(data_vec.as_slice())
.expect("Invalid hex length for account ID; should be 32 bytes")
} else {
AccountId::from_ss58check(uri).ok()
.expect("Invalid SS58-check address given for account ID.")
}
}
fn read_pair<C: Crypto>(
matched_suri: Option<&str>,
password: Option<&str>,
@@ -350,12 +402,21 @@ fn format_public_key<C: Crypto>(public_key: PublicOf<C>) -> String {
format!("0x{}", HexDisplay::from(&public_key.as_ref()))
}
fn create_extrinsic(
fn format_account_id<C: Crypto>(public_key: PublicOf<C>) -> String where
PublicOf<C>: PublicT,
{
format!("0x{}", HexDisplay::from(&public_key.into_runtime().into_account().as_ref()))
}
fn create_extrinsic<C: Crypto>(
function: Call,
index: Index,
signer: <Sr25519 as Crypto>::Pair,
signer: C::Pair,
genesis_hash: H256,
) -> UncheckedExtrinsic {
) -> UncheckedExtrinsic where
PublicOf<C>: PublicT,
SignatureOf<C>: SignatureT,
{
let extra = |i: Index, f: Balance| {
(
system::CheckVersion::<Runtime>::new(),
@@ -380,13 +441,14 @@ fn create_extrinsic(
(),
),
);
let signature = raw_payload.using_encoded(|payload| signer.sign(payload));
let signature = raw_payload.using_encoded(|payload| signer.sign(payload)).into_runtime();
let signer = signer.public().into_runtime();
let (function, extra, _) = raw_payload.deconstruct();
UncheckedExtrinsic::new_signed(
function,
signer.public().into(),
signature.into(),
signer.into_account().into(),
signature,
extra,
)
}
@@ -439,7 +501,7 @@ mod tests {
let matches = App::from_yaml(yaml).get_matches_from(arg_vec);
let matches = matches.subcommand().1.unwrap();
assert!(do_verify::<CryptoType>(matches, message, password));
assert!(do_verify::<CryptoType>(matches, message));
}
#[test]
+4 -2
View File
@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
use super::Crypto;
use super::{PublicOf, PublicT, Crypto};
use primitives::Pair;
use rand::{rngs::OsRng, RngCore};
@@ -62,7 +62,9 @@ fn calculate_score(_desired: &str, key: &str) -> usize {
0
}
pub(super) fn generate_key<C: Crypto>(desired: &str) -> Result<KeyPair<C>, &str> {
pub(super) fn generate_key<C: Crypto>(desired: &str) -> Result<KeyPair<C>, &str> where
PublicOf<C>: PublicT,
{
if desired.is_empty() {
return Err("Pattern must not be empty");
}
@@ -22,7 +22,7 @@ use structopt::StructOpt;
use keystore::{Store as Keystore};
use node_cli::chain_spec::{self, AccountId};
use primitives::{crypto::{Public, Ss58Codec}, traits::BareCryptoStore};
use primitives::{sr25519, crypto::{Public, Ss58Codec}, traits::BareCryptoStore};
/// A utility to easily create a testnet chain spec definition with a given set
/// of authorities and endowed accounts and/or generate random accounts.
@@ -237,11 +237,11 @@ fn main() -> Result<(), String> {
}
let endowed_accounts = endowed_seeds.iter().map(|seed| {
chain_spec::get_from_seed::<AccountId>(seed)
chain_spec::get_account_id_from_seed::<sr25519::Public>(seed)
.to_ss58check()
}).collect();
let sudo_account = chain_spec::get_from_seed::<AccountId>(&sudo_seed)
let sudo_account = chain_spec::get_account_id_from_seed::<sr25519::Public>(&sudo_seed)
.to_ss58check();
(authority_seeds, endowed_accounts, sudo_account)