Completed
Pull Request — master (#391)
by thomas
259:03 queued 188:03
created

InputSigner::verifySolution()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 3
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 2
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 84
101
    /**
102 84
     * @var TransactionSignatureInterface[]
103 84
     */
104 84
    private $signatures = [];
105 84
106 84
    /**
107 84
     * @var int
108 84
     */
109
    private $requiredSigs = 0;
110 84
111 84
    /**
112 84
     * @var OutputClassifier
113
     */
114
    private $classifier;
115
116
    /**
117
     * @var Interpreter
118
     */
119
    private $interpreter;
120 26
121
    /**
122 24
     * @var Checker
123 24
     */
124
    private $signatureChecker;
125 24
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 8
     */
134
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $tx, $nInput, TransactionOutputInterface $txOut, SignData $signData)
135 26
    {
136
        if (!isset($tx->getInputs()[$nInput])) {
137
            throw new \RuntimeException('No input at this index');
138
        }
139
140
        $this->ecAdapter = $ecAdapter;
141
        $this->tx = $tx;
142
        $this->nInput = $nInput;
143
        $this->txOut = $txOut;
144
        $this->classifier = new OutputClassifier();
145 78
        $this->interpreter = new Interpreter();
146
        $this->signatureChecker = new Checker($this->ecAdapter, $this->tx, $nInput, $txOut->getValue());
147 78
        $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 78
        $this->publicKeys = [];
149 42
        $this->signatures = [];
150 42
151 24
        $this->solve($signData);
152 24
        $this->extractSignatures();
153 8
    }
154 14
155
    /**
156 78
     * @param int $sigVersion
157 12
     * @param TransactionSignatureInterface[] $stack
158 12
     * @param ScriptInterface $scriptCode
159 6
     * @return \SplObjectStorage
160 2
     */
161 4
    private function sortMultiSigs($sigVersion, $stack, ScriptInterface $scriptCode)
162
    {
163 78
        $sigSort = new SignatureSort($this->ecAdapter);
164 24
        $sigs = new \SplObjectStorage;
165 24
166 24
        foreach ($stack as $txSig) {
167
            $hash = $this->calculateSigHash($scriptCode, $txSig->getHashType(), $sigVersion);
168 24
            $linked = $sigSort->link([$txSig->getSignature()], $this->publicKeys, $hash);
169 24
            foreach ($this->publicKeys as $key) {
170 24
                if ($linked->contains($key)) {
171
                    $sigs[$key] = $txSig;
172
                }
173
            }
174 24
        }
175 24
176 24
        return $sigs;
177 8
    }
178 8
179 8
    /**
180
     * @param ScriptInterface $script
181 78
     * @return \BitWasp\Buffertools\BufferInterface[]
182
     */
183
    private function evalPushOnly(ScriptInterface $script)
184
    {
185
        $stack = new Stack();
186
        $interpreter = new Interpreter();
187
        $interpreter->evaluate($script, $stack, SigHash::V0, $this->flags | Interpreter::VERIFY_SIGPUSHONLY, new Checker($this->ecAdapter, $this->tx, $this->nInput, $this->txOut->getValue()));
188
        return $stack->all();
189 84
    }
190
191 84
    /**
192 84
     * @param BufferInterface[] $buffers
193 84
     * @return ScriptInterface
194 84
     */
195 84
    private function pushAll(array $buffers)
196
    {
197
        return ScriptFactory::sequence(array_map(function ($buffer) {
198
            if (!($buffer instanceof BufferInterface)) {
199 84
                throw new \RuntimeException('Script contained a non-push opcode');
200 24
            }
201 24
202
            $size = $buffer->getSize();
203
            if ($size === 0) {
204 24
                return Opcodes::OP_0;
205 24
            }
206
207
            $first = ord($buffer->getBinary());
208 8
            if ($size === 1 && $first >= 1 && $first <= 16) {
209
                return \BitWasp\Bitcoin\Script\encodeOpN($first);
210 84
            } else {
211 18
                return $buffer;
212 18
            }
213
        }, $buffers));
214
    }
