Completed
Pull Request — master (#391)
by thomas
256:49 queued 254:17
created

InputSigner::verifySolution()   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 3
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\Adapter\EcAdapterInterface;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
8
use BitWasp\Bitcoin\Crypto\Random\Rfc6979;
9
use BitWasp\Bitcoin\Key\PublicKeyFactory;
10
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
11
use BitWasp\Bitcoin\Script\Classifier\OutputData;
12
use BitWasp\Bitcoin\Script\Interpreter\Checker;
13
use BitWasp\Bitcoin\Script\Interpreter\Interpreter;
14
use BitWasp\Bitcoin\Script\Interpreter\Stack;
15
use BitWasp\Bitcoin\Script\Opcodes;
16
use BitWasp\Bitcoin\Script\Script;
17
use BitWasp\Bitcoin\Script\ScriptFactory;
18
use BitWasp\Bitcoin\Script\ScriptInfo\Multisig;
19
use BitWasp\Bitcoin\Script\ScriptInterface;
20
use BitWasp\Bitcoin\Script\ScriptWitness;
21
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
22
use BitWasp\Bitcoin\Signature\SignatureSort;
23
use BitWasp\Bitcoin\Signature\TransactionSignature;
24
use BitWasp\Bitcoin\Signature\TransactionSignatureFactory;
25
use BitWasp\Bitcoin\Signature\TransactionSignatureInterface;
26
use BitWasp\Bitcoin\Transaction\SignatureHash\Hasher;
27
use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash;
28
use BitWasp\Bitcoin\Transaction\SignatureHash\V1Hasher;
29
use BitWasp\Bitcoin\Transaction\TransactionInterface;
30
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface;
31
use BitWasp\Buffertools\Buffer;
32
use BitWasp\Buffertools\BufferInterface;
33
34
class InputSigner
35
{
36
    /**
37
     * @var array
38
     */
39
    protected static $canSign = [
40
        OutputClassifier::PAYTOPUBKEYHASH,
41
        OutputClassifier::PAYTOPUBKEY,
42
        OutputClassifier::MULTISIG
43
    ];
44
45
    /**
46
     * @var array
47
     */
48
    protected static $validP2sh = [
49
        OutputClassifier::WITNESS_V0_KEYHASH,
50
        OutputClassifier::WITNESS_V0_SCRIPTHASH,
51
        OutputClassifier::PAYTOPUBKEYHASH,
52
        OutputClassifier::PAYTOPUBKEY,
53
        OutputClassifier::MULTISIG
54
    ];
55
56
    /**
57
     * @var EcAdapterInterface
58
     */
59
    private $ecAdapter;
60
61
    /**
62
     * @var OutputData $scriptPubKey
63
     */
64
    private $scriptPubKey;
65
66
    /**
67
     * @var OutputData $redeemScript
68
     */
69
    private $redeemScript;
70
71
    /**
72
     * @var OutputData $witnessScript
73
     */
74
    private $witnessScript;
75
76
    /**
77
     * @var OutputData $witnessKeyHash
78
     */
79
    private $witnessKeyHash;
80
81
    /**
82
     * @var TransactionInterface
83
     */
84
    private $tx;
85
86
    /**
87
     * @var int
88
     */
89
    private $nInput;
90
91
    /**
92
     * @var TransactionOutputInterface
93
     */
94
    private $txOut;
95
96
    /**
97
     * @var PublicKeyInterface[]
98
     */
99
    private $publicKeys = [];
100
101
    /**
102
     * @var TransactionSignatureInterface[]
103
     */
104
    private $signatures = [];
105
106
    /**
107
     * @var int
108
     */
109
    private $requiredSigs = 0;
110
111
    /**
112
     * @var OutputClassifier
113
     */
114
    private $classifier;
115
116
    /**
117
     * @var Interpreter
118
     */
119
    private $interpreter;
120
121
    /**
122
     * @var Checker
123
     */
124
    private $signatureChecker;
125
126
    /**
127
     * TxInputSigning constructor.
128
     * @param EcAdapterInterface $ecAdapter
129
     * @param TransactionInterface $tx
130
     * @param int $nInput
131
     * @param TransactionOutputInterface $txOut
132
     * @param SignData $signData
133
     */
134 84
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $tx, $nInput, TransactionOutputInterface $txOut, SignData $signData)
135 2
    {
136 84
        if (!isset($tx->getInputs()[$nInput])) {
137
            throw new \RuntimeException('No input at this index');
138
        }
139
140 84
        $this->ecAdapter = $ecAdapter;
141 84
        $this->tx = $tx;
142 84
        $this->nInput = $nInput;
143 84
        $this->txOut = $txOut;
144 84
        $this->classifier = new OutputClassifier();
145 84
        $this->interpreter = new Interpreter();
146 84
        $this->signatureChecker = new Checker($this->ecAdapter, $this->tx, $nInput, $txOut->getValue());
147 84
        $this->flags = $signData->hasSignaturePolicy() ? $signData->getSignaturePolicy() : Interpreter::VERIFY_NONE;
0 ignored issues
show
Bug introduced by
The property flags does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
148 84
        $this->publicKeys = [];
149 84
        $this->signatures = [];
150
151 84
        $this->solve($signData);
152 84
        $this->extractSignatures();
153 84
    }
154
155
    /**
156
     * @param int $sigVersion
157
     * @param TransactionSignatureInterface[] $stack
158
     * @param ScriptInterface $scriptCode
159
     * @return \SplObjectStorage
160
     */
161 24
    private function sortMultiSigs($sigVersion, $stack, ScriptInterface $scriptCode)
162
    {
163 24
        $sigSort = new SignatureSort($this->ecAdapter);
164 24
        $sigs = new \SplObjectStorage;
165
166 24
        foreach ($stack as $txSig) {
167
            $hash = $this->calculateSigHash($scriptCode, $txSig->getHashType(), $sigVersion);
168
            $linked = $sigSort->link([$txSig->getSignature()], $this->publicKeys, $hash);
169
            foreach ($this->publicKeys as $key) {
170
                if ($linked->contains($key)) {
171
                    $sigs[$key] = $txSig;
172
                }
173
            }
174 8
        }
175
176 24
        return $sigs;
177
    }
178
179
    /**
180
     * @param ScriptInterface $script
181
     * @return \BitWasp\Buffertools\BufferInterface[]
182
     */
183 66
    private function evalPushOnly(ScriptInterface $script)
184
    {
185 66
        $stack = new Stack();
186 66
        $interpreter = new Interpreter();
187 66
        $interpreter->evaluate($script, $stack, SigHash::V0, $this->flags | Interpreter::VERIFY_SIGPUSHONLY, new Checker($this->ecAdapter, $this->tx, $this->nInput, $this->txOut->getValue()));
188 66
        return $stack->all();
189
    }
190
191
    /**
192
     * @param BufferInterface[] $buffers
193
     * @return ScriptInterface
194
     */
195
    private function pushAll(array $buffers)
196
    {
197 84
        return ScriptFactory::sequence(array_map(function ($buffer) {
198 66
            if (!($buffer instanceof BufferInterface)) {
199
                throw new \RuntimeException('Script contained a non-push opcode');
200
            }
201
202 66
            $size = $buffer->getSize();
203 66
            if ($size === 0) {
204 18
                return Opcodes::OP_0;
205
            }
206
207 66
            $first = ord($buffer->getBinary());
208 66
            if ($size === 1 && $first >= 1 && $first <= 16) {
209
                return \BitWasp\Bitcoin\Script\encodeOpN($first);
210
            } else {
211 66
                return $buffer;
212
            }
213 84
        }, $buffers));
214
    }
215
216
217
    /**
218
     * @param ScriptInterface $scriptSig
219
     * @param ScriptInterface $scriptPubKey
220
     * @param ScriptWitnessInterface|null $scriptWitness
221
     * @return bool
222
     */
223 36
    private function verifySolution(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, ScriptWitnessInterface $scriptWitness = null)
224
    {
225 36
        return $this->interpreter->verify($scriptSig, $scriptPubKey, $this->flags, $this->signatureChecker, $scriptWitness);
226
    }
227
228
    /**
229
     * @param ScriptInterface $scriptPubKey
230
     * @param array $chunks
231
     * @param int $sigVersion
232
     * @return bool
233
     */
234 54
    private function evaluateSolution(ScriptInterface $scriptPubKey, array $chunks, $sigVersion)
235
    {
236 54
        return $this->interpreter->evaluate($scriptPubKey, new Stack($chunks), $sigVersion, $this->flags, $this->signatureChecker);
237
    }
238
239
    /**
240
     * Called upon instance creation.
241
     * This function must throw an exception whenever execution
242
     * does not yield a signable script.
243
     *
244
     * It ensures:
245
     *  - the scriptPubKey can be directly signed, or leads to P2SH/P2WSH/P2WKH
246
     *  - the P2SH script covers signable types and P2WSH/P2WKH
247
     *  - the witnessScript covers signable types only.
248
     *  - violating the above prevents instance creation
249
     * @param SignData $signData
250
     * @return $this
251
     * @throws \Exception
252
     */
253 84
    private function solve(SignData $signData)
254
    {
255 84
        $scriptPubKey = $this->txOut->getScript();
256 84
        $solution = $this->scriptPubKey = $this->classifier->decode($scriptPubKey);
257 84
        if ($solution->getType() !== OutputClassifier::PAYTOSCRIPTHASH && !in_array($solution->getType(), self::$validP2sh)) {
258
            throw new \RuntimeException('scriptPubKey not supported');
259
        }
260
261 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
262 24
            $redeemScript = $signData->getRedeemScript();
263 24
            if (!$this->verifySolution(ScriptFactory::sequence([$redeemScript->getBuffer()]), $solution->getScript())) {
264
                throw new \Exception('Redeem script fails to solve pay-to-script-hash');
265
            }
266 24
            $solution = $this->redeemScript = $this->classifier->decode($redeemScript);
267 24
            if (!in_array($solution->getType(), self::$validP2sh)) {
268
                throw new \Exception('Unsupported pay-to-script-hash script');
269
            }
270 8
        }
271
272 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
273 12
            $this->witnessKeyHash = $this->classifier->decode(ScriptFactory::scriptPubKey()->payToPubKeyHashFromHash($solution->getSolution()));
274 76
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
275 18
            $witnessScript = $signData->getWitnessScript();
276 18
            if (!$this->verifySolution(ScriptFactory::sequence([$witnessScript->getBuffer()]), $solution->getScript())) {
277
                throw new \Exception('Witness script fails to solve witness-script-hash');
278
            }
279 18
            $this->witnessScript = $this->classifier->decode($witnessScript);
280 18
            if (!in_array($this->witnessScript->getType(), self::$canSign)) {
281
                throw new \Exception('Unsupported witness-script-hash script');
282
            }
283 6
        }
