Skip to content

Commit

Permalink
HierarchicalKey::derivePath - accept absolute paths if the key is the…
Browse files Browse the repository at this point in the history
… root key
  • Loading branch information
afk11 committed Apr 16, 2021
1 parent bfea639 commit 39955be
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 2 deletions.
15 changes: 13 additions & 2 deletions src/Key/Deterministic/HierarchicalKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -320,8 +320,17 @@ public function deriveChild(int $sequence): HierarchicalKey
*/
public function derivePath(string $path): HierarchicalKey
{
if ($path == "") {
throw new \InvalidArgumentException("Path cannot be empty!");
}

$sequences = new HierarchicalKeySequence();
$parts = $sequences->decodeRelative($path);
if ($this->depth == 0 && ($path[0] == 'm' || $path[0] == 'M')) {
list($retainPrivate, $parts) = $sequences->decodeAbsolute($path);
} else {
$retainPrivate = true;
$parts = $sequences->decodeRelative($path);
}
$numParts = count($parts);

$key = $this;
Expand All @@ -336,7 +345,9 @@ public function derivePath(string $path): HierarchicalKey
}
}
}

if (!$retainPrivate) {
$key = $key->withoutPrivateKey();
}
return $key;
}

Expand Down
46 changes: 46 additions & 0 deletions tests/Key/Deterministic/HierarchicalKeyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,52 @@ public function testDerivePath(EcAdapterInterface $ecAdapter)
$this->assertEquals("xprvA4A9CuBXhdBtCaLxwrw64Jaran4n1rgzeS5mjH47Ds8V67uZS8tTkG8jV3BZi83QqYXPcN4v8EjK2Aof4YcEeqLt688mV57gF4j6QZWdP9U", $bip44ChildKey->toExtendedKey($network));
}

/**
* @dataProvider getEcAdapters
* @param EcAdapterInterface $ecAdapter
*/
public function testMasterKeyCanDeriveAbsolutePaths(EcAdapterInterface $ecAdapter)
{
$xPrv = 'xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHi';
$hdFactory = new HierarchicalKeyFactory($ecAdapter);
$key = $hdFactory->fromExtended($xPrv, $this->network);
$derivPriv = $key->derivePath("m/0'");
$this->assertTrue($derivPriv->isPrivate());
$this->assertEquals("xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", $derivPriv->toExtendedPrivateKey($this->network));
$this->assertEquals("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", $derivPriv->toExtendedPublicKey($this->network));

$derivPriv = $key->derivePath("0'");
$this->assertTrue($derivPriv->isPrivate());
$this->assertEquals("xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7", $derivPriv->toExtendedPrivateKey($this->network));
$this->assertEquals("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", $derivPriv->toExtendedPublicKey($this->network));

$derivPub = $key->derivePath("M/0'");
$this->assertFalse($derivPub->isPrivate());
$this->assertEquals("xpub68Gmy5EdvgibQVfPdqkBBCHxA5htiqg55crXYuXoQRKfDBFA1WEjWgP6LHhwBZeNK1VTsfTFUHCdrfp1bgwQ9xv5ski8PX9rL2dZXvgGDnw", $derivPub->toExtendedPublicKey($this->network));
}

public function testChildCannotDeriveAbsolutePaths()
{
$ecAdapter = Bitcoin::getEcAdapter();
$xPrv = 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7';
$hdFactory = new HierarchicalKeyFactory($ecAdapter);
$key = $hdFactory->fromExtended($xPrv, $this->network);
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage("Only relative paths accepted");
$key->derivePath("m/0'");
}

public function testChildCannotDeriveAbsolutePathsPublic()
{
$ecAdapter = Bitcoin::getEcAdapter();
$xPrv = 'xprv9uHRZZhk6KAJC1avXpDAp4MDc3sQKNxDiPvvkX8Br5ngLNv1TxvUxt4cV1rGL5hj6KCesnDYUhd7oWgT11eZG7XnxHrnYeSvkzY7d2bhkJ7';
$hdFactory = new HierarchicalKeyFactory($ecAdapter);
$key = $hdFactory->fromExtended($xPrv, $this->network);
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage("Only relative paths accepted");
$key->derivePath("M/0'");
}

/**
* @dataProvider getEcAdapters
* @param EcAdapterInterface $ecAdapter
Expand Down

0 comments on commit 39955be

Please sign in to comment.