Completed
Push — master ( aa7198...3fd737 )
by thomas
31:45
created

InputSigner::isFullySigned()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 2
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 4
ccs 2
cts 2
cp 1
crap 2
rs 10
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\Factory;
4
5
use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface;
6
use BitWasp\Bitcoin\Crypto\EcAdapter\EcSerializer;
7
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PrivateKeyInterface;
8
use BitWasp\Bitcoin\Crypto\EcAdapter\Key\PublicKeyInterface;
9
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Key\PublicKeySerializerInterface;
10
use BitWasp\Bitcoin\Crypto\EcAdapter\Serializer\Signature\DerSignatureSerializerInterface;
11
use BitWasp\Bitcoin\Crypto\Random\Rfc6979;
12
use BitWasp\Bitcoin\Script\Classifier\OutputClassifier;
13
use BitWasp\Bitcoin\Script\Classifier\OutputData;
14
use BitWasp\Bitcoin\Script\Interpreter\Checker;
15
use BitWasp\Bitcoin\Script\Interpreter\Interpreter;
16
use BitWasp\Bitcoin\Script\Interpreter\Stack;
17
use BitWasp\Bitcoin\Script\Opcodes;
18
use BitWasp\Bitcoin\Script\Script;
19
use BitWasp\Bitcoin\Script\ScriptFactory;
20
use BitWasp\Bitcoin\Script\ScriptInfo\Multisig;
21
use BitWasp\Bitcoin\Script\ScriptInterface;
22
use BitWasp\Bitcoin\Script\ScriptType;
23
use BitWasp\Bitcoin\Script\ScriptWitness;
24
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
25
use BitWasp\Bitcoin\Serializer\Signature\TransactionSignatureSerializer;
26
use BitWasp\Bitcoin\Signature\TransactionSignature;
27
use BitWasp\Bitcoin\Signature\TransactionSignatureInterface;
28
use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash;
29
use BitWasp\Bitcoin\Transaction\TransactionFactory;
30
use BitWasp\Bitcoin\Transaction\TransactionInterface;
31
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface;
32
use BitWasp\Buffertools\Buffer;
33
use BitWasp\Buffertools\BufferInterface;
34
35
class InputSigner
36
{
37
    /**
38
     * @var array
39
     */
40
    protected static $canSign = [
41
        ScriptType::P2PKH,
42
        ScriptType::P2PK,
43
        ScriptType::MULTISIG
44
    ];
45
46
    /**
47
     * @var array
48
     */
49
    protected static $validP2sh = [
50
        ScriptType::P2WKH,
51
        ScriptType::P2WSH,
52
        ScriptType::P2PKH,
53
        ScriptType::P2PK,
54
        ScriptType::MULTISIG
55
    ];
56
57
    /**
58
     * @var EcAdapterInterface
59
     */
60
    private $ecAdapter;
61
62
    /**
63
     * @var OutputData $scriptPubKey
64
     */
65
    private $scriptPubKey;
66
67
    /**
68
     * @var OutputData $redeemScript
69
     */
70
    private $redeemScript;
71
72
    /**
73
     * @var OutputData $witnessScript
74
     */
75
    private $witnessScript;
76
77
    /**
78
     * @var OutputData
79
     */
80
    private $signScript;
81
82
    /**
83
     * @var int
84
     */
85
    private $sigVersion;
86
87
    /**
88
     * @var OutputData $witnessKeyHash
89
     */
90
    private $witnessKeyHash;
91
92
    /**
93
     * @var TransactionInterface
94
     */
95
    private $tx;
96
97
    /**
98
     * @var int
99
     */
100
    private $nInput;
101
102
    /**
103
     * @var TransactionOutputInterface
104
     */
105
    private $txOut;
106
107
    /**
108
     * @var PublicKeyInterface[]
109
     */
110
    private $publicKeys = [];
111
112
    /**
113
     * @var TransactionSignatureInterface[]
114
     */
115
    private $signatures = [];
116
117
    /**
118
     * @var int
119
     */
120
    private $requiredSigs = 0;
121
122
    /**
123
     * @var Interpreter
124
     */
125
    private $interpreter;
126
127
    /**
128
     * @var Checker
129
     */
130
    private $signatureChecker;
131
132
    /**
133
     * InputSigner constructor.
134
     * @param EcAdapterInterface $ecAdapter
135
     * @param TransactionInterface $tx
136
     * @param $nInput
137
     * @param TransactionOutputInterface $txOut
138
     * @param SignData $signData
139
     * @param TransactionSignatureSerializer|null $sigSerializer
140
     * @param PublicKeySerializerInterface|null $pubKeySerializer
141
     */
142 88
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $tx, $nInput, TransactionOutputInterface $txOut, SignData $signData, TransactionSignatureSerializer $sigSerializer = null, PublicKeySerializerInterface $pubKeySerializer = null)
143
    {
144 88
        $inputs = $tx->getInputs();
145 88
        if (!isset($inputs[$nInput])) {
146 2
            throw new \RuntimeException('No input at this index');
147
        }
148
149 86
        $this->ecAdapter = $ecAdapter;
150 86
        $this->tx = $tx;
151 86
        $this->nInput = $nInput;
152 86
        $this->txOut = $txOut;
153 86
        $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...
154 86
        $this->publicKeys = [];
155 86
        $this->signatures = [];
156
157 86
        $this->txSigSerializer = $sigSerializer ?: new TransactionSignatureSerializer(EcSerializer::getSerializer(DerSignatureSerializerInterface::class, true, $ecAdapter));
0 ignored issues
show
Bug introduced by
The property txSigSerializer 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...
158 86
        $this->pubKeySerializer = $pubKeySerializer ?: EcSerializer::getSerializer(PublicKeySerializerInterface::class, true, $ecAdapter);
0 ignored issues
show
Bug introduced by
The property pubKeySerializer 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...
159 86
        $this->signatureChecker = new Checker($this->ecAdapter, $this->tx, $nInput, $txOut->getValue(), $this->txSigSerializer, $this->pubKeySerializer);
160 86
        $this->interpreter = new Interpreter($this->ecAdapter);
161
162 86
        $this->solve($signData, $txOut->getScript(), $inputs[$nInput]->getScript(), isset($tx->getWitnesses()[$nInput]) ? $tx->getWitnesses()[$nInput]->all() : []);
163 58
    }