284
285 84
        return $this;
286
    }
287
288
    /**
289
     * This function is strictly for $canSign types.
290
     * It will extract signatures/publicKeys when given $scriptType, $scriptCode, and $stack.
291
     * $stack is the result of decompiling a scriptSig, or taking the witness data.
292
     *
293
     * @param string $type - the scriptCode type
294
     * @param ScriptInterface $scriptCode - the script being solved by $stack
295
     * @param BufferInterface[] $stack - list of elements which satisfy $scriptCode
296
     * @param int $sigVersion - tx signature hashing version
297
     * @return string
298
     */
299 84
    public function extractFromValues($type, ScriptInterface $scriptCode, array $stack, $sigVersion)
300
    {
301 84
        $size = count($stack);
302 84
        if ($type === OutputClassifier::PAYTOPUBKEYHASH) {
303 42
            $this->requiredSigs = 1;
304 42
            if ($size === 2) {
305 24
                if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
306
                    throw new \RuntimeException('Existing signatures are invalid!');
307
                }
308 24
                $this->signatures = [TransactionSignatureFactory::fromHex($stack[0], $this->ecAdapter)];
309 24
                $this->publicKeys = [PublicKeyFactory::fromHex($stack[1], $this->ecAdapter)];
310 8
            }
311 14
        }
