Completed
Pull Request — master (#684)
by
unknown
36:43
created

Network::getAddressPrefixLength()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Network;
6
7
use BitWasp\Bitcoin\Exceptions\InvalidNetworkParameter;
8
use BitWasp\Bitcoin\Exceptions\MissingBase58Prefix;
9
use BitWasp\Bitcoin\Exceptions\MissingBech32Prefix;
10
use BitWasp\Bitcoin\Exceptions\MissingBip32Prefix;
11
use BitWasp\Bitcoin\Exceptions\MissingNetworkParameter;
12
13
class Network implements NetworkInterface
14
{
15
    const BECH32_PREFIX_SEGWIT = "segwit";
16
17
    const BASE58_ADDRESS_P2PKH = "p2pkh";
18
    const BASE58_ADDRESS_P2SH = "p2sh";
19
    const BASE58_WIF = "wif";
20
    const BIP32_PREFIX_XPUB = "xpub";
21
    const BIP32_PREFIX_XPRV = "xprv";
22
23
    /**
24
     * @var array map of base58 address type to byte
25
     */
26
    protected $base58PrefixMap = [];
27
28
    /**
29
     * @var array map of bech32 address type to HRP
30
     */
31
    protected $bech32PrefixMap = [];
32
33
    /**
34
     * @var array map of bip32 type to bytes
35
     */
36
    protected $bip32PrefixMap = [];
37
38
    /**
39
     * @var array map of bip32 key type to script type
40
     */
41
    protected $bip32ScriptTypeMap = [];
42
43
    /**
44
     * @var string - message prefix for bitcoin signed messages
45
     */
46
    protected $signedMessagePrefix;
47
48
    /**
49
     * @var string - 4 bytes for p2p magic
50
     */
51
    protected $p2pMagic;
52
53
    /**
54
     * @param string $field - name of field being validated
55
     * @param string $value - we check this value
56
     * @param int $length - length we require
57
     * @throws InvalidNetworkParameter
58
     */
59 142
    private function validateHexString(string $field, string $value, int $length)
60
    {
61 142
        if (!is_string($value) || strlen($value) !== 2 * $length) {
62
            throw new InvalidNetworkParameter("{$field} must be a {$length} byte hex string");
63
        }
64
65 142
        if (!ctype_xdigit($value)) {
66
            throw new InvalidNetworkParameter("{$field} prefix must be a valid hex string");
67
        }
68 142
    }
69
70
    /**
71
     * Network constructor.
72
     * @throws InvalidNetworkParameter
73
     */
74 142
    public function __construct()
75
    {
76 142
        if (null !== $this->p2pMagic) {
77 142
            $this->validateHexString("P2P magic", $this->p2pMagic, 4);
78
        }
79
80 142
        foreach ($this->base58PrefixMap as $type => $byte) {
81 142
            $this->validateHexString("{$type} base58 prefix", $byte, 1);
82
        }
83
84 142
        foreach ($this->bip32PrefixMap as $type => $bytes) {
85 142
            $this->validateHexString("{$type} bip32 prefix", $bytes, 4);
86
        }
87
88 142
        if (count($this->bip32ScriptTypeMap) !== count($this->bip32PrefixMap)) {
89
            throw new InvalidNetworkParameter("BIP32 prefixes not configured correctly");
90
        }
91 142
    }
92
93
    /**
94
     * @param string $prefixType
95
     * @return bool
96
     */
97 79
    protected function hasBase58Prefix(string $prefixType): bool
98
    {
99 79
        return array_key_exists($prefixType, $this->base58PrefixMap);
100
    }
101
102
    /**
103
     * @param string $prefixType
104
     * @return string
105
     * @throws MissingBase58Prefix
106
     */
107 77
    protected function getBase58Prefix(string $prefixType): string
108
    {
109 77
        if (!$this->hasBase58Prefix($prefixType)) {
110 1
            throw new MissingBase58Prefix();
111
        }
112 76
        return $this->base58PrefixMap[$prefixType];
113
    }
114
115
    /**
116
     * @param string $prefixType
117
     * @return bool
118
     */
119 42
    protected function hasBech32Prefix(string $prefixType): bool
120
    {
121 42
        return array_key_exists($prefixType, $this->bech32PrefixMap);
122
    }
123
124
    /**
125
     * @param string $prefixType
126
     * @return string
127
     * @throws MissingBech32Prefix
128
     */
129 40
    protected function getBech32Prefix(string $prefixType): string
130
    {
131 40
        if (!$this->hasBech32Prefix($prefixType)) {
132 1
            throw new MissingBech32Prefix();
133
        }
134 39
        return $this->bech32PrefixMap[$prefixType];
135
    }
136
137
    /**
138
     * @param string $prefixType
139
     * @return bool
140
     */
141 65
    protected function hasBip32Prefix(string $prefixType): bool
142
    {
143 65
        return array_key_exists($prefixType, $this->bip32PrefixMap);
144
    }
145
146
    /**
147
     * @param string $prefixType
148
     * @return string
149
     * @throws MissingBip32Prefix
150
     */
151 63
    protected function getBip32Prefix(string $prefixType): string
152
    {
153 63
        if (!$this->hasBip32Prefix($prefixType)) {
154 1
            throw new MissingBip32Prefix();
155
        }
156 62
        return $this->bip32PrefixMap[$prefixType];
157
    }
158
159
    /**
160
     * @return string
161
     * @throws MissingNetworkParameter
162
     * @see NetworkInterface::getSignedMessageMagic
163
     */
164 22
    public function getSignedMessageMagic(): string
165
    {
166 22
        if (null === $this->signedMessagePrefix) {
167
            throw new MissingNetworkParameter("Missing magic string for signed message");
168
        }
169 22
        return $this->signedMessagePrefix;
170
    }
171
172
    /**
173
     * @return string
174
     * @throws MissingNetworkParameter
175
     * @see NetworkInterface::getNetMagicBytes()
176
     */
177 15
    public function getNetMagicBytes(): string
178
    {
179 15
        if (null === $this->p2pMagic) {
180
            throw new MissingNetworkParameter("Missing network magic bytes");
181
        }
182 15
        return $this->p2pMagic;
183
    }
184
185
    /**
186
     * @return string
187
     * @throws MissingBase58Prefix
188
     */
189 28
    public function getPrivByte(): string
190
    {
191 28
        return $this->getBase58Prefix(self::BASE58_WIF);
192
    }
193
194
    /**
195
     * @return string
196
     * @throws MissingBase58Prefix
197
     * @see NetworkInterface::getAddressByte()
198
     */
199 47
    public function getAddressByte(): string
200
    {
201 47
        return $this->getBase58Prefix(self::BASE58_ADDRESS_P2PKH);
202
    }
203
    /**
204
     * @return int
205
     * @throws MissingBase58Prefix
206
     * @see NetworkInterface::getAddressPrefixLength()
207
     */
208
    public function getAddressPrefixLength(): int
209 54
    {
210
        return strlen($this->getAddressByte()) / 2;
211 54
    }
212
213
    /**
214
     * @return string
215
     * @throws MissingBase58Prefix
216
     * @see NetworkInterface::getP2shByte()
217
     */
218
    public function getP2shByte(): string
219 61
    {
220
        return $this->getBase58Prefix(self::BASE58_ADDRESS_P2SH);
221 61
    }
222
223
    /**
224
     * @return int
225
     * @throws MissingBase58Prefix
226
     * @see NetworkInterface::getP2shPrefixLength()
227
     */
228
    public function getP2shPrefixLength(): int
229 61
    {
230
        return strlen($this->getP2shByte()) / 2;
231 61
    }
232
233
    /**
234
     * @return string
235
     * @throws MissingBip32Prefix
236
     * @see NetworkInterface::getHDPubByte()
237
     */
238
    public function getHDPubByte(): string
239 38
    {
240
        return $this->getBip32Prefix(self::BIP32_PREFIX_XPUB);
241 38
    }
242
243
    /**
244
     * @return string
245
     * @throws MissingBip32Prefix
246
     * @see NetworkInterface::getHDPrivByte()
247
     */
248
    public function getHDPrivByte(): string
249
    {
250
        return $this->getBip32Prefix(self::BIP32_PREFIX_XPRV);
251
    }
252
253
    /**
254
     * @return string
255
     * @throws MissingBech32Prefix
256
     * @see NetworkInterface::getSegwitBech32Prefix()
257
     */
258
    public function getSegwitBech32Prefix(): string
259
    {
260
        return $this->getBech32Prefix(self::BECH32_PREFIX_SEGWIT);
261
    }
262
}
263