215 18
216 18
217
    /**
218
     * @param ScriptInterface $scriptSig
219 6
     * @param ScriptInterface $scriptPubKey
220
     * @param ScriptWitnessInterface|null $scriptWitness
221 84
     * @return bool
222
     */
223
    private function verifySolution(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, ScriptWitnessInterface $scriptWitness = null)
224
    {
225
        return $this->interpreter->verify($scriptSig, $scriptPubKey, $this->flags, $this->signatureChecker, $scriptWitness);
226
    }
227 84
228
    /**
229 84
     * @param ScriptInterface $scriptPubKey
230 84
     * @param array $chunks
231 84
     * @param int $sigVersion
232 42
     * @return bool
233 14
     */
234
    private function evaluateSolution(ScriptInterface $scriptPubKey, array $chunks, $sigVersion)
235 84
    {
236 24
        return $this->interpreter->evaluate($scriptPubKey, new Stack($chunks), $sigVersion, $this->flags, $this->signatureChecker);
237 24
    }
238 18
239 18
    /**
240
     * Called upon instance creation.
241
     * This function must throw an exception whenever execution
242
     * does not yield a signable script.
243 18
     *
244 18
     * It ensures:
245 6
     *  - the scriptPubKey can be directly signed, or leads to P2SH/P2WSH/P2WKH
246 8
     *  - the P2SH script covers signable types and P2WSH/P2WKH
247
     *  - the witnessScript covers signable types only.
248 84
     *  - violating the above prevents instance creation
249 84
     * @param SignData $signData
250 22
     * @return $this
251 12
     * @throws \Exception
252 12
     */
253 80
    private function solve(SignData $signData)
254 18
    {
255 18
        $scriptPubKey = $this->txOut->getScript();
256 18
        $solution = $this->scriptPubKey = $this->classifier->decode($scriptPubKey);
257 18
        if ($solution->getType() !== OutputClassifier::PAYTOSCRIPTHASH && !in_array($solution->getType(), self::$validP2sh)) {
258 18
            throw new \RuntimeException('scriptPubKey not supported');
259
        }
260
261
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
262 18
            $redeemScript = $signData->getRedeemScript();
263 18
            if (!$this->verifySolution(ScriptFactory::sequence([$redeemScript->getBuffer()]), $solution->getScript())) {
264 6
                throw new \Exception('Redeem script fails to solve pay-to-script-hash');
265 6
            }
266 6
            $solution = $this->redeemScript = $this->classifier->decode($redeemScript);
267
            if (!in_array($solution->getType(), self::$validP2sh)) {
268 84
                throw new \Exception('Unsupported pay-to-script-hash script');
269
            }
270
        }
271
272
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
273
            $this->witnessKeyHash = $this->classifier->decode(ScriptFactory::scriptPubKey()->payToPubKeyHashFromHash($solution->getSolution()));
274
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
275
            $witnessScript = $signData->getWitnessScript();
276
            if (!$this->verifySolution(ScriptFactory::sequence([$witnessScript->getBuffer()]), $solution->getScript())) {
277 84
                throw new \Exception('Witness script fails to solve witness-script-hash');
278
            }
279 84
            $this->witnessScript = $this->classifier->decode($witnessScript);
280 30
            if (!in_array($this->witnessScript->getType(), self::$canSign)) {
281 10
                throw new \Exception('Unsupported witness-script-hash script');
282 54
            }
283
        }
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 84
     * @param BufferInterface[] $stack - list of elements which satisfy $scriptCode
296
     * @param int $sigVersion - tx signature hashing version
297 84
     * @return string
298 84
     */
299 84
    public function extractFromValues($type, ScriptInterface $scriptCode, array $stack, $sigVersion)