312
313 84
        if ($type === OutputClassifier::PAYTOPUBKEY && count($stack) === 1) {
314 6
            $this->requiredSigs = 1;
315 6
            if ($size === 1) {
316 6
                if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
317
                    throw new \RuntimeException('Existing signatures are invalid!');
318
                }
319 6
                $this->signatures = [TransactionSignatureFactory::fromHex($stack[0], $this->ecAdapter)];
320 2
            }
321 2
        }
322
323 84
        if ($type === OutputClassifier::MULTISIG) {
324 24
            $info = new Multisig($scriptCode);
325 24
            $this->requiredSigs = $info->getRequiredSigCount();
326 24
            $this->publicKeys = $info->getKeys();
327
328 24
            if ($size > 1) {
329 24
                $vars = [];
330 24
                for ($i = 1, $j = $size - 1; $i < $j; $i++) {
331
                    $vars[] = TransactionSignatureFactory::fromHex($stack[$i], $this->ecAdapter);
332
                }
333
334 24
                $sigs = $this->sortMultiSigs($sigVersion, $vars, $scriptCode);
335 24
                foreach ($this->publicKeys as $idx => $key) {
336 24
                    $this->signatures[$idx] = isset($sigs[$key]) ? $sigs[$key]->getBuffer() : null;
337 8
                }
338
339 24
                if (count(array_filter($this->signatures, 'is_null')) === count($this->publicKeys)) {
340 24
                    if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
341
                        throw new \RuntimeException('Existing signatures are invalid!');
342
                    }
343 8
                }
344 8
            }
