|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Blocktrail\SDK\Bitcoin; |
|
4
|
|
|
|
|
5
|
|
|
use BitWasp\Bitcoin\Crypto\EcAdapter\Impl\PhpEcc\Key\PublicKey; |
|
6
|
|
|
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface; |
|
7
|
|
|
use BitWasp\Bitcoin\Key\Deterministic\HierarchicalKey; |
|
8
|
|
|
|
|
9
|
|
|
/** |
|
10
|
|
|
* Class BIP32Key |
|
11
|
|
|
* |
|
12
|
|
|
* Container for a BIP32 key and path |
|
13
|
|
|
*/ |
|
14
|
|
|
class BIP32Key { |
|
15
|
|
|
/** |
|
16
|
|
|
* @var HierarchicalKey |
|
17
|
|
|
*/ |
|
18
|
|
|
private $key; |
|
19
|
|
|
|
|
20
|
|
|
/** |
|
21
|
|
|
* @var BIP32Path |
|
22
|
|
|
*/ |
|
23
|
|
|
private $path; |
|
24
|
|
|
|
|
25
|
|
|
/** |
|
26
|
|
|
* @var string|null |
|
27
|
|
|
*/ |
|
28
|
|
|
private $publicKeyHex = null; |
|
29
|
|
|
|
|
30
|
|
|
/** |
|
31
|
|
|
* @var BIP32Key[] |
|
32
|
|
|
*/ |
|
33
|
|
|
private $derivations = []; |
|
34
|
|
|
|
|
35
|
|
|
/** |
|
36
|
|
|
* @param HierarchicalKey $key |
|
37
|
|
|
* @param string|null $path |
|
38
|
|
|
* @throws \Exception |
|
39
|
|
|
*/ |
|
40
|
3 |
|
public function __construct(HierarchicalKey $key, $path = null) { |
|
41
|
3 |
|
$this->key = $key; |
|
42
|
3 |
|
$this->path = BIP32Path::path($path); |
|
43
|
|
|
|
|
44
|
3 |
|
return; |
|
45
|
|
|
|
|
46
|
|
|
if (is_array($key) && count($key) == 2) { |
|
|
|
|
|
|
47
|
|
|
$this->key = $key[0]; |
|
48
|
|
|
$this->path = BIP32Path::path($key[1]); |
|
49
|
|
|
} elseif (is_string($key) && is_string($path) && strlen($key) && strlen($path)) { |
|
50
|
|
|
$this->key = $key; |
|
|
|
|
|
|
51
|
|
|
$this->path = BIP32Path::path($path); |
|
52
|
|
|
} else { |
|
53
|
|
|
throw new \Exception("Bad input"); |
|
54
|
|
|
} |
|
55
|
|
|
} |
|
56
|
|
|
|
|
57
|
|
|
/** |
|
58
|
|
|
* static method to initialize class |
|
59
|
|
|
* |
|
60
|
|
|
* @param HierarchicalKey $key |
|
61
|
|
|
* @param string|null $path |
|
62
|
|
|
* @return BIP32Key |
|
63
|
|
|
*/ |
|
64
|
3 |
|
public static function create(HierarchicalKey $key, $path = null) { |
|
65
|
3 |
|
return new BIP32Key($key, $path); |
|
66
|
|
|
} |
|
67
|
|
|
|
|
68
|
|
|
/** |
|
69
|
|
|
* @return HierarchicalKey |
|
70
|
|
|
*/ |
|
71
|
2 |
|
public function key() { |
|
72
|
2 |
|
return $this->key; |
|
73
|
|
|
} |
|
74
|
|
|
|
|
75
|
|
|
/** |
|
76
|
|
|
* @return PublicKeyInterface |
|
77
|
|
|
*/ |
|
78
|
3 |
|
public function publicKey() { |
|
79
|
3 |
|
return $this->key->getPublicKey(); |
|
80
|
|
|
} |
|
81
|
|
|
|
|
82
|
|
|
/** |
|
83
|
|
|
* get the HEX of the plain public key for the current BIP32 key |
|
84
|
|
|
* |
|
85
|
|
|
* @return string |
|
86
|
|
|
*/ |
|
87
|
|
|
public function publicKeyHex() { |
|
88
|
|
|
// if this is a BIP32 Private key then we first build the public key |
|
89
|
|
|
// that way it will be cached nicely |
|
90
|
|
|
if (!$this->path->isPublicPath()) { |
|
91
|
|
|
return $this->buildKey($this->path->publicPath())->publicKey()->getHex(); |
|
92
|
|
|
} else { |
|
93
|
|
|
if (is_null($this->publicKeyHex)) { |
|
94
|
|
|
$this->publicKeyHex = $this->key->getPublicKey()->getHex(); |
|
95
|
|
|
} |
|
96
|
|
|
|
|
97
|
|
|
return $this->publicKeyHex; |
|
98
|
|
|
} |
|
99
|
|
|
} |
|
100
|
|
|
|
|
101
|
|
|
/** |
|
102
|
|
|
* @return BIP32Path |
|
103
|
|
|
*/ |
|
104
|
3 |
|
public function path() { |
|
105
|
3 |
|
return $this->path; |
|
106
|
|
|
} |
|
107
|
|
|
|
|
108
|
|
|
/** |
|
109
|
|
|
* @return BIP32Path |
|
110
|
|
|
*/ |
|
111
|
|
|
public function bip32Path() { |
|
112
|
|
|
return BIP32Path::path($this->path); |
|
113
|
|
|
} |
|
114
|
|
|
|
|
115
|
2 |
|
public function tuple() { |
|
116
|
2 |
|
return [$this->key->toExtendedKey(), (string)$this->path]; |
|
117
|
|
|
} |
|
118
|
|
|
|
|
119
|
|
|
/** |
|
120
|
|
|
* build child key |
|
121
|
|
|
* |
|
122
|
|
|
* @param string|BIP32Path $path |
|
123
|
|
|
* @return BIP32Key |
|
124
|
|
|
* @throws \Exception |
|
125
|
|
|
*/ |
|
126
|
3 |
|
public function buildKey($path) { |
|
127
|
3 |
|
$path = BIP32Path::path($path); |
|
128
|
3 |
|
$originalPath = (string)$path; |
|
129
|
|
|
|
|
130
|
3 |
|
if (!isset($this->derivations[$originalPath])) { |
|
131
|
3 |
|
$key = $this->key; |
|
132
|
|
|
|
|
133
|
3 |
|
$toPublic = $path[0] === "M" && $this->path[0] === "m"; |
|
134
|
3 |
|
if ($toPublic) { |
|
135
|
1 |
|
$path = $path->privatePath(); |
|
136
|
|
|
} |
|
137
|
|
|
|
|
138
|
3 |
|
assert(strpos(strtolower((string)$path), strtolower((string)$this->path)) === 0); |
|
139
|
|
|
|
|
140
|
3 |
|
$path = substr((string)$path, strlen((string)$this->path)); |
|
141
|
|
|
|
|
142
|
3 |
|
if (substr($path, 0, 1) == "/") { |
|
143
|
3 |
|
$path = substr($path, 1); |
|
144
|
|
|
} |
|
145
|
|
|
|
|
146
|
3 |
|
if (strlen($path)) { |
|
147
|
3 |
|
$key = $key->derivePath($path); |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
3 |
|
if ($toPublic) { |
|
151
|
1 |
|
$key = $key->toPublic(); |
|
152
|
|
|
} |
|
153
|
|
|
|
|
154
|
3 |
|
$this->derivations[$originalPath] = BIP32Key::create($key, $originalPath); |
|
155
|
|
|
} |
|
156
|
|
|
|
|
157
|
3 |
|
return $this->derivations[$originalPath]; |
|
158
|
|
|
} |
|
159
|
|
|
} |
|
160
|
|
|
|
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.