Completed
Push — master ( ab371d...f7f683 )
by thomas
10:57
created

NetworkMessageSerializer::serialize()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 13
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 1
Metric Value
dl 0
loc 13
ccs 10
cts 10
cp 1
rs 9.4285
cc 1
eloc 9
nc 1
nop 1
crap 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 99
    public function __construct(NetworkInterface $network)
159
    {
160 99
        $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 99
        $this->network = $network;
162 99
        $this->txSerializer = new TransactionSerializer();
163 99
        $this->headerSerializer = new BlockHeaderSerializer();
164 99
        $this->blockSerializer = new BlockSerializer($this->math, $this->headerSerializer, $this->txSerializer);
165 99
        $this->filteredBlockSerializer = new FilteredBlockSerializer($this->headerSerializer, new PartialMerkleTreeSerializer());
166 99
        $this->headersSerializer = new HeadersSerializer($this->headerSerializer);
167 99
        $this->filterAddSerializer = new FilterAddSerializer();
168 99
        $this->filterLoadSerializer = new FilterLoadSerializer(new BloomFilterSerializer());
169 99
        $this->merkleBlockSerializer = new MerkleBlockSerializer($this->filteredBlockSerializer);
170 99
        $this->pingSerializer = new PingSerializer();
171 99
        $this->pongSerializer = new PongSerializer();
172 99
        $this->alertSerializer = new AlertSerializer(new AlertDetailSerializer());
173 99
        $this->inventorySerializer = new InventorySerializer();
174 99
        $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 99
        $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 99
        $this->notFoundSerializer = new NotFoundSerializer($this->inventorySerializer);
177 99
        $this->feeFilterSerializer = new FeeFilterSerializer();
178 99
        $this->rejectSerializer = new RejectSerializer();
179 99
        $this->blockLocatorSerializer = new BlockLocatorSerializer();
180 99
        $this->getBlocksSerializer = new GetBlocksSerializer($this->blockLocatorSerializer);
181 99
        $this->getHeadersSerializer = new GetHeadersSerializer($this->blockLocatorSerializer);
182 99
        $this->versionSerializer = new VersionSerializer(new NetworkAddressSerializer());
183 99
        $this->addrSerializer = new AddrSerializer(new NetworkAddressTimestampSerializer());
184 99
    }
185
186
    /**
187
     * @return \BitWasp\Buffertools\Template
188
     */
189 99
    private function getHeaderTemplate()
190
    {
191 99
        return (new TemplateFactory())
192 99
            ->bytestringle(4)
193 99
            ->bytestring(12)
194 99
            ->uint32le()
195 99
            ->bytestring(4)
196 99
            ->getTemplate();
197
    }
198
199
    /**
200
     * @param Parser $parser
201
     * @return NetworkMessage
202
     * @throws \BitWasp\Buffertools\Exceptions\ParserOutOfRange
203
     * @throws \Exception
204
     */
205 93
    public function fromParser(Parser & $parser)
206
    {
207 93
        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 93
        if ($netBytes->getHex() !== $this->network->getNetMagicBytes()) {
214 3
            throw new \RuntimeException('Invalid magic bytes for network');
215
        }
216
217
        $buffer = $payloadSize > 0
218 90
            ? $parser->readBytes($payloadSize)
219 90
            : new Buffer();
220
221
        // Compare payload checksum against header value
222 90
        if (Hash::sha256d($buffer)->slice(0, 4)->getBinary() !== $checksum->getBinary()) {
223 3
            throw new \RuntimeException('Invalid packet checksum');
224
        }
225
226 87
        $cmd = trim($command->getBinary());
227
        switch ($cmd) {
228 87
            case 'version':
229 18
                $payload = $this->versionSerializer->parse($buffer);
230 18
                break;
231 84
            case 'verack':
232 18
                $payload = new VerAck();
233 18
                break;
234 71
            case 'sendheaders':
235 3
                $payload = new SendHeaders();
236 3
                break;
237 68
            case 'addr':
238 5
                $payload = $this->addrSerializer->parse($buffer);
239 5
                break;
240 65
            case 'inv':
241 6
                $payload = $this->invSerializer->parse($buffer);
242 6
                break;
243 62
            case 'getdata':
244 3
                $payload = $this->getDataSerializer->parse($buffer);
245 3
                break;
246 59
            case 'notfound':
247 3
                $payload = $this->notFoundSerializer->parse($buffer);
248 3
                break;
249 56
            case 'getblocks':
250 3
                $payload = $this->getBlocksSerializer->parse($buffer);
251 3
                break;
252 53
            case 'getheaders':
253 8
                $payload = $this->getHeadersSerializer->parse($buffer);
254 8
                break;
255 50
            case 'tx':
256 3
                $payload = new Tx($this->txSerializer->parse($buffer));
257 3
                break;
258 47
            case 'block':
259 3
                $payload = new Block($this->blockSerializer->parse($buffer));
260 3
                break;
261 44
            case 'headers':
262 3
                $payload = $this->headersSerializer->parse($buffer);
263 3
                break;
264 41
            case 'getaddr':
265 3
                $payload = new GetAddr();
266 3
                break;
267 38
            case 'mempool':
268 3
                $payload = new MemPool();
269 3
                break;
270 35
            case 'feefilter':
271 3
                $payload = $this->feeFilterSerializer->parse($buffer);
272 3
                break;
273 32
            case 'filterload':
274 3
                $payload = $this->filterLoadSerializer->parse($buffer);
275 3
                break;
276 29
            case 'filteradd':
277 3
                $payload = $this->filterAddSerializer->parse($buffer);
278 3
                break;
279 26
            case 'filterclear':
280 3
                $payload = new FilterClear();
281 3
                break;
282 23
            case 'merkleblock':
283 3
                $payload = $this->merkleBlockSerializer->parse($buffer);
284 3
                break;
285 20
            case 'ping':
286 8
                $payload = $this->pingSerializer->parse($buffer);
287 8
                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 84
        return new NetworkMessage(
302 84
            $this->network,
303
            $payload
304 84
        );
305
    }
306
307
    /**
308
     * @param NetworkMessage $object
309
     * @return Buffer
310
     */
311 99
    public function serialize(NetworkMessage $object)
312
    {
313 99
        $payload = $object->getPayload()->getBuffer();
314 99
        $command = str_pad(unpack("H*", $object->getCommand())[1], 24, '0', STR_PAD_RIGHT);
315 99
        $header = $this->getHeaderTemplate()->write([
316 99
            Buffer::hex($this->network->getNetMagicBytes()),
317 99
            Buffer::hex($command),
318 99
            $payload->getSize(),
319 99
            $object->getChecksum()
320 99
        ]);
321
322 99
        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