300
    {
301
        $size = count($stack);
302
        if ($type === OutputClassifier::PAYTOPUBKEYHASH) {
303
            $this->requiredSigs = 1;
304
            if ($size === 2) {
305 54
                if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
306
                    throw new \RuntimeException('Existing signatures are invalid!');
307 54
                }
308
                $this->signatures = [TransactionSignatureFactory::fromHex($stack[0], $this->ecAdapter)];
309
                $this->publicKeys = [PublicKeyFactory::fromHex($stack[1], $this->ecAdapter)];
310
            }
311
        }
312
313
        if ($type === OutputClassifier::PAYTOPUBKEY && count($stack) === 1) {
314
            $this->requiredSigs = 1;
315
            if ($size === 1) {
316
                if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
317
                    throw new \RuntimeException('Existing signatures are invalid!');
318 84
                }
319
                $this->signatures = [TransactionSignatureFactory::fromHex($stack[0], $this->ecAdapter)];
320 84
            }
321 12
        }
322
323
        if ($type === OutputClassifier::MULTISIG) {
324 12
            $info = new Multisig($scriptCode);
325 12
            $this->requiredSigs = $info->getRequiredSigCount();
326 12
            $this->publicKeys = $info->getKeys();
327 76
328 42
            if ($size > 1) {
329
                $vars = [];
330
                for ($i = 1, $j = $size - 1; $i < $j; $i++) {
331 42
                    $vars[] = TransactionSignatureFactory::fromHex($stack[$i], $this->ecAdapter);
332 42
                }
333 42
334 44
                $sigs = $this->sortMultiSigs($sigVersion, $vars, $scriptCode);
335 30
                foreach ($this->publicKeys as $idx => $key) {
336 30
                    $this->signatures[$idx] = isset($sigs[$key]) ? $sigs[$key]->getBuffer() : null;
337 30
                }
338
339 30
                if (count(array_filter($this->signatures, 'is_null')) === count($this->publicKeys)) {
340 30
                    if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
341 30
                        throw new \RuntimeException('Existing signatures are invalid!');
342 30
                    }
343 30
                }
344 30
            }
345 10
        }
346 10
347
        return $type;
348 30
    }
349 20
350
    /**
351 10
     * High level function for extracting signatures from a pre-signed
352
     * transaction.
353
     *
354 84
     * @return $this
355
     */
356
    public function extractSignatures()
357
    {
358
        $scriptSig = $this->tx->getInput($this->nInput)->getScript();
359
        $witnesses = $this->tx->getWitnesses();
360
        $witness = isset($witnesses[$this->nInput]) ? $witnesses[$this->nInput]->all() : [];
361 84
362
        $solution = $this->scriptPubKey;
363 84
        $sigVersion = SigHash::V0;
364 42
        $chunks = [];
365 42
        if ($solution->canSign()) {
366
            $chunks = $this->evalPushOnly($scriptSig);
367 42
        }
368 42
369 24
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
370 12
            $chunks = $this->evalPushOnly($scriptSig);
371 12
            if (count($chunks) > 0) {
372
                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 12
                    throw new \RuntimeException('Extracted redeemScript did not match script-hash');
374 4
                }
375
376 30
                $solution = $this->redeemScript;
377 12
                $chunks = array_slice($chunks, 0, -1);
378 12
            }
379 32
        }
380 18
381 18
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
382 18
            $solution = $this->witnessKeyHash;
383 18
            $sigVersion = SigHash::V1;
384
            $chunks = $witness;
385
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
386
            if (count($witness) > 0) {
387
                if (!end($witness)->equals($this->witnessScript->getScript()->getBuffer())) {
388
                    throw new \RuntimeException('Extracted witnessScript did not match witness-script-hash');
389
                }
390
391
                $solution = $this->witnessScript;
392
                $sigVersion = SigHash::V1;
393
                $chunks = array_slice($witness, 0, -1);
394 84
            }
395
        }
