mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-07-03 14:37:24 +00:00
0a94124d24
This is the significant step to make BEEFY client able to handle both ECDSA and (ECDSA, BLS) type signature. The idea is having BEEFY Client generic on crypto types makes migration to new types smoother. This makes the BEEFY Keystore generic over AuthorityId and extends its tests to cover the case when the AuthorityId is of type (ECDSA, BLS12-377) --------- Co-authored-by: Davide Galassi <davxy@datawok.net> Co-authored-by: Robert Hambrock <roberthambrock@gmail.com>
645 lines
17 KiB
Rust
645 lines
17 KiB
Rust
// This file is part of Substrate.
|
|
|
|
// Copyright (C) Parity Technologies (UK) Ltd.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// tag::description[]
|
|
//! Simple Ed25519 API.
|
|
// end::description[]
|
|
|
|
#[cfg(feature = "full_crypto")]
|
|
use sp_std::vec::Vec;
|
|
|
|
use crate::{
|
|
crypto::ByteArray,
|
|
hash::{H256, H512},
|
|
};
|
|
use codec::{Decode, Encode, MaxEncodedLen};
|
|
use scale_info::TypeInfo;
|
|
|
|
#[cfg(feature = "serde")]
|
|
use crate::crypto::Ss58Codec;
|
|
use crate::crypto::{
|
|
CryptoType, CryptoTypeId, Derive, FromEntropy, Public as TraitPublic, UncheckedFrom,
|
|
};
|
|
#[cfg(feature = "full_crypto")]
|
|
use crate::crypto::{DeriveError, DeriveJunction, Pair as TraitPair, SecretStringError};
|
|
#[cfg(feature = "full_crypto")]
|
|
use core::convert::TryFrom;
|
|
#[cfg(feature = "full_crypto")]
|
|
use ed25519_zebra::{SigningKey, VerificationKey};
|
|
#[cfg(feature = "serde")]
|
|
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
|
|
use sp_runtime_interface::pass_by::PassByInner;
|
|
#[cfg(all(not(feature = "std"), feature = "serde"))]
|
|
use sp_std::alloc::{format, string::String};
|
|
use sp_std::ops::Deref;
|
|
|
|
/// An identifier used to match public keys against ed25519 keys
|
|
pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"ed25");
|
|
|
|
/// A secret seed. It's not called a "secret key" because ring doesn't expose the secret keys
|
|
/// of the key pair (yeah, dumb); as such we're forced to remember the seed manually if we
|
|
/// will need it later (such as for HDKD).
|
|
#[cfg(feature = "full_crypto")]
|
|
type Seed = [u8; 32];
|
|
|
|
/// A public key.
|
|
#[cfg_attr(feature = "full_crypto", derive(Hash))]
|
|
#[derive(
|
|
PartialEq,
|
|
Eq,
|
|
PartialOrd,
|
|
Ord,
|
|
Clone,
|
|
Copy,
|
|
Encode,
|
|
Decode,
|
|
PassByInner,
|
|
MaxEncodedLen,
|
|
TypeInfo,
|
|
)]
|
|
pub struct Public(pub [u8; 32]);
|
|
|
|
/// A key pair.
|
|
#[cfg(feature = "full_crypto")]
|
|
#[derive(Copy, Clone)]
|
|
pub struct Pair {
|
|
public: VerificationKey,
|
|
secret: SigningKey,
|
|
}
|
|
|
|
impl FromEntropy for Public {
|
|
fn from_entropy(input: &mut impl codec::Input) -> Result<Self, codec::Error> {
|
|
let mut result = Self([0u8; 32]);
|
|
input.read(&mut result.0[..])?;
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
impl AsRef<[u8; 32]> for Public {
|
|
fn as_ref(&self) -> &[u8; 32] {
|
|
&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 Deref for Public {
|
|
type Target = [u8];
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.0
|
|
}
|
|
}
|
|
|
|
impl TryFrom<&[u8]> for Public {
|
|
type Error = ();
|
|
|
|
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
|
|
if data.len() != Self::LEN {
|
|
return Err(())
|
|
}
|
|
let mut r = [0u8; Self::LEN];
|
|
r.copy_from_slice(data);
|
|
Ok(Self::unchecked_from(r))
|
|
}
|
|
}
|
|
|
|
impl From<Public> for [u8; 32] {
|
|
fn from(x: Public) -> Self {
|
|
x.0
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full_crypto")]
|
|
impl From<Pair> for Public {
|
|
fn from(x: Pair) -> Self {
|
|
x.public()
|
|
}
|
|
}
|
|
|
|
impl From<Public> for H256 {
|
|
fn from(x: Public) -> Self {
|
|
x.0.into()
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "std")]
|
|
impl std::str::FromStr for Public {
|
|
type Err = crate::crypto::PublicError;
|
|
|
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
|
Self::from_ss58check(s)
|
|
}
|
|
}
|
|
|
|
impl UncheckedFrom<[u8; 32]> for Public {
|
|
fn unchecked_from(x: [u8; 32]) -> Self {
|
|
Public::from_raw(x)
|
|
}
|
|
}
|
|
|
|
impl UncheckedFrom<H256> for Public {
|
|
fn unchecked_from(x: H256) -> Self {
|
|
Public::from_h256(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())
|
|
}
|
|
}
|
|
|
|
impl sp_std::fmt::Debug for Public {
|
|
#[cfg(feature = "std")]
|
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::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 sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "serde")]
|
|
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 = "serde")]
|
|
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)))
|
|
}
|
|
}
|
|
|
|
/// A signature (a 512-bit value).
|
|
#[cfg_attr(feature = "full_crypto", derive(Hash))]
|
|
#[derive(Encode, Decode, MaxEncodedLen, PassByInner, TypeInfo, PartialEq, Eq)]
|
|
pub struct Signature(pub [u8; 64]);
|
|
|
|
impl TryFrom<&[u8]> for Signature {
|
|
type Error = ();
|
|
|
|
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
|
|
if data.len() == 64 {
|
|
let mut inner = [0u8; 64];
|
|
inner.copy_from_slice(data);
|
|
Ok(Signature(inner))
|
|
} else {
|
|
Err(())
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "serde")]
|
|
impl Serialize for Signature {
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
serializer.serialize_str(&array_bytes::bytes2hex("", self))
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "serde")]
|
|
impl<'de> Deserialize<'de> for Signature {
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
where
|
|
D: Deserializer<'de>,
|
|
{
|
|
let signature_hex = array_bytes::hex2bytes(&String::deserialize(deserializer)?)
|
|
.map_err(|e| de::Error::custom(format!("{:?}", e)))?;
|
|
Signature::try_from(signature_hex.as_ref())
|
|
.map_err(|e| de::Error::custom(format!("{:?}", e)))
|
|
}
|
|
}
|
|
|
|
impl Clone for Signature {
|
|
fn clone(&self) -> Self {
|
|
let mut r = [0u8; 64];
|
|
r.copy_from_slice(&self.0[..]);
|
|
Signature(r)
|
|
}
|
|
}
|
|
|
|
impl From<Signature> for H512 {
|
|
fn from(v: Signature) -> H512 {
|
|
H512::from(v.0)
|
|
}
|
|
}
|
|
|
|
impl From<Signature> for [u8; 64] {
|
|
fn from(v: Signature) -> [u8; 64] {
|
|
v.0
|
|
}
|
|
}
|
|
|
|
impl AsRef<[u8; 64]> for Signature {
|
|
fn as_ref(&self) -> &[u8; 64] {
|
|
&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[..]
|
|
}
|
|
}
|
|
|
|
impl sp_std::fmt::Debug for Signature {
|
|
#[cfg(feature = "std")]
|
|
fn fmt(&self, f: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
write!(f, "{}", crate::hexdisplay::HexDisplay::from(&self.0))
|
|
}
|
|
|
|
#[cfg(not(feature = "std"))]
|
|
fn fmt(&self, _: &mut sp_std::fmt::Formatter) -> sp_std::fmt::Result {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl UncheckedFrom<[u8; 64]> for Signature {
|
|
fn unchecked_from(data: [u8; 64]) -> Signature {
|
|
Signature(data)
|
|
}
|
|
}
|
|
|
|
impl Signature {
|
|
/// A new instance from the given 64-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; 64]) -> Signature {
|
|
Signature(data)
|
|
}
|
|
|
|
/// A new instance from the given slice that should be 64 bytes long.
|
|
///
|
|
/// 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_slice(data: &[u8]) -> Option<Self> {
|
|
if data.len() != 64 {
|
|
return None
|
|
}
|
|
let mut r = [0u8; 64];
|
|
r.copy_from_slice(data);
|
|
Some(Signature(r))
|
|
}
|
|
|
|
/// A new instance from an H512.
|
|
///
|
|
/// 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_h512(v: H512) -> Signature {
|
|
Signature(v.into())
|
|
}
|
|
}
|
|
|
|
impl Public {
|
|
/// A new instance from the given 32-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; 32]) -> Self {
|
|
Public(data)
|
|
}
|
|
|
|
/// A new instance from an H256.
|
|
///
|
|
/// 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_h256(x: H256) -> Self {
|
|
Public(x.into())
|
|
}
|
|
|
|
/// Return a slice filled with raw data.
|
|
pub fn as_array_ref(&self) -> &[u8; 32] {
|
|
self.as_ref()
|
|
}
|
|
}
|
|
|
|
impl ByteArray for Public {
|
|
const LEN: usize = 32;
|
|
}
|
|
|
|
impl TraitPublic for Public {}
|
|
|
|
impl Derive for Public {}
|
|
|
|
/// Derive a single hard junction.
|
|
#[cfg(feature = "full_crypto")]
|
|
fn derive_hard_junction(secret_seed: &Seed, cc: &[u8; 32]) -> Seed {
|
|
("Ed25519HDKD", secret_seed, cc).using_encoded(sp_crypto_hashing::blake2_256)
|
|
}
|
|
|
|
#[cfg(feature = "full_crypto")]
|
|
impl TraitPair for Pair {
|
|
type Public = Public;
|
|
type Seed = Seed;
|
|
type Signature = Signature;
|
|
|
|
/// 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 =
|
|
SigningKey::try_from(seed_slice).map_err(|_| SecretStringError::InvalidSeedLength)?;
|
|
let public = VerificationKey::from(&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.into();
|
|
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.into())
|
|
}
|
|
|
|
/// Sign a message.
|
|
fn sign(&self, message: &[u8]) -> Signature {
|
|
Signature::from_raw(self.secret.sign(message).into())
|
|
}
|
|
|
|
/// Verify a signature on a message.
|
|
///
|
|
/// Returns true if the signature is good.
|
|
fn verify<M: AsRef<[u8]>>(sig: &Signature, message: M, public: &Public) -> bool {
|
|
let Ok(public) = VerificationKey::try_from(public.as_slice()) else { return false };
|
|
let Ok(signature) = ed25519_zebra::Signature::try_from(sig.as_ref()) else { return false };
|
|
public.verify(&signature, message.as_ref()).is_ok()
|
|
}
|
|
|
|
/// Return a vec filled with raw data.
|
|
fn to_raw_vec(&self) -> Vec<u8> {
|
|
self.seed().to_vec()
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "full_crypto")]
|
|
impl Pair {
|
|
/// Get the seed for this key.
|
|
pub fn seed(&self) -> Seed {
|
|
self.secret.into()
|
|
}
|
|
|
|
/// 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.
|
|
#[cfg(feature = "std")]
|
|
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 = [b' '; 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 = "full_crypto")]
|
|
type Pair = Pair;
|
|
}
|
|
|
|
impl CryptoType for Signature {
|
|
#[cfg(feature = "full_crypto")]
|
|
type Pair = Pair;
|
|
}
|
|
|
|
#[cfg(feature = "full_crypto")]
|
|
impl CryptoType for Pair {
|
|
type Pair = Pair;
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
use super::*;
|
|
use crate::crypto::DEV_PHRASE;
|
|
use serde_json;
|
|
|
|
#[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 = array_bytes::hex2array_unchecked(
|
|
"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().0;
|
|
assert_eq!(
|
|
derived.seed(),
|
|
array_bytes::hex2array_unchecked::<_, 32>(
|
|
"ede3354e133f9c8e337ddd6ee5415ed4b4ffe5fc7d21e933f4930a3730e5b21c"
|
|
)
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn generate_with_phrase_should_be_recoverable_with_from_string() {
|
|
let (pair, phrase, seed) = Pair::generate_with_phrase(None);
|
|
let repair_seed = Pair::from_seed_slice(seed.as_ref()).expect("seed slice is valid");
|
|
assert_eq!(pair.public(), repair_seed.public());
|
|
assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
|
|
let (repair_phrase, reseed) =
|
|
Pair::from_phrase(phrase.as_ref(), None).expect("seed slice is valid");
|
|
assert_eq!(seed, reseed);
|
|
assert_eq!(pair.public(), repair_phrase.public());
|
|
assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
|
|
let repair_string = Pair::from_string(phrase.as_str(), None).expect("seed slice is valid");
|
|
assert_eq!(pair.public(), repair_string.public());
|
|
assert_eq!(pair.to_raw_vec(), repair_seed.to_raw_vec());
|
|
}
|
|
|
|
#[test]
|
|
fn test_vector_should_work() {
|
|
let pair = Pair::from_seed(&array_bytes::hex2array_unchecked(
|
|
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
|
|
));
|
|
let public = pair.public();
|
|
assert_eq!(
|
|
public,
|
|
Public::from_raw(array_bytes::hex2array_unchecked(
|
|
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"
|
|
))
|
|
);
|
|
let message = b"";
|
|
let signature = array_bytes::hex2array_unchecked("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
|
|
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(array_bytes::hex2array_unchecked(
|
|
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a"
|
|
))
|
|
);
|
|
let message = b"";
|
|
let signature = array_bytes::hex2array_unchecked("e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b");
|
|
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(array_bytes::hex2array_unchecked(
|
|
"2f8c6129d816cf51c374bc7f08c3e63ed156cf78aefb4a6550d97b87997977ee"
|
|
))
|
|
);
|
|
let message = array_bytes::hex2bytes_unchecked("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());
|
|
assert_ne!(pair1.to_raw_vec(), pair2.to_raw_vec());
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
|
|
#[test]
|
|
fn signature_serialization_works() {
|
|
let pair = Pair::from_seed(b"12345678901234567890123456789012");
|
|
let message = b"Something important";
|
|
let signature = pair.sign(&message[..]);
|
|
let serialized_signature = serde_json::to_string(&signature).unwrap();
|
|
// Signature is 64 bytes, so 128 chars + 2 quote chars
|
|
assert_eq!(serialized_signature.len(), 130);
|
|
let signature = serde_json::from_str(&serialized_signature).unwrap();
|
|
assert!(Pair::verify(&signature, &message[..], &pair.public()));
|
|
}
|
|
|
|
#[test]
|
|
fn signature_serialization_doesnt_panic() {
|
|
fn deserialize_signature(text: &str) -> Result<Signature, serde_json::error::Error> {
|
|
serde_json::from_str(text)
|
|
}
|
|
assert!(deserialize_signature("Not valid json.").is_err());
|
|
assert!(deserialize_signature("\"Not an actual signature.\"").is_err());
|
|
// Poorly-sized
|
|
assert!(deserialize_signature("\"abc123\"").is_err());
|
|
}
|
|
}
|