Completed
Pull Request — master (#64)
by thomas
56:33 queued 53:42
created

NetworkMessageSerializer::fromParser()   D

Complexity

Conditions 27
Paths 51

Size

Total Lines 101
Code Lines 85

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 84
CRAP Score 27.0011

Importance

Changes 8
Bugs 1 Features 2
Metric Value
c 8
b 1
f 2
dl 0
loc 101
ccs 84
cts 85
cp 0.9882
rs 4.509
cc 27
eloc 85
nc 51
nop 1
crap 27.0011

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\InventorySerializer;
35
use BitWasp\Bitcoin\Networking\Serializer\Structure\NetworkAddressSerializer;
36
use BitWasp\Bitcoin\Networking\Serializer\Structure\NetworkAddressTimestampSerializer;
37
use BitWasp\Bitcoin\Serializer\Bloom\BloomFilterSerializer;
38
use BitWasp\Bitcoin\Serializer\Block\FilteredBlockSerializer;
39
use BitWasp\Bitcoin\Serializer\Block\BlockHeaderSerializer;
40
use BitWasp\Bitcoin\Serializer\Block\BlockSerializer;
41
use BitWasp\Bitcoin\Serializer\Block\PartialMerkleTreeSerializer;
42
use BitWasp\Bitcoin\Serializer\Chain\BlockLocatorSerializer;
43
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
44
use BitWasp\Buffertools\Buffertools;
45
use BitWasp\Buffertools\Parser;
46
use BitWasp\Buffertools\Buffer;
47
use BitWasp\Buffertools\TemplateFactory;
48
49
class NetworkMessageSerializer
50
{
51
    /**
52
     * @var NetworkInterface
53
     */
54
    private $network;
55
56
    /**
57
     * @var \BitWasp\Bitcoin\Math\Math
58
     */
59
    private $math;
60
61
    /**
62
     * @var TransactionSerializer
63
     */
64
    private $txSerializer;
65
66
    /**
67
     * @var BlockHeaderSerializer
68
     */
69
    private $headerSerializer;
70
71
    /**
72
     * @var GetDataSerializer
73
     */
74
    private $getDataSerializer;
75
76
    /**
77
     * @var InvSerializer
78
     */
79
    private $invSerializer;
80
81
    /**
82
     * @var BlockSerializer
83
     */
84
    private $blockSerializer;
85
86
    /**
87
     * @var FilteredBlockSerializer
88
     */
89
    private $filteredBlockSerializer;
90
91
    /**
92
     * @var HeadersSerializer
93
     */
94
    private $headersSerializer;
95
96
    /**
97
     * @var FilterAddSerializer
98
     */
99
    private $filterAddSerializer;
100
101
    /**
102
     * @var FilterLoadSerializer
103
     */
104
    private $filterLoadSerializer;
105
106
    /**
107
     * @var MerkleBlockSerializer
108
     */
109
    private $merkleBlockSerializer;
110
111
    /**
112
     * @var PingSerializer
113
     */
114
    private $pingSerializer;
115
116
    /**
117
     * @var AlertSerializer
118
     */
119
    private $alertSerializer;
120
121
    /**
122
     * @var InventorySerializer
123
     */
124
    private $inventorySerializer;
125
126
    /**
127
     * @var NotFoundSerializer
128
     */
129
    private $notFoundSerializer;
130
131
    /**
132
     * @var RejectSerializer
133
     */
134
    private $rejectSerializer;
135
136
    /**
137
     * @var BlockLocatorSerializer
138
     */
139
    private $blockLocatorSerializer;
140
141
    /**
142
     * @var GetBlocksSerializer
143
     */
144
    private $getBlocksSerializer;
145
146
    /**
147
     * @var GetHeadersSerializer
148
     */
149
    private $getHeadersSerializer;
150
151
    /**
152
     * @var PongSerializer
153
     */
154
    private $pongSerializer;
155
156
    /**
157
     * @var VersionSerializer
158
     */
159
    private $versionSerializer;
160
161
    /**
162
     * @var FeeFilterSerializer
163
     */
164
    private $feeFilterSerializer;
165
166
    /**
167
     * @var AddrSerializer
168
     */
169
    private $addrSerializer;
170
171
    /**
172
     * @param NetworkInterface $network
173
     */
174 84
    public function __construct(NetworkInterface $network)
175
    {
176 84
        $this->math = Bitcoin::getMath();
177 84
        $this->network = $network;
178 84
        $this->txSerializer = new TransactionSerializer();
179 84
        $this->headerSerializer = new BlockHeaderSerializer();
180 84
        $this->blockSerializer = new BlockSerializer($this->math, $this->headerSerializer, $this->txSerializer);
181 84
        $this->filteredBlockSerializer = new FilteredBlockSerializer($this->headerSerializer, new PartialMerkleTreeSerializer());
182 84
        $this->headersSerializer = new HeadersSerializer($this->headerSerializer);
183 84
        $this->filterAddSerializer = new FilterAddSerializer();
184 84
        $this->filterLoadSerializer = new FilterLoadSerializer(new BloomFilterSerializer());
185 84
        $this->merkleBlockSerializer = new MerkleBlockSerializer($this->filteredBlockSerializer);
186 84
        $this->pingSerializer = new PingSerializer();
187 84
        $this->pongSerializer = new PongSerializer();
188 84
        $this->alertSerializer = new AlertSerializer(new AlertDetailSerializer());
189 84
        $this->inventorySerializer = new InventorySerializer();
190 84
        $this->getDataSerializer = new GetDataSerializer($this->inventorySerializer);
191 84
        $this->invSerializer = new InvSerializer($this->inventorySerializer);
192 84
        $this->notFoundSerializer = new NotFoundSerializer($this->inventorySerializer);
193 84
        $this->feeFilterSerializer = new FeeFilterSerializer();
194 84
        $this->rejectSerializer = new RejectSerializer();
195 84
        $this->blockLocatorSerializer = new BlockLocatorSerializer();
196 84
        $this->getBlocksSerializer = new GetBlocksSerializer($this->blockLocatorSerializer);
197 84
        $this->getHeadersSerializer = new GetHeadersSerializer($this->blockLocatorSerializer);
198 84
        $this->versionSerializer = new VersionSerializer(new NetworkAddressSerializer());
199 84
        $this->addrSerializer = new AddrSerializer(new NetworkAddressTimestampSerializer());
200 84
    }
201
202
    /**
203
     * @return \BitWasp\Buffertools\Template
204
     */
205 84
    private function getHeaderTemplate()
206
    {
207 84
        return (new TemplateFactory())
208 84
            ->bytestringle(4)
209 84
            ->bytestring(12)
210 84
            ->uint32le()
211 84
            ->bytestring(4)
212 84
            ->getTemplate();
213
    }
214
215
    /**
216
     * @param Parser $parser
217
     * @return NetworkMessage
218
     * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
219
     * @throws \Exception
220
     */
221 52
    public function fromParser(Parser & $parser)
222
    {
223 52
        list ($netBytes, $command, $payloadSize, $checksum) = $this->getHeaderTemplate()->parse($parser);
224
        /** @var Buffer $netBytes */
225
        /** @var Buffer $command */
226
        /** @var int|string $payloadSize */
227
        /** @var Buffer $checksum */
228
229 52
        if ($netBytes->getHex() !== $this->network->getNetMagicBytes()) {
230 2
            throw new \RuntimeException('Invalid magic bytes for network');
231
        }
232
233
        $buffer = $payloadSize > 0
234 50
            ? $parser->readBytes($payloadSize)
235 50
            : new Buffer('', 0, $this->math);
236
237
        // Compare payload checksum against header value
238 50
        if (Hash::sha256d($buffer)->slice(0, 4)->getBinary() !== $checksum->getBinary()) {
239 2
            throw new \RuntimeException('Invalid packet checksum');
240
        }
241
242 48
        $cmd = trim($command->getBinary());
243
        switch ($cmd) {
244 48
            case Message::VERSION:
245 2
                $payload = $this->versionSerializer->parse($buffer);
246 2
                break;
247 46
            case Message::VERACK:
248 2
                $payload = new VerAck();
249 2
                break;
250 44
            case Message::SENDHEADERS:
251 2
                $payload = new SendHeaders();
252 2
                break;
253 42
            case Message::ADDR:
254 2
                $payload = $this->addrSerializer->parse($buffer);
255 2
                break;
256 40
            case Message::INV:
257 2
                $payload = $this->invSerializer->parse($buffer);
258 2
                break;
259 38
            case Message::GETDATA:
260 2
                $payload = $this->getDataSerializer->parse($buffer);
261 2
                break;
262 36
            case Message::NOTFOUND:
263 2
                $payload = $this->notFoundSerializer->parse($buffer);
264 2
                break;
265 34
            case Message::GETBLOCKS:
266 2
                $payload = $this->getBlocksSerializer->parse($buffer);
267 2
                break;
268 32
            case Message::GETHEADERS:
269 2
                $payload = $this->getHeadersSerializer->parse($buffer);
270 2
                break;
271 30
            case Message::TX:
272 2
                $payload = new Tx($this->txSerializer->parse($buffer));
273 2
                break;
274 28
            case Message::BLOCK:
275 2
                $payload = new Block($this->blockSerializer->parse($buffer));
276 2
                break;
277 26
            case Message::HEADERS:
278 2
                $payload = $this->headersSerializer->parse($buffer);
279 2
                break;
280 24
            case Message::GETADDR:
281 2
                $payload = new GetAddr();
282 2
                break;
283 22
            case Message::MEMPOOL:
284 2
                $payload = new MemPool();
285 2
                break;
286 20
            case Message::FEEFILTER:
287 2
                $payload = $this->feeFilterSerializer->parse($buffer);
288 2
                break;
289 18
            case Message::FILTERLOAD:
290 2
                $payload = $this->filterLoadSerializer->parse($buffer);
291 2
                break;
292 16
            case Message::FILTERADD:
293 2
                $payload = $this->filterAddSerializer->parse($buffer);
294 2
                break;
295 14
            case Message::FILTERCLEAR:
296 2
                $payload = new FilterClear();
297 2
                break;
298 12
            case Message::MERKLEBLOCK:
299 2
                $payload = $this->merkleBlockSerializer->parse($buffer);
300 2
                break;
301 10
            case Message::PING:
302 2
                $payload = $this->pingSerializer->parse($buffer);
303 2
                break;
304 8
            case Message::PONG:
305 2
                $payload = $this->pongSerializer->parse($buffer);
306 2
                break;
307 6
            case Message::REJECT:
308 2
                $payload = $this->rejectSerializer->parse($buffer);
309 2
                break;
310 4
            case Message::ALERT:
311 2
                $payload = $this->alertSerializer->parse($buffer);
312 2
                break;
313 2
            default:
314 2
                throw new \RuntimeException('Invalid command');
315 2
        }
316
317 46
        return new NetworkMessage(
318 46
            $this->network,
319
            $payload
320 46
        );
321
    }
322
323
    /**
324
     * @param NetworkMessage $object
325
     * @return Buffer
326
     */
327 84
    public function serialize(NetworkMessage $object)
328
    {
329 84
        $payload = $object->getPayload()->getBuffer();
330 84
        $command = str_pad(unpack("H*", $object->getCommand())[1], 24, '0', STR_PAD_RIGHT);
331 84
        $header = $this->getHeaderTemplate()->write([
332 84
            Buffer::hex($this->network->getNetMagicBytes(), null, $this->math),
333 84
            Buffer::hex($command, null, $this->math),
334 84
            $payload->getSize(),
335 84
            $object->getChecksum()
336 56
        ]);
337
338 84
        return Buffertools::concat($header, $payload);
339
    }
340
341
    /**
342
     * @param $data
343
     * @return NetworkMessage
344
     * @throws \Exception
345
     */
346 54
    public function parse($data)
347
    {
348 54
        return $this->fromParser(new Parser($data, $this->math));
349
    }
350
}
351