Completed
Push — master ( b4d73d...6b32ff )
by thomas
20:09
created

HierarchicalKeySequence::decodePath()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 15
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 15
ccs 10
cts 10
cp 1
rs 8.8571
cc 5
eloc 8
nc 4
nop 1
crap 5
1
<?php
2
3
namespace BitWasp\Bitcoin\Key\Deterministic;
4
5
use BitWasp\Bitcoin\Math\Math;
6
7
/**
8
 * NB: Paths returned by this library omit m/M. This is because
9
 * some knowledge is lost during derivations, so the full path
10
 * is already considered 'meta-data'. It also allows the library
11
 * to assume derivations are relative to the current instance.
12
 */
13
class HierarchicalKeySequence
14
{
15
    /**
16
     * @var Math
17
     */
18
    private $math;
19
20
    const START_HARDENED = 2147483648; // 2^31
21
22
    /**
23
     * @param Math $math
24
     */
25 156
    public function __construct(Math $math)
26
    {
27 156
        $this->math = $math;
28 156
    }
29
30
    /**
31
     * @return \BitWasp\Bitcoin\Math\BinaryMath
32
     */
33 114
    private function binaryMath()
34
    {
35 114
        return $this->math->getBinaryMath();
36
    }
37
38
    /**
39
     * @param $sequence
40
     * @return bool
41
     */
42 114
    public function isHardened($sequence)
43
    {
44 114
        return $this->binaryMath()->isNegative($sequence, 32);
45
    }
46
47
    /**
48
     * @param $sequence
49
     * @return int|string
50
     */
51 84
    public function getHardened($sequence)
52
    {
53 84
        if ($this->isHardened($sequence)) {
54 6
            throw new \LogicException('Sequence is already for a hardened key');
55
        }
56
57 78
        $prime = $this->binaryMath()->makeNegative($sequence, 32);
58 78
        return $prime;
59
    }
60
61
    /**
62
     * Convert a human readable path node (eg, "0", "0'", or "0h") into the correct sequence (0, 0x80000000, 0x80000000)
63
     *
64
     * @param $node
65
     * @return int|string
66
     */
67 96
    public function fromNode($node)
68
    {
69 96
        $hardened = false;
70 96
        if (in_array(substr(strtolower($node), -1), array('h', "'"), true) === true) {
71 78
            $intEnd = strlen($node) - 1;
72 78
            $node = substr($node, 0, $intEnd);
73 78
            $hardened = true;
74 78
        }
75
76 96
        if ($hardened) {
77 78
            $node = $this->getHardened($node);
78 78
        }
79
80 96
        return $node;
81
    }
82
83
    /**
84
     * Given a sequence, get the human readable node. Ie, 0 -> 0, 0x80000000 -> 0h
85
     *
86
     * @param $sequence
87
     * @return string
88
     */
89 60
    public function getNode($sequence)
90
    {
91 60
        if ($this->isHardened($sequence)) {
92 48
            $sequence = $this->math->sub($sequence, self::START_HARDENED) . 'h';
93 48
        }
94
95 60
        return $sequence;
96
    }
97
98
    /**
99
     * Decodes a human-readable path, into an array of integers (sequences)
100
     *
101
     * @param string $path
102
     * @return array
103
     */
104 72
    public function decodePath($path)
105
    {
106 72
        if ($path === '') {
107 6
            throw new \InvalidArgumentException('Invalid path passed to decodePath()');
108
        }
109
110 66
        $list = [];
111 66
        foreach (explode('/', $path) as $segment) {
112 66
            if ($segment !== 'm' || $segment !== 'M') {
113 66
                $list[] = $this->fromNode($segment);
114 66
            }
115 66
        }
116
117 66
        return $list;
118
    }
119
120
    /**
121
     * Encodes a list of sequences to the human-readable path.
122
     *
123
     * @param array|\stdClass|\Traversable $list
124
     * @return string
125
     */
126 18
    public function encodePath($list)
127
    {
128 18
        self::validateListType($list);
129
130 18
        $path = [];
131 18
        foreach ($list as $sequence) {
0 ignored issues
show
Bug introduced by
The expression $list of type array|object<stdClass>|object<Traversable> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
132 18
            $path[] = $this->getNode($sequence);
133 18
        }
134
135 18
        return implode('/', $path);
136
    }
137
138
    /**
139
     * Check the list, mainly that it works for foreach()
140
     *
141
     * @param \stdClass|array|\Traversable $list
142
     */
143 30
    public static function validateListType($list)
144
    {
145 30
        if (!is_array($list) && !$list instanceof \Traversable && !$list instanceof \stdClass) {
146
            throw new \InvalidArgumentException('Sequence list must be an array or \Traversable');
147
        }
148
149 30
    }
150
}
151