164
165
    /**
166
     * A snipped from OP_CHECKMULTISIG - verifies signatures according to the
167
     * order of the given public keys (taken from the script).
168
     *
169
     * @param ScriptInterface $script
170
     * @param BufferInterface[] $signatures
171
     * @param BufferInterface[] $publicKeys
172
     * @param int $sigVersion
173
     * @return \SplObjectStorage
174
     */
175 10
    private function sortMultisigs(ScriptInterface $script, array $signatures, array $publicKeys, $sigVersion)
176
    {
177 10
        $sigCount = count($signatures);
178 10
        $keyCount = count($publicKeys);
179 10
        $ikey = $isig = 0;
180 10
        $fSuccess = true;
181 10
        $result = new \SplObjectStorage;
182
183 10
        while ($fSuccess && $sigCount > 0) {
184
            // Fetch the signature and public key
185 10
            $sig = $signatures[$isig];
186 10
            $pubkey = $publicKeys[$ikey];
187
188 10
            if ($this->signatureChecker->checkSig($script, $sig, $pubkey, $sigVersion, $this->flags)) {
189 10
                $result[$pubkey] = $sig;
190 10
                $isig++;
191 10
                $sigCount--;
192
            }
193
194 10
            $ikey++;
195 10
            $keyCount--;
196
197
            // If there are more signatures left than keys left,
198
            // then too many signatures have failed. Exit early,
199
            // without checking any further signatures.
200 10
            if ($sigCount > $keyCount) {
201
                $fSuccess = false;
202
            }
203
        }
204
205 10
        return $result;
206
    }
207
208
    /**
209
     * @param ScriptInterface $script
210
     * @return \BitWasp\Buffertools\BufferInterface[]
211
     */
212 68
    private function evalPushOnly(ScriptInterface $script)
213
    {
214 68
        $stack = new Stack();
215 68
        $this->interpreter->evaluate($script, $stack, SigHash::V0, $this->flags | Interpreter::VERIFY_SIGPUSHONLY, $this->signatureChecker);
216 68
        return $stack->all();
217
    }
218
219
    /**
220
     * Create a script consisting only of push-data operations.
221
     * Suitable for a scriptSig.
222
     *
223
     * @param BufferInterface[] $buffers
224
     * @return ScriptInterface
225
     */
226
    private function pushAll(array $buffers)
227
    {
228 50
        return ScriptFactory::sequence(array_map(function ($buffer) {
229 42
            if (!($buffer instanceof BufferInterface)) {
230
                throw new \RuntimeException('Script contained a non-push opcode');
231
            }
232
233 42
            $size = $buffer->getSize();
234 42
            if ($size === 0) {
235 4
                return Opcodes::OP_0;
236
            }
237
238 42
            $first = ord($buffer->getBinary());
239 42
            if ($size === 1 && $first >= 1 && $first <= 16) {
240
                return \BitWasp\Bitcoin\Script\encodeOpN($first);
241
            } else {
242 42
                return $buffer;
243
            }
244 50
        }, $buffers));
245
    }
246
247
    /**
248
     * Verify a scriptSig / scriptWitness against a scriptPubKey.
249
     * Useful for checking the outcome of certain things, like hash locks (p2sh)
250
     *
251
     * @param int $flags
252
     * @param ScriptInterface $scriptSig
253
     * @param ScriptInterface $scriptPubKey
254
     * @param ScriptWitnessInterface|null $scriptWitness
255
     * @return bool
256
     */
257 26
    private function verifySolution($flags, ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, ScriptWitnessInterface $scriptWitness = null)
258
    {
259 26
        return $this->interpreter->verify($scriptSig, $scriptPubKey, $flags, $this->signatureChecker, $scriptWitness);
260
    }
261
262
    /**
263
     * Evaluates a scriptPubKey against the provided chunks.
264
     *
265
     * @param ScriptInterface $scriptPubKey
266
     * @param array $chunks
267
     * @param int $sigVersion
268
     * @return bool
269
     */
270 56
    private function evaluateSolution(ScriptInterface $scriptPubKey, array $chunks, $sigVersion)
271
    {
272 56
        $stack = new Stack($chunks);
273 56
        if (!$this->interpreter->evaluate($scriptPubKey, $stack, $sigVersion, $this->flags, $this->signatureChecker)) {
274 2
            return false;
275
        }
276
277 54
        if ($stack->isEmpty()) {
278
            return false;
279
        }
280
281 54
        if (false === $this->interpreter->castToBool($stack[-1])) {
282 4
            return false;
283
        }
284
285 50
        return true;
286
    }
287
288
    /**
289
     * This function is strictly for $canSign types.
290
     * It will extract signatures/publicKeys when given $outputData, and $stack.
291
     * $stack is the result of decompiling a scriptSig, or taking the witness data.
292
     *
293
     * @param OutputData $outputData
294
     * @param array $stack
295
     * @param int $sigVersion
296
     * @return string
297
     */
298 64
    public function extractFromValues(OutputData $outputData, array $stack, $sigVersion)