345 8
        }
346
347 84
        return $type;
348
    }
349
350
    /**
351
     * High level function for extracting signatures from a pre-signed
352
     * transaction.
353
     *
354
     * @return $this
355
     */
356 84
    public function extractSignatures()
357 2
    {
358 84
        $scriptSig = $this->tx->getInput($this->nInput)->getScript();
359 84
        $witnesses = $this->tx->getWitnesses();
360 84
        $witness = isset($witnesses[$this->nInput]) ? $witnesses[$this->nInput]->all() : [];
361
362 84
        $solution = $this->scriptPubKey;
363 84
        $sigVersion = SigHash::V0;
364 84
        $chunks = [];
365 84
        if ($solution->canSign()) {
366 42
            $chunks = $this->evalPushOnly($scriptSig);
367 14
        }
368
369 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
370 24
            $chunks = $this->evalPushOnly($scriptSig);
371 24
            if (count($chunks) > 0) {
372 18
                if (!end($chunks)->equals($this->redeemScript->getScript()->getBuffer())) {
0 ignored issues
show
Documentation introduced by
$this->redeemScript->getScript()->getBuffer() is of type object<BitWasp\Buffertools\Buffer>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
373
                    throw new \RuntimeException('Extracted redeemScript did not match script-hash');
374
                }
375
376 18
                $solution = $this->redeemScript;
377 18
                $chunks = array_slice($chunks, 0, -1);
378 6
            }
379 8
        }
380
381 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
382 12
            $solution = $this->witnessKeyHash;
383 12
            $sigVersion = SigHash::V1;
384 12
            $chunks = $witness;
385 80
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
386 18
            if (count($witness) > 0) {
387 18
                if (!end($witness)->equals($this->witnessScript->getScript()->getBuffer())) {
388
                    throw new \RuntimeException('Extracted witnessScript did not match witness-script-hash');
389
                }
390
391 18
                $solution = $this->witnessScript;
392 18
                $sigVersion = SigHash::V1;
393 18
                $chunks = array_slice($witness, 0, -1);
394 6
            }
395 6
        }
396
397 84
        $this->extractFromValues($solution->getType(), $solution->getScript(), $chunks, $sigVersion);
398
399 84
        return $this;
400
    }
401
402
    /**
403
     * @param ScriptInterface $scriptCode
404
     * @param int $sigHashType
405
     * @param int $sigVersion
406
     * @return BufferInterface
407
     */
