Passed
Push — master ( 4c9478...3e3b54 )
by Russell
10:59
created

VerifiableService::proofIsPartial()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 3
nc 2
nop 1
dl 0
loc 7
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\Verify;
9
10
use SilverStripe\Core\Injector\Injector;
11
use SilverStripe\Core\Injector\Injectable;
12
use SilverStripe\Core\Config\Configurable;
13
use SilverStripe\Core\ClassInfo;
14
use PhpTek\Verifiable\Exception\VerifiableBackendException;
15
use PhpTek\Verifiable\Job\BackendVerificationJob;
16
use Symbiote\QueuedJobs\Services\QueuedJobService;
17
use PhpTek\Verifiable\Backend\BackendProvider;
18
use SilverStripe\ORM\DataObject;
19
20
/**
21
 * Service class that works as an intermediary between any data model and the
22
 * currently selected Merkle Tree storage backend.
23
 */
24
class VerifiableService
25
{
26
    use Injectable;
27
    use Configurable;
28
29
    /**
30
     * Represents a failed verification.
31
     *
32
     * @var string
33
     */
34
    const STATUS_FAILURE = 'FAIL';
35
36
    /**
37
     * Represents a passed verification.
38
     *
39
     * @var string
40
     */
41
    const STATUS_PASSED = 'PASS';
42
43
    /**
44
     * Represents a pending verification.
45
     *
46
     * @var string
47
     */
48
    const STATUS_PENDING = 'PENDING';
49
50
    /**
51
     * The hashing function to use.
52
     *
53
     * @var string
54
     * @see {@link $this->hash()}
55
     * @config
56
     */
57
    private static $hash_func = 'sha1';
0 ignored issues
show
introduced by
The private property $hash_func is not used, and could be removed.
Loading history...
58
59
    /**
60
     * @var BackendProvider
61
     */
62
    protected $backend;
63
64
    /**
65
     * @return void
66
     * @throws VerifiableBackendException
67
     */
68
    public function __construct()
69
    {
70
        $this->setBackend();
71
    }
72
73
    /**
74
     * Write a hash of data as per the "verifiable_fields" confifg static on each
75
     * {@link DataObject}.
76
     *
77
     * @param  array $data
78
     * @return mixed  The result of this call to the backend.
79
     */
80
    public function write(array $data)
81
    {
82
        return $this->backend->writeHash([$this->hash($data)]);
83
    }
84
85
    /**
86
     * Fetch a chainpoint proof for the passed $hash.
87
     *
88
     * @param  string $hash
89
     * @return string The JSON-LD chainpoint proof.
90
     */
91
    public function read(string $hash) : string
92
    {
93
        return $this->backend->getProof($hash);
94
    }
95
96
    /**
97
     * Verify the given JSON-LD chainpoint proof against the backend.
98
     *
99
     * @param  string $proof A JSON-LD chainpoint proof.
100
     * @return bool
101
     */
102
    public function verify(string $proof) : bool
103
    {
104
        return $this->backend->verify($proof);
0 ignored issues
show
Bug introduced by
The method verify() does not exist on PhpTek\Verifiable\Backend\BackendProvider. Did you maybe mean verifyProof()? ( Ignorable by Annotation )

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

104
        return $this->backend->/** @scrutinizer ignore-call */ verify($proof);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
105
    }
106
107
    /**
108
     * Gives us the current verification status of the given record. Takes into
109
     * account the state of the saved proof and a backend verification call.
110
     *
111
     * @param  DataObject $record
112
     * @return string
113
     */
114
    public function getVerificationStatus(DataObject $record)
115
    {
116
        $proofData = $record->dbObject('Proof')->getStoreAsArray();
0 ignored issues
show
Bug introduced by
The method getStoreAsArray() does not exist on SilverStripe\ORM\FieldType\DBField. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

116
        $proofData = $record->dbObject('Proof')->/** @scrutinizer ignore-call */ getStoreAsArray();
Loading history...
117
        $fieldData = $record->normaliseData();
0 ignored issues
show
Bug introduced by
The method normaliseData() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

117
        /** @scrutinizer ignore-call */ 
118
        $fieldData = $record->normaliseData();
Loading history...
118
        $proofIsComplete = $this->proofIsComplete($proofData);
119
        $proofIsPartial = $this->proofIsPartial($proofData);
120
        $proofIsVerified = $record->verify($fieldData, false) === true;
0 ignored issues
show
Bug introduced by
The method verify() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

120
        $proofIsVerified = $record->/** @scrutinizer ignore-call */ verify($fieldData, false) === true;
Loading history...
121
122
        switch (true) {
123
            case $proofIsComplete && $proofIsVerified:
124
                return self::STATUS_PASS;
0 ignored issues
show
Bug introduced by
The constant PhpTek\Verifiable\Verify...bleService::STATUS_PASS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
125
            case $proofIsPartial:
126
                return self::STATUS_PENDING;
127
            case $proofIsComplete && !$proofIsVerified:
128
            default:
129
                return self::STATUS_FAILURE;
130
        }
131
    }
132
133
    /**
134
     * Does the passed data response represent a PARTIAL verification as far as
135
     * the local database is concerned?
136
     *
137
     * @param  array $data
138
     * @return bool
139
     */
140
    public function proofIsPartial(array $data) : bool
141
    {
142
        if (isset($data['anchors_complete']) && count($data['anchors_complete']) === 0) {
143
            return false;
144
        }
145
146
        return true;
147
    }
148
149
    /**
150
     * Does the passed data represent a FULL verification as far as the local database
151
     * is concerned?
152
     *
153
     * @param  array $data
154
     * @return bool
155
     */
156
    public function proofIsComplete(array $data) : bool
157
    {
158
        if (empty($data['anchors_complete']) || empty($data['anchors'])) {
159
            return false;
160
        }
161
162
        // "Full" means anchors to both Etheruem and Bitcoin blockchains
163
        return count($data['anchors']) === 3; // "cal" + "btc" + "eth"
164
    }
165
166
    /**
167
     * Set, instantiate and return a new Merkle Tree storage backend.
168
     *
169
     * @param  BackendProvider $provider Optional manually pased backend.
170
     * @return VerifiableService
171
     * @throws VerifiableBackendException
172
     */
173
    public function setBackend(BackendProvider $provider = null)
174
    {
175
        if ($provider) {
176
            $this->backend = $provider;
177
178
            return $this;
179
        }
180
181
        $namedBackend = $this->config()->get('backend');
182
        $backends = ClassInfo::implementorsOf(BackendProvider::class);
183
184
        foreach ($backends as $backend) {
185
            if (singleton($backend)->name() === $namedBackend) {
186
                $this->backend = Injector::inst()->create($backend);
187
188
                return $this;
189
            }
190
        }
191
192
        // Cannt continue without a legit backend
193
        throw new VerifiableBackendException('No backend found');
194
    }
195
196
    /**
197
     * @return BackendProvider
198
     */
199
    public function getBackend()
200
    {
201
        return $this->backend;
202
    }
203
204
    /**
205
     * Hashes the data passed into the $hash param.
206
     *
207
     * @param  array $data An array of data who's values should be hashed.
208
     * @return string      The resulting hashed data.
209
     * @todo               Take user input in the form of a digital signature
210
     */
211
    public function hash(array $data) : string
212
    {
213
        $func = $this->config()->get('hash_func');
214
        $text = json_encode($data); // Simply used to stringify arrays of arbitary depth
215
216
        return $func($text);
217
    }
218
219
    /**
220
     * Setup a {@link QueuedJob} to ping a backend and update the passed dataobject's
221
     * "Proof" field when a chainpoint proof has been generated.
222
     *
223
     * @param  DataObject $model The {@link DataObject} model subclass with a "Proof" field
224
     * @return void
225
     */
226
    public function queueVerification(DataObject $model)
227
    {
228
        $job = BackendVerificationJob::create()->setObject($model);
0 ignored issues
show
Bug introduced by
The method create() does not exist on PhpTek\Verifiable\Job\BackendVerificationJob. ( Ignorable by Annotation )

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

228
        $job = BackendVerificationJob::/** @scrutinizer ignore-call */ create()->setObject($model);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
229
        // Ping the backend 1 hour hence
230
        $time = date('Y-m-d H:i:s', time() + 3600);
231
232
        singleton(QueuedJobService::class)->queueJob($job, $time);
233
    }
234
235
}
236