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

RpcServer   A

Complexity

Total Complexity 29

Size/Duplication

Total Lines 206
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 5

Importance

Changes 0
Metric Value
dl 0
loc 206
rs 10
c 0
b 0
f 0
wmc 29
lcom 1
cbo 5

11 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 7 1
A getPidFile() 0 4 1
A getConfigFile() 0 4 1
A writeConfigToFile() 0 11 3
C start() 0 40 8
B recursiveDelete() 0 17 5
A destroy() 0 13 3
A isRunning() 0 4 1
A makeClient() 0 12 3
A request() 0 10 2
A makeRpcRequest() 0 4 1
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
        $result = exec(sprintf("%s -datadir=%s", $this->bitcoind, $this->dataDir));
94
95
        if ($result !== "") {
96
            throw new \RuntimeException("Failed to start bitcoind: {$this->dataDir}\nResult: {$result}");
97
        }
98
99
        $start = microtime(true);
100
        $limit = 10;
101
        $connected = false;
102
103
        $conn = new \Nbobtc\Http\Client($this->credential->getDsn());
104
        do {
105
            try {
106
                $result = json_decode($conn->sendCommand(new Command("getblockchaininfo"))->getBody()->getContents(), true);
107
                if ($result['error'] === null) {
108
                    $connected = true;
109
                } else {
110
                    if ($result['error']['code'] !== -28) {
111
                        throw new \RuntimeException("Unexpected error code during startup");
112
                    }
113
114
                    sleep(0.2);
115
                }
116
117
            } catch (\Exception $e) {
118
                sleep(0.2);
119
            }
120
121
            if (microtime(true) > $start + $limit) {
122
                throw new \RuntimeException("Timeout elapsed, never made connection to bitcoind");
123
            }
124
        } while (!$connected);
125
    }
126
127
    /**
128
     * @param string $src
129
     */
130
    private function recursiveDelete($src)
131
    {
132
        $dir = opendir($src);
133
        while(false !== ( $file = readdir($dir)) ) {
134
            if (( $file != '.' ) && ( $file != '..' )) {
135
                $full = $src . '/' . $file;
136
                if ( is_dir($full) ) {
137
                    $this->recursiveDelete($full);
138
                }
139
                else {
140
                    unlink($full);
141
                }
142
            }
143
        }
144
        closedir($dir);
145
        rmdir($src);
146
    }
147
148
    /**
149
     * @return void
150
     */
151
    public function destroy()
152
    {
153
        if ($this->isRunning()) {
154
            $this->request("stop");
155
156
            do {
157
                sleep(0.2);
158
            } while($this->isRunning());
159
160
            $this->recursiveDelete($this->dataDir);
161
162
        }
163
    }
164
165
    /**
166
     * @return bool
167
     */
168
    public function isRunning()
169
    {
170
        return file_exists($this->getPidFile());
171
    }
172
173
    /**
174
     * @return Client
175
     */
176
    public function makeClient()
177
    {
178
        if (!$this->isRunning()) {
179
            throw new \RuntimeException("No client, server not running");
180
        }
181
182
        if (null === $this->client) {
183
            $this->client = new \Nbobtc\Http\Client($this->credential->getDsn());
184
        }
185
186
        return $this->client;
187
    }
188
189
    /**
190
     * @param string $method
191
     * @param array $params
192
     * @return mixed
193
     */
194
    public function request($method, array $params = [])
195
    {
196
        $unsorted = $this->makeClient()->sendCommand(new Command($method, $params));
197
        $jsonResult = $unsorted->getBody()->getContents();
198
        $json = json_decode($jsonResult, true);
199
        if (false === $json) {
200
            throw new \RuntimeException("Invalid JSON from server");
201
        }
202
        return $json;
203
    }
204
205
    /**
206
     * @param string $method
207
     * @param array $params
208
     * @return mixed
209
     */
210
    public function makeRpcRequest($method, $params = [])
211
    {
212
        return $this->request($method, $params);
213
    }
214
215
}
216