Completed
Pull Request — master (#391)
by thomas
214:44 queued 212:05
created

InputSigner::calculateSigHash()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 10
ccs 6
cts 6
cp 1
crap 2
rs 9.4285
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 string $type
181
     * @param ScriptInterface $scriptCode
182
     * @param BufferInterface[] $stack
183
     * @param int $sigVersion
184
     * @return string
185
     */
186 84
    public function extractFromValues($type, ScriptInterface $scriptCode, array $stack, $sigVersion)
187
    {
188 84
        $size = count($stack);
189 84
        if ($type === OutputClassifier::PAYTOPUBKEYHASH) {
190 42
            $this->requiredSigs = 1;
191 42
            if ($size === 2) {
192 24
                if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
193
                    throw new \RuntimeException('Existing signatures are invalid!');
194
                }
195 24
                $this->signatures = [TransactionSignatureFactory::fromHex($stack[0], $this->ecAdapter)];
196 24
                $this->publicKeys = [PublicKeyFactory::fromHex($stack[1], $this->ecAdapter)];
197 8
            }
198 14
        }
199
200 84
        if ($type === OutputClassifier::PAYTOPUBKEY && count($stack) === 1) {
201 6
            $this->requiredSigs = 1;
202 6
            if ($size === 1) {
203 6
                if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
204
                    throw new \RuntimeException('Existing signatures are invalid!');
205
                }
206 6
                $this->signatures = [TransactionSignatureFactory::fromHex($stack[0], $this->ecAdapter)];
207 2
            }
208 2
        }
209
210 84
        if ($type === OutputClassifier::MULTISIG) {
211 24
            $info = new Multisig($scriptCode);
212 24
            $this->requiredSigs = $info->getRequiredSigCount();
213 24
            $this->publicKeys = $info->getKeys();
214
215 24
            if ($size > 1) {
216 24
                $vars = [];
217 24
                for ($i = 1, $j = $size - 1; $i < $j; $i++) {
218
                    $vars[] = TransactionSignatureFactory::fromHex($stack[$i], $this->ecAdapter);
219
                }
220
221 24
                $sigs = $this->sortMultiSigs($sigVersion, $vars, $scriptCode);
222 24
                foreach ($this->publicKeys as $idx => $key) {
223 24
                    $this->signatures[$idx] = isset($sigs[$key]) ? $sigs[$key]->getBuffer() : null;
224 8
                }
225
226 24
                if (count(array_filter($this->signatures, 'is_null')) === count($this->publicKeys)) {
227 24
                    if (!$this->evaluateSolution($scriptCode, $stack, $sigVersion)) {
228
                        throw new \RuntimeException('Existing signatures are invalid!');
229
                    }
230 8
                }
231 8
            }
232 8
        }
233
234 84
        return $type;
235
    }
236
237
    /**
238
     * This function must throw an exception whenever execution
239
     * has not yielded a signable script.
240
     *
241
     * @param SignData $signData
242
     * @return $this
243
     * @throws \Exception
244
     */
245 84
    private function solve(SignData $signData)
246
    {
247 84
        $scriptPubKey = $this->txOut->getScript();
248 84
        $solution = $this->scriptPubKey = $this->classifier->decode($scriptPubKey);
249 84
        if ($solution->getType() === OutputClassifier::UNKNOWN) {
250
            throw new \RuntimeException('scriptPubKey type is unknown');
251 12
        }
252
253 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
254 24
            $redeemScript = $signData->getRedeemScript();
255 24
            if (!$this->verifySolution(ScriptFactory::sequence([$redeemScript->getBuffer()]), $solution->getScript())) {
256
                throw new \Exception('Redeem script fails to solve pay-to-script-hash');
257
            }
258 24
            $solution = $this->redeemScript = $this->classifier->decode($redeemScript);
259 24
            if (!in_array($solution->getType(), self::$validP2sh)) {
260
                throw new \Exception('Unsupported pay-to-script-hash script');
261
            }
262 8
        }
263
264 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
265 12
            $this->witnessKeyHash = $this->classifier->decode(ScriptFactory::scriptPubKey()->payToPubKeyHashFromHash($solution->getSolution()));
266 76
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
267 18
            $witnessScript = $signData->getWitnessScript();
268 18
            if (!$this->verifySolution(ScriptFactory::sequence([$witnessScript->getBuffer()]), $solution->getScript())) {
269
                throw new \Exception('Witness script fails to solve witness-script-hash');
270
            }
271 18
            $this->witnessScript = $this->classifier->decode($witnessScript);
272 18
            if (!in_array($this->witnessScript->getType(), self::$canSign)) {
273
                throw new \Exception('Unsupported witness-script-hash script');
274
            }
275 6
        }
276
277 84
        return $this;
278
    }
279
280
    /**
281
     * @param ScriptInterface $scriptSig
282
     * @param ScriptInterface $scriptPubKey
283
     * @param ScriptWitnessInterface|null $scriptWitness
284
     * @return bool
285
     */
286 36
    private function verifySolution(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, ScriptWitnessInterface $scriptWitness = null)
287
    {
288 36
        return $this->interpreter->verify($scriptSig, $scriptPubKey, $this->flags, $this->signatureChecker, $scriptWitness);
289
    }
290
291
    /**
292
     * @param ScriptInterface $scriptPubKey
293
     * @param array $chunks
294
     * @param int $sigVersion
295
     * @return bool
296
     */
297 54
    private function evaluateSolution(ScriptInterface $scriptPubKey, array $chunks, $sigVersion)
298
    {
299 54
        return $this->interpreter->evaluate($scriptPubKey, new Stack($chunks), $sigVersion, $this->flags, $this->signatureChecker);
300
    }
301
302
    /**
303
     * @return $this
304
     */
305 84
    public function extractSignatures()
306
    {
307 84
        $scriptSig = $this->tx->getInput($this->nInput)->getScript();
308 84
        $witnesses = $this->tx->getWitnesses();
309 84
        $witness = isset($witnesses[$this->nInput]) ? $witnesses[$this->nInput]->all() : [];
310
311 84
        $solution = $this->scriptPubKey;
312 84
        $sigVersion = SigHash::V0;
313 84
        $chunks = [];
314
315 84
        if ($solution->canSign()) {
316 42
            $chunks = $this->evalPushOnly($scriptSig);
317 14
        }
318
319 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
320 24
            $chunks = $this->evalPushOnly($scriptSig);
321 24
            if (count($chunks) > 0) {
322 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...
323
                    throw new \RuntimeException('Extracted redeemScript did not match script-hash');
324
                }
325
326 18
                $solution = $this->redeemScript;
327 18
                $chunks = array_slice($chunks, 0, -1);
328 6
            }
329 8
        }
330
331 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
332 12
            $solution = $this->witnessKeyHash;
333 12
            $sigVersion = SigHash::V1;
334 12
            $chunks = $witness;
335 80
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
336 18
            if (count($witness) > 0) {
337 18
                if (!end($witness)->equals($this->witnessScript->getScript()->getBuffer())) {
338
                    throw new \RuntimeException('Extracted witnessScript did not match witness-script-hash');
339
                }
340
341 18
                $solution = $this->witnessScript;
342 18
                $sigVersion = SigHash::V1;
343 18
                $chunks = array_slice($witness, 0, -1);
344 6
            }
345 6
        }
346
347 84
        $this->extractFromValues($solution->getType(), $solution->getScript(), $chunks, $sigVersion);
348
349 84
        return $this;
350
    }
351
352
    /**
353
     * @param ScriptInterface $scriptCode
354
     * @param int $sigHashType
355
     * @param int $sigVersion
356
     * @return BufferInterface
357
     */
358 84
    public function calculateSigHash(ScriptInterface $scriptCode, $sigHashType, $sigVersion)
359
    {
360 84
        if ($sigVersion === SigHash::V1) {
361 30
            $hasher = new V1Hasher($this->tx, $this->txOut->getValue());
362 10
        } else {
363 54
            $hasher = new Hasher($this->tx);
364
        }
365
366 84
        return $hasher->calculate($scriptCode, $this->nInput, $sigHashType);
367
    }
368
369
    /**
370
     * @param PrivateKeyInterface $key
371
     * @param ScriptInterface $scriptCode
372
     * @param int $sigHashType
373
     * @param int $sigVersion
374
     * @return TransactionSignature
375
     */
376 84
    public function calculateSignature(PrivateKeyInterface $key, ScriptInterface $scriptCode, $sigHashType, $sigVersion)
377
    {
378 84
        $hash = $this->calculateSigHash($scriptCode, $sigHashType, $sigVersion);
379 84
        $ecSignature = $this->ecAdapter->sign($hash, $key, new Rfc6979($this->ecAdapter, $key, $hash, 'sha256'));
380 84
        return new TransactionSignature($this->ecAdapter, $ecSignature, $sigHashType);
381
    }
382
383
    /**
384
     * @return bool
385
     */
386 54
    public function isFullySigned()
387
    {
388 54
        return $this->requiredSigs !== 0 && $this->requiredSigs === count($this->signatures);
389
    }
390
391
    /**
392
     * The function only returns true when $scriptPubKey could be classified
393
     *
394
     * @param PrivateKeyInterface $key
395
     * @param OutputData $solution
396
     * @param int $sigHashType
397
     * @param int $sigVersion
398
     */
399 84
    private function doSignature(PrivateKeyInterface $key, OutputData $solution, $sigHashType, $sigVersion = SigHash::V0)
400
    {
401 84
        if ($solution->getType() === OutputClassifier::PAYTOPUBKEY) {
402 12
            if (!$key->getPublicKey()->getBuffer()->equals($solution->getSolution())) {
403
                throw new \RuntimeException('Signing with the wrong private key');
404
            }
405 12
            $this->signatures[0] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
406 12
            $this->publicKeys[0] = $key->getPublicKey();
407 12
            $this->requiredSigs = 1;
408 76
        } else if ($solution->getType() === OutputClassifier::PAYTOPUBKEYHASH) {
409 42
            if (!$key->getPubKeyHash()->equals($solution->getSolution())) {
410
                throw new \RuntimeException('Signing with the wrong private key');
411
            }
412 42
            $this->signatures[0] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
413 42
            $this->publicKeys[0] = $key->getPublicKey();
414 42
            $this->requiredSigs = 1;
415 44
        } else if ($solution->getType() === OutputClassifier::MULTISIG) {
416 30
            $info = new Multisig($solution->getScript());
417 30
            $this->publicKeys = $info->getKeys();
418 30
            $this->requiredSigs = $info->getRequiredSigCount();
419
420 30
            $myKey = $key->getPublicKey()->getBuffer();
421 30
            $signed = false;
422 30
            foreach ($info->getKeys() as $keyIdx => $publicKey) {
423 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...
424 30
                    $this->signatures[$keyIdx] = $this->calculateSignature($key, $solution->getScript(), $sigHashType, $sigVersion);
425 30
                    $signed = true;
426 10
                }
427 10
            }
428
429 30
            if (!$signed) {
430 20
                throw new \RuntimeException('Signing with the wrong private key');
431
            }
432 10
        } else {
433
            throw new \RuntimeException('Cannot sign unknown script type');
434
        }
435 84
    }
436
437
    /**
438
     * @param PrivateKeyInterface $key
439
     * @param int $sigHashType
440
     * @return bool
441
     */
442 84
    public function sign(PrivateKeyInterface $key, $sigHashType = SigHash::ALL)
443
    {
444 84
        $solution = $this->scriptPubKey;
445 84
        $sigVersion = SigHash::V0;
446 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
447 24
            $solution = $this->redeemScript;
448 8
        }
449
450 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
451 12
            $solution = $this->witnessKeyHash;
452 12
            $sigVersion = SigHash::V1;
453 76
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
454 18
            $solution = $this->witnessScript;
455 18
            $sigVersion = SigHash::V1;
456 6
        }
457
458 84
        if ($solution->canSign()) {
459 84
            $this->doSignature($key, $solution, $sigHashType, $sigVersion);
460 84
            return true;
461
        }
462
463
        return false;
464
    }
465
466
    /**
467
     * @param string $outputType
468
     * @return BufferInterface[]
469
     */
470 84
    private function serializeSolution($outputType)
471
    {
472 84
        if ($outputType === OutputClassifier::PAYTOPUBKEY) {
473 12
            return [$this->signatures[0]->getBuffer()];
474 72
        } else if ($outputType === OutputClassifier::PAYTOPUBKEYHASH) {
475 42
            return [$this->signatures[0]->getBuffer(), $this->publicKeys[0]->getBuffer()];
476 30
        } else if ($outputType === OutputClassifier::MULTISIG) {
477 30
            $sequence = [new Buffer()];
478 30
            for ($i = 0, $nPubKeys = count($this->publicKeys); $i < $nPubKeys; $i++) {
479 30
                if (isset($this->signatures[$i])) {
480 30
                    $sequence[] = $this->signatures[$i]->getBuffer();
481 10
                }
482 10
            }
483
484 30
            return $sequence;
485
        } else {
486
            throw new \RuntimeException('Cannot serialize this script sig');
487
        }
488
    }
489
490
    /**
491
     * @param ScriptInterface $script
492
     * @return \BitWasp\Buffertools\BufferInterface[]
493
     */
494 66
    private function evalPushOnly(ScriptInterface $script)
495
    {
496 66
        $stack = new Stack();
497 66
        $interpreter = new Interpreter();
498 66
        $interpreter->evaluate($script, $stack, SigHash::V0, $this->flags | Interpreter::VERIFY_SIGPUSHONLY, new Checker($this->ecAdapter, $this->tx, $this->nInput, $this->txOut->getValue()));
499 66
        return $stack->all();
500
    }
501
502
    /**
503
     * @param BufferInterface[] $buffers
504
     * @return ScriptInterface
505
     */
506
    public function pushAll(array $buffers)
507
    {
508 84
        return ScriptFactory::sequence(array_map(function ($buffer) {
509 66
            if (!($buffer instanceof BufferInterface)) {
510
                throw new \RuntimeException('Script contained a non-push opcode');
511
            }
512
513 66
            $size = $buffer->getSize();
514 66
            if ($size === 0) {
515 18
                return Opcodes::OP_0;
516
            }
517
518 66
            $first = ord($buffer->getBinary());
519 66
            if ($size === 1 && $first >= 1 && $first <= 16) {
520
                return \BitWasp\Bitcoin\Script\encodeOpN($first);
521
            } else {
522 66
                return $buffer;
523
            }
524 84
        }, $buffers));
525
    }
526
527
    /**
528
     * @return SigValues
529
     */
530 84
    public function serializeSignatures()
531
    {
532 84
        static $emptyScript = null;
533 84
        static $emptyWitness = null;
534 84
        if (is_null($emptyScript) || is_null($emptyWitness)) {
535 6
            $emptyScript = new Script();
536 6
            $emptyWitness = new ScriptWitness([]);
537 2
        }
538
539 84
        $scriptSigChunks = [];
540 84
        $witness = [];
541 84
        if ($this->scriptPubKey->canSign()) {
542 42
            $scriptSigChunks = $this->serializeSolution($this->scriptPubKey->getType());
543 14
        }
544
545 84
        $solution = $this->scriptPubKey;
546 84
        $p2sh = false;
547 84
        if ($solution->getType() === OutputClassifier::PAYTOSCRIPTHASH) {
548 24
            $p2sh = true;
549 24
            if ($this->redeemScript->canSign()) {
550 12
                $scriptSigChunks = $this->serializeSolution($this->redeemScript->getType());
551 4
            }
552 24
            $solution = $this->redeemScript;
553 8
        }
554
555 84
        if ($solution->getType() === OutputClassifier::WITNESS_V0_KEYHASH) {
556 12
            $witness = $this->serializeSolution(OutputClassifier::PAYTOPUBKEYHASH);
557 76
        } else if ($solution->getType() === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
558 18
            if ($this->witnessScript->canSign()) {
559 18
                $witness = $this->serializeSolution($this->witnessScript->getType());
560 18
                $witness[] = $this->witnessScript->getScript()->getBuffer();
561 6
            }
562 6
        }
563
564 84
        if ($p2sh) {
565 24
            $scriptSigChunks[] = $this->redeemScript->getScript()->getBuffer();
566 8
        }
567
568 84
        return new SigValues($this->pushAll($scriptSigChunks), new ScriptWitness($witness));
569
    }
570
}
571