Merge pull request #470 from LNP-BP/feat/bip32-bin
BIP 32 binary encoding functions are extracted from base58
This commit is contained in:
commit
ee192eb61d
|
@ -27,7 +27,7 @@ use secp256k1::{self, Secp256k1};
|
||||||
|
|
||||||
use network::constants::Network;
|
use network::constants::Network;
|
||||||
use util::{base58, endian};
|
use util::{base58, endian};
|
||||||
use util::key::{PublicKey, PrivateKey};
|
use util::key::{self, PublicKey, PrivateKey};
|
||||||
|
|
||||||
/// A chain code
|
/// A chain code
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -388,6 +388,12 @@ pub enum Error {
|
||||||
InvalidChildNumberFormat,
|
InvalidChildNumberFormat,
|
||||||
/// Invalid derivation path format.
|
/// Invalid derivation path format.
|
||||||
InvalidDerivationPathFormat,
|
InvalidDerivationPathFormat,
|
||||||
|
/// Unknown version magic bytes
|
||||||
|
UnknownVersion([u8; 4]),
|
||||||
|
/// Encoded extended key data have wrong length
|
||||||
|
WrongExtendedKeyLength(usize),
|
||||||
|
/// Base58 encoding error
|
||||||
|
Base58(base58::Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -399,6 +405,9 @@ impl fmt::Display for Error {
|
||||||
Error::RngError(ref s) => write!(f, "rng error {}", s),
|
Error::RngError(ref s) => write!(f, "rng error {}", s),
|
||||||
Error::InvalidChildNumberFormat => f.write_str("invalid child number format"),
|
Error::InvalidChildNumberFormat => f.write_str("invalid child number format"),
|
||||||
Error::InvalidDerivationPathFormat => f.write_str("invalid derivation path format"),
|
Error::InvalidDerivationPathFormat => f.write_str("invalid derivation path format"),
|
||||||
|
Error::UnknownVersion(ref bytes) => write!(f, "unknown version magic bytes: {:?}", bytes),
|
||||||
|
Error::WrongExtendedKeyLength(ref len) => write!(f, "encoded extended key data have wrong length {}", len),
|
||||||
|
Error::Base58(ref err) => write!(f, "base58 encoding error: {}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,10 +422,25 @@ impl error::Error for Error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<key::Error> for Error {
|
||||||
|
fn from(err: key::Error) -> Self {
|
||||||
|
match err {
|
||||||
|
key::Error::Base58(e) => Error::Base58(e),
|
||||||
|
key::Error::Secp256k1(e) => Error::Ecdsa(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<secp256k1::Error> for Error {
|
impl From<secp256k1::Error> for Error {
|
||||||
fn from(e: secp256k1::Error) -> Error { Error::Ecdsa(e) }
|
fn from(e: secp256k1::Error) -> Error { Error::Ecdsa(e) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<base58::Error> for Error {
|
||||||
|
fn from(err: base58::Error) -> Self {
|
||||||
|
Error::Base58(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ExtendedPrivKey {
|
impl ExtendedPrivKey {
|
||||||
/// Construct a new master key from a seed value
|
/// Construct a new master key from a seed value
|
||||||
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
pub fn new_master(network: Network, seed: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
||||||
|
@ -459,11 +483,11 @@ impl ExtendedPrivKey {
|
||||||
pub fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
pub fn ckd_priv<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>, i: ChildNumber) -> Result<ExtendedPrivKey, Error> {
|
||||||
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
|
let mut hmac_engine: HmacEngine<sha512::Hash> = HmacEngine::new(&self.chain_code[..]);
|
||||||
match i {
|
match i {
|
||||||
ChildNumber::Normal {..} => {
|
ChildNumber::Normal { .. } => {
|
||||||
// Non-hardened key: compute public data and use that
|
// Non-hardened key: compute public data and use that
|
||||||
hmac_engine.input(&PublicKey::from_private_key(secp, &self.private_key).key.serialize()[..]);
|
hmac_engine.input(&PublicKey::from_private_key(secp, &self.private_key).key.serialize()[..]);
|
||||||
}
|
}
|
||||||
ChildNumber::Hardened {..} => {
|
ChildNumber::Hardened { .. } => {
|
||||||
// Hardened key: use only secret data to prevent public derivation
|
// Hardened key: use only secret data to prevent public derivation
|
||||||
hmac_engine.input(&[0u8]);
|
hmac_engine.input(&[0u8]);
|
||||||
hmac_engine.input(&self.private_key[..]);
|
hmac_engine.input(&self.private_key[..]);
|
||||||
|
@ -489,6 +513,54 @@ impl ExtendedPrivKey {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Decoding extended private key from binary data according to BIP 32
|
||||||
|
pub fn decode(data: &[u8]) -> Result<ExtendedPrivKey, Error> {
|
||||||
|
if data.len() != 78 {
|
||||||
|
return Err(Error::WrongExtendedKeyLength(data.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
let network = if data[0..4] == [0x04u8, 0x88, 0xAD, 0xE4] {
|
||||||
|
Network::Bitcoin
|
||||||
|
} else if data[0..4] == [0x04u8, 0x35, 0x83, 0x94] {
|
||||||
|
Network::Testnet
|
||||||
|
} else {
|
||||||
|
let mut ver = [0u8; 4];
|
||||||
|
ver.copy_from_slice(&data[0..4]);
|
||||||
|
return Err(Error::UnknownVersion(ver));
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(ExtendedPrivKey {
|
||||||
|
network: network,
|
||||||
|
depth: data[4],
|
||||||
|
parent_fingerprint: Fingerprint::from(&data[5..9]),
|
||||||
|
child_number: endian::slice_to_u32_be(&data[9..13]).into(),
|
||||||
|
chain_code: ChainCode::from(&data[13..45]),
|
||||||
|
private_key: PrivateKey {
|
||||||
|
compressed: true,
|
||||||
|
network: network,
|
||||||
|
key: secp256k1::SecretKey::from_slice(
|
||||||
|
&data[46..78]
|
||||||
|
).map_err(Error::Ecdsa)?,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extended private key binary encoding according to BIP 32
|
||||||
|
pub fn encode(&self) -> [u8; 78] {
|
||||||
|
let mut ret = [0; 78];
|
||||||
|
ret[0..4].copy_from_slice(&match self.network {
|
||||||
|
Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4],
|
||||||
|
Network::Testnet | Network::Signet | Network::Regtest => [0x04, 0x35, 0x83, 0x94],
|
||||||
|
}[..]);
|
||||||
|
ret[4] = self.depth as u8;
|
||||||
|
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
||||||
|
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
|
||||||
|
ret[13..45].copy_from_slice(&self.chain_code[..]);
|
||||||
|
ret[45] = 0;
|
||||||
|
ret[46..78].copy_from_slice(&self.private_key[..]);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the HASH160 of the public key belonging to the xpriv
|
/// Returns the HASH160 of the public key belonging to the xpriv
|
||||||
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> XpubIdentifier {
|
pub fn identifier<C: secp256k1::Signing>(&self, secp: &Secp256k1<C>) -> XpubIdentifier {
|
||||||
ExtendedPubKey::from_private(secp, self).identifier()
|
ExtendedPubKey::from_private(secp, self).identifier()
|
||||||
|
@ -531,7 +603,7 @@ impl ExtendedPubKey {
|
||||||
/// Compute the scalar tweak added to this key to get a child key
|
/// Compute the scalar tweak added to this key to get a child key
|
||||||
pub fn ckd_pub_tweak(&self, i: ChildNumber) -> Result<(PrivateKey, ChainCode), Error> {
|
pub fn ckd_pub_tweak(&self, i: ChildNumber) -> Result<(PrivateKey, ChainCode), Error> {
|
||||||
match i {
|
match i {
|
||||||
ChildNumber::Hardened {..} => {
|
ChildNumber::Hardened { .. } => {
|
||||||
Err(Error::CannotDeriveFromHardenedKey)
|
Err(Error::CannotDeriveFromHardenedKey)
|
||||||
}
|
}
|
||||||
ChildNumber::Normal { index: n } => {
|
ChildNumber::Normal { index: n } => {
|
||||||
|
@ -572,7 +644,46 @@ impl ExtendedPubKey {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the HASH160 of the public key of the xpub
|
/// Decoding extended public key from binary data according to BIP 32
|
||||||
|
pub fn decode(data: &[u8]) -> Result<ExtendedPubKey, Error> {
|
||||||
|
if data.len() != 78 {
|
||||||
|
return Err(Error::WrongExtendedKeyLength(data.len()))
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ExtendedPubKey {
|
||||||
|
network: if data[0..4] == [0x04u8, 0x88, 0xB2, 0x1E] {
|
||||||
|
Network::Bitcoin
|
||||||
|
} else if data[0..4] == [0x04u8, 0x35, 0x87, 0xCF] {
|
||||||
|
Network::Testnet
|
||||||
|
} else {
|
||||||
|
let mut ver = [0u8; 4];
|
||||||
|
ver.copy_from_slice(&data[0..4]);
|
||||||
|
return Err(Error::UnknownVersion(ver));
|
||||||
|
},
|
||||||
|
depth: data[4],
|
||||||
|
parent_fingerprint: Fingerprint::from(&data[5..9]),
|
||||||
|
child_number: endian::slice_to_u32_be(&data[9..13]).into(),
|
||||||
|
chain_code: ChainCode::from(&data[13..45]),
|
||||||
|
public_key: PublicKey::from_slice(&data[45..78])?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extended public key binary encoding according to BIP 32
|
||||||
|
pub fn encode(&self) -> [u8; 78] {
|
||||||
|
let mut ret = [0; 78];
|
||||||
|
ret[0..4].copy_from_slice(&match self.network {
|
||||||
|
Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E],
|
||||||
|
Network::Testnet | Network::Signet | Network::Regtest => [0x04u8, 0x35, 0x87, 0xCF],
|
||||||
|
}[..]);
|
||||||
|
ret[4] = self.depth as u8;
|
||||||
|
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
||||||
|
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
|
||||||
|
ret[13..45].copy_from_slice(&self.chain_code[..]);
|
||||||
|
ret[45..78].copy_from_slice(&self.public_key.key.serialize()[..]);
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the HASH160 of the chaincode
|
||||||
pub fn identifier(&self) -> XpubIdentifier {
|
pub fn identifier(&self) -> XpubIdentifier {
|
||||||
let mut engine = XpubIdentifier::engine();
|
let mut engine = XpubIdentifier::engine();
|
||||||
self.public_key.write_into(&mut engine).expect("engines don't error");
|
self.public_key.write_into(&mut engine).expect("engines don't error");
|
||||||
|
@ -587,106 +698,41 @@ impl ExtendedPubKey {
|
||||||
|
|
||||||
impl fmt::Display for ExtendedPrivKey {
|
impl fmt::Display for ExtendedPrivKey {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut ret = [0; 78];
|
base58::check_encode_slice_to_fmt(fmt, &self.encode()[..])
|
||||||
ret[0..4].copy_from_slice(&match self.network {
|
|
||||||
Network::Bitcoin => [0x04, 0x88, 0xAD, 0xE4],
|
|
||||||
Network::Testnet | Network::Signet | Network::Regtest => [0x04, 0x35, 0x83, 0x94],
|
|
||||||
}[..]);
|
|
||||||
ret[4] = self.depth as u8;
|
|
||||||
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
|
||||||
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
|
|
||||||
ret[13..45].copy_from_slice(&self.chain_code[..]);
|
|
||||||
ret[45] = 0;
|
|
||||||
ret[46..78].copy_from_slice(&self.private_key[..]);
|
|
||||||
fmt.write_str(&base58::check_encode_slice(&ret[..]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for ExtendedPrivKey {
|
impl FromStr for ExtendedPrivKey {
|
||||||
type Err = base58::Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(inp: &str) -> Result<ExtendedPrivKey, base58::Error> {
|
fn from_str(inp: &str) -> Result<ExtendedPrivKey, Error> {
|
||||||
let data = base58::from_check(inp)?;
|
let data = base58::from_check(inp)?;
|
||||||
|
|
||||||
if data.len() != 78 {
|
if data.len() != 78 {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(base58::Error::InvalidLength(data.len()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
|
Ok(ExtendedPrivKey::decode(&data[..])?)
|
||||||
let child_number: ChildNumber = ChildNumber::from(cn_int);
|
|
||||||
|
|
||||||
let network = if data[0..4] == [0x04u8, 0x88, 0xAD, 0xE4] {
|
|
||||||
Network::Bitcoin
|
|
||||||
} else if data[0..4] == [0x04u8, 0x35, 0x83, 0x94] {
|
|
||||||
Network::Testnet
|
|
||||||
} else {
|
|
||||||
return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()));
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(ExtendedPrivKey {
|
|
||||||
network: network,
|
|
||||||
depth: data[4],
|
|
||||||
parent_fingerprint: Fingerprint::from(&data[5..9]),
|
|
||||||
child_number: child_number,
|
|
||||||
chain_code: ChainCode::from(&data[13..45]),
|
|
||||||
private_key: PrivateKey {
|
|
||||||
compressed: true,
|
|
||||||
network: network,
|
|
||||||
key: secp256k1::SecretKey::from_slice(
|
|
||||||
&data[46..78]
|
|
||||||
).map_err(|e|
|
|
||||||
base58::Error::Other(e.to_string())
|
|
||||||
)?,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ExtendedPubKey {
|
impl fmt::Display for ExtendedPubKey {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let mut ret = [0; 78];
|
base58::check_encode_slice_to_fmt(fmt, &self.encode()[..])
|
||||||
ret[0..4].copy_from_slice(&match self.network {
|
|
||||||
Network::Bitcoin => [0x04u8, 0x88, 0xB2, 0x1E],
|
|
||||||
Network::Testnet | Network::Signet | Network::Regtest => [0x04u8, 0x35, 0x87, 0xCF],
|
|
||||||
}[..]);
|
|
||||||
ret[4] = self.depth as u8;
|
|
||||||
ret[5..9].copy_from_slice(&self.parent_fingerprint[..]);
|
|
||||||
ret[9..13].copy_from_slice(&endian::u32_to_array_be(u32::from(self.child_number)));
|
|
||||||
ret[13..45].copy_from_slice(&self.chain_code[..]);
|
|
||||||
ret[45..78].copy_from_slice(&self.public_key.key.serialize()[..]);
|
|
||||||
fmt.write_str(&base58::check_encode_slice(&ret[..]))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for ExtendedPubKey {
|
impl FromStr for ExtendedPubKey {
|
||||||
type Err = base58::Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn from_str(inp: &str) -> Result<ExtendedPubKey, base58::Error> {
|
fn from_str(inp: &str) -> Result<ExtendedPubKey, Error> {
|
||||||
let data = base58::from_check(inp)?;
|
let data = base58::from_check(inp)?;
|
||||||
|
|
||||||
if data.len() != 78 {
|
if data.len() != 78 {
|
||||||
return Err(base58::Error::InvalidLength(data.len()));
|
return Err(base58::Error::InvalidLength(data.len()).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let cn_int: u32 = endian::slice_to_u32_be(&data[9..13]);
|
Ok(ExtendedPubKey::decode(&data[..])?)
|
||||||
let child_number: ChildNumber = ChildNumber::from(cn_int);
|
|
||||||
|
|
||||||
Ok(ExtendedPubKey {
|
|
||||||
network: if data[0..4] == [0x04u8, 0x88, 0xB2, 0x1E] {
|
|
||||||
Network::Bitcoin
|
|
||||||
} else if data[0..4] == [0x04u8, 0x35, 0x87, 0xCF] {
|
|
||||||
Network::Testnet
|
|
||||||
} else {
|
|
||||||
return Err(base58::Error::InvalidVersion((&data[0..4]).to_vec()));
|
|
||||||
},
|
|
||||||
depth: data[4],
|
|
||||||
parent_fingerprint: Fingerprint::from(&data[5..9]),
|
|
||||||
child_number: child_number,
|
|
||||||
chain_code: ChainCode::from(&data[13..45]),
|
|
||||||
public_key: PublicKey::from_slice(
|
|
||||||
&data[45..78]).map_err(|e|
|
|
||||||
base58::Error::Other(e.to_string()))?
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue