Completed
Pull Request — master (#546)
by thomas
75:01 queued 05:00
created

RpcServer::getConfigFile()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
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 = new \Nbobtc\Http\Client($this->credential->getDsn());
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
    /**
130
     * @param string $src
131
     */
132
    private function recursiveDelete($src)
133
    {
134
        $dir = opendir($src);
135
        while(false !== ( $file = readdir($dir)) ) {
136
            if (( $file != '.' ) && ( $file != '..' )) {
137
                $full = $src . '/' . $file;
138
                if ( is_dir($full) ) {
139
                    $this->recursiveDelete($full);
140
                }
141
                else {
142
                    unlink($full);
143
                }
144
            }
145
        }
146
        closedir($dir);
147
        rmdir($src);
148
    }
149
150
    /**
151
     * @return void
152
     */
153
    public function destroy()
154
    {
155
        if ($this->isRunning()) {
156
            $this->request("stop");
157
158
            do {
159
                sleep(0.2);
160
            } while($this->isRunning());
161
162
            $this->recursiveDelete($this->dataDir);
163
164
        }
165
    }
166
167
    /**
168
     * @return bool
169
     */
170
    public function isRunning()
171
    {
172
        return file_exists($this->getPidFile());
173
    }
174
175
    /**
176
     * @return Client
177
     */
178
    public function makeClient()
179
    {
180
        if (!$this->isRunning()) {
181
            throw new \RuntimeException("No client, server not running");
182
        }
183
184
        if (null === $this->client) {
185
            $this->client = new \Nbobtc\Http\Client($this->credential->getDsn());
186
        }
187
188
        return $this->client;
189
    }
190
191
    /**
192
     * @param string $method
193
     * @param array $params
194
     * @return mixed
195
     */
196
    public function request($method, array $params = [])
197
    {
198
        echo "Request: {$method}\n";
199
        $unsorted = $this->makeClient()->sendCommand(new Command($method, $params));
200
        $jsonResult = $unsorted->getBody()->getContents();
201
        $json = json_decode($jsonResult, true);
202
        if (false === $json) {
203
            throw new \RuntimeException("Invalid JSON from server");
204
        }
205
        return $json;
206
    }
207
208
    /**
209
     * @param string $method
210
     * @param array $params
211
     * @return mixed
212
     */
213
    public function makeRpcRequest($method, $params = [])
214
    {
215
        return $this->request($method, $params);
216
    }
217
218
}
219