Completed
Pull Request — master (#85)
by thomas
04:06
created

Peer::getLocalVersion()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
namespace BitWasp\Bitcoin\Networking\Peer;
4
5
use BitWasp\Bitcoin\Block\BlockInterface;
6
use BitWasp\Bitcoin\Block\FilteredBlock;
7
use BitWasp\Bitcoin\Bloom\BloomFilter;
8
use BitWasp\Bitcoin\Chain\BlockLocator;
9
use BitWasp\Bitcoin\Networking\Message;
10
use BitWasp\Bitcoin\Networking\Messages\Version;
11
use BitWasp\Bitcoin\Networking\Messages\Ping;
12
use BitWasp\Bitcoin\Networking\NetworkMessage;
13
use BitWasp\Bitcoin\Networking\NetworkSerializable;
14
use BitWasp\Bitcoin\Networking\Structure\AlertDetail;
15
use BitWasp\Bitcoin\Networking\Structure\Header;
16
use BitWasp\Bitcoin\Networking\Structure\Inventory;
17
use BitWasp\Bitcoin\Networking\Structure\NetworkAddressInterface;
18
use BitWasp\Bitcoin\Networking\Structure\NetworkAddressTimestamp;
19
use BitWasp\Bitcoin\Crypto\EcAdapter\Signature\SignatureInterface;
20
use BitWasp\Bitcoin\Transaction\TransactionInterface;
21
use BitWasp\Buffertools\Buffer;
22
use BitWasp\Buffertools\BufferInterface;
23
use BitWasp\Buffertools\Parser;
24
use Evenement\EventEmitter;
25
use React\EventLoop\LoopInterface;
26
use React\Promise\Deferred;
27
use React\Socket\ConnectionInterface;
28
use React\Stream\Stream;
29
30
class Peer extends EventEmitter
31
{
32
    /**
33
     * @var string
34
     */
35
    private $buffer = '';
36
37
    /**
38
     * @var LoopInterface
39
     */
40
    private $loop;
41
42
    /**
43
     * @var \BitWasp\Bitcoin\Networking\Messages\Factory
44
     */
45
    private $msgs;
46
47
    /**
48
     * @var Stream
49
     */
50
    private $stream;
51
52
    /**
53
     * @var Version
54
     */
55
    private $localVersion;
56
57
    /**
58
     * @var Version
59
     */
60
    private $remoteVersion;
61
62
    /**
63
     * @var NetworkAddressInterface
64
     */
65
    private $peerAddress;
66
67
    /**
68
     * @var ConnectionParams
69
     */
70
    private $connectionParams;
71
72
    /**
73
     * @var bool
74
     */
75
    private $exchangedVersion = false;
76
77
    /**
78
     * @var Header
79
     */
80
    private $incomingMsgHeader;
81
82
    /**
83
     * @param \BitWasp\Bitcoin\Networking\Messages\Factory $msgs
84
     * @param LoopInterface $loop
85
     */
86 11
    public function __construct(\BitWasp\Bitcoin\Networking\Messages\Factory $msgs, LoopInterface $loop)
87
    {
88 11
        $this->msgs = $msgs;
89 11
        $this->loop = $loop;
90 11
    }
91
92
    /**
93
     * @return Version
94
     */
95 3
    public function getLocalVersion()
96
    {
97 3
        return $this->localVersion;
98
    }
99
100
    /**
101
     * @return Version
102
     */
103 3
    public function getRemoteVersion()
104
    {
105 3
        return $this->remoteVersion;
106
    }
107
108
    /**
109
     * Reliably returns the remote peers NetAddr when known through
110
     * the connection process. Often better than the data contained
111
     * in a Version message.
112
     *
113
     * @return NetworkAddressInterface
114
     */
115 3
    public function getRemoteAddress()
116
    {
117 3
        return $this->peerAddress;
118
    }
119
120
    /**
121
     * @return ConnectionParams
122
     */
123 3
    public function getConnectionParams()
124
    {
125 3
        return $this->connectionParams;
126
    }
127
128
    /**
129
     * @param NetworkSerializable $msg
130
     */
131 11
    public function send(NetworkSerializable $msg)
132
    {
133 11
        $netMsg = $msg->getNetworkMessage($this->msgs->getNetwork());
134 11
        $serialized = $this->msgs->getSerializer()->serialize($netMsg);
135 11
        $this->stream->write($serialized->getBinary());
136 11
        $this->emit('send', [$netMsg]);
137 11
    }
138
139
    /**
140
     * @param Stream $stream
141
     * @return $this
142
     */
143 11
    public function setupStream(ConnectionInterface $stream)
144
    {
145 11
        $this->stream = $stream;
0 ignored issues
show
Documentation Bug introduced by
It seems like $stream of type object<React\Socket\ConnectionInterface> is incompatible with the declared type object<React\Stream\Stream> of property $stream.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
146
        $this->stream->on('data', function ($data) {
147 11
            $this->buffer .= $data;
148
149 11
            $data = new Buffer($this->buffer);
150 11
            $parser = new Parser($data);
151
152 11
            $pos = $parser->getPosition();
153 11
            $sz = $parser->getSize();
154
155
            try {
156 11
                while ($pos < $sz) {
157 11
                    if (null === $this->incomingMsgHeader) {
158 11
                        if ($sz - $pos < 24) {
159
                            break;
160
                        }
161
162 11
                        $this->incomingMsgHeader = $this->msgs->getSerializer()->parseHeader($parser);
163 11
                        $pos = $parser->getPosition();
164 4
                    }
165
166 11
                    if ($sz - $pos < $this->incomingMsgHeader->getLength()) {
167
                        break;
168
                    }
169
170 11
                    $message = $this->msgs->getSerializer()->parsePacket($this->incomingMsgHeader, $parser);
171 11
                    $this->incomingMsgHeader = null;
172 11
                    $this->emit('msg', [$this, $message]);
173 11
                    $pos = $parser->getPosition();
174 4
                }
175 4
            } catch (\Exception $e) {
0 ignored issues
show
Coding Style Comprehensibility introduced by
Consider adding a comment why this CATCH block is empty.
Loading history...
176
177
            }
178
179 11
            $this->buffer = $parser->getBuffer()->slice($pos)->getBinary();
180 11
        });
181
182
        $this->stream->once('close', function () {
183 6
            $this->close();
184 11
        });
185
186
        $this->on('msg', function (Peer $peer, NetworkMessage $msg) {
187 11
            $this->emit($msg->getCommand(), [$peer, $msg->getPayload()]);
188 11
        });
189
190 11
        return $this;
191
    }
192
193
    /**
194
     * @param ConnectionInterface $connection
195
     * @param ConnectionParams $params
196
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
197
     */
198 9
    public function inboundHandshake(ConnectionInterface $connection, ConnectionParams $params)
0 ignored issues
show
Unused Code introduced by
The parameter $connection is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
199
    {
200 9
        $this->connectionParams = $params;
201
202 9
        $deferred = new Deferred();
203
        $this->on(Message::VERSION, function (Peer $peer, Version $version) use ($params) {
204 9
            $this->peerAddress = $version->getSenderAddress();
205 9
            $this->remoteVersion = $version;
206 9
            $this->localVersion = $localVersion = $params->produceVersion($this->msgs, $version->getSenderAddress());
207 9
            $this->send($localVersion);
208 9
        });
209
210
        $this->on(Message::VERACK, function () use ($deferred) {
211 9
            if (false === $this->exchangedVersion) {
212 9
                $this->exchangedVersion = true;
213 9
                $this->verack();
214 9
                $this->emit('ready', [$this]);
215 9
                $deferred->resolve($this);
216 3
            }
217 9
        });
218
219 9
        return $deferred->promise();
220
    }
221
222
    /**
223
     * @param NetworkAddressInterface $remotePeer
224
     * @param ConnectionParams $params
225
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
226
     */
227 11
    public function outboundHandshake(NetworkAddressInterface $remotePeer, ConnectionParams $params)
228
    {
229 11
        $deferred = new Deferred();
230
        
231 11
        $awaitVersion = true;
232
        $this->stream->once('close', function () use (&$awaitVersion, $deferred) {
233 6
            if ($awaitVersion) {
234 6
                $awaitVersion = false;
235 6
                $deferred->reject(new \Exception('peer disconnected'));
236 2
            }
237 11
        });
238
239
        $this->on(Message::VERSION, function (Peer $peer, Version $version) use ($params) {
240 11
            $this->remoteVersion = $version;
241 11
            $this->verack();
242 11
        });
243
244 11
        $this->on(Message::VERACK, function () use ($deferred) {
245 8
            if (false === $this->exchangedVersion) {
246 8
                $this->exchangedVersion = true;
247 8
                $this->emit('ready', [$this]);
248 8
                $deferred->resolve($this);
249 3
            }
250 11
        });
251
252 11
        $this->peerAddress = $remotePeer;
253 11
        $this->localVersion = $version = $params->produceVersion($this->msgs, $remotePeer);
254 11
        $this->connectionParams = $params;
255
256 11
        $this->send($version);
257
258 11
        return $deferred->promise();
259
    }
260
261
    /**
262
     *
263
     */
264
    public function intentionalClose()
265
    {
266
        $this->emit('intentionaldisconnect', [$this]);
267
        $this->close();
268
    }
269
270
    /**
271
     *
272
     */
273 8
    public function close()
274
    {
275 8
        $this->emit('close', [$this]);
276 8
        $this->stream->end();
277 8
        $this->removeAllListeners();
278 8
    }
279
280
    /**
281
     * @param int $protocolVersion
282
     * @param int $services
283
     * @param int $timestamp
284
     * @param NetworkAddressInterface $remoteAddr
285
     * @param NetworkAddressInterface $localAddr
286
     * @param string $userAgent
287
     * @param int $blockHeight
288
     * @param bool $relayToUs
289
     */
290
    public function version(
291
        $protocolVersion,
292
        $services,
293
        $timestamp,
294
        NetworkAddressInterface $remoteAddr,
295
        NetworkAddressInterface $localAddr,
296
        $userAgent,
297
        $blockHeight,
298
        $relayToUs
299
    ) {
300
        $this->send($this->msgs->version(
301
            $protocolVersion,
302
            $services,
303
            $timestamp,
304
            $remoteAddr,
305
            $localAddr,
306
            new Buffer($userAgent),
307
            $blockHeight,
308
            $relayToUs
309
        ));
310
    }
311
312
    /**
313
     *
314
     */
315 11
    public function verack()
316
    {
317 11
        $this->send($this->msgs->verack());
318 11
    }
319
320
    /**
321
     *
322
     */
323
    public function sendheaders()
324
    {
325
        $this->send($this->msgs->sendheaders());
326
    }
327
328
    /**
329
     * @param Inventory[] $vInv
330
     */
331
    public function inv(array $vInv)
332
    {
333
        $this->send($this->msgs->inv($vInv));
334
    }
335
336
    /**
337
     * @param Inventory[] $vInv
338
     */
339
    public function getdata(array $vInv)
340
    {
341
        $this->send($this->msgs->getdata($vInv));
342
    }
343
344
    /**
345
     * @param array $vInv
346
     */
347
    public function notfound(array $vInv)
348
    {
349
        $this->send($this->msgs->notfound($vInv));
350
    }
351
352
    /**
353
     * @param NetworkAddressTimestamp[] $vNetAddr
354
     */
355
    public function addr(array $vNetAddr)
356
    {
357
        $this->send($this->msgs->addr($vNetAddr));
358
    }
359
360
    /**
361
     *
362
     */
363
    public function getaddr()
364
    {
365
        $this->send($this->msgs->getaddr());
366
    }
367
368
    /**
369
     *
370
     */
371
    public function ping()
372
    {
373
        $this->send($this->msgs->ping());
374
    }
375
376
    /**
377
     * @param Ping $ping
378
     */
379
    public function pong(Ping $ping)
380
    {
381
        $this->send($this->msgs->pong($ping));
382
    }
383
384
    /**
385
     * @param TransactionInterface $tx
386
     */
387
    public function tx(TransactionInterface $tx)
388
    {
389
        $this->send($this->msgs->tx($tx));
390
    }
391
392
    /**
393
     * @param BlockLocator $locator
394
     */
395
    public function getblocks(BlockLocator $locator)
396
    {
397
        $this->send($this->msgs->getblocks(
398
            $this->localVersion->getVersion(),
399
            $locator
400
        ));
401
    }
402
403
    /**
404
     * @param BlockLocator $locator
405
     */
406
    public function getheaders(BlockLocator $locator)
407
    {
408
        $this->send($this->msgs->getheaders(
409
            $this->localVersion->getVersion(),
410
            $locator
411
        ));
412
    }
413
414
    /**
415
     * @param BlockInterface $block
416
     */
417
    public function block(BlockInterface $block)
418
    {
419
        $this->send($this->msgs->block($block));
420
    }
421
422
    /**
423
     * @param array $vHeaders
424
     */
425
    public function headers(array $vHeaders)
426
    {
427
        $this->send($this->msgs->headers($vHeaders));
428
    }
429
430
    /**
431
     * @param AlertDetail $detail
432
     * @param SignatureInterface $signature
433
     */
434
    public function alert(AlertDetail $detail, SignatureInterface $signature)
435
    {
436
        $this->send($this->msgs->alert($detail, $signature));
437
    }
438
439
    /**
440
     * @param int $feeRate
441
     */
442
    public function feefilter($feeRate)
443
    {
444
        $this->send($this->msgs->feefilter($feeRate));
445
    }
446
447
    /**
448
     * @param BufferInterface $data
449
     */
450
    public function filteradd(BufferInterface $data)
451
    {
452
        $this->send($this->msgs->filteradd($data));
453
    }
454
455
    /**
456
     * @param BloomFilter $filter
457
     */
458
    public function filterload(BloomFilter $filter)
459
    {
460
        $this->send($this->msgs->filterload($filter));
461
    }
462
463
    /**
464
     *
465
     */
466
    public function filterclear()
467
    {
468
        $this->send($this->msgs->filterclear());
469
    }
470
471
    /**
472
     * @param FilteredBlock $filtered
473
     */
474
    public function merkleblock(FilteredBlock $filtered)
475
    {
476
        $this->send($this->msgs->merkleblock($filtered));
477
    }
478
479
    /**
480
     *
481
     */
482
    public function mempool()
483
    {
484
        $this->send($this->msgs->mempool());
485
    }
486
487
    /**
488
     * Issue a Reject message, with a required $msg, $code, and $reason
489
     *
490
     * @param BufferInterface $msg
491
     * @param int $code
492
     * @param BufferInterface $reason
493
     * @param BufferInterface $data
494
     */
495
    public function reject(BufferInterface $msg, $code, BufferInterface $reason, BufferInterface $data = null)
496
    {
497
        $this->send($this->msgs->reject($msg, $code, $reason, $data));
498
    }
499
}
500