299
    {
300 64
        $type = $outputData->getType();
301 64
        $size = count($stack);
302
303 64
        if (ScriptType::P2PKH === $type) {
304 38
            $this->requiredSigs = 1;
305 38
            if ($size === 2) {
306 34
                if (!$this->evaluateSolution($outputData->getScript(), $stack, $sigVersion)) {
307 2
                    throw new \RuntimeException('Existing signatures are invalid!');
308
                }
309 32
                $this->signatures = [$this->txSigSerializer->parse($stack[0])];
310 36
                $this->publicKeys = [$this->pubKeySerializer->parse($stack[1])];
311
            }
312 28
        } else if (ScriptType::P2PK === $type) {
313 14
            $this->requiredSigs = 1;
314 14
            if ($size === 1) {
315 12
                if (!$this->evaluateSolution($outputData->getScript(), $stack, $sigVersion)) {
316 2
                    throw new \RuntimeException('Existing signatures are invalid!');
317
                }
318 10
                $this->signatures = [$this->txSigSerializer->parse($stack[0])];
319
            }
320 12
            $this->publicKeys = [$this->pubKeySerializer->parse($outputData->getSolution())];
321 14
        } else if (ScriptType::MULTISIG === $type) {
322 14
            $info = new Multisig($outputData->getScript(), $this->pubKeySerializer);
323 14
            $this->requiredSigs = $info->getRequiredSigCount();
324 14
            $this->publicKeys = $info->getKeys();
325 14
            $keyBufs = $info->getKeyBuffers();
326 14
            if ($size > 1) {
327
                // Check signatures irrespective of scriptSig size, primes Checker cache, and need info
328 12
                $check = $this->evaluateSolution($outputData->getScript(), $stack, $sigVersion);
329 12
                $sigBufs = array_slice($stack, 1, $size - 1);
330 12
                $sigBufCount = count($sigBufs);
331
332
                // If we seem to have all signatures but fail evaluation, abort
333 12
                if ($sigBufCount === $this->requiredSigs && !$check) {
334 2
                    throw new \RuntimeException('Existing signatures are invalid!');
335
                }
336
337 10
                $keyToSigMap = $this->sortMultiSigs($outputData->getScript(), $sigBufs, $keyBufs, $sigVersion);
338
339
                // Here we learn if any signatures were invalid, it won't be in the map.
340 10
                if ($sigBufCount !== count($keyToSigMap)) {
341
                    throw new \RuntimeException('Existing signatures are invalid!');
342
                }
343
344 10
                foreach ($keyBufs as $idx => $key) {
345 10
                    if (isset($keyToSigMap[$key])) {
346 12
                        $this->signatures[$idx] = $this->txSigSerializer->parse($keyToSigMap[$key]);
347
                    }
348
                }
349
            }
350
        } else {
351
            throw new \RuntimeException('Unsupported output type passed to extractFromValues');
352
        }
353
354 58
        return $type;
355
    }
356
357
    /**
358
     * Checks $chunks (a decompiled scriptSig) for it's last element,
359
     * or defers to SignData. If both are provided, it checks the
360
     * value from $chunks against SignData.
361
     *
362
     * @param BufferInterface[] $chunks
363
     * @param SignData $signData
364
     * @return ScriptInterface
365
     */
366 30
    private function findRedeemScript(array $chunks, SignData $signData)
367
    {
368 30
        if (count($chunks) > 0) {
369 22
            $redeemScript = new Script($chunks[count($chunks) - 1]);
370 22
            if ($signData->hasRedeemScript()) {
371 22
                if (!$redeemScript->equals($signData->getRedeemScript())) {
372 22
                    throw new \RuntimeException('Extracted redeemScript did not match sign data');
373
                }
374
            }
375
        } else {
376 26
            if (!$signData->hasRedeemScript()) {
377 2
                throw new \RuntimeException('Redeem script not provided in sign data or scriptSig');
378
            }
379 24
            $redeemScript = $signData->getRedeemScript();
380
        }
381
382 26
        return $redeemScript;
383
    }
384
385
    /**
386
     * Checks $witness (a witness structure) for it's last element,
387
     * or defers to SignData. If both are provided, it checks the
388
     * value from $chunks against SignData.
389
     *
390
     * @param BufferInterface[] $witness
391
     * @param SignData $signData
392
     * @return ScriptInterface
393
     */
394 26
    private function findWitnessScript(array $witness, SignData $signData)
395
    {
396 26
        if (count($witness) > 0) {
397 18
            $witnessScript = new Script($witness[count($witness) - 1]);
398 18
            if ($signData->hasWitnessScript()) {
399 18
                if (!$witnessScript->equals($signData->getWitnessScript())) {
400 18
                    throw new \RuntimeException('Extracted witnessScript did not match sign data');
401
                }
402
            }
403
        } else {
404 22
            if (!$signData->hasWitnessScript()) {
405 4
                throw new \RuntimeException('Witness script not provided in sign data or witness');
406
            }
407 18
            $witnessScript = $signData->getWitnessScript();
408
        }
409
410 18
        return $witnessScript;
411
    }
412
413
    /**
414
     * Called upon instance creation.
415
     *
416
     * It ensures that violating the following prevents instance creation
417
     *  - the scriptPubKey can be directly signed, or leads to P2SH/P2WSH/P2WKH
418
     *  - the P2SH script covers signable types and P2WSH/P2WKH
419
     *  - the witnessScript covers signable types only
420
     *
421
     * @param SignData $signData
422
     * @param ScriptInterface $scriptPubKey
423
     * @param ScriptInterface $scriptSig
424
     * @param BufferInterface[] $witness
425
     * @return $this
426
     */
