Completed
Push — master ( a22894...37130c )
by thomas
62:03 queued 58:09
created

HierarchicalKeySequence::encodePath()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 11
ccs 7
cts 7
cp 1
crap 2
rs 9.4285
c 0
b 0
f 0
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 78
    public function __construct(Math $math)
26
    {
27 78
        $this->math = $math;
28 78
    }
29
30
    /**
31
     * @return \BitWasp\Bitcoin\Math\BinaryMath
32
     */
33 57
    private function binaryMath()
34
    {
35 57
        return $this->math->getBinaryMath();
36
    }
37
38
    /**
39
     * @param $sequence
40
     * @return bool
41
     */
42 57
    public function isHardened($sequence)
43
    {
44 57
        return $this->binaryMath()->isNegative(gmp_init($sequence, 10), 32);
45
    }
46
47
    /**
48
     * @param $sequence
49
     * @return int|string
50
     */
51 42
    public function getHardened($sequence)
52
    {
53 42
        if ($this->isHardened($sequence)) {
54 3
            throw new \LogicException('Sequence is already for a hardened key');
55
        }
56
57 39
        $prime = $this->binaryMath()->makeNegative(gmp_init($sequence, 10), 32);
58 39
        return gmp_strval($prime, 10);
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 48
    public function fromNode($node)
68
    {
69 48
        $hardened = false;
70 48
        if (in_array(substr(strtolower($node), -1), array('h', "'"), true) === true) {
71 39
            $intEnd = strlen($node) - 1;
72 39
            $node = substr($node, 0, $intEnd);
73 39
            $hardened = true;
74 26
        }
75
76 48
        if ($hardened) {
77 39
            $node = $this->getHardened($node);
78 26
        }
79
80 48
        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 30
    public function getNode($sequence)
90
    {
91 30
        if ($this->isHardened($sequence)) {
92 24
            $sequence = $this->math->sub(gmp_init($sequence), gmp_init(self::START_HARDENED));
93 24
            $sequence = gmp_strval($sequence, 10) . 'h';
94 16
        }
95
96 30
        return $sequence;
97
    }
98
99
    /**
100
     * Decodes a human-readable path, into an array of integers (sequences)
101
     *
102
     * @param string $path
103
     * @return array
104
     */
105 36
    public function decodePath($path)
106
    {
107 36
        if ($path === '') {
108 3
            throw new \InvalidArgumentException('Invalid path passed to decodePath()');
109
        }
110
111 33
        $list = [];
112 33
        foreach (explode('/', $path) as $segment) {
113 33
            if ($segment !== 'm' && $segment !== 'M') {
114 33
                $list[] = $this->fromNode($segment);
115 22
            }
116 22
        }
117
118 33
        return $list;
119
    }
120
121
    /**
122
     * Encodes a list of sequences to the human-readable path.
123
     *
124
     * @param array|\stdClass|\Traversable $list
125
     * @return string
126
     */
127 9
    public function encodePath($list)
128
    {
129 9
        self::validateListType($list);
130
131 9
        $path = [];
132 9
        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...
133 9
            $path[] = $this->getNode($sequence);
134 6
        }
135
136 9
        return implode('/', $path);
137
    }
138
139
    /**
140
     * Check the list, mainly that it works for foreach()
141
     *
142
     * @param \stdClass|array|\Traversable $list
143
     */
144 15
    public static function validateListType($list)
145
    {
146 15
        if (!is_array($list) && !$list instanceof \Traversable && !$list instanceof \stdClass) {
147
            throw new \InvalidArgumentException('Sequence list must be an array or \Traversable');
148
        }
149
150 15
    }
151
}
152