|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Blocktrail\SDK\Bitcoin; |
|
4
|
|
|
|
|
5
|
|
|
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface; |
|
6
|
|
|
use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKey; |
|
7
|
|
|
use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKeyFactory; |
|
8
|
|
|
use BitWasp\Bitcoin\Network\NetworkInterface; |
|
9
|
|
|
|
|
10
|
|
|
/** |
|
11
|
|
|
* Class BIP32Key |
|
12
|
|
|
* |
|
13
|
|
|
* Container for a BIP32 key and path |
|
14
|
|
|
*/ |
|
15
|
|
|
class BIP32Key { |
|
16
|
|
|
/** |
|
17
|
|
|
* @var HierarchicalKey |
|
18
|
|
|
*/ |
|
19
|
|
|
private $key; |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* @var BIP32Path |
|
23
|
|
|
*/ |
|
24
|
|
|
private $path; |
|
25
|
|
|
|
|
26
|
|
|
/** |
|
27
|
|
|
* @var NetworkInterface |
|
28
|
|
|
*/ |
|
29
|
|
|
private $network; |
|
30
|
|
|
|
|
31
|
|
|
/** |
|
32
|
|
|
* @var string|null |
|
33
|
|
|
*/ |
|
34
|
|
|
private $publicKeyHex = null; |
|
35
|
|
|
|
|
36
|
|
|
/** |
|
37
|
|
|
* @var BIP32Key[] |
|
38
|
|
|
*/ |
|
39
|
|
|
private $derivations = []; |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* @param HierarchicalKey $key |
|
43
|
|
|
* @param NetworkInterface $network |
|
44
|
|
|
* @param string|null $path |
|
45
|
|
|
* @throws \Exception |
|
46
|
|
|
*/ |
|
47
|
23 |
|
public function __construct(NetworkInterface $network, HierarchicalKey $key, $path = null) { |
|
48
|
23 |
|
$this->key = $key; |
|
49
|
23 |
|
$this->path = BIP32Path::path($path); |
|
50
|
23 |
|
$this->network = $network; |
|
51
|
|
|
|
|
52
|
23 |
|
return; |
|
53
|
|
|
|
|
54
|
|
|
if (is_array($key) && count($key) == 2) { |
|
|
|
|
|
|
55
|
|
|
$this->key = $key[0]; |
|
56
|
|
|
$this->path = BIP32Path::path($key[1]); |
|
57
|
|
|
} elseif (is_string($key) && is_string($path) && strlen($key) && strlen($path)) { |
|
58
|
|
|
$this->key = $key; |
|
|
|
|
|
|
59
|
|
|
$this->path = BIP32Path::path($path); |
|
60
|
|
|
} else { |
|
61
|
|
|
throw new \Exception("Bad input"); |
|
62
|
|
|
} |
|
63
|
|
|
} |
|
64
|
|
|
|
|
65
|
|
|
/** |
|
66
|
|
|
* static method to initialize class |
|
67
|
|
|
* |
|
68
|
|
|
* @param NetworkInterface $network |
|
69
|
|
|
* @param HierarchicalKey $key |
|
70
|
|
|
* @param string|null $path |
|
71
|
|
|
* @return BIP32Key |
|
72
|
|
|
*/ |
|
73
|
23 |
|
public static function create(NetworkInterface $network, HierarchicalKey $key, $path = null) { |
|
74
|
23 |
|
return new BIP32Key($network, $key, $path); |
|
75
|
|
|
} |
|
76
|
|
|
|
|
77
|
|
|
/** |
|
78
|
|
|
* @param NetworkInterface $network |
|
79
|
|
|
* @param string $key |
|
80
|
|
|
* @param string|null $path |
|
81
|
|
|
* @return BIP32Key |
|
82
|
|
|
*/ |
|
83
|
5 |
|
public static function fromString(NetworkInterface $network, $key, $path = null) { |
|
84
|
5 |
|
return static::create($network, HierarchicalKeyFactory::fromExtended($key, $network), $path); |
|
85
|
|
|
} |
|
86
|
|
|
|
|
87
|
|
|
/** |
|
88
|
|
|
* @return NetworkInterface |
|
89
|
|
|
*/ |
|
90
|
|
|
public function network() { |
|
91
|
|
|
return $this->network; |
|
92
|
|
|
} |
|
93
|
|
|
|
|
94
|
|
|
/** |
|
95
|
|
|
* @return HierarchicalKey |
|
96
|
|
|
*/ |
|
97
|
23 |
|
public function key() { |
|
98
|
23 |
|
return $this->key; |
|
99
|
|
|
} |
|
100
|
|
|
|
|
101
|
|
|
/** |
|
102
|
|
|
* @return PublicKeyInterface |
|
103
|
|
|
*/ |
|
104
|
19 |
|
public function publicKey() { |
|
105
|
19 |
|
return $this->key->getPublicKey(); |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
/** |
|
109
|
|
|
* get the HEX of the plain public key for the current BIP32 key |
|
110
|
|
|
* |
|
111
|
|
|
* @return string |
|
112
|
|
|
*/ |
|
113
|
1 |
|
public function publicKeyHex() { |
|
114
|
|
|
// if this is a BIP32 Private key then we first build the public key |
|
115
|
|
|
// that way it will be cached nicely |
|
116
|
1 |
|
if (!$this->path->isPublicPath()) { |
|
117
|
1 |
|
return $this->buildKey($this->path->publicPath())->publicKey()->getHex(); |
|
118
|
|
|
} else { |
|
119
|
|
|
if (is_null($this->publicKeyHex)) { |
|
120
|
|
|
$this->publicKeyHex = $this->key->getPublicKey()->getHex(); |
|
121
|
|
|
} |
|
122
|
|
|
|
|
123
|
|
|
return $this->publicKeyHex; |
|
124
|
|
|
} |
|
125
|
|
|
} |
|
126
|
|
|
|
|
127
|
|
|
/** |
|
128
|
|
|
* @return BIP32Path |
|
129
|
|
|
*/ |
|
130
|
17 |
|
public function path() { |
|
131
|
17 |
|
return $this->path; |
|
132
|
|
|
} |
|
133
|
|
|
|
|
134
|
|
|
/** |
|
135
|
|
|
* @return BIP32Path |
|
136
|
|
|
*/ |
|
137
|
1 |
|
public function bip32Path() { |
|
138
|
1 |
|
return BIP32Path::path($this->path); |
|
139
|
|
|
} |
|
140
|
|
|
|
|
141
|
14 |
|
public function tuple() { |
|
142
|
14 |
|
return [$this->key->toExtendedKey($this->network), (string)$this->path]; |
|
143
|
|
|
} |
|
144
|
|
|
|
|
145
|
|
|
/** |
|
146
|
|
|
* build child key |
|
147
|
|
|
* |
|
148
|
|
|
* @param string|BIP32Path $path |
|
149
|
|
|
* @return BIP32Key |
|
150
|
|
|
* @throws \Exception |
|
151
|
|
|
*/ |
|
152
|
20 |
|
public function buildKey($path) { |
|
153
|
20 |
|
$path = BIP32Path::path($path); |
|
154
|
20 |
|
$originalPath = (string)$path; |
|
155
|
|
|
|
|
156
|
20 |
|
if (!isset($this->derivations[$originalPath])) { |
|
157
|
20 |
|
$key = $this->key; |
|
158
|
|
|
|
|
159
|
20 |
|
$toPublic = $path[0] === "M" && $this->path[0] === "m"; |
|
160
|
20 |
|
if ($toPublic) { |
|
161
|
12 |
|
$path = $path->privatePath(); |
|
162
|
|
|
} |
|
163
|
|
|
|
|
164
|
20 |
|
assert(strpos(strtolower((string)$path), strtolower((string)$this->path)) === 0); |
|
165
|
|
|
|
|
166
|
20 |
|
$path = substr((string)$path, strlen((string)$this->path)); |
|
167
|
|
|
|
|
168
|
20 |
|
if (substr($path, 0, 1) == "/") { |
|
169
|
19 |
|
$path = substr($path, 1); |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
20 |
|
if (strlen($path)) { |
|
173
|
19 |
|
$key = $key->derivePath($path); |
|
174
|
|
|
} |
|
175
|
|
|
|
|
176
|
20 |
|
if ($toPublic) { |
|
177
|
12 |
|
$key = $key->toPublic(); |
|
178
|
|
|
} |
|
179
|
|
|
|
|
180
|
20 |
|
$this->derivations[$originalPath] = BIP32Key::create($this->network, $key, $originalPath); |
|
181
|
|
|
} |
|
182
|
|
|
|
|
183
|
20 |
|
return $this->derivations[$originalPath]; |
|
184
|
|
|
} |
|
185
|
|
|
} |
|
186
|
|
|
|
This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.
Unreachable code is most often the result of
return,dieorexitstatements that have been added for debug purposes.In the above example, the last
return falsewill never be executed, because a return statement has already been met in every possible execution path.