Passed
Pull Request — master (#89)
by thomas
02:49
created

NetworkMessageSerializer::parse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 1
cts 1
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\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\Bloom\BloomFilterSerializer;
42
use BitWasp\Bitcoin\Serializer\Block\FilteredBlockSerializer;
43
use BitWasp\Bitcoin\Serializer\Block\BlockHeaderSerializer;
44
use BitWasp\Bitcoin\Serializer\Block\BlockSerializer;
45
use BitWasp\Bitcoin\Serializer\Block\PartialMerkleTreeSerializer;
46
use BitWasp\Bitcoin\Serializer\Chain\BlockLocatorSerializer;
47
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
48
use BitWasp\Bitcoin\Serializer\Types;
49
use BitWasp\Buffertools\Parser;
50
use BitWasp\Buffertools\Buffer;
51
52
class NetworkMessageSerializer
53
{
54
    /**
55
     * @var NetworkInterface
56
     */
57
    private $network;
58
59
    /**
60
     * @var \BitWasp\Bitcoin\Math\Math
61
     */
62
    private $math;
63
64
    /**
65
     * @var TransactionSerializer
66
     */
67
    private $txSerializer;
68
69
    /**
70
     * @var BlockHeaderSerializer
71
     */
72
    private $headerSerializer;
73
74
    /**
75
     * @var GetDataSerializer
76
     */
77
    private $getDataSerializer;
78
79
    /**
80
     * @var InvSerializer
81
     */
82
    private $invSerializer;
83
84
    /**
85
     * @var BlockSerializer
86
     */
87
    private $blockSerializer;
88
89
    /**
90
     * @var FilteredBlockSerializer
91
     */
92
    private $filteredBlockSerializer;
93
94
    /**
95
     * @var HeadersSerializer
96
     */
97
    private $headersSerializer;
98
99
    /**
100
     * @var FilterAddSerializer
101
     */
102
    private $filterAddSerializer;
103
104
    /**
105
     * @var FilterLoadSerializer
106
     */
107
    private $filterLoadSerializer;
108
109
    /**
110
     * @var MerkleBlockSerializer
111
     */
112
    private $merkleBlockSerializer;
113
114
    /**
115
     * @var PingSerializer
116
     */
117
    private $pingSerializer;
118
119
    /**
120
     * @var AlertSerializer
121
     */
122
    private $alertSerializer;
123
124
    /**
125
     * @var InventorySerializer
126
     */
127
    private $inventorySerializer;
128
129
    /**
130
     * @var NotFoundSerializer
131
     */
132
    private $notFoundSerializer;
133
134
    /**
135
     * @var RejectSerializer
136
     */
137
    private $rejectSerializer;
138
139
    /**
140
     * @var BlockLocatorSerializer
141
     */
142
    private $blockLocatorSerializer;
143
144
    /**
145
     * @var GetBlocksSerializer
146
     */
147
    private $getBlocksSerializer;
148
149
    /**
150
     * @var GetHeadersSerializer
151
     */
152
    private $getHeadersSerializer;
153
154
    /**
155
     * @var PongSerializer
156
     */
157
    private $pongSerializer;
158
159
    /**
160
     * @var VersionSerializer
161
     */
162
    private $versionSerializer;
163
164
    /**
165
     * @var FeeFilterSerializer
166
     */
167
    private $feeFilterSerializer;
168
169
    /**
170
     * @var AddrSerializer
171
     */
172
    private $addrSerializer;
173
174 96
    /**
175
     * @param NetworkInterface $network
176 96
     */
177 96
    public function __construct(NetworkInterface $network)
178 96
    {
179 96
        $this->math = Bitcoin::getMath();
180 96
        $this->network = $network;
181 96
        $this->bs4le = Types::bytestringle(4);
0 ignored issues
show
Bug Best Practice introduced by
The property bs4le does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
182 96
        $this->txSerializer = new TransactionSerializer();
183 96
        $this->headerSerializer = new BlockHeaderSerializer();
184 96
        $this->blockSerializer = new BlockSerializer($this->math, $this->headerSerializer, $this->txSerializer);
185 96
        $this->filteredBlockSerializer = new FilteredBlockSerializer($this->headerSerializer, new PartialMerkleTreeSerializer());
186 96
        $this->headersSerializer = new HeadersSerializer($this->headerSerializer);
187 96
        $this->filterAddSerializer = new FilterAddSerializer();
188 96
        $this->filterLoadSerializer = new FilterLoadSerializer(new BloomFilterSerializer());
189 96
        $this->merkleBlockSerializer = new MerkleBlockSerializer($this->filteredBlockSerializer);
190 96
        $this->pingSerializer = new PingSerializer();
191 96
        $this->pongSerializer = new PongSerializer();
192 96
        $this->alertSerializer = new AlertSerializer(new AlertDetailSerializer());
193 96
        $this->inventorySerializer = new InventorySerializer();
194 96
        $this->getDataSerializer = new GetDataSerializer($this->inventorySerializer);
195 96
        $this->invSerializer = new InvSerializer($this->inventorySerializer);
196 96
        $this->notFoundSerializer = new NotFoundSerializer($this->inventorySerializer);
197 96
        $this->feeFilterSerializer = new FeeFilterSerializer();
198 96
        $this->rejectSerializer = new RejectSerializer();
199 96
        $this->blockLocatorSerializer = new BlockLocatorSerializer();
200 96
        $this->getBlocksSerializer = new GetBlocksSerializer($this->blockLocatorSerializer);
201
        $this->getHeadersSerializer = new GetHeadersSerializer($this->blockLocatorSerializer);
202
        $this->versionSerializer = new VersionSerializer(new NetworkAddressSerializer());
203
        $this->addrSerializer = new AddrSerializer(new NetworkAddressTimestampSerializer());
204
        $this->packetHeaderSerializer = new HeaderSerializer();
0 ignored issues
show
Bug Best Practice introduced by
The property packetHeaderSerializer does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
205 96
    }
206
207 96
    /**
208 96
     * @param Parser $parser
209 96
     * @return Header
210 96
     */
211 96
    public function parseHeader(Parser $parser)
212 96
    {
213
        $prefix = $this->bs4le->read($parser);
214
        if ($prefix->getHex() !== $this->network->getNetMagicBytes()) {
215
            throw new \RuntimeException('Invalid magic bytes for network');
216
        }
217
218
        return $this->packetHeaderSerializer->fromParser($parser);
219
    }
220
221 90
    /**
222
     * @param Header $header
223 90
     * @param Parser $parser
224
     * @return NetworkMessage
225
     */
226
    public function parsePacket(Header $header, Parser $parser)
227
    {
228
        $buffer = $header->getLength() > 0
229 90
            ? $parser->readBytes($header->getLength())
230 3
            : new Buffer('', 0, $this->math);
231
232
        // Compare payload checksum against header value
233 29
        if (!Hash::sha256d($buffer)->slice(0, 4)->equals($header->getChecksum())) {
234 81
            throw new \RuntimeException('Invalid packet checksum');
235 87
        }
236
237
        $cmd = trim($header->getCommand());
238 87
        switch ($cmd) {
239 3
            case Message::VERSION:
240
                $payload = $this->versionSerializer->parse($buffer);
241
                break;
242 84
            case Message::VERACK:
243
                $payload = new VerAck();
244 84
                break;
245 15
            case Message::SENDHEADERS:
246 15
                $payload = new SendHeaders();
247 81
                break;
248 15
            case Message::ADDR:
249 15
                $payload = $this->addrSerializer->parse($buffer);
250 69
                break;
251 3
            case Message::INV:
252 3
                $payload = $this->invSerializer->parse($buffer);
253 66
                break;
254 5
            case Message::GETDATA:
255 5
                $payload = $this->getDataSerializer->parse($buffer);
256 63
                break;
257 3
            case Message::NOTFOUND:
258 3
                $payload = $this->notFoundSerializer->parse($buffer);
259 60
                break;
260 3
            case Message::GETBLOCKS:
261 3
                $payload = $this->getBlocksSerializer->parse($buffer);
262 57
                break;
263 3
            case Message::GETHEADERS:
264 3
                $payload = $this->getHeadersSerializer->parse($buffer);
265 54
                break;
266 3
            case Message::TX:
267 3
                $payload = new Tx($this->txSerializer->parse($buffer));
268 51
                break;
269 3
            case Message::BLOCK:
270 3
                $payload = new Block($this->blockSerializer->parse($buffer));
271 48
                break;
272 3
            case Message::HEADERS:
273 3
                $payload = $this->headersSerializer->parse($buffer);
274 45
                break;
275 3
            case Message::GETADDR:
276 3
                $payload = new GetAddr();
277 42
                break;
278 3
            case Message::MEMPOOL:
279 3
                $payload = new MemPool();
280 39
                break;
281 3
            case Message::FEEFILTER:
282 3
                $payload = $this->feeFilterSerializer->parse($buffer);
283 36
                break;
284 3
            case Message::FILTERLOAD:
285 3
                $payload = $this->filterLoadSerializer->parse($buffer);
286 33
                break;
287 3
            case Message::FILTERADD:
288 3
                $payload = $this->filterAddSerializer->parse($buffer);
289 30
                break;
290 3
            case Message::FILTERCLEAR:
291 3
                $payload = new FilterClear();
292 27
                break;
293 3
            case Message::MERKLEBLOCK:
294 3
                $payload = $this->merkleBlockSerializer->parse($buffer);
295 24
                break;
296 3
            case Message::PING:
297 3
                $payload = $this->pingSerializer->parse($buffer);
298 21
                break;
299 3
            case Message::PONG:
300 3
                $payload = $this->pongSerializer->parse($buffer);
301 18
                break;
302 6
            case Message::REJECT:
303 6
                $payload = $this->rejectSerializer->parse($buffer);
304 12
                break;
305 3
            case Message::ALERT:
306 3
                $payload = $this->alertSerializer->parse($buffer);
307 9
                break;
308 3
            default:
309 3
                throw new \RuntimeException('Unsupported message type');
310 6
        }
311 3
312 3
        return new NetworkMessage($this->network, $payload);
0 ignored issues
show
Bug introduced by
It seems like $payload can also be of type array; however, parameter $message of BitWasp\Bitcoin\Networki...kMessage::__construct() does only seem to accept BitWasp\Bitcoin\Networki...rkSerializableInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

312
        return new NetworkMessage($this->network, /** @scrutinizer ignore-type */ $payload);
Loading history...
313 2
    }
314 3
315 2
    /**
316
     * @param Parser $parser
317 81
     * @return NetworkMessage
318 81
     * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
319
     * @throws \Exception
320 54
     */
321
    public function fromParser(Parser $parser)
322
    {
323
        $header = $this->parseHeader($parser);
324
        return $this->parsePacket($header, $parser);
325
    }
326
327 96
    /**
328
     * @param NetworkMessage $object
329 96
     * @return Buffer
330 96
     */
331 96
    public function serialize(NetworkMessage $object)
332 96
    {
333 96
        $prefix = $this->bs4le->write(Buffer::hex($this->network->getNetMagicBytes(), null, $this->math));
334 96
        $header = $this->packetHeaderSerializer->serialize($object->getHeader());
335 96
        $payload = $object->getPayload()->getBuffer();
336 64
337
        return new Buffer(
338 96
            "{$prefix}{$header->getBinary()}{$payload->getBinary()}"
339
        );
340
    }
341
342
    /**
343
     * @param $data
344
     * @return NetworkMessage
345
     * @throws \Exception
346 54
     */
347
    public function parse($data)
348 54
    {
349
        return $this->fromParser(new Parser($data, $this->math));
350
    }
351
}
352