Completed
Pull Request — master (#85)
by thomas
02:23
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\Settings\NetworkSettings;
8
use BitWasp\Bitcoin\Networking\Services;
9
use BitWasp\Bitcoin\Networking\Structure\NetworkAddress;
10
use BitWasp\Bitcoin\Networking\Structure\NetworkAddressInterface;
11
use React\Dns\Resolver\Resolver;
12
use React\Promise\Deferred;
13
14
class Locator
15
{
16
17
    /**
18
     * @var Resolver
19
     */
20
    private $dns;
21
22
    /**
23
     * @var DnsSeedList
24
     */
25
    private $seeds;
26
27
    /**
28
     * @var NetworkAddressInterface[]
29
     */
30
    private $knownAddresses = [];
31
32
    /**
33
     * Locator constructor.
34
     * @param Resolver $dns
35
     * @param NetworkSettings $settings
36
     */
37 12
    public function __construct(Resolver $dns, NetworkSettings $settings)
38
    {
39 12
        $this->seeds = $settings->getDnsSeedList();
40 12
        $this->dns = $dns;
41 12
        $this->settings = $settings;
0 ignored issues
show
Bug introduced by
The property settings 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...
42 12
    }
43
44
    /**
45
     * Takes an arbitrary list of dns seed hostnames, and attempts
46
     * to return a list from each. Request fails if any hosts are
47
     * offline or cause an error.
48
     *
49
     * @param array $seeds
50
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
51
     */
52 9
    public function querySeeds(array $seeds)
53
    {
54 9
        $peerList = new Deferred();
55
56
        // Connect to $numSeeds peers
57
        /** @var Peer[] $vNetAddr */
58 9
        $vNetAddr = [];
59 9
        $c = 0;
60 9
        foreach ($seeds as $seed) {
61 9
            $this->dns
62 9
                ->resolve($seed)
63
                ->then(function ($ipList) use (&$vNetAddr, $peerList, &$numSeeds, &$c) {
64 6
                    $peerList->resolve($ipList);
65
                }, function ($error) use ($peerList) {
66 3
                    $peerList->reject($error);
67 9
                })
68
            ;
69 3
        }
70
71
        // Compile the list of lists of peers into $this->knownAddresses
72 9
        return $peerList->promise();
73
    }
74
75
    /**
76
     * Given a number of DNS seeds to query, select a random few and
77
     * return their peers.
78
     *
79
     * @param int $numSeeds
80
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
81
     */
82 6
    private function getPeerList($numSeeds = 1)
83
    {
84
        // Take $numSeeds
85 6
        $seedHosts = $this->seeds->getHosts();
86 6
        shuffle($seedHosts);
87 6
        $seeds = array_slice($seedHosts, 0, min($numSeeds, count($seedHosts)));
88
89 6
        return $this->querySeeds($seeds);
90
    }
91
92
    /**
93
     * Query $numSeeds DNS seeds, returning the NetworkAddress[] result.
94
     * Is rejected if any of the seeds fail.
95
     *
96
     * @param int $numSeeds
97
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
98
     */
99 6
    public function queryDnsSeeds($numSeeds = 1)
100
    {
101 6
        $deferred = new Deferred();
102 2
        $this
103 6
            ->getPeerList($numSeeds)
104 6
            ->then(
105
                function (array $vPeerVAddrs) use ($deferred) {
106 6
                    shuffle($vPeerVAddrs);
107
108
                    /** @var NetworkAddressInterface[] $addresses */
109 6
                    $addresses = [];
110 6
                    foreach ($vPeerVAddrs as $ip) {
111 6
                        $addresses[] = new NetworkAddress(
112 6
                            Services::NETWORK,
113 6
                            new Ipv4($ip),
114 6
                            $this->settings->getDefaultP2PPort()
115 2
                        );
116 2
                    }
117
118 6
                    $this->knownAddresses = array_merge(
119 6
                        $this->knownAddresses,
120 2
                        $addresses
121 2
                    );
122
123 6
                    $deferred->resolve($this);
124 6
                },
125 4
                function ($error) use ($deferred) {
126
                    $deferred->reject($error);
127 4
                }
128 2
            )
129
        ;
130
131 6
        return $deferred->promise();
132
    }
133
134
    /**
135
     * @return NetworkAddressInterface[]
136
     */
137 3
    public function getKnownAddresses()
138
    {
139 3
        return $this->knownAddresses;
140
    }
141
142
    /**
143
     * Pop an address from the discovered peers
144
     *
145
     * @return NetworkAddressInterface
146
     * @throws \Exception
147
     */
148 6
    public function popAddress()
149
    {
150 6
        if (count($this->knownAddresses) < 1) {
151 3
            throw new \Exception('No peers');
152
        }
153
154 3
        return array_pop($this->knownAddresses);
155
    }
156
}
157