Completed
Branch master (6cbd51)
by
unknown
02:55
created

BIP32Path::getKeyIndex()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 2
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Blocktrail\SDK\Bitcoin;
4
5
/**
6
 * Class BIP32Path
7
 *
8
 * BIP32 path, does not mutate itself but returns new instance everytime
9
 *
10
 * @package Blocktrail\SDK
11
 */
12
class BIP32Path implements \ArrayAccess {
13
    protected $path;
14
15 9
    public function __construct($path) {
16 9
        $this->path = is_array($path) ? $path : explode("/", $path);
17
18 9
        if (strtolower($this->path[0]) != "m") {
19
            throw new \InvalidArgumentException("BIP32Path can only be used for absolute paths");
20
        }
21 9
    }
22
23
    /**
24
     * @return int
25
     */
26 1
    public function depth() {
27 1
        return count($this->path);
28
    }
29
30 1
    public function insert($insert, $offset) {
31 1
        $path = $this->path;
32
33 1
        array_splice($path, $offset+1, 0, [$insert]);
34
35 1
        return new static($path);
36
    }
37
38
    /**
39
     * increase the last level of the path by 1 and return the new path
40
     *
41
     * @return BIP32Path
42
     */
43 1
    public function next() {
44 1
        $path = $this->path;
45
46 1
        $last = array_pop($path);
47
48 1
        if ($hardened = (strpos($last, "'") !== false)) {
49
            $last = str_replace("'", "", $last);
50
        }
51
52 1
        $last = (int)$last;
53 1
        $last += 1;
54
55 1
        if ($hardened) {
56
            $last .= "'";
57
        }
58
59 1
        $path[] = $last;
60
61 1
        return new static($path);
62
    }
63
64
    /**
65
     * pop off one level of the path and return the new path
66
     *
67
     * @return BIP32Path
68
     */
69 1
    public function parent() {
70 1
        $path = $this->path;
71
72 1
        array_pop($path);
73
74 1
        if (empty($path)) {
75
            throw new \RuntimeException("Can't get parent of root path");
76
        }
77
78 1
        return new static($path);
79
    }
80
81
    /**
82
     * get child $child of the current path and return the new path
83
     *
84
     * @param $child
85
     * @return BIP32Path
86
     */
87 1
    public function child($child) {
88 1
        $path = $this->path;
89
90 1
        $path[] = $child;
91
92 1
        return new static($path);
93
    }
94
95
    /**
96
     * pop off one level of the path and add $last and return the new path
97
     *
98
     * @param $last
99
     * @return BIP32Path
100
     */
101 1
    public function last($last) {
102 1
        $path = $this->path;
103
104 1
        array_pop($path);
105 1
        $path[] = $last;
106
107 1
        return new static($path);
108
    }
109
110
    /**
111
     * harden the last level of the path and return the new path
112
     *
113
     * @return BIP32Path
114
     */
115 1
    public function hardened() {
116 1
        $path = $this->path;
117
118 1
        $last = array_pop($path);
119
120 1
        if (strpos($last, "'") !== false) {
121
            return $this;
122
        }
123
124 1
        $last .= "'";
125
126 1
        $path[] = $last;
127
128 1
        return new static($path);
129
    }
130
131
    /**
132
     * unharden the last level of the path and return the new path
133
     *
134
     * @return BIP32Path
135
     */
136 1
    public function unhardenedLast() {
137 1
        $path = $this->path;
138
139 1
        $last = array_pop($path);
140
141 1
        $last = str_replace("'", "", $last);
142
143 1
        $path[] = $last;
144
145 1
        return new static($path);
146
    }
147
148
    /**
149
     * unharden all levels of the path and return the new path
150
     *
151
     * @return BIP32Path
152
     */
153 1
    public function unhardenedPath() {
154 1
        $path = $this->path;
155
156 1
        foreach ($path as $i => $level) {
157 1
            $path[$i] = str_replace("'", "", $level);
158
        }
159
160 1
        return new static($path);
161
    }
162
163
    /**
164
     * change the path to be for the public key (starting with M/) and return the new path
165
     *
166
     * @return BIP32Path
167
     */
168 5
    public function publicPath() {
169 5
        $path = $this->path;
170
171 5
        if ($path[0] === "M") {
172
            return new static($path);
173
        } else {
174 5
            $path[0] = "M";
175
176 5
            return new static($path);
177
        }
178
    }
179
180
    /**
181
     * change the path to be for the private key (starting with m/) and return the new path
182
     *
183
     * @return BIP32Path
184
     */
185 6
    public function privatePath() {
186 6
        $path = $this->path;
187
188 6
        if ($path[0] === "m") {
189 1
            return new static($path);
190
        } else {
191 5
            $path[0] = "m";
192
193 5
            return new static($path);
194
        }
195
    }
196
197
    /**
198
     * get the string representation of the path
199
     *
200
     * @return string
201
     */
202 8
    public function getPath() {
203 8
        return implode("/", $this->path);
204
    }
205
206
    /**
207
     * get the last part of the path
208
     *
209
     * @return string
210
     */
211
    public function getLast() {
212
        return $this->path[count($this->path)-1];
213
    }
214
215
    /**
216
     * check if the last level of the path is hardened
217
     *
218
     * @return bool
219
     */
220 1
    public function isHardened() {
221 1
        $path = $this->path;
222
223 1
        $last = array_pop($path);
224
225 1
        return strpos($last, "'") !== false;
226
    }
227
228
    /**
229
     * check if the last level of the path is hardened
230
     *
231
     * @return bool
232
     */
233 2
    public function isPublicPath() {
234 2
        $path = $this->path;
235
236 2
        return $path[0] == "M";
237
    }
238
239
    /**
240
     * check if this path is parent path of the provided path
241
     *
242
     * @param string|BIP32Path $path
243
     * @return bool
244
     */
245 1
    public function isParentOf($path) {
246 1
        $path = BIP32Path::path($path);
247
248 1
        return strlen((string)$path) > strlen((string)$this) && strpos((string)$path, (string)$this) === 0;
249
    }
250
251
    /**
252
     * static method to initialize class
253
     *
254
     * @param $path
255
     * @return BIP32Path
256
     */
257 9
    public static function path($path) {
258 9
        if ($path instanceof static) {
259 2
            return $path;
260
        }
261
262 9
        return new static($path);
263
    }
264
265
    public function getKeyIndex() {
266
        return str_replace("'", "", $this->path[1]);
267
    }
268
269
    /**
270
     * count the levels in the path (including master)
271
     *
272
     * @return int
273
     */
274
    public function count() {
275
        return count($this->path);
276
    }
277
278
279
    public function offsetExists($offset) {
280
        return isset($this->path[$offset]);
281
    }
282
283 6
    public function offsetGet($offset) {
284 6
        return isset($this->path[$offset]) ? $this->path[$offset] : null;
285
    }
286
287
    public function offsetSet($offset, $value) {
288
        throw new \Exception("Not implemented");
289
    }
290
291
    public function offsetUnset($offset) {
292
        throw new \Exception("Not implemented");
293
    }
294
295
    /**
296
     * @return string
297
     */
298 8
    public function __toString() {
299 8
        return $this->getPath();
300
    }
301
}
302