Completed
Pull Request — master (#583)
by thomas
272:08 queued 202:05
created

Network::validateHexString()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 4

Importance

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