Completed
Push — master ( 4d00a0...e4a998 )
by thomas
98:35 queued 58:37
created

Locator::getPeerList()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 9
ccs 7
cts 7
cp 1
rs 9.6666
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 1
crap 1
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 9
     */
37
    public function __construct(Resolver $dns, NetworkSettings $settings)
38 9
    {
39 9
        $this->seeds = $settings->getDnsSeedList();
40 9
        $this->dns = $dns;
41
        $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
    }
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
    public function querySeeds(array $seeds)
53
    {
54
        $peerList = new Deferred();
55
56
        // Connect to $numSeeds peers
57
        /** @var Peer[] $vNetAddr */
58
        $vNetAddr = [];
59
        $c = 0;
60
        foreach ($seeds as $seed) {
61
            $this->dns
62
                ->resolve($seed)
63
                ->then(function ($ipList) use (&$vNetAddr, $peerList, &$numSeeds, &$c) {
64
                    $peerList->resolve($ipList);
65
                }, function ($error) use ($peerList) {
66
                    $peerList->reject($error);
67
                })
68
            ;
69 6
        }
70
71 6
        // Compile the list of lists of peers into $this->knownAddresses
72
        return $peerList->promise();
73
    }
74 6
75 6
    /**
76 6
     * Given a number of DNS seeds to query, select a random few and
77
     * return their peers.
78
     *
79
     * @param int $numSeeds
80 6
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
81 6
     */
82 6
    private function getPeerList($numSeeds = 1)
83 6
    {
84 6
        // Take $numSeeds
85
        $seedHosts = $this->seeds->getHosts();
86 5
        shuffle($seedHosts);
87 5
        $seeds = array_slice($seedHosts, 0, min($numSeeds, count($seedHosts)));
88 5
89 3
        return $this->querySeeds($seeds);
90
    }
91 1
92 6
    /**
93
     * Query $numSeeds DNS seeds, returning the NetworkAddress[] result.
94 4
     * Is rejected if any of the seeds fail.
95
     *
96
     * @param int $numSeeds
97 6
     * @return \React\Promise\Promise|\React\Promise\PromiseInterface
98
     */
99
    public function queryDnsSeeds($numSeeds = 1)
100
    {
101
        $deferred = new Deferred();
102
        $this
103
            ->getPeerList($numSeeds)
104
            ->then(
105
                function (array $vPeerVAddrs) use ($deferred) {
106 6
                    shuffle($vPeerVAddrs);
107
108 6
                    /** @var NetworkAddressInterface[] $addresses */
109 4
                    $addresses = [];
110 6
                    foreach ($vPeerVAddrs as $ip) {
111 6
                        $addresses[] = new NetworkAddress(
112
                            Services::NETWORK,
113 5
                            new Ipv4($ip),
114
                            $this->settings->getDefaultP2PPort()
115
                        );
116 5
                    }
117 5
118 5
                    $this->knownAddresses = array_merge(
119 5
                        $this->knownAddresses,
120 5
                        $addresses
121 2
                    );
122 3
123 3
                    $deferred->resolve($this);
124
                },
125 5
                function ($error) use ($deferred) {
126 5
                    $deferred->reject($error);
127
                }
128 3
            )
129
        ;
130 5
131 6
        return $deferred->promise();
132 3
    }
133 1
134 3
    /**
135 4
     * @return NetworkAddressInterface[]
136
     */
137
    public function getKnownAddresses()
138 6
    {
139
        return $this->knownAddresses;
140
    }
141
142
    /**
143
     * Pop an address from the discovered peers
144 2
     *
145
     * @return NetworkAddressInterface
146 2
     * @throws \Exception
147
     */
148
    public function popAddress()
149
    {
150
        if (count($this->knownAddresses) < 1) {
151
            throw new \Exception('No peers');
152
        }
153
154
        return array_pop($this->knownAddresses);
155 6
    }
156
}
157