Completed
Pull Request — master (#42)
by thomas
04:49
created

NetworkMessageSerializer   D

Complexity

Total Complexity 31

Size/Duplication

Total Lines 287
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 44

Test Coverage

Coverage 100%

Importance

Changes 6
Bugs 1 Features 2
Metric Value
wmc 31
c 6
b 1
f 2
lcom 1
cbo 44
dl 0
loc 287
ccs 129
cts 129
cp 1
rs 4.9

5 Methods

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