mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-24 17:05:47 +00:00
no_std compatibility for subxt-signer (#1477)
* subxt-signer no-std * impl error * address james comments * fix signer wasm tests * error impl for secret uri error
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
// see LICENSE for license details.
|
||||
|
||||
use super::DeriveJunction;
|
||||
use alloc::vec::Vec;
|
||||
use derive_more::Display;
|
||||
use regex::Regex;
|
||||
use secrecy::SecretString;
|
||||
|
||||
@@ -88,7 +90,7 @@ pub struct SecretUri {
|
||||
pub junctions: Vec<DeriveJunction>,
|
||||
}
|
||||
|
||||
impl std::str::FromStr for SecretUri {
|
||||
impl core::str::FromStr for SecretUri {
|
||||
type Err = SecretUriError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
@@ -115,14 +117,17 @@ impl std::str::FromStr for SecretUri {
|
||||
}
|
||||
|
||||
/// This is returned if `FromStr` cannot parse a string into a `SecretUri`.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, thiserror::Error)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Display)]
|
||||
pub enum SecretUriError {
|
||||
/// Parsing the secret URI from a string failed; wrong format.
|
||||
#[error("Invalid secret phrase format")]
|
||||
#[display(fmt = "Invalid secret phrase format")]
|
||||
InvalidFormat,
|
||||
}
|
||||
|
||||
once_static! {
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for SecretUriError {}
|
||||
|
||||
once_static_cloned! {
|
||||
/// Interpret a phrase like:
|
||||
///
|
||||
/// ```text
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// This file is dual-licensed as Apache-2.0 or GPL-3.0.
|
||||
// see LICENSE for license details.
|
||||
|
||||
use alloc::string::String;
|
||||
use hmac::Hmac;
|
||||
use pbkdf2::pbkdf2;
|
||||
use sha2::Sha512;
|
||||
|
||||
+28
-16
@@ -6,8 +6,10 @@
|
||||
use codec::Encode;
|
||||
|
||||
use crate::crypto::{seed_from_entropy, DeriveJunction, SecretUri};
|
||||
use core::str::FromStr;
|
||||
use derive_more::{Display, From};
|
||||
use hex::FromHex;
|
||||
use secp256k1::{ecdsa::RecoverableSignature, Message, SecretKey, SECP256K1};
|
||||
use secp256k1::{ecdsa::RecoverableSignature, Message, Secp256k1, SecretKey};
|
||||
use secrecy::ExposeSecret;
|
||||
|
||||
const SEED_LENGTH: usize = 32;
|
||||
@@ -68,7 +70,7 @@ impl Keypair {
|
||||
let seed = Seed::from_hex(hex_str)?;
|
||||
Self::from_seed(seed)?
|
||||
} else {
|
||||
let phrase = bip39::Mnemonic::parse(phrase.expose_secret().as_str())?;
|
||||
let phrase = bip39::Mnemonic::from_str(phrase.expose_secret().as_str())?;
|
||||
let pass_str = password.as_ref().map(|p| p.expose_secret().as_str());
|
||||
Self::from_phrase(&phrase, pass_str)?
|
||||
};
|
||||
@@ -91,8 +93,9 @@ impl Keypair {
|
||||
/// keypair.sign(b"Hello world!");
|
||||
/// ```
|
||||
pub fn from_phrase(mnemonic: &bip39::Mnemonic, password: Option<&str>) -> Result<Self, Error> {
|
||||
let big_seed = seed_from_entropy(&mnemonic.to_entropy(), password.unwrap_or(""))
|
||||
.ok_or(Error::InvalidSeed)?;
|
||||
let (arr, len) = mnemonic.to_entropy_array();
|
||||
let big_seed =
|
||||
seed_from_entropy(&arr[0..len], password.unwrap_or("")).ok_or(Error::InvalidSeed)?;
|
||||
|
||||
let seed: Seed = big_seed[..SEED_LENGTH]
|
||||
.try_into()
|
||||
@@ -109,7 +112,8 @@ impl Keypair {
|
||||
pub fn from_seed(seed: Seed) -> Result<Self, Error> {
|
||||
let secret = SecretKey::from_slice(&seed).map_err(|_| Error::InvalidSeed)?;
|
||||
Ok(Self(secp256k1::Keypair::from_secret_key(
|
||||
SECP256K1, &secret,
|
||||
&Secp256k1::signing_only(),
|
||||
&secret,
|
||||
)))
|
||||
}
|
||||
|
||||
@@ -161,9 +165,9 @@ impl Keypair {
|
||||
// From sp_core::ecdsa::sign_prehashed:
|
||||
let wrapped = Message::from_digest_slice(&message_hash).expect("Message is 32 bytes; qed");
|
||||
let recsig: RecoverableSignature =
|
||||
SECP256K1.sign_ecdsa_recoverable(&wrapped, &self.0.secret_key());
|
||||
Secp256k1::signing_only().sign_ecdsa_recoverable(&wrapped, &self.0.secret_key());
|
||||
// From sp_core::ecdsa's `impl From<RecoverableSignature> for Signature`:
|
||||
let (recid, sig) = recsig.serialize_compact();
|
||||
let (recid, sig): (_, [u8; 64]) = recsig.serialize_compact();
|
||||
let mut signature_bytes: [u8; 65] = [0; 65];
|
||||
signature_bytes[..64].copy_from_slice(&sig);
|
||||
signature_bytes[64] = (recid.to_i32() & 0xFF) as u8;
|
||||
@@ -192,31 +196,39 @@ pub fn verify<M: AsRef<[u8]>>(sig: &Signature, message: M, pubkey: &PublicKey) -
|
||||
};
|
||||
let message_hash = sp_core_hashing::blake2_256(message.as_ref());
|
||||
let wrapped = Message::from_digest_slice(&message_hash).expect("Message is 32 bytes; qed");
|
||||
signature.verify(&wrapped, &public).is_ok()
|
||||
|
||||
Secp256k1::verification_only()
|
||||
.verify_ecdsa(&wrapped, &signature, &public)
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
/// An error handed back if creating a keypair fails.
|
||||
#[derive(Debug, PartialEq, thiserror::Error)]
|
||||
#[derive(Debug, PartialEq, Display, From)]
|
||||
pub enum Error {
|
||||
/// Invalid seed.
|
||||
#[error("Invalid seed (was it the wrong length?)")]
|
||||
#[display(fmt = "Invalid seed (was it the wrong length?)")]
|
||||
#[from(ignore)]
|
||||
InvalidSeed,
|
||||
/// Invalid seed.
|
||||
#[error("Invalid seed for ECDSA, contained soft junction")]
|
||||
#[display(fmt = "Invalid seed for ECDSA, contained soft junction")]
|
||||
#[from(ignore)]
|
||||
SoftJunction,
|
||||
/// Invalid phrase.
|
||||
#[error("Cannot parse phrase: {0}")]
|
||||
Phrase(#[from] bip39::Error),
|
||||
#[display(fmt = "Cannot parse phrase: {_0}")]
|
||||
Phrase(bip39::Error),
|
||||
/// Invalid hex.
|
||||
#[error("Cannot parse hex string: {0}")]
|
||||
Hex(#[from] hex::FromHexError),
|
||||
#[display(fmt = "Cannot parse hex string: {_0}")]
|
||||
Hex(hex::FromHexError),
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
/// Dev accounts, helpful for testing but not to be used in production,
|
||||
/// since the secret keys are known.
|
||||
pub mod dev {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use core::str::FromStr;
|
||||
|
||||
once_static_cloned! {
|
||||
/// Equivalent to `{DEV_PHRASE}//Alice`.
|
||||
|
||||
+3
-6
@@ -14,6 +14,9 @@
|
||||
//! subxt transactions for chains supporting sr25519 signatures.
|
||||
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
#[macro_use]
|
||||
mod utils;
|
||||
@@ -40,9 +43,3 @@ pub use secrecy::{ExposeSecret, SecretString};
|
||||
// SecretUri's can be parsed from strings and used to generate key pairs.
|
||||
// DeriveJunctions are the "path" part of these SecretUris.
|
||||
pub use crypto::{DeriveJunction, SecretUri, SecretUriError, DEV_PHRASE};
|
||||
|
||||
#[cfg(any(
|
||||
all(feature = "web", feature = "native"),
|
||||
not(any(feature = "web", feature = "native"))
|
||||
))]
|
||||
compile_error!("subxt-signer: exactly one of the 'web' and 'native' features should be used.");
|
||||
|
||||
+19
-10
@@ -4,7 +4,11 @@
|
||||
|
||||
//! An sr25519 keypair implementation.
|
||||
|
||||
use core::str::FromStr;
|
||||
|
||||
use crate::crypto::{seed_from_entropy, DeriveJunction, SecretUri};
|
||||
|
||||
use derive_more::{Display, From};
|
||||
use hex::FromHex;
|
||||
use schnorrkel::{
|
||||
derive::{ChainCode, Derivation},
|
||||
@@ -72,7 +76,7 @@ impl Keypair {
|
||||
let seed = Seed::from_hex(hex_str)?;
|
||||
Self::from_seed(seed)?
|
||||
} else {
|
||||
let phrase = bip39::Mnemonic::parse(phrase.expose_secret().as_str())?;
|
||||
let phrase = bip39::Mnemonic::from_str(phrase.expose_secret().as_str())?;
|
||||
let pass_str = password.as_ref().map(|p| p.expose_secret().as_str());
|
||||
Self::from_phrase(&phrase, pass_str)?
|
||||
};
|
||||
@@ -95,8 +99,9 @@ impl Keypair {
|
||||
/// keypair.sign(b"Hello world!");
|
||||
/// ```
|
||||
pub fn from_phrase(mnemonic: &bip39::Mnemonic, password: Option<&str>) -> Result<Self, Error> {
|
||||
let big_seed = seed_from_entropy(&mnemonic.to_entropy(), password.unwrap_or(""))
|
||||
.ok_or(Error::InvalidSeed)?;
|
||||
let (arr, len) = mnemonic.to_entropy_array();
|
||||
let big_seed =
|
||||
seed_from_entropy(&arr[0..len], password.unwrap_or("")).ok_or(Error::InvalidSeed)?;
|
||||
|
||||
let seed: Seed = big_seed[..SEED_LENGTH]
|
||||
.try_into()
|
||||
@@ -187,24 +192,28 @@ pub fn verify<M: AsRef<[u8]>>(sig: &Signature, message: M, pubkey: &PublicKey) -
|
||||
}
|
||||
|
||||
/// An error handed back if creating a keypair fails.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[derive(Debug, Display, From)]
|
||||
pub enum Error {
|
||||
/// Invalid seed.
|
||||
#[error("Invalid seed (was it the wrong length?)")]
|
||||
#[display(fmt = "Invalid seed (was it the wrong length?)")]
|
||||
#[from(ignore)]
|
||||
InvalidSeed,
|
||||
/// Invalid phrase.
|
||||
#[error("Cannot parse phrase: {0}")]
|
||||
Phrase(#[from] bip39::Error),
|
||||
#[display(fmt = "Cannot parse phrase: {_0}")]
|
||||
Phrase(bip39::Error),
|
||||
/// Invalid hex.
|
||||
#[error("Cannot parse hex string: {0}")]
|
||||
Hex(#[from] hex::FromHexError),
|
||||
#[display(fmt = "Cannot parse hex string: {_0}")]
|
||||
Hex(hex::FromHexError),
|
||||
}
|
||||
|
||||
#[cfg(feature = "std")]
|
||||
impl std::error::Error for Error {}
|
||||
|
||||
/// Dev accounts, helpful for testing but not to be used in production,
|
||||
/// since the secret keys are known.
|
||||
pub mod dev {
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use core::str::FromStr;
|
||||
|
||||
once_static_cloned! {
|
||||
/// Equivalent to `{DEV_PHRASE}//Alice`.
|
||||
|
||||
+11
-16
@@ -7,34 +7,29 @@
|
||||
/// Use like:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// once_static!{
|
||||
/// once_static_cloned!{
|
||||
/// /// Some documentation.
|
||||
/// fn foo() -> Vec<u8> {
|
||||
/// vec![1,2,3,4]
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
macro_rules! once_static {
|
||||
($($(#[$attr:meta])* $vis:vis fn $name:ident() -> $ty:ty { $expr:expr } )+) => {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
$vis fn $name() -> &'static $ty {
|
||||
static VAR: std::sync::OnceLock<$ty> = std::sync::OnceLock::new();
|
||||
VAR.get_or_init(|| { $expr })
|
||||
}
|
||||
)+
|
||||
};
|
||||
}
|
||||
|
||||
/// Like `once_static!` but clones the item out of static storage. Useful if it
|
||||
///
|
||||
/// Clones the item out of static storage. Useful if it
|
||||
/// takes a while to create the item but cloning it is fairly cheap.
|
||||
macro_rules! once_static_cloned {
|
||||
($($(#[$attr:meta])* $vis:vis fn $name:ident() -> $ty:ty { $expr:expr } )+) => {
|
||||
$(
|
||||
$(#[$attr])*
|
||||
$vis fn $name() -> $ty {
|
||||
static VAR: std::sync::OnceLock<$ty> = std::sync::OnceLock::new();
|
||||
VAR.get_or_init(|| { $expr }).clone()
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(feature = "std")] {
|
||||
static VAR: std::sync::OnceLock<$ty> = std::sync::OnceLock::new();
|
||||
VAR.get_or_init(|| { $expr }).clone()
|
||||
} else {
|
||||
{ $expr }
|
||||
}
|
||||
}
|
||||
}
|
||||
)+
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user