Completed
Pull Request — master (#546)
by thomas
113:48 queued 41:22
created

RpcServer::recursiveDelete()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 4
nop 1
dl 0
loc 17
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\RpcTest;
4
5
6
use BitWasp\Bitcoin\Network\NetworkInterface;
7
use Nbobtc\Command\Command;
8
use Nbobtc\Http\Client;
9
10
class RpcServer
11
{
12
    /**
13
     * @var string
14
     */
15
    private $dataDir;
16
17
    /**
18
     * @var string
19
     */
20
    private $bitcoind;
21
22
    /**
23
     * @var NetworkInterface
24
     */
25
    private $network;
26
27
    /**
28
     * @var RpcCredential
29
     */
30
    private $credential;
31
32
    /**
33
     * @var Client
34
     */
35
    private $client;
36
37
    /**
38
     * RpcServer constructor.
39
     * @param $bitcoind
40
     * @param $dataDir
41
     * @param NetworkInterface $network
42
     * @param RpcCredential $credential
43
     */
44
    public function __construct($bitcoind, $dataDir, NetworkInterface $network, RpcCredential $credential)
45
    {
46
        $this->bitcoind = $bitcoind;
47
        $this->dataDir = $dataDir;
48
        $this->network = $network;
49
        $this->credential = $credential;
50
    }
51
52
    /**
53
     * @return string
54
     */
55
    private function getPidFile()
56
    {
57
        return "{$this->dataDir}/regtest/bitcoind.pid";
58
    }
59
60
    /**
61
     * @return string
62
     */
63
    private function getConfigFile()
64
    {
65
        return "{$this->dataDir}/bitcoin.conf";
66
    }
67
68
    /**
69
     * @param RpcCredential $rpcCredential
70
     */
71
    private function writeConfigToFile(RpcCredential $rpcCredential)
72
    {
73
        $fd = fopen($this->getConfigFile(), "w");
74
        if (!$fd) {
75
            throw new \RuntimeException("Failed to open bitcoin.conf for writing");
76
        }
77
        if (!fwrite($fd, $rpcCredential->getConfig())) {
78
            throw new \RuntimeException("Failed to write to bitcoin.conf");
79
        }
80
        fclose($fd);
81
    }
82
83
    /**
84
     * @return void
85
     */
86
    public function start()
87
    {
88
        if ($this->isRunning()) {
89
            return;
90
        }
91
92
        $this->writeConfigToFile($this->credential);
93
        $res = 0;
94
        $out = '';
95
        $result = exec(sprintf("%s -datadir=%s", $this->bitcoind, $this->dataDir), $out, $res);
96
97
        if ($res !== 0) {
98
            throw new \RuntimeException("Failed to start bitcoind: {$this->dataDir}\n");
99
        }
100
101
        $start = microtime(true);
102
        $limit = 10;
103
        $connected = false;
104
105
        $conn = $this->getClient();
106
        do {
107
            try {
108
                $result = json_decode($conn->sendCommand(new Command("getblockchaininfo"))->getBody()->getContents(), true);
109
                if ($result['error'] === null) {
110
                    $connected = true;
111
                } else {
112
                    if ($result['error']['code'] !== -28) {
113
                        throw new \RuntimeException("Unexpected error code during startup");
114
                    }
115
116
                    sleep(0.2);
117
                }
118
119
            } catch (\Exception $e) {
120
                sleep(0.2);
121
            }
122
123
            if (microtime(true) > $start + $limit) {
124
                throw new \RuntimeException("Timeout elapsed, never made connection to bitcoind");
125
            }
126
        } while (!$connected);
127
    }
128
129
    private function getClient() {
130
        $client = new \Nbobtc\Http\Client($this->credential->getDsn());
131
        $client->withDriver(new CurlDriver());
132
        return $client;
133
    }
134
135
    /**
136
     * @param string $src
137
     */
138
    private function recursiveDelete($src)
139
    {
140
        $dir = opendir($src);
141
        while(false !== ( $file = readdir($dir)) ) {
142
            if (( $file != '.' ) && ( $file != '..' )) {
143
                $full = $src . '/' . $file;
144
                if ( is_dir($full) ) {
145
                    $this->recursiveDelete($full);
146
                }
147
                else {
148
                    unlink($full);
149
                }
150
            }
151
        }
152
        closedir($dir);
153
        rmdir($src);
154
    }
155
156
    /**
157
     * @return void
158
     */
159
    public function destroy()
160
    {
161
        if ($this->isRunning()) {
162
            $this->request("stop");
163
164
            do {
165
                sleep(0.2);
166
            } while($this->isRunning());
167
168
            $this->recursiveDelete($this->dataDir);
169
170
        }
171
    }
172
173
    /**
174
     * @return bool
175
     */
176
    public function isRunning()
177
    {
178
        return file_exists($this->getPidFile());
179
    }
180
181
    /**
182
     * @return Client
183
     */
184
    public function makeClient()
185
    {
186
        if (!$this->isRunning()) {
187
            throw new \RuntimeException("No client, server not running");
188
        }
189
190
        if (null === $this->client) {
191
            $this->client = $this->getClient();
192
        }
193
194
        return $this->client;
195
    }
196
197
    /**
198
     * @param string $method
199
     * @param array $params
200
     * @return mixed
201
     */
202
    public function request($method, array $params = [])
203
    {
204
        $unsorted = $this->makeClient()->sendCommand(new Command($method, $params));
205
        $jsonResult = $unsorted->getBody()->getContents();
206
        $json = json_decode($jsonResult, true);
207
        if (false === $json) {
208
            throw new \RuntimeException("Invalid JSON from server");
209
        }
210
        return $json;
211
    }
212
213
    /**
214
     * @param string $method
215
     * @param array $params
216
     * @return mixed
217
     */
218
    public function makeRpcRequest($method, $params = [])
219
    {
220
        return $this->request($method, $params);
221
    }
222
223
}
224