Merge pull request #257 from stevenroose/bip32-increment
bip32: Add increment method to DerivationPath and ChildNumber
This commit is contained in:
commit
b471a12487
|
@ -137,6 +137,13 @@ impl ChildNumber {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the child number that is a single increment from this one.
|
||||||
|
pub fn increment(self) -> Result<ChildNumber, Error> {
|
||||||
|
match self {
|
||||||
|
ChildNumber::Normal{ index: idx } => ChildNumber::from_normal_idx(idx+1),
|
||||||
|
ChildNumber::Hardened{ index: idx } => ChildNumber::from_hardened_idx(idx+1),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<u32> for ChildNumber {
|
impl From<u32> for ChildNumber {
|
||||||
|
@ -260,20 +267,69 @@ impl FromStr for DerivationPath {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An iterator over children of a [DerivationPath].
|
||||||
|
///
|
||||||
|
/// It is returned by the methods [DerivationPath::children_since],
|
||||||
|
/// [DerivationPath::normal_children] and [DerivationPath::hardened_children].
|
||||||
|
pub struct DerivationPathIterator<'a> {
|
||||||
|
base: &'a DerivationPath,
|
||||||
|
next_child: Option<ChildNumber>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DerivationPathIterator<'a> {
|
||||||
|
/// Start a new [DerivationPathIterator] at the given child.
|
||||||
|
pub fn start_from(path: &'a DerivationPath, start: ChildNumber) -> DerivationPathIterator<'a> {
|
||||||
|
DerivationPathIterator {
|
||||||
|
base: path,
|
||||||
|
next_child: Some(start),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for DerivationPathIterator<'a> {
|
||||||
|
type Item = DerivationPath;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.next_child.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret = self.next_child.unwrap();
|
||||||
|
self.next_child = ret.increment().ok();
|
||||||
|
Some(self.base.child(ret))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl DerivationPath {
|
impl DerivationPath {
|
||||||
/// Create a new DerivationPath that is a child of this one.
|
/// Create a new [DerivationPath] that is a child of this one.
|
||||||
pub fn child(&self, cn: ChildNumber) -> DerivationPath {
|
pub fn child(&self, cn: ChildNumber) -> DerivationPath {
|
||||||
let mut path = self.0.clone();
|
let mut path = self.0.clone();
|
||||||
path.push(cn);
|
path.push(cn);
|
||||||
DerivationPath(path)
|
DerivationPath(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert into a DerivationPath that is a child of this one.
|
/// Convert into a [DerivationPath] that is a child of this one.
|
||||||
pub fn into_child(self, cn: ChildNumber) -> DerivationPath {
|
pub fn into_child(self, cn: ChildNumber) -> DerivationPath {
|
||||||
let mut path = self.0;
|
let mut path = self.0;
|
||||||
path.push(cn);
|
path.push(cn);
|
||||||
DerivationPath(path)
|
DerivationPath(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get an [Iterator] over the children of this [DerivationPath]
|
||||||
|
/// starting with the given [ChildNumber].
|
||||||
|
pub fn children_from(&self, cn: ChildNumber) -> DerivationPathIterator {
|
||||||
|
DerivationPathIterator::start_from(&self, cn)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an [Iterator] over the unhardened children of this [DerivationPath].
|
||||||
|
pub fn normal_children(&self) -> DerivationPathIterator {
|
||||||
|
DerivationPathIterator::start_from(&self, ChildNumber::Normal{ index: 0 })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an [Iterator] over the hardened children of this [DerivationPath].
|
||||||
|
pub fn hardened_children(&self) -> DerivationPathIterator {
|
||||||
|
DerivationPathIterator::start_from(&self, ChildNumber::Hardened{ index: 0 })
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for DerivationPath {
|
impl fmt::Display for DerivationPath {
|
||||||
|
@ -799,6 +855,49 @@ mod tests {
|
||||||
assert_eq!(Ok(pk), decoded_pk);
|
assert_eq!(Ok(pk), decoded_pk);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_increment() {
|
||||||
|
let idx = 9345497; // randomly generated, I promise
|
||||||
|
let cn = ChildNumber::from_normal_idx(idx).unwrap();
|
||||||
|
assert_eq!(cn.increment().ok(), Some(ChildNumber::from_normal_idx(idx+1).unwrap()));
|
||||||
|
let cn = ChildNumber::from_hardened_idx(idx).unwrap();
|
||||||
|
assert_eq!(cn.increment().ok(), Some(ChildNumber::from_hardened_idx(idx+1).unwrap()));
|
||||||
|
|
||||||
|
let max = (1<<31)-1;
|
||||||
|
let cn = ChildNumber::from_normal_idx(max).unwrap();
|
||||||
|
assert_eq!(cn.increment().err(), Some(Error::InvalidChildNumber(1<<31)));
|
||||||
|
let cn = ChildNumber::from_hardened_idx(max).unwrap();
|
||||||
|
assert_eq!(cn.increment().err(), Some(Error::InvalidChildNumber(1<<31)));
|
||||||
|
|
||||||
|
let cn = ChildNumber::from_normal_idx(350).unwrap();
|
||||||
|
let path = DerivationPath::from_str("m/42'").unwrap();
|
||||||
|
let mut iter = path.children_from(cn);
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/350".parse().unwrap()));
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/351".parse().unwrap()));
|
||||||
|
|
||||||
|
let path = DerivationPath::from_str("m/42'/350'").unwrap();
|
||||||
|
let mut iter = path.normal_children();
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/350'/0".parse().unwrap()));
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/350'/1".parse().unwrap()));
|
||||||
|
|
||||||
|
let path = DerivationPath::from_str("m/42'/350'").unwrap();
|
||||||
|
let mut iter = path.hardened_children();
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/350'/0'".parse().unwrap()));
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/350'/1'".parse().unwrap()));
|
||||||
|
|
||||||
|
let cn = ChildNumber::from_hardened_idx(42350).unwrap();
|
||||||
|
let path = DerivationPath::from_str("m/42'").unwrap();
|
||||||
|
let mut iter = path.children_from(cn);
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/42350'".parse().unwrap()));
|
||||||
|
assert_eq!(iter.next(), Some("m/42'/42351'".parse().unwrap()));
|
||||||
|
|
||||||
|
let cn = ChildNumber::from_hardened_idx(max).unwrap();
|
||||||
|
let path = DerivationPath::from_str("m/42'").unwrap();
|
||||||
|
let mut iter = path.children_from(cn);
|
||||||
|
assert!(iter.next().is_some());
|
||||||
|
assert!(iter.next().is_none());
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vector_1() {
|
fn test_vector_1() {
|
||||||
let secp = Secp256k1::new();
|
let secp = Secp256k1::new();
|
||||||
|
|
Loading…
Reference in New Issue