Skip to content

Commit

Permalink
Support for keyring in runtimes (paritytech#2044)
Browse files Browse the repository at this point in the history
This functionality is required for paritytech#1984.

This PR enables
[`sp-keyring`](https://github.com/paritytech/polkadot-sdk/blob/21d36b7b4229c4d5225944f197918cde23fda4ea/substrate/primitives/keyring/src/sr25519.rs#L31-L40)
in `no-std` environments, allowing to generate the public key (e.g.
`AccountKeyring::Alice.public().to_ss58check()`), which can be later
used in the any of built-in [_runtime-genesis-config_
variant](https://github.com/paritytech/polkadot-sdk/blob/21d36b7b4229c4d5225944f197918cde23fda4ea/polkadot/node/service/src/chain_spec.rs#L1066-L1073).


The proposal is as follows:
- expose [`core::Pair`
trait](https://github.com/paritytech/polkadot-sdk/blob/d6f15306282e3de848a09c9aa9cba6f95a7811f0/substrate/primitives/core/src/crypto.rs#L832)
in `no-std`,
- `full_crypto` feature enables `sign` method,
- `std` feature enables `generate_with_phrase` and `generate` methods
(randomness is required),
- All other functionality, currently gated by `full_crypto` will be
available unconditionally (`no-std`):
-- `from_string`
-- `from_string_with_seed`
-- `from seed`
-- `from_seed_slice`
-- `from_phrase`
-- `derive`
-- `verify`

---

Depends on rust-bitcoin/rust-bip39#57

---------

Co-authored-by: command-bot <>
Co-authored-by: Davide Galassi <[email protected]>
  • Loading branch information
michalkucharczyk and davxy authored Mar 12, 2024
1 parent 688c25b commit b764deb
Show file tree
Hide file tree
Showing 28 changed files with 193 additions and 227 deletions.
3 changes: 2 additions & 1 deletion substrate/client/cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ rpassword = "7.0.0"
serde = { workspace = true, default-features = true }
serde_json = { workspace = true, default-features = true }
thiserror = { workspace = true }
bip39 = "2.0.0"
# personal fork here as workaround for: https://github.com/rust-bitcoin/rust-bip39/pull/64
bip39 = { package = "parity-bip39", version = "2.0.1", features = ["rand"] }
tokio = { version = "1.22.0", features = ["parking_lot", "rt-multi-thread", "signal"] }
sc-client-api = { path = "../api" }
sc-client-db = { path = "../db", default-features = false }
Expand Down
2 changes: 1 addition & 1 deletion substrate/client/cli/src/commands/generate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl GenerateCmd {
let password = self.keystore_params.read_password()?;
let output = self.output_scheme.output_type;

let phrase = mnemonic.word_iter().join(" ");
let phrase = mnemonic.words().join(" ");

with_crypto_scheme!(
self.crypto_scheme.scheme,
Expand Down
12 changes: 12 additions & 0 deletions substrate/primitives/application-crypto/check-features-variants.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/usr/bin/env -S bash -eux

export RUSTFLAGS="-Cdebug-assertions=y -Dwarnings"
T=wasm32-unknown-unknown
cargo check --release
cargo check --release --target=$T --no-default-features
cargo check --release --target=$T --no-default-features --features="full_crypto"
cargo check --release --target=$T --no-default-features --features="serde"
cargo check --release --target=$T --no-default-features --features="serde,full_crypto"
cargo check --release --target=$T --no-default-features --features="bandersnatch-experimental"
cargo check --release --target=$T --no-default-features --features="bls-experimental"
cargo check --release --target=$T --no-default-features --features="bls-experimental,full_crypto"
5 changes: 2 additions & 3 deletions substrate/primitives/application-crypto/src/bls377.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,13 @@
use crate::{KeyTypeId, RuntimePublic};

pub use sp_core::bls::bls377::*;
use sp_std::vec::Vec;

mod app {
crate::app_crypto!(super, sp_core::testing::BLS377);
}

#[cfg(feature = "full_crypto")]
pub use app::Pair as AppPair;
pub use app::{Public as AppPublic, Signature as AppSignature};
pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature};

impl RuntimePublic for Public {
type Signature = Signature;
Expand Down
4 changes: 1 addition & 3 deletions substrate/primitives/application-crypto/src/ecdsa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ mod app {
crate::app_crypto!(super, sp_core::testing::ECDSA);
}

#[cfg(feature = "full_crypto")]
pub use app::Pair as AppPair;
pub use app::{Public as AppPublic, Signature as AppSignature};
pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature};

impl RuntimePublic for Public {
type Signature = Signature;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
//! ECDSA and BLS12-377 paired crypto applications.
use crate::{KeyTypeId, RuntimePublic};
use sp_std::vec::Vec;

pub use sp_core::paired_crypto::ecdsa_bls377::*;

Expand Down
4 changes: 1 addition & 3 deletions substrate/primitives/application-crypto/src/ed25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ mod app {
crate::app_crypto!(super, sp_core::testing::ED25519);
}

#[cfg(feature = "full_crypto")]
pub use app::Pair as AppPair;
pub use app::{Public as AppPublic, Signature as AppSignature};
pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature};

impl RuntimePublic for Public {
type Signature = Signature;
Expand Down
59 changes: 40 additions & 19 deletions substrate/primitives/application-crypto/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,9 @@
#![warn(missing_docs)]
#![cfg_attr(not(feature = "std"), no_std)]

pub use sp_core::crypto::{key_types, CryptoTypeId, KeyTypeId};
pub use sp_core::crypto::{key_types, CryptoTypeId, DeriveJunction, KeyTypeId, Ss58Codec};
#[doc(hidden)]
#[cfg(feature = "full_crypto")]
pub use sp_core::crypto::{DeriveError, Pair, SecretStringError};
#[cfg(any(feature = "full_crypto", feature = "serde"))]
pub use sp_core::crypto::{DeriveJunction, Ss58Codec};
#[doc(hidden)]
pub use sp_core::{
self,
Expand Down Expand Up @@ -85,7 +82,7 @@ macro_rules! app_crypto {
$module::CRYPTO_ID
);
$crate::app_crypto_signature_common!($module::Signature, $key_type);
$crate::app_crypto_pair!($module::Pair, $key_type, $module::CRYPTO_ID);
$crate::app_crypto_pair_common!($module::Pair, $key_type, $module::CRYPTO_ID);
};
}

Expand Down Expand Up @@ -116,13 +113,15 @@ macro_rules! app_crypto {
$module::CRYPTO_ID
);
$crate::app_crypto_signature_common!($module::Signature, $key_type);
$crate::app_crypto_pair_common!($module::Pair, $key_type, $module::CRYPTO_ID);
};
}

/// Declares `Pair` type which is functionally equivalent to `$pair`, but is
/// new application-specific type whose identifier is `$key_type`.
/// It is a common part shared between full_crypto and non full_crypto environments.
#[macro_export]
macro_rules! app_crypto_pair {
macro_rules! app_crypto_pair_common {
($pair:ty, $key_type:expr, $crypto_type:expr) => {
$crate::wrap! {
/// A generic `AppPublic` wrapper type over $pair crypto; this has no specific App.
Expand All @@ -140,7 +139,14 @@ macro_rules! app_crypto_pair {
type Signature = Signature;

$crate::app_crypto_pair_functions_if_std!($pair);
$crate::app_crypto_pair_functions_if_full_crypto!($pair);

fn from_phrase(
phrase: &str,
password: Option<&str>,
) -> Result<(Self, Self::Seed), $crate::SecretStringError> {
<$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1))
}
fn derive<Iter: Iterator<Item = $crate::DeriveJunction>>(
&self,
path: Iter,
Expand All @@ -154,9 +160,6 @@ macro_rules! app_crypto_pair {
fn from_seed_slice(seed: &[u8]) -> Result<Self, $crate::SecretStringError> {
<$pair>::from_seed_slice(seed).map(Self)
}
fn sign(&self, msg: &[u8]) -> Self::Signature {
Signature(self.0.sign(msg))
}
fn verify<M: AsRef<[u8]>>(
sig: &Self::Signature,
message: M,
Expand Down Expand Up @@ -203,13 +206,6 @@ macro_rules! app_crypto_pair_functions_if_std {
let r = <$pair>::generate_with_phrase(password);
(Self(r.0), r.1, r.2)
}

fn from_phrase(
phrase: &str,
password: Option<&str>,
) -> Result<(Self, Self::Seed), $crate::SecretStringError> {
<$pair>::from_phrase(phrase, password).map(|r| (Self(r.0), r.1))
}
};
}

Expand All @@ -220,6 +216,25 @@ macro_rules! app_crypto_pair_functions_if_std {
($pair:ty) => {};
}

/// Implements functions for the `Pair` trait when `feature = "full_crypto"` is enabled.
#[doc(hidden)]
#[cfg(feature = "full_crypto")]
#[macro_export]
macro_rules! app_crypto_pair_functions_if_full_crypto {
($pair:ty) => {
fn sign(&self, msg: &[u8]) -> Self::Signature {
Signature(self.0.sign(msg))
}
};
}

#[doc(hidden)]
#[cfg(not(feature = "full_crypto"))]
#[macro_export]
macro_rules! app_crypto_pair_functions_if_full_crypto {
($pair:ty) => {};
}

/// Declares `Public` type which is functionally equivalent to `$public` but is
/// new application-specific type whose identifier is `$key_type`.
/// For full functionality, `app_crypto_public_common!` must be called too.
Expand Down Expand Up @@ -267,7 +282,7 @@ macro_rules! app_crypto_public_not_full_crypto {
$crate::wrap! {
/// A generic `AppPublic` wrapper type over $public crypto; this has no specific App.
#[derive(
Clone, Eq, PartialEq, Ord, PartialOrd,
Clone, Eq, Hash, PartialEq, Ord, PartialOrd,
$crate::codec::Encode,
$crate::codec::Decode,
$crate::RuntimeDebug,
Expand All @@ -277,10 +292,13 @@ macro_rules! app_crypto_public_not_full_crypto {
pub struct Public($public);
}

impl $crate::CryptoType for Public {}
impl $crate::CryptoType for Public {
type Pair = Pair;
}

impl $crate::AppCrypto for Public {
type Public = Public;
type Pair = Pair;
type Signature = Signature;
const ID: $crate::KeyTypeId = $key_type;
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
Expand Down Expand Up @@ -452,10 +470,13 @@ macro_rules! app_crypto_signature_not_full_crypto {
pub struct Signature($sig);
}

impl $crate::CryptoType for Signature {}
impl $crate::CryptoType for Signature {
type Pair = Pair;
}

impl $crate::AppCrypto for Signature {
type Public = Public;
type Pair = Pair;
type Signature = Signature;
const ID: $crate::KeyTypeId = $key_type;
const CRYPTO_ID: $crate::CryptoTypeId = $crypto_type;
Expand Down
4 changes: 1 addition & 3 deletions substrate/primitives/application-crypto/src/sr25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ mod app {
crate::app_crypto!(super, sp_core::testing::SR25519);
}

#[cfg(feature = "full_crypto")]
pub use app::Pair as AppPair;
pub use app::{Public as AppPublic, Signature as AppSignature};
pub use app::{Pair as AppPair, Public as AppPublic, Signature as AppSignature};

impl RuntimePublic for Public {
type Signature = Signature;
Expand Down
14 changes: 1 addition & 13 deletions substrate/primitives/application-crypto/src/traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
use codec::Codec;
use scale_info::TypeInfo;

#[cfg(feature = "full_crypto")]
use sp_core::crypto::Pair;
use sp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Public};
use sp_core::crypto::{CryptoType, CryptoTypeId, IsWrappedBy, KeyTypeId, Pair, Public};
use sp_std::{fmt::Debug, vec::Vec};

/// Application-specific cryptographic object.
Expand All @@ -45,24 +43,14 @@ pub trait AppCrypto: 'static + Sized + CryptoType {
type Signature: AppSignature;

/// The corresponding key pair type in this application scheme.
#[cfg(feature = "full_crypto")]
type Pair: AppPair;
}

/// Type which implements Hash in std, not when no-std (std variant).
#[cfg(any(feature = "std", feature = "full_crypto"))]
pub trait MaybeHash: sp_std::hash::Hash {}
#[cfg(any(feature = "std", feature = "full_crypto"))]
impl<T: sp_std::hash::Hash> MaybeHash for T {}

/// Type which implements Hash in std, not when no-std (no-std variant).
#[cfg(all(not(feature = "std"), not(feature = "full_crypto")))]
pub trait MaybeHash {}
#[cfg(all(not(feature = "std"), not(feature = "full_crypto")))]
impl<T> MaybeHash for T {}

/// Application-specific key pair.
#[cfg(feature = "full_crypto")]
pub trait AppPair:
AppCrypto + Pair<Public = <Self as AppCrypto>::Public, Signature = <Self as AppCrypto>::Signature>
{
Expand Down
24 changes: 8 additions & 16 deletions substrate/primitives/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ hash-db = { version = "0.16.0", default-features = false }
hash256-std-hasher = { version = "0.15.2", default-features = false }
bs58 = { version = "0.5.0", default-features = false, optional = true }
rand = { version = "0.8.5", features = ["small_rng"], optional = true }
substrate-bip39 = { path = "../../utils/substrate-bip39", optional = true }
bip39 = { version = "2.0.0", default-features = false }
substrate-bip39 = { path = "../../utils/substrate-bip39", default-features = false }
# personal fork here as workaround for: https://github.com/rust-bitcoin/rust-bip39/pull/64
bip39 = { package = "parity-bip39", version = "2.0.1", default-features = false, features = ["alloc"] }
zeroize = { version = "1.4.3", default-features = false }
secrecy = { version = "0.8.0", default-features = false }
secrecy = { version = "0.8.0", default-features = false, features = ["alloc"] }
parking_lot = { version = "0.12.1", optional = true }
ss58-registry = { version = "1.34.0", default-features = false }
sp-std = { path = "../std", default-features = false }
Expand All @@ -46,13 +47,13 @@ paste = "1.0.7"
itertools = { version = "0.10.3", optional = true }

# full crypto
array-bytes = { version = "6.1", optional = true }
ed25519-zebra = { version = "3.1.0", default-features = false, optional = true }
array-bytes = { version = "6.1" }
ed25519-zebra = { version = "3.1.0", default-features = false }
blake2 = { version = "0.10.4", default-features = false, optional = true }
libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"], optional = true }
libsecp256k1 = { version = "0.7", default-features = false, features = ["static-context"] }
schnorrkel = { version = "0.11.4", features = ["preaudit_deprecated"], default-features = false }
merlin = { version = "3.0", default-features = false }
sp-crypto-hashing = { path = "../crypto/hashing", default-features = false, optional = true }
sp-crypto-hashing = { path = "../crypto/hashing", default-features = false }
sp-runtime-interface = { path = "../runtime-interface", default-features = false }
# k256 crate, better portability, intended to be used in substrate-runtimes (no-std)
k256 = { version = "0.13.3", features = ["alloc", "ecdsa"], default-features = false }
Expand Down Expand Up @@ -81,7 +82,6 @@ bench = false
default = ["std"]

std = [
"array-bytes",
"bandersnatch_vrfs?/std",
"bip39/rand",
"bip39/std",
Expand Down Expand Up @@ -112,7 +112,6 @@ std = [
"schnorrkel/std",
"secp256k1/global-context",
"secp256k1/std",
"secrecy/alloc",
"serde/std",
"sp-crypto-hashing/std",
"sp-debug-derive/std",
Expand All @@ -131,7 +130,6 @@ std = [

# Serde support without relying on std features.
serde = [
"array-bytes",
"blake2",
"bounded-collections/serde",
"bs58/alloc",
Expand All @@ -140,20 +138,14 @@ serde = [
"k256/serde",
"primitive-types/serde_no_std",
"scale-info/serde",
"secrecy/alloc",
"sp-crypto-hashing",
"sp-storage/serde",
]

# This feature enables all crypto primitives for `no_std` builds like microcontrollers
# or Intel SGX.
# For the regular wasm runtime builds this should not be used.
full_crypto = [
"array-bytes",
"blake2",
"ed25519-zebra",
"libsecp256k1",
"sp-crypto-hashing",
"sp-runtime-interface/disable_target_static_assertions",
]

Expand Down
13 changes: 13 additions & 0 deletions substrate/primitives/core/check-features-variants.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/usr/bin/env -S bash -eux

export RUSTFLAGS="-Cdebug-assertions=y -Dwarnings"
T=wasm32-unknown-unknown

cargo check --target=$T --release --no-default-features --features="bls-experimental"
cargo check --target=$T --release --no-default-features --features="full_crypto,bls-experimental"
cargo check --target=$T --release --no-default-features --features="bandersnatch-experimental"
cargo check --target=$T --release --no-default-features --features="full_crypto,serde,bandersnatch-experimental"
cargo check --target=$T --release --no-default-features --features="full_crypto,serde"
cargo check --target=$T --release --no-default-features --features="full_crypto"
cargo check --target=$T --release --no-default-features --features="serde"
cargo check --target=$T --release --no-default-features
2 changes: 1 addition & 1 deletion substrate/primitives/core/src/address_uri.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

//! Little util for parsing an address URI. Replaces regular expressions.
#[cfg(all(not(feature = "std"), any(feature = "serde", feature = "full_crypto")))]
#[cfg(not(feature = "std"))]
use sp_std::{
alloc::string::{String, ToString},
vec::Vec,
Expand Down
Loading

0 comments on commit b764deb

Please sign in to comment.