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

Locator::queryDnsSeeds()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 2.0002

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 34
ccs 24
cts 25
cp 0.96
rs 8.8571
cc 2
eloc 20
nc 1
nop 1
crap 2.0002
1
<?php
2
3
namespace BitWasp\Bitcoin\Networking\Peer;
4
5
use BitWasp\Bitcoin\Networking\DnsSeeds\DnsSeedList;
6
use BitWasp\Bitcoin\Networking\Ip\Ipv4;
7
use BitWasp\Bitcoin\Networking\Services;
8
use BitWasp\Bitcoin\Networking\Structure\NetworkAddress;
9
use BitWasp\Bitcoin\Networking\Structure\NetworkAddressInterface;
10
use React\Dns\Resolver\Resolver;
11
use React\Promise\Deferred;
12
13
class Locator
14
{
15
16
    /**
17
     * @var Resolver
18
     */
19
    private $dns;
20
21
    /**
22
     * @var DnsSeedList
23
     */
24
    private $seeds;
25
26
    /**
27
     * @var NetworkAddressInterface[]
28
     */
29
    private $knownAddresses = [];
30
31
    /**
32
     * Locator constructor.
33
     * @param DnsSeedList $list
34
     * @param Resolver $dns
35
     */
36 9
    public function __construct(DnsSeedList $list, Resolver $dns, $defaultNetPort = 8333)
37
    {
38 9
        $this->seeds = $list;
39 9
        $this->dns = $dns;
40 9
        $this->defaultPort = $defaultNetPort;
0 ignored issues
show
Bug introduced by
The property defaultPort 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...
41 9
    }
42
43
    /**
44
     * Sets the default port for network addresses returned by this instance.
45
     *
46
     * @param int $defaultNetPort
47
     */
48
    public function setDefaultPort($defaultNetPort)
49
    {
50
        $this->defaultPort = $defaultNetPort;
51
    }
52
53
    /**
54
     * @param int $numSeeds
55
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
56
     */
57 6
    private function getPeerList($numSeeds = 1)
58
    {
59 6
        $peerList = new Deferred();
60
61
        // Take $numSeeds
62 6
        $seedHosts = $this->seeds->getHosts();
63 6
        shuffle($seedHosts);
64 6
        $seeds = array_slice($seedHosts, 0, min($numSeeds, count($seedHosts)));
65
66
        // Connect to $numSeeds peers
67
        /** @var Peer[] $vNetAddr */
68 6
        $vNetAddr = [];
69 6
        $c = 0;
70 6
        foreach ($seeds as $seed) {
71 6
            $this->dns
72 6
                ->resolve($seed)
73
                ->then(function ($ipList) use (&$vNetAddr, $peerList, &$numSeeds, &$c) {
74 6
                    $vNetAddr = array_merge($vNetAddr, $ipList);
75 6
                    if ($numSeeds === ++$c) {
76 6
                        $peerList->resolve($vNetAddr);
77 2
                    }
78
                }, function ($error) use ($peerList) {
79
                    $peerList->reject($error);
80 6
                })
81
            ;
82 2
        }
83
84
        // Compile the list of lists of peers into $this->knownAddresses
85 6
        return $peerList->promise();
86
    }
87
88
    /**
89
     * Connect to $numSeeds DNS seeds
90
     *
91
     * @param int $numSeeds
92
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
93
     */
94 6
    public function queryDnsSeeds($numSeeds = 1)
95
    {
96 6
        $deferred = new Deferred();
97 2
        $this
98 6
            ->getPeerList($numSeeds)
99 6
            ->then(
100
                function (array $vPeerVAddrs) use ($deferred) {
101 6
                    shuffle($vPeerVAddrs);
102
103
                    /** @var NetworkAddressInterface[] $addresses */
104 6
                    $addresses = [];
105 6
                    foreach ($vPeerVAddrs as $ip) {
106 6
                        $addresses[] = new NetworkAddress(
107 6
                            Services::NETWORK,
108 6
                            new Ipv4($ip),
109 6
                            $this->defaultPort
110 2
                        );
111 2
                    }
112
113 6
                    $this->knownAddresses = array_merge(
114 6
                        $this->knownAddresses,
115 2
                        $addresses
116 2
                    );
117
118 6
                    $deferred->resolve($this);
119 6
                },
120 4
                function ($error) use ($deferred) {
121
                    $deferred->reject($error);
122 4
                }
123 2
            )
124
        ;
125
126 6
        return $deferred->promise();
127
    }
128
129
    /**
130
     * @return NetworkAddressInterface[]
131
     */
132 3
    public function getKnownAddresses()
133
    {
134 3
        return $this->knownAddresses;
135
    }
136
137
    /**
138
     * Pop an address from the discovered peers
139
     *
140
     * @return NetworkAddressInterface
141
     * @throws \Exception
142
     */
143 6
    public function popAddress()
144
    {
145 6
        if (count($this->knownAddresses) < 1) {
146 3
            throw new \Exception('No peers');
147
        }
148
149 3
        return array_pop($this->knownAddresses);
150
    }
151
}
152