396 84
397 12
        $this->extractFromValues($solution->getType(), $solution->getScript(), $chunks, $sigVersion);
398 72
399 42
        return $this;
400 30
    }
401 30
402 30
    /**
403 30
     * @param ScriptInterface $scriptCode
404 30
     * @param int $sigHashType
405 10
     * @param int $sigVersion
406 10
     * @return BufferInterface
407
     */
408 30
    public function calculateSigHash(ScriptInterface $scriptCode, $sigHashType, $sigVersion)
409
    {
410
        if (!$this->signatureChecker->isDefinedHashtype($sigHashType)) {
411
            throw new \RuntimeException('Invalid sigHashType requested');
412
        }
413
414
        if ($sigVersion === SigHash::V1) {
415
            $hasher = new V1Hasher($this->tx, $this->txOut->getValue());
416
        } else {
417
            $hasher = new Hasher($this->tx);
418
        }
419 66
420
        return $hasher->calculate($scriptCode, $this->nInput, $sigHashType);
421 66
    }
422 66
423 66
    /**
424 66
     * @param PrivateKeyInterface $key
425
     * @param ScriptInterface $scriptCode
426
     * @param int $sigHashType
427
     * @param int $sigVersion
428
     * @return TransactionSignature
429
     */
430
    public function calculateSignature(PrivateKeyInterface $key, ScriptInterface $scriptCode, $sigHashType, $sigVersion)
431
    {
432
        $hash = $this->calculateSigHash($scriptCode, $sigHashType, $sigVersion);
433 54
        $ecSignature = $this->ecAdapter->sign($hash, $key, new Rfc6979($this->ecAdapter, $key, $hash, 'sha256'));
434 54
        return new TransactionSignature($this->ecAdapter, $ecSignature, $sigHashType);
435
    }
436
437
    /**
438 54
     * @return bool
439 54
     */
440 18
    public function isFullySigned()
441
    {
442
        return $this->requiredSigs !== 0 && $this->requiredSigs === count($this->signatures);
443 54
    }
444 54
445
    /**
446
     * @return int
447 54
     */
448
    public function getRequiredSigs()
449 54
    {
450
        return $this->requiredSigs;
451
    }
452
453
    /**
454
     * The function only returns true when $scriptPubKey could be classified
455 84
     *
456
     * @param PrivateKeyInterface $key
457 84
     * @param OutputData $solution
458 84
     * @param int $sigHashType
459 84
     * @param int $sigVersion
460 6
     */
461 6
    private function doSignature(PrivateKeyInterface $key, OutputData $solution, $sigHashType, $sigVersion = SigHash::V0)
462 2
    {
463
        if ($this->isFullySigned()) {
464 84
            return;
465 84
        }
466 84
467 84
        if ($solution->getType() === OutputClassifier::PAYTOPUBKEY) {
468 42
            if (!$key->getPublicKey()->getBuffer()->equals($solution->getSolution())) {
469 14
                throw new \RuntimeException('Signing with the wrong private key');
470
            }
471 84
            $this->signatures[0] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
472 84
            $this->publicKeys[0] = $key->getPublicKey();
473 24
            $this->requiredSigs = 1;
474 24
        } else if ($solution->getType() === OutputClassifier::PAYTOPUBKEYHASH) {
475 12
            if (!$key->getPubKeyHash()->equals($solution->getSolution())) {
476 4
                throw new \RuntimeException('Signing with the wrong private key');
477 24
            }
478 8
            $this->signatures[0] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
479
            $this->publicKeys[0] = $key->getPublicKey();
480 84
            $this->requiredSigs = 1;
481 12
        } else if ($solution->getType() === OutputClassifier::MULTISIG) {
482 12
            $info = new Multisig($solution->getScript());
483 76
            $this->publicKeys = $info->getKeys();
484 18
            $this->requiredSigs = $info->getRequiredSigCount();
485 18
486 18
            $myKey = $key->getPublicKey()->getBuffer();
487 18
            $signed = false;
488 6
            foreach ($info->getKeys() as $keyIdx => $publicKey) {
489 6
                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...
490
                    $this->signatures[$keyIdx] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
491 84
                    $signed = true;
492 24
                }
493 8
            }
494
495 84
            if (!$signed) {
496
                throw new \RuntimeException('Signing with the wrong private key');
497
            }
498
        } else {
499
            throw new \RuntimeException('Cannot sign unknown script type');
500
        }
