Passed
Push — master ( ad1fbd...7f61ea )
by Russell
08:12 queued 12s
created

Chainpoint::verifyProofDirect()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 3
nop 2
dl 0
loc 14
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * @author  Russell MIchell 2018 <[email protected]>
5
 * @package silverstripe-verifiable
6
 */
7
8
namespace PhpTek\Verifiable\Backend;
9
10
use PhpTek\Verifiable\Backend\BackendProvider;
11
use GuzzleHttp\Client;
0 ignored issues
show
Bug introduced by
The type GuzzleHttp\Client was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
12
use Guzzle\Http\Exception\RequestException;
13
use Guzzle\Http\Message\Request;
14
use SilverStripe\Core\Config\Configurable;
15
use PhpTek\Verifiable\Exception\VerifiableBackendException;
16
use SilverStripe\Core\Injector\Injector;
17
18
/**
19
 * See: https://chainpoint.org
20
 */
21
class Chainpoint implements BackendProvider
22
{
23
    use Configurable;
24
25
    /**
26
     * Configuration of this backend's supported blockchain networks and
27
     * connection details for each one's locally-installed full-node.
28
     *
29
     * Tieron supports Bitcoin and Ethereum, but there's nothing to stop custom
30
     * routines and config appropriating an additional blockxhain network to which
31
     * proofs can be saved e.g. a local Hyperledger Fabric network.
32
     *
33
     * @var array
34
     * @config
35
     */
36
    private static $blockchain_config = [
0 ignored issues
show
introduced by
The private property $blockchain_config is not used, and could be removed.
Loading history...
37
        [
38
            'name' => 'Bitcoin',
39
            'implementation' => 'bitcoind',
40
            'host' => '',
41
            'port' => 0,
42
        ],
43
        [
44
            'name' => 'Ethereum',
45
            'implementation' => 'geth',
46
            'host' => '',
47
            'port' => 0,
48
        ],
49
    ];
50
51
    /**
52
     * @return string
53
     */
54
    public function name() : string
55
    {
56
        return 'chainpoint';
57
    }
58
59
    /**
60
     * @param  string $hash
61
     * @return string
62
     * @throws VerifiableBackendException
63
     */
64
    public function getProof(string $hash) : string
65
    {
66
        $response = $this->client('/proofs', 'GET', $hash);
0 ignored issues
show
Bug introduced by
$hash of type string is incompatible with the type array expected by parameter $payload of PhpTek\Verifiable\Backend\Chainpoint::client(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

66
        $response = $this->client('/proofs', 'GET', /** @scrutinizer ignore-type */ $hash);
Loading history...
67
68
        if ($response->getStatusCode() !== 200) {
69
            throw new VerifiableBackendException('Unable to fetch proof from backend.');
70
        }
71
72
        return $response->getBody();
73
    }
74
75
    /**
76
     * @param  string $hash
77
     * @return string
78
     */
79
    public function writeHash(string $hash) : string
80
    {
81
        $response = $this->client('/hashes', 'POST', ['hashes' => $hash]);
82
83
        return $response->getBody();
84
    }
85
86
    /**
87
     * Submit a chainpoint proof to the backend for verification.
88
     *
89
     * @param  string $proof A valid JSON-LD Chainpoint Proof.
90
     * @return bool
91
     */
92
    public function verifyProof(string $proof) : bool
93
    {
94
        // Consult blockchains directly, if so configured and suitable
95
        // blockchain full-nodes are available to our RPC connections
96
        if ((bool) $this->config()->get('direct_verification') === true) {
97
            return $this->backend->verifyDirect($proof);
0 ignored issues
show
Bug Best Practice introduced by
The property backend does not exist on PhpTek\Verifiable\Backend\Chainpoint. Did you maybe forget to declare it?
Loading history...
98
        }
99
100
        $response = $this->client('/verify', 'POST', json_encode(['proofs' => [$hash]]));
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $hash seems to be never defined.
Loading history...
Bug introduced by
json_encode(array('proofs' => array($hash))) of type string is incompatible with the type array expected by parameter $payload of PhpTek\Verifiable\Backend\Chainpoint::client(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

100
        $response = $this->client('/verify', 'POST', /** @scrutinizer ignore-type */ json_encode(['proofs' => [$hash]]));
Loading history...
101
102
        return $response->getStatusCode() === 200 &&
103
                json_decode($response->getBody(), true)['status'] === 'verified';
104
    }
105
106
    /**
107
     * Return a client to use for all RPC traffic to this backend.
108
     *
109
     * @param  string                $url
110
     * @param  string                $verb
111
     * @param  array                 $payload
112
     * @return mixed null | Response Guzzle Response object
113
     * @todo Client()->setSslVerification() if required
114
     */
115
    protected function client(string $url, string $verb, array $payload = [])
116
    {
117
        $verb = strtoupper($verb);
118
        $client = new Client([
119
            'base_uri' => $this->fetchNodeUrl(),
120
            'timeout'  => $this->config()->get('chainpoint', 'params')['timeout'],
121
        ]);
122
        $request = new Request($verb, $url, $payload);
123
124
        try {
125
            return $client->send($request);
126
        } catch (RequestException $e) {
127
            return null;
128
        }
129
    }
130
131
    /**
132
     * The Tieron network comprises many nodes, some of which may or may not be
133
     * online. Pings a randomly selected resource URL, containing IPs of each
134
     * advertised node and calls each until one returns an HTTP 200.
135
     *
136
     * @return string
137
     */
138
    protected function fetchNodeUrl()
139
    {
140
        $chainpointUrls = [
141
            'https://a.chainpoint.org/nodes/random',
142
            'https://b.chainpoint.org/nodes/random',
143
            'https://c.chainpoint.org/nodes/random',
144
        ];
145
146
        $url = $chainpointUrls[rand(count($randUrls))];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $randUrls seems to be never defined.
Loading history...
147
        $response = $this->client($url, 'GET');
148
149
        foreach (json_decode($response->getBody(), true) as $candidate) {
150
            $response = $this->client($candidate['public_uri']);
0 ignored issues
show
Bug introduced by
The call to PhpTek\Verifiable\Backend\Chainpoint::client() has too few arguments starting with verb. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

150
            /** @scrutinizer ignore-call */ 
151
            $response = $this->client($candidate['public_uri']);

This check compares calls to functions or methods with their respective definitions. If the call has less arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
151
152
            if ($response->getStatusCode() === 200) {
153
                return $candidate['public_uri'];
154
            }
155
        }
156
    }
157
158
    /**
159
     * For each of this backend's supported blockchain networks, skips any intermediate
160
     * verification steps through the Tieron network, preferring instead to calculate
161
     * proofs ourselves in consultation directly with the relevant networks.
162
     *
163
     * @param  string $proof    The stored JSON-LD chainpoint proof
164
     * @param  array  $networks An array of available blockchains to consult
165
     * @return bool             Returns true if each blockchain found in $network
166
     *                          can verify our proof.
167
     * @todo   Implement via dedicated classes for each configured blockchain network.
168
     */
169
    protected function verifyProofDirect(string $proof, array $networks = [])
170
    {
171
        $result = [];
172
173
        foreach ($this->config()->get('blockchain_config') as $config) {
174
            if (in_array($config['name'], $networks)) {
175
                $implementation = ucfirst(strtolower($config['name']));
176
                $node = Injector::inst()->createWithArgs($implementation, [$config]);
177
178
                $result[strtolower($config['name'])] = $node->verifyProof($proof);
179
            }
180
        }
181
182
        return !in_array(false, $result);
183
    }
184
185
}
186