408 84
    public function calculateSigHash(ScriptInterface $scriptCode, $sigHashType, $sigVersion)
409
    {
410 84
        if (!$this->signatureChecker->isDefinedHashtype($sigHashType)) {
411
            throw new \RuntimeException('Invalid sigHashType requested');
412
        }
413
414 84
        if ($sigVersion === SigHash::V1) {
415 30
            $hasher = new V1Hasher($this->tx, $this->txOut->getValue());
416 10
        } else {
417 54
            $hasher = new Hasher($this->tx);
418
        }
419
420 84
        return $hasher->calculate($scriptCode, $this->nInput, $sigHashType);
421
    }
422
423
    /**
424
     * @param PrivateKeyInterface $key
425
     * @param ScriptInterface $scriptCode
426
     * @param int $sigHashType
427
     * @param int $sigVersion
428
     * @return TransactionSignature
429
     */
430 84
    public function calculateSignature(PrivateKeyInterface $key, ScriptInterface $scriptCode, $sigHashType, $sigVersion)
431
    {
432 84
        $hash = $this->calculateSigHash($scriptCode, $sigHashType, $sigVersion);
433 84
        $ecSignature = $this->ecAdapter->sign($hash, $key, new Rfc6979($this->ecAdapter, $key, $hash, 'sha256'));
434 84
        return new TransactionSignature($this->ecAdapter, $ecSignature, $sigHashType);
435
    }
436
437
    /**
438
     * @return bool
439
     */
440 84
    public function isFullySigned()
441
    {
442 84
        return $this->requiredSigs !== 0 && $this->requiredSigs === count($this->signatures);
443
    }
444
445
    /**
446
     * @return int
447
     */
448
    public function getRequiredSigs()
449
    {
450
        return $this->requiredSigs;
451
    }
452
453
    /**
454
     * @return TransactionSignatureInterface[]
455
     */
456
    public function getSignatures()
457
    {
458
        return $this->signatures;
459
    }
460
461
    /**
462
     * @return PublicKeyInterface[]
463
     */
464
    public function getPublicKeys()
465
    {
466
        return $this->publicKeys;
467
    }
468
469
    /**
470
     * The function only returns true when $scriptPubKey could be classified
471
     *
472
     * @param PrivateKeyInterface $key
473
     * @param OutputData $solution
474
     * @param int $sigHashType
475
     * @param int $sigVersion
476
     */
477 84
    private function doSignature(PrivateKeyInterface $key, OutputData $solution, $sigHashType, $sigVersion = SigHash::V0)
478
    {
479 84
        if ($this->isFullySigned()) {
480
            return;
481
        }
482
483 84
        if ($solution->getType() === OutputClassifier::PAYTOPUBKEY) {
484 12
            if (!$key->getPublicKey()->getBuffer()->equals($solution->getSolution())) {
485
                throw new \RuntimeException('Signing with the wrong private key');
486
            }
487 12
            $this->signatures[0] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
488 12
            $this->publicKeys[0] = $key->getPublicKey();
489 12
            $this->requiredSigs = 1;
490 76
        } else if ($solution->getType() === OutputClassifier::PAYTOPUBKEYHASH) {
491 42
            if (!$key->getPubKeyHash()->equals($solution->getSolution())) {
492
                throw new \RuntimeException('Signing with the wrong private key');
493
            }
494 42
            $this->signatures[0] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
495 42
            $this->publicKeys[0] = $key->getPublicKey();
496 42
            $this->requiredSigs = 1;
497 44
        } else if ($solution->getType() === OutputClassifier::MULTISIG) {
498 30
            $info = new Multisig($solution->getScript());
499 30
            $this->publicKeys = $info->getKeys();
500 30
            $this->requiredSigs = $info->getRequiredSigCount();
501
502 30
            $myKey = $key->getPublicKey()->getBuffer();
503 30
            $signed = false;
504 30
            foreach ($info->getKeys() as $keyIdx => $publicKey) {
505 30
                if ($myKey->equals($publicKey->getBuffer())) {
0 ignored issues
show
Documentation introduced by
$publicKey->getBuffer() is of type object<BitWasp\Buffertools\BufferInterface>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
506 30
                    $this->signatures[$keyIdx] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
507 30
                    $signed = true;
508 10
                }
509 10
            }
510
511 30
            if (!$signed) {
512 20
                throw new \RuntimeException('Signing with the wrong private key');
513
            }
514 10
        } else {
515
            throw new \RuntimeException('Cannot sign unknown script type');
516
        }
517 84
    }