427 86
    private function solve(SignData $signData, ScriptInterface $scriptPubKey, ScriptInterface $scriptSig, array $witness)
428
    {
429 86
        $classifier = new OutputClassifier();
430 86
        $sigVersion = SigHash::V0;
431 86
        $sigChunks = [];
432 86
        $solution = $this->scriptPubKey = $classifier->decode($scriptPubKey);
433 86
        if ($solution->getType() !== ScriptType::P2SH && !in_array($solution->getType(), self::$validP2sh)) {
434 2
            throw new \RuntimeException('scriptPubKey not supported');
435
        }
436
437 84
        if ($solution->canSign()) {
438 38
            $sigChunks = $this->evalPushOnly($scriptSig);
439
        }
440
441 84
        if ($solution->getType() === ScriptType::P2SH) {
442 30
            $chunks = $this->evalPushOnly($scriptSig);
443 30
            $redeemScript = $this->findRedeemScript($chunks, $signData);
444 26
            if (!$this->verifySolution(Interpreter::VERIFY_SIGPUSHONLY, ScriptFactory::sequence([$redeemScript->getBuffer()]), $solution->getScript())) {
445 2
                throw new \RuntimeException('Redeem script fails to solve pay-to-script-hash');
446
            }
447
448 24
            $solution = $this->redeemScript = $classifier->decode($redeemScript);
449 24
            if (!in_array($solution->getType(), self::$validP2sh)) {
450 2
                throw new \RuntimeException('Unsupported pay-to-script-hash script');
451
            }
452
453 22
            $sigChunks = array_slice($chunks, 0, -1);
454
        }
455
456 76
        if ($solution->getType() === ScriptType::P2WKH) {
457 8
            $sigVersion = SigHash::V1;
458 8
            $solution = $this->witnessKeyHash = $classifier->decode(ScriptFactory::scriptPubKey()->payToPubKeyHash($solution->getSolution()));
459 8
            $sigChunks = $witness;
460 70
        } else if ($solution->getType() === ScriptType::P2WSH) {
461 26
            $sigVersion = SigHash::V1;
462 26
            $witnessScript = $this->findWitnessScript($witness, $signData);
463
464
            // Essentially all the reference implementation does
465 18
            if (!$witnessScript->getWitnessScriptHash()->equals($solution->getSolution())) {
466 2
                throw new \RuntimeException('Witness script fails to solve witness-script-hash');
467
            }
468
469 16
            $solution = $this->witnessScript = $classifier->decode($witnessScript);
470 16
            if (!in_array($this->witnessScript->getType(), self::$canSign)) {
471 2
                throw new \RuntimeException('Unsupported witness-script-hash script');
472
            }
473
474 14
            $sigChunks = array_slice($witness, 0, -1);
475
        }
476
477 64
        $this->sigVersion = $sigVersion;
478 64
        $this->signScript = $solution;
479
480 64
        $this->extractFromValues($solution, $sigChunks, $this->sigVersion);
481
482 58
        return $this;
483
    }
484
485
    /**
486
     * @param ScriptInterface $scriptCode
487
     * @param int $sigHashType
488
     * @param int $sigVersion
489
     * @return BufferInterface
490
     */
491 52
    public function calculateSigHashUnsafe(ScriptInterface $scriptCode, $sigHashType, $sigVersion)
492
    {
493 52
        if (!$this->signatureChecker->isDefinedHashtype($sigHashType)) {
494 2
            throw new \RuntimeException('Invalid sigHashType requested');
495
        }
496
497 50
        return $this->signatureChecker->getSigHash($scriptCode, $sigHashType, $sigVersion);
498
    }
499
500
    /**
501
     * @param int $sigHashType
502
     * @return BufferInterface
503
     */
504 2
    public function getSigHash($sigHashType)
505
    {
506 2
        return $this->calculateSigHashUnsafe($this->signScript->getScript(), $sigHashType, $this->sigVersion);
507
    }
