HierarchicalKeySequence   A
last analyzed

Complexity

Total Complexity 18

Size/Duplication

Total Lines 96
Duplicated Lines 0 %

Test Coverage

Coverage 82.86%

Importance

Changes 0
Metric Value
eloc 37
c 0
b 0
f 0
dl 0
loc 96
ccs 29
cts 35
cp 0.8286
rs 10
wmc 18

3 Methods

Rating   Name   Duplication   Size   Complexity  
B decodeDerivation() 0 36 10
A decodeAbsolute() 0 14 4
A decodeRelative() 0 12 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Key\Deterministic;
6
7
class HierarchicalKeySequence
8
{
9
    private static $filterBip32Index = [
10
        'min_range' => 0,
11
        'max_range' => (1 << 31) - 1,
12
    ];
13
14
    /**
15
     * decodeAbsolute accepts an absolute BIP32 path, one beginning
16
     * with m or M. It returns an array, containing two elements.
17
     * The first is whether the prefix was public or private.
18
     * The second is the array of indices contained in the path.
19
     *
20
     * @param string $path
21
     * @return array - <isPrivate bool, path <int[]>>
22
     */
23 2
    public function decodeAbsolute(string $path)
24
    {
25 2
        $parts = explode("/", $path);
26 2
        if (count($parts) < 1) {
27
            throw new \InvalidArgumentException("Invalid BIP32 path - must have at least one component");
28
        }
29
30 2
        if (!($parts[0] === "m" || $parts[0] === "M")) {
31
            throw new \InvalidArgumentException("Invalid start of absolute BIP32 path - should be m or M");
32
        }
33
34
        return [
35 2
            $parts[0] === "m",
36 2
            $this->decodeDerivation(...array_slice($parts, 1)),
37
        ];
38
    }
39
40
    /**
41
     * decodeRelative accepts a relative BIP32 path, that is,
42
     * one without the prefix of m or M. These are usually provided
43
     * when requesting some child derivation of a key.
44
     *
45
     * @param string $path
46
     * @return int[]
47
     */
48 48
    public function decodeRelative(string $path): array
49
    {
50 48
        $parts = explode("/", $path);
51 48
        if (count($parts) < 1) {
52
            throw new \InvalidArgumentException("Invalid BIP32 path - must have at least one component");
53
        }
54
55 48
        if ($parts[0] === "m" || $parts[0] === "M") {
56 2
            throw new \InvalidArgumentException("Only relative paths accepted");
57
        }
58
59 46
        return $this->decodeDerivation(...$parts);
60
    }
61
62
    /**
63
     * Inner routine for decoding the numeric section of a path
64
     * @param string ...$parts
65
     * @return int[]
66
     */
67 46
    private function decodeDerivation(string... $parts): array
68
    {
69 46
        $indices = [];
70 46
        foreach ($parts as $i => $part) {
71 46
            if ($part === "") {
72 1
                throw new \InvalidArgumentException("Invalid BIP32 path - Empty path section");
73
            }
74
75
            // test the last character for hardened
76 45
            $last = substr($part, -1);
77 45
            $hardened = $last == "h" || $last == "'";
78 45
            if ($hardened) {
79 41
                if (strlen($part) === 1) {
80
                    throw new \InvalidArgumentException("Invalid BIP32 path - section contains only hardened flag");
81
                }
82 41
                $part = substr($part, 0, -1);
83
            }
84
85
            // any other hardened chars are invalid
86 45
            if (false !== strpos($part, "h") || false !== strpos($part, "'")) {
87
                throw new \InvalidArgumentException("Invalid BIP32 path - section contains extra hardened characters");
88
            }
89
90
            // validate part is a valid integer
91 45
            if (false === filter_var($part, FILTER_VALIDATE_INT, self::$filterBip32Index)) {
92
                throw new \InvalidArgumentException("Index is invalid or outside valid range: $part");
93
            }
94
95
            // make index from int $part + $hardened
96 45
            $index = (int) $part;
97 45
            if ($hardened) {
98 41
                $index |= 1 << 31;
99
            }
100 45
            $indices[] = $index;
101
        }
102 45
        return $indices;
103
    }
104
}
105