Completed
Pull Request — master (#526)
by thomas
83:42 queued 12:15
created

Checksig::receivesValue()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 10
ccs 0
cts 6
cp 0
crap 12
rs 9.4285
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
    public function __construct($info)
57 136
    {
58
        if (!is_object($info)) {
59 136
            throw new \RuntimeException("First value to checksig must be an object");
60
        }
61
62
        $infoClass = get_class($info);
63 136
        switch ($infoClass) {
64
            case PayToPubkey::class:
65 136
                /** @var PayToPubkey $info */
66
                $this->scriptType = $info->getType();
67 50
                $this->requiredSigs = $info->getRequiredSigCount();
68 50
                $this->keyCount = 1;
69 50
                break;
70 50
            case PayToPubkeyHash::class:
71 102
                /** @var PayToPubkeyHash $info */
72
                $this->scriptType = ScriptType::P2PKH;
73 42
                $this->requiredSigs = $info->getRequiredSigCount();
74 42
                $this->keyCount = 1;
75 42
                break;
76 42
            case Multisig::class:
77 64
                /** @var Multisig $info */
78
                $this->scriptType = ScriptType::MULTISIG;
79 64
                $this->requiredSigs = $info->getRequiredSigCount();
80 64
                $this->keyCount = $info->getKeyCount();
81 64
                break;
82 64
            default:
83
                throw new \RuntimeException("Unsupported class passed to Checksig");
84
        }
85
86
        $this->info = $info;
87 136
    }
88 136
89 136
    /**
90 136
     * 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
    public function setRequired($setting)
97
    {
98
        if (!is_bool($setting)) {
99
            throw new \RuntimeException("Invalid input to setRequired");
100
        }
101
        $this->required = $setting;
102
        return $this;
103 4
    }
104
105 4
    /**
106
     * Returns whether this opcodes successful completion is
107
     * necessary for the overall successful operation of the
108 4
     * script
109 4
     *
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 84
     * @return Multisig|PayToPubkey|PayToPubkeyHash
121
     */
122 84
    public function getInfo()
123
    {
124
        return $this->info;
125
    }
126
127
    /**
128 136
     * Return the script type
129
     * NB: Checksig overloads the various templates, returning 'multisig'
130 136
     * even if the opcode was multisigverify. Check the getInfo() result,
131
     * or isVerify() result, if this is important.
132
     *
133
     * @return string
134
     */
135
    public function getType()
136 82
    {
137
        return $this->scriptType;
138 82
    }
139
140 82
    /**
141 50
     * @return array|BufferInterface|BufferInterface[]
142
     */
143 38
    public function getSolution()
144
    {
145
        if ($this->info instanceof Multisig) {
146
            return $this->info->getKeyBuffers();
147
        } else if ($this->info instanceof PayToPubkey) {
148
            return $this->info->getKeyBuffer();
149
        } else {
150 120
            return $this->info->getPubKeyHash();
151
        }
152 120
    }
153
154
    /**
155
     * @return int
156
     */
157
    public function getRequiredSigs()
158 122
    {
159
        return $this->requiredSigs;
160 122
    }
161 122
162
    /**
163 4
     * @return bool
164
     */
165
    public function isFullySigned()
166
    {
167
        if ($this->required) {
168
            return $this->requiredSigs === count($this->signatures);
169
        } else {
170
            return true;
171 118
        }
172
    }
173 118
174
    /**
175
     * @param int $idx
176
     * @return bool
177 118
     */
178
    public function hasSignature($idx)
179
    {
180
        if ($idx > $this->requiredSigs) {
181
            throw new \RuntimeException("Out of range signature queried");
182
        }
183
184
        return array_key_exists($idx, $this->signatures);
185 116
    }
186
187 116
    /**
188
     * @param int $idx
189
     * @param TransactionSignatureInterface $signature
190
     * @return $this
191 116
     */
192 116
    public function setSignature($idx, TransactionSignatureInterface $signature)
193
    {
194
        if ($idx < 0 || $idx > $this->keyCount) {
195
            throw new \RuntimeException("Out of range signature for operation");
196
        }
197
198
        $this->signatures[$idx] = $signature;
199 116
        return $this;
200
    }
201 116
202
    /**
203
     * @param int $idx
204
     * @return TransactionSignatureInterface|null
205 116
     */
206
    public function getSignature($idx)
207
    {
208
        if (!$this->hasSignature($idx)) {
209
            return null;
210
        }
211 116
212
        return $this->signatures[$idx];
213 116
    }
214
215
    /**
216
     * @return array
217
     */
218
    public function getSignatures()
219
    {
220 68
        return $this->signatures;
221
    }
222 68
223
    /**
224
     * @param int $idx
225
     * @return bool
226
     */
227
    public function hasKey($idx)
228
    {
229 68
        return array_key_exists($idx, $this->publicKeys);
230
    }
231 68
232
    /**
233
     * @param int $idx
234
     * @return PublicKeyInterface|null
235 68
     */
236
    public function getKey($idx)
237
    {
238
        if (!$this->hasKey($idx)) {
239
            return null;
240
        }
241
242
        return $this->publicKeys[$idx];
243 128
    }
244
245 128
    /**
246
     * @param $idx
247
     * @param PublicKeyInterface|null $key
248
     * @return $this
249 128
     */
250 128
    public function setKey($idx, $key)
251
    {
252
        if ($idx < 0 || $idx > $this->keyCount) {
253
            throw new \RuntimeException("Out of range index for public key");
254
        }
255
256 118
        $this->publicKeys[$idx] = $key;
257
        return $this;
258 118
    }
259
260
    /**
261 118
     * @return PublicKeyInterface[]
262
     */
263 118
    public function getKeys()
264
    {
265
        return $this->publicKeys;
266
    }
267
268
    /**
269 116
     * @return bool
270
     */
271 116
    public function isVerify()
272 116
    {
273
        return $this->info->isChecksigVerify();
274 116
    }
275 46
276 2
    /**
277
     * @param TransactionSignatureSerializer $txSigSerializer
278 46
     * @param PublicKeySerializerInterface $pubKeySerializer
279 46
     * @return array
280
     */
281
    public function serialize(TransactionSignatureSerializer $txSigSerializer, PublicKeySerializerInterface $pubKeySerializer)
282 86
    {
283 36
        $outputType = $this->getType();
284
        $result = [];
285
286 36
        if (ScriptType::P2PK === $outputType) {
287 36
            if (!$this->required) {
288
                $result[0] = new Buffer();
289
            } else {
290 54
                if ($this->hasSignature(0)) {
291 54
                    $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 2
                }
293
            }
294 52
        } else if (ScriptType::P2PKH === $outputType) {
295 54
            if (!$this->required && $this->hasKey(0)) {
296 52
                $result[0] = new Buffer();
297 52
                $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
                if ($this->hasSignature(0) && $this->hasKey(0)) {
300
                    $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
                    $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
        } else if (ScriptType::MULTISIG === $outputType) {
305 116
            if (!$this->required) {
306
                $result = array_fill(0, 1 + $this->getRequiredSigs(), new Buffer());
307
            } else {
308
                $result[] = new Buffer();
309
                for ($i = 0, $nPubKeys = count($this->getKeys()); $i < $nPubKeys; $i++) {
310
                    if ($this->hasSignature($i)) {
311
                        $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
        return $result;
320
    }
321
}
322