518
519
    /**
520
     * @param PrivateKeyInterface $key
521
     * @param int $sigHashType
522
     * @return bool
523
     */
524 84
    public function sign(PrivateKeyInterface $key, $sigHashType = SigHash::ALL)
525
    {
526 84
        $solution = $this->scriptPubKey;
527 84
        $sigVersion = SigHash::V0;
528 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
529 24
            $solution = $this->redeemScript;
530 8
        }
531
532 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
533 12
            $solution = $this->witnessKeyHash;
534 12
            $sigVersion = SigHash::V1;
535 76
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
536 18
            $solution = $this->witnessScript;
537 18
            $sigVersion = SigHash::V1;
538 6
        }
539
540 84
        if ($solution->canSign()) {
541 84
            $this->doSignature($key, $solution, $sigHashType, $sigVersion);
542 84
            return true;
543
        }
544
545
        return false;
546
    }
547
548
    /**
549
     * @param string $outputType
550
     * @return BufferInterface[]
551
     */
552 84
    private function serializeSolution($outputType)
553
    {
554 84
        if ($outputType === OutputClassifier::PAYTOPUBKEY) {
555 12
            return [$this->signatures[0]->getBuffer()];
556 72
        } else if ($outputType === OutputClassifier::PAYTOPUBKEYHASH) {
557 42
            return [$this->signatures[0]->getBuffer(), $this->publicKeys[0]->getBuffer()];
558 30
        } else if ($outputType === OutputClassifier::MULTISIG) {
559 30
            $sequence = [new Buffer()];
560 30
            for ($i = 0, $nPubKeys = count($this->publicKeys); $i < $nPubKeys; $i++) {
561 30
                if (isset($this->signatures[$i])) {
562 30
                    $sequence[] = $this->signatures[$i]->getBuffer();
563 10
                }
564 10
            }
565
566 30
            return $sequence;
567
        } else {
568
            throw new \RuntimeException('Cannot serialize this script sig');
569
        }
570
    }
571
572
    /**
573
     * @return SigValues
574
     */
575 84
    public function serializeSignatures()
576
    {
577 84
        static $emptyScript = null;
578 84
        static $emptyWitness = null;
579 84
        if (is_null($emptyScript) || is_null($emptyWitness)) {
580 6
            $emptyScript = new Script();
581 6
            $emptyWitness = new ScriptWitness([]);
582 2
        }
583
584 84
        $scriptSigChunks = [];
585 84
        $witness = [];
586 84
        if ($this->scriptPubKey->canSign()) {
587 42
            $scriptSigChunks = $this->serializeSolution($this->scriptPubKey->getType());
588 14
        }
589
590 84
        $solution = $this->scriptPubKey;
591 84
        $p2sh = false;
592 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
593 24
            $p2sh = true;
594 24
            if ($this->redeemScript->canSign()) {
595 12
                $scriptSigChunks = $this->serializeSolution($this->redeemScript->getType());
596 4
            }
597 24
            $solution = $this->redeemScript;
598 8
        }
599
600 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
601 12
            $witness = $this->serializeSolution(OutputClassifier::PAYTOPUBKEYHASH);
602 76
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
603 18
            if ($this->witnessScript->canSign()) {
604 18
                $witness = $this->serializeSolution($this->witnessScript->getType());
605 18
                $witness[] = $this->witnessScript->getScript()->getBuffer();
606 6
            }
607 6
        }
608
609 84
        if ($p2sh) {
610 24
            $scriptSigChunks[] = $this->redeemScript->getScript()->getBuffer();
611 8
        }
612
613 84
        return new SigValues($this->pushAll($scriptSigChunks), new ScriptWitness($witness));
614
    }
615
}
616