NetworkMessageSerializer::parseHeader()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

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