Completed
Pull Request — master (#81)
by thomas
02:07
created

NetworkMessageSerializer::parsePacket()   D

Complexity

Conditions 26
Paths 50

Size

Total Lines 91
Code Lines 82

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 80
CRAP Score 26.0318

Importance

Changes 0
Metric Value
dl 0
loc 91
ccs 80
cts 83
cp 0.9639
rs 4.5382
c 0
b 0
f 0
cc 26
eloc 82
nc 50
nop 2
crap 26.0318

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace BitWasp\Bitcoin\Networking\Serializer;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Crypto\Hash;
7
use BitWasp\Bitcoin\Network\NetworkInterface;
8
use BitWasp\Bitcoin\Networking\Message;
9
use BitWasp\Bitcoin\Networking\Messages\Block;
10
use BitWasp\Bitcoin\Networking\Messages\FilterClear;
11
use BitWasp\Bitcoin\Networking\Messages\GetAddr;
12
use BitWasp\Bitcoin\Networking\Messages\MemPool;
13
use BitWasp\Bitcoin\Networking\Messages\SendHeaders;
14
use BitWasp\Bitcoin\Networking\Messages\Tx;
15
use BitWasp\Bitcoin\Networking\Messages\VerAck;
16
use BitWasp\Bitcoin\Networking\NetworkMessage;
17
use BitWasp\Bitcoin\Networking\Serializer\Message\AddrSerializer;
18
use BitWasp\Bitcoin\Networking\Serializer\Message\AlertSerializer;
19
use BitWasp\Bitcoin\Networking\Serializer\Message\FeeFilterSerializer;
20
use BitWasp\Bitcoin\Networking\Serializer\Message\FilterAddSerializer;
21
use BitWasp\Bitcoin\Networking\Serializer\Message\FilterLoadSerializer;
22
use BitWasp\Bitcoin\Networking\Serializer\Message\GetBlocksSerializer;
23
use BitWasp\Bitcoin\Networking\Serializer\Message\GetDataSerializer;
24
use BitWasp\Bitcoin\Networking\Serializer\Message\GetHeadersSerializer;
25
use BitWasp\Bitcoin\Networking\Serializer\Message\HeadersSerializer;
26
use BitWasp\Bitcoin\Networking\Serializer\Message\InvSerializer;
27
use BitWasp\Bitcoin\Networking\Serializer\Message\MerkleBlockSerializer;
28
use BitWasp\Bitcoin\Networking\Serializer\Message\NotFoundSerializer;
29
use BitWasp\Bitcoin\Networking\Serializer\Message\PingSerializer;
30
use BitWasp\Bitcoin\Networking\Serializer\Message\PongSerializer;
31
use BitWasp\Bitcoin\Networking\Serializer\Message\RejectSerializer;
32
use BitWasp\Bitcoin\Networking\Serializer\Message\VersionSerializer;
33
use BitWasp\Bitcoin\Networking\Serializer\Structure\AlertDetailSerializer;
34
use BitWasp\Bitcoin\Networking\Serializer\Structure\HeaderSerializer;
35
use BitWasp\Bitcoin\Networking\Serializer\Structure\InventorySerializer;
36
use BitWasp\Bitcoin\Networking\Serializer\Structure\NetworkAddressSerializer;
37
use BitWasp\Bitcoin\Networking\Serializer\Structure\NetworkAddressTimestampSerializer;
38
use BitWasp\Bitcoin\Networking\Structure\Header;
39
use BitWasp\Bitcoin\Serializer\Bloom\BloomFilterSerializer;
40
use BitWasp\Bitcoin\Serializer\Block\FilteredBlockSerializer;
41
use BitWasp\Bitcoin\Serializer\Block\BlockHeaderSerializer;
42
use BitWasp\Bitcoin\Serializer\Block\BlockSerializer;
43
use BitWasp\Bitcoin\Serializer\Block\PartialMerkleTreeSerializer;
44
use BitWasp\Bitcoin\Serializer\Chain\BlockLocatorSerializer;
45
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
46
use BitWasp\Bitcoin\Serializer\Types;
47
use BitWasp\Buffertools\Parser;
48
use BitWasp\Buffertools\Buffer;
49
50
class NetworkMessageSerializer
51
{
52
    /**
53
     * @var NetworkInterface
54
     */
55
    private $network;
56
57
    /**
58
     * @var \BitWasp\Bitcoin\Math\Math
59
     */
60
    private $math;
61
62
    /**
63
     * @var TransactionSerializer
64
     */
65
    private $txSerializer;
66
67
    /**
68
     * @var BlockHeaderSerializer
69
     */
70
    private $headerSerializer;
71
72
    /**
73
     * @var GetDataSerializer
74
     */
75
    private $getDataSerializer;
76
77
    /**
78
     * @var InvSerializer
79
     */
80
    private $invSerializer;
81
82
    /**
83
     * @var BlockSerializer
84
     */
85
    private $blockSerializer;
86
87
    /**
88
     * @var FilteredBlockSerializer
89
     */
90
    private $filteredBlockSerializer;
91
92
    /**
93
     * @var HeadersSerializer
94
     */
95
    private $headersSerializer;
96
97
    /**
98
     * @var FilterAddSerializer
99
     */
100
    private $filterAddSerializer;
101
102
    /**
103
     * @var FilterLoadSerializer
104
     */
105
    private $filterLoadSerializer;
106
107
    /**
108
     * @var MerkleBlockSerializer
109
     */
110
    private $merkleBlockSerializer;
111
112
    /**
113
     * @var PingSerializer
114
     */
115
    private $pingSerializer;
116
117
    /**
118
     * @var AlertSerializer
119
     */
120
    private $alertSerializer;
121
122
    /**
123
     * @var InventorySerializer
124
     */
125
    private $inventorySerializer;
126
127
    /**
128
     * @var NotFoundSerializer
129
     */
130
    private $notFoundSerializer;
131
132
    /**
133
     * @var RejectSerializer
134
     */
135
    private $rejectSerializer;
136
137
    /**
138
     * @var BlockLocatorSerializer
139
     */
140
    private $blockLocatorSerializer;
141
142
    /**
143
     * @var GetBlocksSerializer
144
     */
145
    private $getBlocksSerializer;
146
147
    /**
148
     * @var GetHeadersSerializer
149
     */
150
    private $getHeadersSerializer;
151
152
    /**
153
     * @var PongSerializer
154
     */
155
    private $pongSerializer;
156
157
    /**
158
     * @var VersionSerializer
159
     */
160
    private $versionSerializer;
161
162
    /**
163
     * @var FeeFilterSerializer
164
     */
165
    private $feeFilterSerializer;
166
167
    /**
168
     * @var AddrSerializer
169
     */
170
    private $addrSerializer;
171
172
    /**
173
     * @param NetworkInterface $network
174
     */
175 96
    public function __construct(NetworkInterface $network)
176
    {
177 96
        $this->math = Bitcoin::getMath();
178 96
        $this->network = $network;
179 96
        $this->bs4le = Types::bytestringle(4);
0 ignored issues
show
Bug introduced by
The property bs4le does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
180 96
        $this->txSerializer = new TransactionSerializer();
181 96
        $this->headerSerializer = new BlockHeaderSerializer();
182 96
        $this->blockSerializer = new BlockSerializer($this->math, $this->headerSerializer, $this->txSerializer);
183 96
        $this->filteredBlockSerializer = new FilteredBlockSerializer($this->headerSerializer, new PartialMerkleTreeSerializer());
184 96
        $this->headersSerializer = new HeadersSerializer($this->headerSerializer);
185 96
        $this->filterAddSerializer = new FilterAddSerializer();
186 96
        $this->filterLoadSerializer = new FilterLoadSerializer(new BloomFilterSerializer());
187 96
        $this->merkleBlockSerializer = new MerkleBlockSerializer($this->filteredBlockSerializer);
188 96
        $this->pingSerializer = new PingSerializer();
189 96
        $this->pongSerializer = new PongSerializer();
190 96
        $this->alertSerializer = new AlertSerializer(new AlertDetailSerializer());
191 96
        $this->inventorySerializer = new InventorySerializer();
192 96
        $this->getDataSerializer = new GetDataSerializer($this->inventorySerializer);
193 96
        $this->invSerializer = new InvSerializer($this->inventorySerializer);
194 96
        $this->notFoundSerializer = new NotFoundSerializer($this->inventorySerializer);
195 96
        $this->feeFilterSerializer = new FeeFilterSerializer();
196 96
        $this->rejectSerializer = new RejectSerializer();
197 96
        $this->blockLocatorSerializer = new BlockLocatorSerializer();
198 96
        $this->getBlocksSerializer = new GetBlocksSerializer($this->blockLocatorSerializer);
199 96
        $this->getHeadersSerializer = new GetHeadersSerializer($this->blockLocatorSerializer);
200 96
        $this->versionSerializer = new VersionSerializer(new NetworkAddressSerializer());
201 96
        $this->addrSerializer = new AddrSerializer(new NetworkAddressTimestampSerializer());
202 96
        $this->packetHeaderSerializer = new HeaderSerializer();
0 ignored issues
show
Bug introduced by
The property packetHeaderSerializer does not seem to exist. Did you mean headerSerializer?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
203 96
    }
204
205
    /**
206
     * @param Parser $parser
207
     * @return Header
208
     */
209 90
    public function parseHeader(Parser $parser)
210
    {
211 90
        $prefix = $this->bs4le->read($parser);
212 90
        if ($prefix->getHex() !== $this->network->getNetMagicBytes()) {
213 3
            throw new \RuntimeException('Invalid magic bytes for network');
214
        }
215
216 87
        return $this->packetHeaderSerializer->fromParser($parser);
0 ignored issues
show
Bug introduced by
The property packetHeaderSerializer does not seem to exist. Did you mean headerSerializer?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
217
    }
218
219
    /**
220
     * @param Header $header
221
     * @param Parser $parser
222
     * @return NetworkMessage
223
     */
224 87
    public function parsePacket(Header $header, Parser $parser)
225
    {
226 87
        $buffer = $header->getLength() > 0
227 77
            ? $parser->readBytes($header->getLength())
228 87
            : new Buffer('', 0, $this->math);
229
230
        // Compare payload checksum against header value
231 87
        if (!Hash::sha256d($buffer)->slice(0, 4)->equals($header->getChecksum())) {
232 3
            throw new \RuntimeException('Invalid packet checksum');
233
        }
234
235 84
        $cmd = trim($header->getCommand());
236
        switch ($cmd) {
237 84
            case Message::VERSION:
238 15
                $payload = $this->versionSerializer->parse($buffer);
239 15
                break;
240 81
            case Message::VERACK:
241 15
                $payload = new VerAck();
242 15
                break;
243 69
            case Message::SENDHEADERS:
244 3
                $payload = new SendHeaders();
245 3
                break;
246 66
            case Message::ADDR:
247 4
                $payload = $this->addrSerializer->parse($buffer);
248 4
                break;
249 63
            case Message::INV:
250 3
                $payload = $this->invSerializer->parse($buffer);
251 3
                break;
252 60
            case Message::GETDATA:
253 3
                $payload = $this->getDataSerializer->parse($buffer);
254 3
                break;
255 57
            case Message::NOTFOUND:
256 3
                $payload = $this->notFoundSerializer->parse($buffer);
257 3
                break;
258 54
            case Message::GETBLOCKS:
259 3
                $payload = $this->getBlocksSerializer->parse($buffer);
260 3
                break;
261 51
            case Message::GETHEADERS:
262 3
                $payload = $this->getHeadersSerializer->parse($buffer);
263 3
                break;
264 48
            case Message::TX:
265 3
                $payload = new Tx($this->txSerializer->parse($buffer));
266 3
                break;
267 45
            case Message::BLOCK:
268 3
                $payload = new Block($this->blockSerializer->parse($buffer));
269 3
                break;
270 42
            case Message::HEADERS:
271 3
                $payload = $this->headersSerializer->parse($buffer);
272 3
                break;
273 39
            case Message::GETADDR:
274 3
                $payload = new GetAddr();
275 3
                break;
276 36
            case Message::MEMPOOL:
277 3
                $payload = new MemPool();
278 3
                break;
279 33
            case Message::FEEFILTER:
280 3
                $payload = $this->feeFilterSerializer->parse($buffer);
281 3
                break;
282 30
            case Message::FILTERLOAD:
283 3
                $payload = $this->filterLoadSerializer->parse($buffer);
284 3
                break;
285 27
            case Message::FILTERADD:
286 3
                $payload = $this->filterAddSerializer->parse($buffer);
287 3
                break;
288 24
            case Message::FILTERCLEAR:
289 3
                $payload = new FilterClear();
290 3
                break;
291 21
            case Message::MERKLEBLOCK:
292 3
                $payload = $this->merkleBlockSerializer->parse($buffer);
293 3
                break;
294 18
            case Message::PING:
295 4
                $payload = $this->pingSerializer->parse($buffer);
296 4
                break;
297 14
            case Message::PONG:
298 3
                $payload = $this->pongSerializer->parse($buffer);
299 3
                break;
300 11
            case Message::REJECT:
301 3
                $payload = $this->rejectSerializer->parse($buffer);
302 3
                break;
303 8
            case Message::ALERT:
304 8
                $payload = $this->alertSerializer->parse($buffer);
305 8
                break;
306
            default:
307
                throw new \RuntimeException('Invalid command');
308
        }
309
310 84
        return new NetworkMessage(
311 84
            $this->network,
312 28
            $payload
313 28
        );
314
    }
315
316
    /**
317
     * @param Parser $parser
318
     * @return NetworkMessage
319
     * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
320
     * @throws \Exception
321
     */
322 90
    public function fromParser(Parser $parser)
323
    {
324 90
        $header = $this->parseHeader($parser);
325 87
        return $this->parsePacket($header, $parser);
326
    }
327
328
    /**
329
     * @param NetworkMessage $object
330
     * @return Buffer
331
     */
332 93
    public function serialize(NetworkMessage $object)
333
    {
334 93
        $prefix = $this->bs4le->write(Buffer::hex($this->network->getNetMagicBytes(), null, $this->math));
335 93
        $header = $this->packetHeaderSerializer->serialize($object->getHeader());
0 ignored issues
show
Bug introduced by
The property packetHeaderSerializer does not seem to exist. Did you mean headerSerializer?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
336 93
        $payload = $object->getPayload()->getBuffer();
337
338 93
        return new Buffer(
339 93
            "{$prefix}{$header->getBinary()}{$payload->getBinary()}"
340 31
        );
341
    }
342
343
    /**
344
     * @param $data
345
     * @return NetworkMessage
346
     * @throws \Exception
347
     */
348 54
    public function parse($data)
349
    {
350 54
        return $this->fromParser(new Parser($data, $this->math));
351
    }
352
}
353