mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-02-01 03:12:15 +00:00
HD derive transparent address (infra only)
This commit is contained in:
parent
5afc6dbf33
commit
c1a02247cd
@ -30,6 +30,8 @@ clap = "2.33"
|
||||
secp256k1 = "=0.15.0"
|
||||
sha2 = "0.8.0"
|
||||
ripemd160 = "0.8.0"
|
||||
ring = "0.14.0"
|
||||
lazy_static = "1.2.0"
|
||||
|
||||
[dependencies.bellman]
|
||||
git = "https://github.com/adityapk00/librustzcash.git"
|
||||
|
224
src/lightwallet/extended_key.rs
Normal file
224
src/lightwallet/extended_key.rs
Normal file
@ -0,0 +1,224 @@
|
||||
use rand::Rng;
|
||||
use ring::{
|
||||
digest,
|
||||
hmac::{SigningContext, SigningKey},
|
||||
};
|
||||
use lazy_static::lazy_static;
|
||||
use secp256k1::{PublicKey, Secp256k1, SecretKey, SignOnly, VerifyOnly, Error};
|
||||
|
||||
lazy_static! {
|
||||
static ref SECP256K1_SIGN_ONLY: Secp256k1<SignOnly> = Secp256k1::signing_only();
|
||||
static ref SECP256K1_VERIFY_ONLY: Secp256k1<VerifyOnly> = Secp256k1::verification_only();
|
||||
}
|
||||
/// Random entropy, part of extended key.
|
||||
type ChainCode = Vec<u8>;
|
||||
|
||||
|
||||
const HARDENED_KEY_START_INDEX: u32 = 2_147_483_648; // 2 ** 31
|
||||
|
||||
/// KeyIndex indicates the key type and index of a child key.
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub enum KeyIndex {
|
||||
/// Normal key, index range is from 0 to 2 ** 31 - 1
|
||||
Normal(u32),
|
||||
/// Hardened key, index range is from 2 ** 31 to 2 ** 32 - 1
|
||||
Hardened(u32),
|
||||
}
|
||||
|
||||
impl KeyIndex {
|
||||
/// Return raw index value
|
||||
pub fn raw_index(self) -> u32 {
|
||||
match self {
|
||||
KeyIndex::Normal(i) => i,
|
||||
KeyIndex::Hardened(i) => i,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return normalize index, it will return index subtract 2 ** 31 for hardended key.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate hdwallet;
|
||||
/// use hdwallet::KeyIndex;
|
||||
///
|
||||
/// assert_eq!(KeyIndex::Normal(0).normalize_index(), 0);
|
||||
/// assert_eq!(KeyIndex::Hardened(2_147_483_648).normalize_index(), 0);
|
||||
/// ```
|
||||
pub fn normalize_index(self) -> u32 {
|
||||
match self {
|
||||
KeyIndex::Normal(i) => i,
|
||||
KeyIndex::Hardened(i) => i - HARDENED_KEY_START_INDEX,
|
||||
}
|
||||
}
|
||||
|
||||
/// Check index range.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate hdwallet;
|
||||
/// use hdwallet::KeyIndex;
|
||||
///
|
||||
/// assert!(KeyIndex::Normal(0).is_valid());
|
||||
/// assert!(!KeyIndex::Normal(2_147_483_648).is_valid());
|
||||
/// assert!(KeyIndex::Hardened(2_147_483_648).is_valid());
|
||||
/// ```
|
||||
pub fn is_valid(self) -> bool {
|
||||
match self {
|
||||
KeyIndex::Normal(i) => i < HARDENED_KEY_START_INDEX,
|
||||
KeyIndex::Hardened(i) => i >= HARDENED_KEY_START_INDEX,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate Hardened KeyIndex from normalize index value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate hdwallet;
|
||||
/// use hdwallet::KeyIndex;
|
||||
///
|
||||
/// // hardended key from zero
|
||||
/// let hardened_index_zero = KeyIndex::hardened_from_normalize_index(0).unwrap();
|
||||
/// assert_eq!(hardened_index_zero, KeyIndex::Hardened(2_147_483_648));
|
||||
/// // also allow raw index for convernient
|
||||
/// let hardened_index_zero = KeyIndex::hardened_from_normalize_index(2_147_483_648).unwrap();
|
||||
/// assert_eq!(hardened_index_zero, KeyIndex::Hardened(2_147_483_648));
|
||||
/// ```
|
||||
pub fn hardened_from_normalize_index(i: u32) -> Result<KeyIndex, Error> {
|
||||
if i < HARDENED_KEY_START_INDEX {
|
||||
Ok(KeyIndex::Hardened(HARDENED_KEY_START_INDEX + i))
|
||||
} else {
|
||||
Ok(KeyIndex::Hardened(i))
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate KeyIndex from raw index value.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate hdwallet;
|
||||
/// use hdwallet::KeyIndex;
|
||||
///
|
||||
/// let normal_key = KeyIndex::from_index(0).unwrap();
|
||||
/// assert_eq!(normal_key, KeyIndex::Normal(0));
|
||||
/// let hardened_key = KeyIndex::from_index(2_147_483_648).unwrap();
|
||||
/// assert_eq!(hardened_key, KeyIndex::Hardened(2_147_483_648));
|
||||
/// ```
|
||||
pub fn from_index(i: u32) -> Result<Self, Error> {
|
||||
if i < HARDENED_KEY_START_INDEX {
|
||||
Ok(KeyIndex::Normal(i))
|
||||
} else {
|
||||
Ok(KeyIndex::Hardened(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for KeyIndex {
|
||||
fn from(index: u32) -> Self {
|
||||
KeyIndex::from_index(index).expect("KeyIndex")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// ExtendedPrivKey is used for child key derivation.
|
||||
/// See [secp256k1 crate documentation](https://docs.rs/secp256k1) for SecretKey signatures usage.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// # extern crate hdwallet;
|
||||
/// use hdwallet::{ExtendedPrivKey, KeyIndex};
|
||||
///
|
||||
/// let master_key = ExtendedPrivKey::random().unwrap();
|
||||
/// let hardened_key_index = KeyIndex::hardened_from_normalize_index(0).unwrap();
|
||||
/// let hardended_child_priv_key = master_key.derive_private_key(hardened_key_index).unwrap();
|
||||
/// let normal_key_index = KeyIndex::Normal(0);
|
||||
/// let noamal_child_priv_key = master_key.derive_private_key(normal_key_index).unwrap();
|
||||
/// ```
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExtendedPrivKey {
|
||||
pub private_key: SecretKey,
|
||||
pub chain_code: ChainCode,
|
||||
}
|
||||
|
||||
/// Indicate bits of random seed used to generate private key, 256 is recommended.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum KeySeed {
|
||||
S128 = 128,
|
||||
S256 = 256,
|
||||
S512 = 512,
|
||||
}
|
||||
|
||||
impl ExtendedPrivKey {
|
||||
/// Generate an ExtendedPrivKey, use 256 size random seed.
|
||||
pub fn random() -> Result<ExtendedPrivKey, Error> {
|
||||
ExtendedPrivKey::random_with_seed_size(KeySeed::S256)
|
||||
}
|
||||
/// Generate an ExtendedPrivKey which use 128 or 256 or 512 bits random seed.
|
||||
pub fn random_with_seed_size(seed_size: KeySeed) -> Result<ExtendedPrivKey, Error> {
|
||||
let seed = {
|
||||
let mut seed = vec![0u8; seed_size as usize / 8];
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.fill(seed.as_mut_slice());
|
||||
seed
|
||||
};
|
||||
Self::with_seed(&seed)
|
||||
}
|
||||
|
||||
/// Generate an ExtendedPrivKey from seed
|
||||
pub fn with_seed(seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
||||
let signature = {
|
||||
let signing_key = SigningKey::new(&digest::SHA512, b"Bitcoin seed");
|
||||
let mut h = SigningContext::with_key(&signing_key);
|
||||
h.update(&seed);
|
||||
h.sign()
|
||||
};
|
||||
let sig_bytes = signature.as_ref();
|
||||
let (key, chain_code) = sig_bytes.split_at(sig_bytes.len() / 2);
|
||||
let private_key = SecretKey::from_slice(key)?;
|
||||
Ok(ExtendedPrivKey {
|
||||
private_key,
|
||||
chain_code: chain_code.to_vec(),
|
||||
})
|
||||
}
|
||||
|
||||
fn sign_hardended_key(&self, index: u32) -> ring::hmac::Signature {
|
||||
let signing_key = SigningKey::new(&digest::SHA512, &self.chain_code);
|
||||
let mut h = SigningContext::with_key(&signing_key);
|
||||
h.update(&[0x00]);
|
||||
h.update(&self.private_key[..]);
|
||||
h.update(&index.to_be_bytes());
|
||||
h.sign()
|
||||
}
|
||||
|
||||
fn sign_normal_key(&self, index: u32) -> ring::hmac::Signature {
|
||||
let signing_key = SigningKey::new(&digest::SHA512, &self.chain_code);
|
||||
let mut h = SigningContext::with_key(&signing_key);
|
||||
let public_key = PublicKey::from_secret_key(&SECP256K1_SIGN_ONLY, &self.private_key);
|
||||
h.update(&public_key.serialize());
|
||||
h.update(&index.to_be_bytes());
|
||||
h.sign()
|
||||
}
|
||||
|
||||
/// Derive a child key from ExtendedPrivKey.
|
||||
pub fn derive_private_key(&self, key_index: KeyIndex) -> Result<ExtendedPrivKey, Error> {
|
||||
if !key_index.is_valid() {
|
||||
return Err(Error::InvalidTweak);
|
||||
}
|
||||
let signature = match key_index {
|
||||
KeyIndex::Hardened(index) => self.sign_hardended_key(index),
|
||||
KeyIndex::Normal(index) => self.sign_normal_key(index),
|
||||
};
|
||||
let sig_bytes = signature.as_ref();
|
||||
let (key, chain_code) = sig_bytes.split_at(sig_bytes.len() / 2);
|
||||
let mut private_key = SecretKey::from_slice(key)?;
|
||||
private_key.add_assign(&self.private_key[..])?;
|
||||
Ok(ExtendedPrivKey {
|
||||
private_key,
|
||||
chain_code: chain_code.to_vec(),
|
||||
})
|
||||
}
|
||||
}
|
@ -36,6 +36,7 @@ use zcash_primitives::{
|
||||
};
|
||||
|
||||
pub mod data;
|
||||
pub mod extended_key;
|
||||
|
||||
use data::{BlockData, WalletTx, Utxo, SaplingNoteData, SpendableNote};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user