508
509
    /**
510
     * @param PrivateKeyInterface $key
511
     * @param ScriptInterface $scriptCode
512
     * @param int $sigHashType
513
     * @param int $sigVersion
514
     * @return TransactionSignatureInterface
515
     */
516 50
    private function calculateSignature(PrivateKeyInterface $key, ScriptInterface $scriptCode, $sigHashType, $sigVersion)
517
    {
518 50
        $hash = $this->calculateSigHashUnsafe($scriptCode, $sigHashType, $sigVersion);
519 50
        $ecSignature = $this->ecAdapter->sign($hash, $key, new Rfc6979($this->ecAdapter, $key, $hash, 'sha256'));
520 50
        return new TransactionSignature($this->ecAdapter, $ecSignature, $sigHashType);
521
    }
522
523
    /**
524
     * @return bool
525
     */
526 56
    public function isFullySigned()
527
    {
528 56
        return $this->requiredSigs !== 0 && $this->requiredSigs === count($this->signatures);
529
    }
530
531
    /**
532
     * @return int
533
     */
534 50
    public function getRequiredSigs()
535
    {
536 50
        return $this->requiredSigs;
537
    }
538
539
    /**
540
     * @return TransactionSignatureInterface[]
541
     */
542 50
    public function getSignatures()
543
    {
544 50
        return $this->signatures;
545
    }
546
547
    /**
548
     * @return PublicKeyInterface[]
549
     */
550 50
    public function getPublicKeys()
551
    {
552 50
        return $this->publicKeys;
553
    }
554
555
    /**
556
     * Sign the input using $key and $sigHashTypes
557
     *
558
     * @param PrivateKeyInterface $key
559
     * @param int $sigHashType
560
     * @return $this
561
     */
562 56
    public function sign(PrivateKeyInterface $key, $sigHashType = SigHash::ALL)