501
    }
502
503
    /**
504
     * @param PrivateKeyInterface $key
505
     * @param int $sigHashType
506
     * @return bool
507
     */
508
    public function sign(PrivateKeyInterface $key, $sigHashType = SigHash::ALL)
509
    {
510
        $solution = $this->scriptPubKey;
511
        $sigVersion = SigHash::V0;
512
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
513
            $solution = $this->redeemScript;
514
        }
515
516
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
517
            $solution = $this->witnessKeyHash;
518
            $sigVersion = SigHash::V1;
519
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
520
            $solution = $this->witnessScript;
521
            $sigVersion = SigHash::V1;
522
        }
523
524
        if ($solution->canSign()) {
525
            $this->doSignature($key, $solution, $sigHashType, $sigVersion);
526
            return true;
527
        }
528
529
        return false;
530
    }
531
532
    /**
533
     * @param string $outputType
534
     * @return BufferInterface[]
535
     */
536
    private function serializeSolution($outputType)
537
    {
538
        if ($outputType === OutputClassifier::PAYTOPUBKEY) {
539
            return [$this->signatures[0]->getBuffer()];
540
        } else if ($outputType === OutputClassifier::PAYTOPUBKEYHASH) {
541
            return [$this->signatures[0]->getBuffer(), $this->publicKeys[0]->getBuffer()];
542
        } else if ($outputType === OutputClassifier::MULTISIG) {
543
            $sequence = [new Buffer()];
544
            for ($i = 0, $nPubKeys = count($this->publicKeys); $i < $nPubKeys; $i++) {
545
                if (isset($this->signatures[$i])) {
546
                    $sequence[] = $this->signatures[$i]->getBuffer();
547
                }
548
            }
549
550
            return $sequence;
551
        } else {
552
            throw new \RuntimeException('Cannot serialize this script sig');
553
        }
554
    }
555
556
    /**
557
     * @return SigValues
558
     */
559
    public function serializeSignatures()
560
    {
561
        static $emptyScript = null;
562
        static $emptyWitness = null;
563
        if (is_null($emptyScript) || is_null($emptyWitness)) {
564
            $emptyScript = new Script();
565
            $emptyWitness = new ScriptWitness([]);
566
        }
567
568
        $scriptSigChunks = [];
569
        $witness = [];
570
        if ($this->scriptPubKey->canSign()) {
571
            $scriptSigChunks = $this->serializeSolution($this->scriptPubKey->getType());
572
        }
573
574
        $solution = $this->scriptPubKey;
575
        $p2sh = false;
576
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
577
            $p2sh = true;
578
            if ($this->redeemScript->canSign()) {
579
                $scriptSigChunks = $this->serializeSolution($this->redeemScript->getType());
580
            }
581
            $solution = $this->redeemScript;
582
        }
583
584
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
585
            $witness = $this->serializeSolution(OutputClassifier::PAYTOPUBKEYHASH);
586
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
587
            if ($this->witnessScript->canSign()) {
588
                $witness = $this->serializeSolution($this->witnessScript->getType());
589
                $witness[] = $this->witnessScript->getScript()->getBuffer();
590
            }
591
        }
592
593
        if ($p2sh) {
594
            $scriptSigChunks[] = $this->redeemScript->getScript()->getBuffer();
595
        }
596
597
        return new SigValues($this->pushAll($scriptSigChunks), new ScriptWitness($witness));
598
    }
599
}
600