Locator::queryDnsSeeds()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 22
CRAP Score 2

Importance

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