Completed
Push — master ( 40a4e1...e12674 )
by thomas
37:14 queued 34:53
created

Checksig::hasKey()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\Factory;
4
5
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Key\PublicKeySerializerInterface;
7
use BitWasp\Bitcoin\Script\ScriptInfo\Multisig;
8
use BitWasp\Bitcoin\Script\ScriptInfo\PayToPubkey;
9
use BitWasp\Bitcoin\Script\ScriptInfo\PayToPubkeyHash;
10
use BitWasp\Bitcoin\Script\ScriptType;
11
use BitWasp\Bitcoin\Serializer\Signature\TransactionSignatureSerializer;
12
use BitWasp\Bitcoin\Signature\TransactionSignatureInterface;
13
use BitWasp\Buffertools\Buffer;
14
use BitWasp\Buffertools\BufferInterface;
15
16
class Checksig
17
{
18
    /**
19
     * @var string
20
     */
21
    private $scriptType;
22
23
    /**
24
     * @var bool
25
     */
26
    private $required = true;
27
28
    /**
29
     * @var PayToPubkeyHash|PayToPubkey|Multisig
30
     */
31
    private $info;
32
33
    /**
34
     * @var int
35
     */
36
    protected $requiredSigs;
37
38
    /**
39
     * @var int
40
     */
41
    protected $keyCount;
42
43
    /**
44
     * @var TransactionSignatureInterface[]
45
     */
46
    protected $signatures = [];
47
48
    /**
49
     * @var PublicKeyInterface[]|null[]
50
     */
51
    protected $publicKeys = [];
52
    /**
53
     * Checksig constructor.
54
     * @param Multisig|PayToPubkeyHash|PayToPubkey $info
55
     */
56 136
    public function __construct($info)
57
    {
58 136
        if (!is_object($info)) {
59
            throw new \RuntimeException("First value to checksig must be an object");
60
        }
61
62 136
        $infoClass = get_class($info);
63
        switch ($infoClass) {
64 136
            case PayToPubkey::class:
65
                /** @var PayToPubkey $info */
66 50
                $this->scriptType = $info->getType();
67 50
                $this->requiredSigs = $info->getRequiredSigCount();
68 50
                $this->keyCount = 1;
69 50
                break;
70 102
            case PayToPubkeyHash::class:
71
                /** @var PayToPubkeyHash $info */
72 42
                $this->scriptType = ScriptType::P2PKH;
73 42
                $this->requiredSigs = $info->getRequiredSigCount();
74 42
                $this->keyCount = 1;
75 42
                break;
76 64
            case Multisig::class:
77
                /** @var Multisig $info */
78 64
                $this->scriptType = ScriptType::MULTISIG;
79 64
                $this->requiredSigs = $info->getRequiredSigCount();
80 64
                $this->keyCount = $info->getKeyCount();
81 64
                break;
82
            default:
83
                throw new \RuntimeException("Unsupported class passed to Checksig");
84
        }
85
86 136
        $this->info = $info;
87 136
    }
88
89
    /**
90
     * Mark this Checksig operation as not required. Will use OP_0
91
     * in place of all values (satisfying MINIMALDATA / MINIMALIF)
92
     *
93
     * @param bool $setting
94
     * @return $this
95
     */
96 4
    public function setRequired($setting)
97
    {
98 4
        if (!is_bool($setting)) {
99
            throw new \RuntimeException("Invalid input to setRequired");
100
        }
101 4
        $this->required = $setting;
102 4
        return $this;
103
    }
104
105
    /**
106
     * Returns whether this opcodes successful completion is
107
     * necessary for the overall successful operation of the
108
     * script
109
     *
110
     * @return bool
111
     */
112 116
    public function isRequired()
113
    {
114 116
        return $this->required;
115
    }
116
117
    /**
118
     * Returns the underlying script info class
119
     *
120
     * @return Multisig|PayToPubkey|PayToPubkeyHash
121
     */
122 84
    public function getInfo()
123
    {
124 84
        return $this->info;
125
    }
126
127
    /**
128
     * Return the script type
129
     * NB: Checksig overloads the various templates, returning 'multisig'
130
     * even if the opcode was multisigverify. Check the getInfo() result,
131
     * or isVerify() result, if this is important.
132
     *
133
     * @return string
134
     */
135 136
    public function getType()
136
    {
137 136
        return $this->scriptType;
138
    }
139
140
    /**
141
     * @return array|BufferInterface|BufferInterface[]
142
     */
143 82
    public function getSolution()
144
    {
145 82
        if ($this->info instanceof Multisig) {
146
            return $this->info->getKeyBuffers();
147 82
        } else if ($this->info instanceof PayToPubkey) {
148 50
            return $this->info->getKeyBuffer();
149
        } else {
150 38
            return $this->info->getPubKeyHash();
151
        }
152
    }
153
154
    /**
155
     * @return int
156
     */
157 120
    public function getRequiredSigs()
158
    {
159 120
        return $this->requiredSigs;
160
    }
161
162
    /**
163
     * @return bool
164
     */
165 122
    public function isFullySigned()
166
    {
167 122
        if ($this->required) {
168 122
            return $this->requiredSigs === count($this->signatures);
169
        } else {
170 4
            return true;
171
        }
172
    }
173
174
    /**
175
     * @param int $idx
176
     * @return bool
177
     */
178 118
    public function hasSignature($idx)
179
    {
180 118
        if ($idx > $this->requiredSigs) {
181
            throw new \RuntimeException("Out of range signature queried");
182
        }
183
184 118
        return array_key_exists($idx, $this->signatures);
185
    }
186
187
    /**
188
     * @param int $idx
189
     * @param TransactionSignatureInterface $signature
190
     * @return $this
191
     */
192 116
    public function setSignature($idx, TransactionSignatureInterface $signature)
193
    {
194 116
        if ($idx < 0 || $idx > $this->keyCount) {
195
            throw new \RuntimeException("Out of range signature for operation");
196
        }
197
198 116
        $this->signatures[$idx] = $signature;
199 116
        return $this;
200
    }
201
202
    /**
203
     * @param int $idx
204
     * @return TransactionSignatureInterface|null
205
     */
206 116
    public function getSignature($idx)
207
    {
208 116
        if (!$this->hasSignature($idx)) {
209
            return null;
210
        }
211
212 116
        return $this->signatures[$idx];
213
    }
214
215
    /**
216
     * @return array
217
     */
218 116
    public function getSignatures()
219
    {
220 116
        return $this->signatures;
221
    }
222
223
    /**
224
     * @param int $idx
225
     * @return bool
226
     */
227 68
    public function hasKey($idx)
228
    {
229 68
        return array_key_exists($idx, $this->publicKeys);
230
    }
231
232
    /**
233
     * @param int $idx
234
     * @return PublicKeyInterface|null
235
     */
236 68
    public function getKey($idx)
237
    {
238 68
        if (!$this->hasKey($idx)) {
239
            return null;
240
        }
241
242 68
        return $this->publicKeys[$idx];
243
    }
244
245
    /**
246
     * @param $idx
247
     * @param PublicKeyInterface|null $key
248
     * @return $this
249
     */
250 128
    public function setKey($idx, $key)
251
    {
252 128
        if ($idx < 0 || $idx > $this->keyCount) {
253
            throw new \RuntimeException("Out of range index for public key");
254
        }
255
256 128
        $this->publicKeys[$idx] = $key;
257 128
        return $this;
258
    }
259
260
    /**
261
     * @return PublicKeyInterface[]
262
     */
263 118
    public function getKeys()
264
    {
265 118
        return $this->publicKeys;
266
    }
267
268
    /**
269
     * @return bool
270
     */
271 118
    public function isVerify()
272
    {
273 118
        return $this->info->isChecksigVerify();
274
    }
275
276
    /**
277
     * @param TransactionSignatureSerializer $txSigSerializer
278
     * @param PublicKeySerializerInterface $pubKeySerializer
279
     * @return array
280
     */
281 116
    public function serialize(TransactionSignatureSerializer $txSigSerializer, PublicKeySerializerInterface $pubKeySerializer)
282
    {
283 116
        $outputType = $this->getType();
284 116
        $result = [];
285
286 116
        if (ScriptType::P2PK === $outputType) {
287 46
            if (!$this->required) {
288 2
                $result[0] = new Buffer();
289
            } else {
290 46
                if ($this->hasSignature(0)) {
291 46
                    $result[0] = $txSigSerializer->serialize($this->getSignature(0));
0 ignored issues
show
Bug introduced by
It seems like $this->getSignature(0) can be null; however, serialize() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
292
                }
293
            }
294 86
        } else if (ScriptType::P2PKH === $outputType) {
295 36
            if (!$this->required && $this->hasKey(0)) {
296
                $result[0] = new Buffer();
297
                $result[1] = $pubKeySerializer->serialize($this->getKey(0));
0 ignored issues
show
Bug introduced by
It seems like $this->getKey(0) can be null; however, serialize() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
298
            } else {
299 36
                if ($this->hasSignature(0) && $this->hasKey(0)) {
300 36
                    $result[0] = $txSigSerializer->serialize($this->getSignature(0));
0 ignored issues
show
Bug introduced by
It seems like $this->getSignature(0) can be null; however, serialize() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
301 36
                    $result[1] = $pubKeySerializer->serialize($this->getKey(0));
0 ignored issues
show
Bug introduced by
It seems like $this->getKey(0) can be null; however, serialize() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
302
                }
303
            }
304 54
        } else if (ScriptType::MULTISIG === $outputType) {
305 54
            if (!$this->required) {
306 2
                $result = array_fill(0, 1 + $this->getRequiredSigs(), new Buffer());
307
            } else {
308 52
                $result[] = new Buffer();
309 54
                for ($i = 0, $nPubKeys = count($this->getKeys()); $i < $nPubKeys; $i++) {
310 52
                    if ($this->hasSignature($i)) {
311 52
                        $result[] = $txSigSerializer->serialize($this->getSignature($i));
0 ignored issues
show
Bug introduced by
It seems like $this->getSignature($i) can be null; however, serialize() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
312
                    }
313
                }
314
            }
315
        } else {
316
            throw new \RuntimeException('Parameter 0 for serializeSolution was a non-standard input type');
317
        }
318
319 116
        return $result;
320
    }
321
}
322