563
    {
564 56
        if ($this->isFullySigned()) {
565
            return $this;
566
        }
567
568 56
        if (SigHash::V1 === $this->sigVersion && !$key->isCompressed()) {
569
            throw new \RuntimeException('Uncompressed keys are disallowed in segwit scripts - refusing to sign');
570
        }
571
572 56
        if ($this->signScript->getType() === ScriptType::P2PK) {
573 12
            if (!$this->pubKeySerializer->serialize($key->getPublicKey())->equals($this->signScript->getSolution())) {
574 2
                throw new \RuntimeException('Signing with the wrong private key');
575
            }
576 10
            $this->signatures[0] = $this->calculateSignature($key, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
577 46
        } else if ($this->signScript->getType() === ScriptType::P2PKH) {
578 34
            if (!$key->getPubKeyHash($this->pubKeySerializer)->equals($this->signScript->getSolution())) {
579 2
                throw new \RuntimeException('Signing with the wrong private key');
580
            }
581 32
            $this->signatures[0] = $this->calculateSignature($key, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
582 32
            $this->publicKeys[0] = $key->getPublicKey();
583 12
        } else if ($this->signScript->getType() === ScriptType::MULTISIG) {
584 12
            $signed = false;
585 12
            foreach ($this->publicKeys as $keyIdx => $publicKey) {
586 12
                if ($key->getPublicKey()->equals($publicKey)) {
587 10
                    $this->signatures[$keyIdx] = $this->calculateSignature($key, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
588 12
                    $signed = true;
589
                }
590
            }
591
592 12
            if (!$signed) {
593 12
                throw new \RuntimeException('Signing with the wrong private key');
594
            }
595
        } else {
596
            throw new \RuntimeException('Unexpected error - sign script had an unexpected type');
597
        }
598
599 50
        return $this;
600
    }
601
602
    /**
603
     * Verifies the input using $flags for script verification
604
     *
605
     * @param int $flags
606
     * @return bool
607
     */
608 50
    public function verify($flags = null)
609
    {
610 50
        $consensus = ScriptFactory::consensus();
611
612 50
        if ($flags === null) {
613 50
            $flags = $this->flags;
614
        }
615
616 50
        $flags |= Interpreter::VERIFY_P2SH;
617 50
        if (SigHash::V1 === $this->sigVersion) {
618 22
            $flags |= Interpreter::VERIFY_WITNESS;
619
        }
620
621 50
        $sig = $this->serializeSignatures();
622
623
        // Take serialized signatures, and use mutator to add this inputs sig data
624 50
        $mutator = TransactionFactory::mutate($this->tx);
625 50
        $mutator->inputsMutator()[$this->nInput]->script($sig->getScriptSig());
626
627 50
        if (SigHash::V1 === $this->sigVersion) {
628 22
            $witness = [];
629 22
            for ($i = 0, $j = count($this->tx->getInputs()); $i < $j; $i++) {
630 22
                if ($i === $this->nInput) {
631 22
                    $witness[] = $sig->getScriptWitness();
632
                } else {
633 2
                    $witness[] = new ScriptWitness([]);
634
                }
635
            }
636
637 22
            $mutator->witness($witness);
638
        }
639
640 50
        return $consensus->verify($mutator->done(), $this->txOut->getScript(), $flags, $this->nInput, $this->txOut->getValue());
641
    }
642
643
    /**
644
     * Produces the script stack that solves the $outputType
645
     *
646
     * @param string $outputType
647
     * @return BufferInterface[]
648
     */
649 50
    private function serializeSolution($outputType)
650
    {
651 50
        $result = [];
652 50
        if (ScriptType::P2PK === $outputType) {
653 10
            if (count($this->signatures) === 1) {
654 10
                $result = [$this->txSigSerializer->serialize($this->signatures[0])];
655
            }
656 42
        } else if (ScriptType::P2PKH === $outputType) {
657 32
            if (count($this->signatures) === 1 && count($this->publicKeys) === 1) {
658 32
                $result = [$this->txSigSerializer->serialize($this->signatures[0]), $this->pubKeySerializer->serialize($this->publicKeys[0])];
659
            }
660 10
        } else if (ScriptType::MULTISIG === $outputType) {
661 10
            $result[] = new Buffer();
662 10
            for ($i = 0, $nPubKeys = count($this->publicKeys); $i < $nPubKeys; $i++) {
663 10
                if (isset($this->signatures[$i])) {
664 10
                    $result[] = $this->txSigSerializer->serialize($this->signatures[$i]);
665
                }
666
            }
667
        } else {
668
            throw new \RuntimeException('Parameter 0 for serializeSolution was a non-standard input type');
669
        }
670
671 50
        return $result;
672
    }
673
674
    /**
675
     * Produces a SigValues instance containing the scriptSig & script witness
676
     *
677
     * @return SigValues
678
     */
679 50
    public function serializeSignatures()
680
    {
681 50
        static $emptyScript = null;
682 50
        static $emptyWitness = null;
683 50
        if (is_null($emptyScript) || is_null($emptyWitness)) {
684 2
            $emptyScript = new Script();
685 2
            $emptyWitness = new ScriptWitness([]);
686
        }
687
688 50
        $scriptSigChunks = [];
689 50
        $witness = [];
690 50
        if ($this->scriptPubKey->canSign()) {
691 24
            $scriptSigChunks = $this->serializeSolution($this->scriptPubKey->getType());
692
        }
693
694 50
        $solution = $this->scriptPubKey;
695 50
        $p2sh = false;
696 50
        if ($solution->getType() === ScriptType::P2SH) {
697 18
            $p2sh = true;
698 18
            if ($this->redeemScript->canSign()) {
699 6
                $scriptSigChunks = $this->serializeSolution($this->redeemScript->getType());
700
            }
701 18
            $solution = $this->redeemScript;
702
        }
703
704 50
        if ($solution->getType() === ScriptType::P2WKH) {
705 8
            $witness = $this->serializeSolution(ScriptType::P2PKH);
706 44
        } else if ($solution->getType() === ScriptType::P2WSH) {
707 14
            if ($this->witnessScript->canSign()) {
708 14
                $witness = $this->serializeSolution($this->witnessScript->getType());
709 14
                $witness[] = $this->witnessScript->getScript()->getBuffer();
710
            }
711
        }
712
713 50
        if ($p2sh) {
714 18
            $scriptSigChunks[] = $this->redeemScript->getScript()->getBuffer();
715
        }
716
717 50
        return new SigValues($this->pushAll($scriptSigChunks), new ScriptWitness($witness));
718
    }
719
}
720