Completed
Pull Request — master (#523)
by thomas
73:16
created

InputSigner::padUnsignedMultisigs()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
ccs 0
cts 0
cp 0
crap 2
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\Factory;
4
5
use BitWasp\Bitcoin\Crypto\EcAdapter\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\BitcoinCashChecker;
15
use BitWasp\Bitcoin\Script\Interpreter\Checker;
16
use BitWasp\Bitcoin\Script\Interpreter\Interpreter;
17
use BitWasp\Bitcoin\Script\Interpreter\Stack;
18
use BitWasp\Bitcoin\Script\Opcodes;
19
use BitWasp\Bitcoin\Script\Script;
20
use BitWasp\Bitcoin\Script\ScriptFactory;
21
use BitWasp\Bitcoin\Script\ScriptInfo\Multisig;
22
use BitWasp\Bitcoin\Script\ScriptInterface;
23
use BitWasp\Bitcoin\Script\ScriptType;
24
use BitWasp\Bitcoin\Script\ScriptWitness;
25
use BitWasp\Bitcoin\Script\ScriptWitnessInterface;
26
use BitWasp\Bitcoin\Serializer\Signature\TransactionSignatureSerializer;
27
use BitWasp\Bitcoin\Signature\TransactionSignature;
28
use BitWasp\Bitcoin\Signature\TransactionSignatureInterface;
29
use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash;
30
use BitWasp\Bitcoin\Transaction\TransactionFactory;
31
use BitWasp\Bitcoin\Transaction\TransactionInterface;
32
use BitWasp\Bitcoin\Transaction\TransactionOutputInterface;
33
use BitWasp\Buffertools\Buffer;
34
use BitWasp\Buffertools\BufferInterface;
35
36
class InputSigner implements InputSignerInterface
37
{
38
    /**
39
     * @var array
40
     */
41
    protected static $canSign = [
42
        ScriptType::P2PKH,
43
        ScriptType::P2PK,
44
        ScriptType::MULTISIG
45
    ];
46
47
    /**
48
     * @var array
49
     */
50
    protected static $validP2sh = [
51
        ScriptType::P2WKH,
52
        ScriptType::P2WSH,
53
        ScriptType::P2PKH,
54
        ScriptType::P2PK,
55
        ScriptType::MULTISIG
56
    ];
57
58
    /**
59
     * @var EcAdapterInterface
60
     */
61
    private $ecAdapter;
62
63
    /**
64
     * @var OutputData $scriptPubKey
65
     */
66
    private $scriptPubKey;
67
68
    /**
69
     * @var OutputData $redeemScript
70
     */
71
    private $redeemScript;
72
73
    /**
74
     * @var OutputData $witnessScript
75
     */
76
    private $witnessScript;
77
78
    /**
79
     * @var OutputData
80
     */
81
    private $signScript;
82
83
    /**
84
     * @var bool
85
     */
86
    private $padUnsignedMultisigs = false;
87
88
    /**
89
     * @var bool
90
     */
91
    private $tolerateInvalidPublicKey = false;
92
93
    /**
94
     * @var bool
95
     */
96
    private $redeemBitcoinCash = false;
97
98
    /**
99
     * @var SignData
100
     */
101
    private $signData;
102
103
    /**
104
     * @var int
105
     */
106
    private $sigVersion;
107
108
    /**
109
     * @var int
110
     */
111
    private $flags;
112
113
    /**
114
     * @var OutputData $witnessKeyHash
115
     */
116
    private $witnessKeyHash;
117
118
    /**
119
     * @var TransactionInterface
120
     */
121
    private $tx;
122
123
    /**
124
     * @var int
125
     */
126
    private $nInput;
127
128
    /**
129
     * @var TransactionOutputInterface
130
     */
131
    private $txOut;
132
133
    /**
134
     * @var PublicKeyInterface[]
135
     */
136
    private $publicKeys = [];
137
138
    /**
139
     * @var TransactionSignatureInterface[]
140
     */
141
    private $signatures = [];
142
143
    /**
144
     * @var int
145
     */
146
    private $requiredSigs = 0;
147
148
    /**
149
     * @var Interpreter
150
     */
151
    private $interpreter;
152
153
    /**
154
     * @var Checker
155
     */
156
    private $signatureChecker;
157
158
    /**
159
     * @var TransactionSignatureSerializer
160
     */
161
    private $txSigSerializer;
162
163
    /**
164
     * @var PublicKeySerializerInterface
165
     */
166
    private $pubKeySerializer;
167
168
    /**
169
     * InputSigner constructor.
170
     *
171
     * Note, the implementation of this class is considered internal
172
     * and only the methods exposed on InputSignerInterface should
173
     * be depended on to avoid BC breaks.
174
     *
175
     * The only recommended way to produce this class is using Signer::input()
176
     *
177
     * @param EcAdapterInterface $ecAdapter
178
     * @param TransactionInterface $tx
179
     * @param int $nInput
180 96
     * @param TransactionOutputInterface $txOut
181
     * @param SignData $signData
182 96
     * @param TransactionSignatureSerializer|null $sigSerializer
183 96
     * @param PublicKeySerializerInterface|null $pubKeySerializer
184 96
     */
185 96
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $tx, $nInput, TransactionOutputInterface $txOut, SignData $signData, TransactionSignatureSerializer $sigSerializer = null, PublicKeySerializerInterface $pubKeySerializer = null)
186 96
    {
187 96
        $this->ecAdapter = $ecAdapter;
188 96
        $this->tx = $tx;
189
        $this->nInput = $nInput;
190 96
        $this->txOut = $txOut;
191 96
        $this->signData = $signData;
192 96
        $this->publicKeys = [];
193 96
        $this->signatures = [];
194
195
        $this->txSigSerializer = $sigSerializer ?: new TransactionSignatureSerializer(EcSerializer::getSerializer(DerSignatureSerializerInterface::class, true, $ecAdapter));
196
        $this->pubKeySerializer = $pubKeySerializer ?: EcSerializer::getSerializer(PublicKeySerializerInterface::class, true, $ecAdapter);
197
        $this->interpreter = new Interpreter($this->ecAdapter);
198 96
    }
199
200 96
    /**
201 96
     * @return InputSigner
202
     */
203 96
    public function extract()
204
    {
205 2
        $defaultFlags = Interpreter::VERIFY_DERSIG | Interpreter::VERIFY_P2SH | Interpreter::VERIFY_CHECKLOCKTIMEVERIFY | Interpreter::VERIFY_CHECKSEQUENCEVERIFY | Interpreter::VERIFY_WITNESS;
206
        $checker = new Checker($this->ecAdapter, $this->tx, $this->nInput, $this->txOut->getValue(), $this->txSigSerializer, $this->pubKeySerializer);
207 2
208
        if ($this->redeemBitcoinCash) {
209
            // unset VERIFY_WITNESS default
210
            $defaultFlags = $defaultFlags & (~Interpreter::VERIFY_WITNESS);
211
212
            if ($this->signData->hasSignaturePolicy()) {
213 2
                if ($this->signData->getSignaturePolicy() & Interpreter::VERIFY_WITNESS) {
214
                    throw new \RuntimeException("VERIFY_WITNESS is not possible for bitcoin cash");
215
                }
216 96
            }
217 96
218
            $checker = new BitcoinCashChecker($this->ecAdapter, $this->tx, $this->nInput, $this->txOut->getValue(), $this->txSigSerializer, $this->pubKeySerializer);
219 96
        }
220 96
221
        $this->flags = $this->signData->hasSignaturePolicy() ? $this->signData->getSignaturePolicy() : $defaultFlags;
222 96
        $this->signatureChecker = $checker;
223 96
224 96
        $witnesses = $this->tx->getWitnesses();
225 96
        $witness = array_key_exists($this->nInput, $witnesses) ? $witnesses[$this->nInput]->all() : [];
226
227
        return $this->solve(
228
            $this->signData,
229
            $this->txOut->getScript(),
230
            $this->tx->getInput($this->nInput)->getScript(),
231
            $witness
232
        );
233
    }
234 66
235
    /**
236 66
     * @param bool $setting
237 66
     * @return $this
238
     */
239
    public function padUnsignedMultisigs($setting)
240
    {
241
        $this->padUnsignedMultisigs = (bool) $setting;
242
        return $this;
243
    }
244 66
245
    /**
246 66
     * @param bool $setting
247 66
     * @return $this
248
     */
249
    public function tolerateInvalidPublicKey($setting)
250
    {
251
        $this->tolerateInvalidPublicKey = (bool) $setting;
252
        return $this;
253
    }
254
255 64
    /**
256
     * @param bool $setting
257
     * @return $this
258 64
     */
259 6
    public function redeemBitcoinCash($setting)
260 6
    {
261 2
        $this->redeemBitcoinCash = (bool) $setting;
262
        return $this;
263
    }
264 4
265
    /**
266
     * @param BufferInterface $vchPubKey
267
     * @return PublicKeyInterface|null
268
     * @throws \Exception
269
     */
270
    protected function parseStepPublicKey(BufferInterface $vchPubKey)
271
    {
272
        try {
273
            return $this->pubKeySerializer->parse($vchPubKey);
274
        } catch (\Exception $e) {
275
            if ($this->tolerateInvalidPublicKey) {
276
                return null;
277
            }
278 10
279
            throw $e;
280 10
        }
281 10
    }
282 10
283 10
    /**
284 10
     * A snipped from OP_CHECKMULTISIG - verifies signatures according to the
285
     * order of the given public keys (taken from the script).
286 10
     *
287
     * @param ScriptInterface $script
288 10
     * @param BufferInterface[] $signatures
289 10
     * @param BufferInterface[] $publicKeys
290
     * @param int $sigVersion
291 10
     * @return \SplObjectStorage
292 10
     */
293 10
    private function sortMultisigs(ScriptInterface $script, array $signatures, array $publicKeys, $sigVersion)
294 10
    {
295
        $sigCount = count($signatures);
296
        $keyCount = count($publicKeys);
297 10
        $ikey = $isig = 0;
298 10
        $fSuccess = true;
299
        $result = new \SplObjectStorage;
300
301
        while ($fSuccess && $sigCount > 0) {
302
            // Fetch the signature and public key
303 10
            $sig = $signatures[$isig];
304
            $pubkey = $publicKeys[$ikey];
305
306
            if ($this->signatureChecker->checkSig($script, $sig, $pubkey, $sigVersion, $this->flags)) {
307
                $result[$pubkey] = $sig;
308 10
                $isig++;
309
                $sigCount--;
310
            }
311
312
            $ikey++;
313
            $keyCount--;
314
315 76
            // If there are more signatures left than keys left,
316
            // then too many signatures have failed. Exit early,
317 76
            // without checking any further signatures.
318 76
            if ($sigCount > $keyCount) {
319 76
                $fSuccess = false;
320
            }
321
        }
322
323
        return $result;
324
    }
325
326
    /**
327
     * @param ScriptInterface $script
328
     * @return \BitWasp\Buffertools\BufferInterface[]
329
     */
330
    private function evalPushOnly(ScriptInterface $script)
331 52
    {
332 44
        $stack = new Stack();
333
        $this->interpreter->evaluate($script, $stack, SigHash::V0, $this->flags | Interpreter::VERIFY_SIGPUSHONLY, $this->signatureChecker);
334
        return $stack->all();
335
    }
336 44
337 44
    /**
338 4
     * Create a script consisting only of push-data operations.
339
     * Suitable for a scriptSig.
340
     *
341 44
     * @param BufferInterface[] $buffers
342 44
     * @return ScriptInterface
343
     */
344
    private function pushAll(array $buffers)
345 44
    {
346
        return ScriptFactory::sequence(array_map(function ($buffer) {
347 52
            if (!($buffer instanceof BufferInterface)) {
348
                throw new \RuntimeException('Script contained a non-push opcode');
349
            }
350
351
            $size = $buffer->getSize();
352
            if ($size === 0) {
353
                return Opcodes::OP_0;
354
            }
355
356
            $first = ord($buffer->getBinary());
357
            if ($size === 1 && $first >= 1 && $first <= 16) {
358
                return \BitWasp\Bitcoin\Script\encodeOpN($first);
359
            } else {
360 26
                return $buffer;
361
            }
362 26
        }, $buffers));
363
    }
364
365
    /**
366
     * Verify a scriptSig / scriptWitness against a scriptPubKey.
367
     * Useful for checking the outcome of certain things, like hash locks (p2sh)
368
     *
369
     * @param int $flags
370
     * @param ScriptInterface $scriptSig
371
     * @param ScriptInterface $scriptPubKey
372
     * @param ScriptWitnessInterface|null $scriptWitness
373 56
     * @return bool
374
     */
375 56
    private function verifySolution($flags, ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, ScriptWitnessInterface $scriptWitness = null)
376 56
    {
377 2
        return $this->interpreter->verify($scriptSig, $scriptPubKey, $flags, $this->signatureChecker, $scriptWitness);
378
    }
379
380 54
    /**
381
     * Evaluates a scriptPubKey against the provided chunks.
382
     *
383
     * @param ScriptInterface $scriptPubKey
384 54
     * @param array $chunks
385 4
     * @param int $sigVersion
386
     * @return bool
387
     */
388 50
    private function evaluateSolution(ScriptInterface $scriptPubKey, array $chunks, $sigVersion)
389
    {
390
        $stack = new Stack($chunks);
391
        if (!$this->interpreter->evaluate($scriptPubKey, $stack, $sigVersion, $this->flags, $this->signatureChecker)) {
392
            return false;
393
        }
394
395
        if ($stack->isEmpty()) {
396
            return false;
397
        }
398
399
        if (false === $this->interpreter->castToBool($stack[-1])) {
400
            return false;
401 72
        }
402
403 72
        return true;
404 72
    }
405
406 72
    /**
407 38
     * This function is strictly for $canSign types.
408 38
     * It will extract signatures/publicKeys when given $outputData, and $stack.
409 34
     * $stack is the result of decompiling a scriptSig, or taking the witness data.
410 2
     *
411
     * @param OutputData $outputData
412 32
     * @param array $stack
413 36
     * @param int $sigVersion
414
     * @return string
415 36
     */
416 16
    public function extractFromValues(OutputData $outputData, array $stack, $sigVersion)
417 16
    {
418 12
        $type = $outputData->getType();
419 2
        $size = count($stack);
420
421 10
        if (ScriptType::P2PKH === $type) {
422
            $this->requiredSigs = 1;
423 14
            if ($size === 2) {
424 20
                if (!$this->evaluateSolution($outputData->getScript(), $stack, $sigVersion)) {
425 20
                    throw new \RuntimeException('Existing signatures are invalid!');
426 20
                }
427
                $this->signatures = [$this->txSigSerializer->parse($stack[0])];
428 20
                $this->publicKeys = [$this->parseStepPublicKey($stack[1])];
0 ignored issues
show
Documentation Bug introduced by
It seems like array($this->parseStepPublicKey($stack[1])) of type array<integer,object<Bit...icKeyInterface>|null"}> is incompatible with the declared type array<integer,object<Bit...ey\PublicKeyInterface>> of property $publicKeys.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
429 20
            }
430 20
        } else if (ScriptType::P2PK === $type) {
431 20
            $this->requiredSigs = 1;
432
            if ($size === 1) {
433
                if (!$this->evaluateSolution($outputData->getScript(), $stack, $sigVersion)) {
434 16
                    throw new \RuntimeException('Existing signatures are invalid!');
435
                }
436 12
                $this->signatures = [$this->txSigSerializer->parse($stack[0])];
437 12
            }
438 12
            $this->publicKeys = [$this->parseStepPublicKey($outputData->getSolution())];
0 ignored issues
show
Documentation Bug introduced by
It seems like array($this->parseStepPu...utData->getSolution())) of type array<integer,object<Bit...icKeyInterface>|null"}> is incompatible with the declared type array<integer,object<Bit...ey\PublicKeyInterface>> of property $publicKeys.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
439
        } else if (ScriptType::MULTISIG === $type) {
440
            $info = new Multisig($outputData->getScript(), $this->pubKeySerializer);
441 12
442 2
            $keyBuffers = $info->getKeyBuffers();
443
            $this->requiredSigs = $info->getRequiredSigCount();
444
            $this->publicKeys = [];
445 10
            for ($i = 0; $i < $info->getKeyCount(); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
446
                $this->publicKeys[$i] = $this->parseStepPublicKey($keyBuffers[$i]);
447
            }
448 10
449
            if ($this->padUnsignedMultisigs) {
450
                // Multisig padding is only used for partially signed transactions,
451
                // never fully signed. It is recognized by a scriptSig with $keyCount+1
452 10
                // values (including the dummy), with one for each candidate signature,
453 10
                // such that $this->signatures state is captured.
454 14
                // The feature serves to skip validation/sorting an incomplete multisig.
455
456
                if ($size === 1 + $info->getKeyCount()) {
457
                    $sigBufCount = 0;
458
                    $null = new Buffer();
459
                    $keyToSigMap = new \SplObjectStorage();
460
461
                    // Reproduce $keyToSigMap and $sigBufCount
462 62
                    for ($i = 0; $i < $info->getKeyCount(); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
Consider avoiding function calls on each iteration of the for loop.

If you have a function call in the test part of a for loop, this function is executed on each iteration. Often such a function, can be moved to the initialization part and be cached.

// count() is called on each iteration
for ($i=0; $i < count($collection); $i++) { }

// count() is only called once
for ($i=0, $c=count($collection); $i<$c; $i++) { }
Loading history...
463
                        if (!$stack[1 + $i]->equals($null)) {
464
                            $keyToSigMap[$keyBuffers[$i]] = $stack[1 + $i];
465
                            $sigBufCount++;
466
                        }
467
                    }
468
469
                    // We observed $this->requiredSigs sigs, therefore we can
470
                    // say the implementation is incompatible
471
                    if ($sigBufCount === $this->requiredSigs) {
472
                        throw new \RuntimeException("Padding is forbidden for a fully signed multisig script");
473
                    }
474 30
                }
475
            }
476 30
477 22
            if (!isset($keyToSigMap)) {
478 22
                // Check signatures irrespective of scriptSig size, primes Checker for sorting
479 22
                $check = $this->evaluateSolution($outputData->getScript(), $stack, $sigVersion);
480 22
481
                $sigBufs = array_slice($stack, 1, $size - 1);
482
                $sigBufCount = count($sigBufs);
483
484 26
                // If we seem to have all signatures but fail evaluation, abort
485 2
                if ($sigBufCount === $this->requiredSigs && !$check) {
486
                    throw new \RuntimeException('Existing signatures are invalid!');
487 24
                }
488
489
                if ($sigBufCount > 0) {
490 26
                    $keyToSigMap = $this->sortMultiSigs($outputData->getScript(), $sigBufs, $keyBuffers, $sigVersion);
491
492
                    if (count($keyToSigMap) !== $sigBufCount) {
493
                        throw new \RuntimeException('Existing signatures are invalid!');
494
                    }
495
                } else {
496
                    $keyToSigMap = new \SplObjectStorage();
497
                }
498
            }
499
500
            foreach ($keyBuffers as $idx => $key) {
501
                if (isset($keyToSigMap[$key])) {
502 26
                    $this->signatures[$idx] = $this->txSigSerializer->parse($keyToSigMap[$key]);
503
                }
504 26
            }
505 18
        } else {
506 18
            throw new \RuntimeException('Unsupported output type passed to extractFromValues');
507 18
        }
508 18
509
        return $type;
510
    }
511
512 22
    /**
513 4
     * Checks $chunks (a decompiled scriptSig) for it's last element,
514
     * or defers to SignData. If both are provided, it checks the
515 18
     * value from $chunks against SignData.
516
     *
517
     * @param BufferInterface[] $chunks
518 18
     * @param SignData $signData
519
     * @return ScriptInterface
520
     */
521
    private function findRedeemScript(array $chunks, SignData $signData)
522
    {
523
        if (count($chunks) > 0) {
524
            $redeemScript = new Script($chunks[count($chunks) - 1]);
525
            if ($signData->hasRedeemScript()) {
526
                if (!$redeemScript->equals($signData->getRedeemScript())) {
527
                    throw new \RuntimeException('Extracted redeemScript did not match sign data');
528
                }
529
            }
530
        } else {
531
            if (!$signData->hasRedeemScript()) {
532
                throw new \RuntimeException('Redeem script not provided in sign data or scriptSig');
533
            }
534
            $redeemScript = $signData->getRedeemScript();
535 94
        }
536
537 94
        return $redeemScript;
538 94
    }
539 94
540 94
    /**
541 94
     * Checks $witness (a witness structure) for it's last element,
542 2
     * or defers to SignData. If both are provided, it checks the
543
     * value from $chunks against SignData.
544
     *
545 92
     * @param BufferInterface[] $witness
546 46
     * @param SignData $signData
547
     * @return ScriptInterface
548
     */
549 92
    private function findWitnessScript(array $witness, SignData $signData)
550 30
    {
551 30
        if (count($witness) > 0) {
552 26
            $witnessScript = new Script($witness[count($witness) - 1]);
553 2
            if ($signData->hasWitnessScript()) {
554
                if (!$witnessScript->equals($signData->getWitnessScript())) {
555
                    throw new \RuntimeException('Extracted witnessScript did not match sign data');
556 24
                }
557 24
            }
558 2
        } else {
559
            if (!$signData->hasWitnessScript()) {
560
                throw new \RuntimeException('Witness script not provided in sign data or witness');
561 22
            }
562
            $witnessScript = $signData->getWitnessScript();
563
        }
564 84
565 8
        return $witnessScript;
566 8
    }
567 8
568 78
    /**
569 26
     * Needs to be called before using the instance. By `extract`.
570 26
     *
571
     * It ensures that violating the following prevents instance creation
572
     *  - the scriptPubKey can be directly signed, or leads to P2SH/P2WSH/P2WKH
573 18
     *  - the P2SH script covers signable types and P2WSH/P2WKH
574 2
     *  - the witnessScript covers signable types only
575
     *
576
     * @param SignData $signData
577 16
     * @param ScriptInterface $scriptPubKey
578 16
     * @param ScriptInterface $scriptSig
579 2
     * @param BufferInterface[] $witness
580
     * @return $this
581
     */
582 14
    private function solve(SignData $signData, ScriptInterface $scriptPubKey, ScriptInterface $scriptSig, array $witness)
583
    {
584
        $classifier = new OutputClassifier();
585 72
        $sigVersion = SigHash::V0;
586 72
        $sigChunks = [];
587
        $solution = $this->scriptPubKey = $classifier->decode($scriptPubKey);
588 72
        if ($solution->getType() !== ScriptType::P2SH && !in_array($solution->getType(), self::$validP2sh)) {
589
            throw new \RuntimeException('scriptPubKey not supported');
590 62
        }
591
592
        if ($solution->canSign()) {
593
            $sigChunks = $this->evalPushOnly($scriptSig);
594
        }
595
596
        if ($solution->getType() === ScriptType::P2SH) {
597
            $chunks = $this->evalPushOnly($scriptSig);
598
            $redeemScript = $this->findRedeemScript($chunks, $signData);
599
            if (!$this->verifySolution(Interpreter::VERIFY_SIGPUSHONLY, ScriptFactory::sequence([$redeemScript->getBuffer()]), $solution->getScript())) {
600
                throw new \RuntimeException('Redeem script fails to solve pay-to-script-hash');
601 54
            }
602
603 54
            $solution = $this->redeemScript = $classifier->decode($redeemScript);
604 2
            if (!in_array($solution->getType(), self::$validP2sh)) {
605
                throw new \RuntimeException('Unsupported pay-to-script-hash script');
606
            }
607 52
608
            $sigChunks = array_slice($chunks, 0, -1);
609
        }
610
611
        if ($solution->getType() === ScriptType::P2WKH) {
612
            $sigVersion = SigHash::V1;
613
            $solution = $this->witnessKeyHash = $classifier->decode(ScriptFactory::scriptPubKey()->payToPubKeyHash($solution->getSolution()));
614
            $sigChunks = $witness;
615
        } else if ($solution->getType() === ScriptType::P2WSH) {
616 2
            $sigVersion = SigHash::V1;
617
            $witnessScript = $this->findWitnessScript($witness, $signData);
618 2
619
            // Essentially all the reference implementation does
620
            if (!$witnessScript->getWitnessScriptHash()->equals($solution->getSolution())) {
621
                throw new \RuntimeException('Witness script fails to solve witness-script-hash');
622
            }
623
624
            $solution = $this->witnessScript = $classifier->decode($witnessScript);
625
            if (!in_array($this->witnessScript->getType(), self::$canSign)) {
626
                throw new \RuntimeException('Unsupported witness-script-hash script');
627
            }
628
629
            $sigChunks = array_slice($witness, 0, -1);
630 52
        }
631
632 52
        $this->sigVersion = $sigVersion;
633 52
        $this->signScript = $solution;
634 52
635
        $this->extractFromValues($solution, $sigChunks, $this->sigVersion);
636
637
        return $this;
638
    }
639
640
    /**
641
     * Pure function to produce a signature hash for a given $scriptCode, $sigHashType, $sigVersion.
642 58
     *
643
     * @param ScriptInterface $scriptCode
644 58
     * @param int $sigHashType
645
     * @param int $sigVersion
646
     * @return BufferInterface
647
     */
648
    public function calculateSigHashUnsafe(ScriptInterface $scriptCode, $sigHashType, $sigVersion)
649
    {
650
        if (!$this->signatureChecker->isDefinedHashtype($sigHashType)) {
651
            throw new \RuntimeException('Invalid sigHashType requested');
652 50
        }
653
654 50
        return $this->signatureChecker->getSigHash($scriptCode, $sigHashType, $sigVersion);
655
    }
656
657
    /**
658
     * Calculates the signature hash for the input for the given $sigHashType.
659
     *
660
     * @param int $sigHashType
661
     * @return BufferInterface
662
     */
663 50
    public function getSigHash($sigHashType)
664
    {
665 50
        return $this->calculateSigHashUnsafe($this->signScript->getScript(), $sigHashType, $this->sigVersion);
666
    }
667
668
    /**
669
     * Pure function to produce a signature for a given $key, $scriptCode, $sigHashType, $sigVersion.
670
     *
671
     * @param PrivateKeyInterface $key
672
     * @param ScriptInterface $scriptCode
673
     * @param int $sigHashType
674 52
     * @param int $sigVersion
675
     * @return TransactionSignatureInterface
676 52
     */
677
    private function calculateSignature(PrivateKeyInterface $key, ScriptInterface $scriptCode, $sigHashType, $sigVersion)
678
    {
679
        $hash = $this->calculateSigHashUnsafe($scriptCode, $sigHashType, $sigVersion);
680
        $ecSignature = $this->ecAdapter->sign($hash, $key, new Rfc6979($this->ecAdapter, $key, $hash, 'sha256'));
681
        return new TransactionSignature($this->ecAdapter, $ecSignature, $sigHashType);
682
    }
683
684
    /**
685
     * Returns whether all required signatures have been provided.
686 50
     *
687
     * @return bool
688 50
     */
689
    public function isFullySigned()
690
    {
691
        return $this->requiredSigs !== 0 && $this->requiredSigs === count($this->signatures);
692
    }
693
694
    /**
695
     * Returns the required number of signatures for this input.
696 24
     *
697
     * @return int
698 24
     */
699
    public function getRequiredSigs()
700
    {
701
        return $this->requiredSigs;
702
    }
703
704
    /**
705
     * Returns an array where the values are either null,
706 18
     * or a TransactionSignatureInterface.
707
     *
708 18
     * @return TransactionSignatureInterface[]
709
     */
710
    public function getSignatures()
711
    {
712 18
        return $this->signatures;
713
    }
714
715
    /**
716
     * Returns an array where the values are either null,
717
     * or a PublicKeyInterface.
718
     *
719
     * @return PublicKeyInterface[]
720 14
     */
721
    public function getPublicKeys()
722 14
    {
723
        return $this->publicKeys;
724
    }
725
726 14
    /**
727
     * OutputData for the script to be signed (will be
728
     * equal to getScriptPubKey, or getRedeemScript, or
729
     * getWitnessScript.
730
     *
731
     * @return OutputData
732
     */
733
    public function getSignScript()
734 50
    {
735
        return $this->signScript;
736 50
    }
737 18
738
    /**
739
     * OutputData for the txOut script.
740 32
     *
741
     * @return OutputData
742
     */
743
    public function getScriptPubKey()
744
    {
745
        return $this->scriptPubKey;
746
    }
747
748 50
    /**
749
     * Returns OutputData for the P2SH redeemScript.
750 50
     *
751 18
     * @return OutputData
752 8
     */
753
    public function getRedeemScript()
754
    {
755
        if (null === $this->redeemScript) {
756 42
            throw new \RuntimeException("Input has no redeemScript, cannot call getRedeemScript");
757 6
        }
758
759
        return $this->redeemScript;
760 36
    }
761
762
    /**
763
     * Returns OutputData for the P2WSH witnessScript.
764
     *
765
     * @return OutputData
766
     */
767
    public function getWitnessScript()
768
    {
769
        if (null === $this->witnessScript) {
770 58
            throw new \RuntimeException("Input has no witnessScript, cannot call getWitnessScript");
771
        }
772 58
773
        return $this->witnessScript;
774
    }
775
776 58
    /**
777
     * Returns whether the scriptPubKey is P2SH.
778
     *
779
     * @return bool
780 58
     */
781 14
    public function isP2SH()
782 2
    {
783
        if ($this->scriptPubKey->getType() === ScriptType::P2SH && ($this->redeemScript instanceof OutputData)) {
784 12
            return true;
785 46
        }
786 34
787 34
        return false;
788 2
    }
789
790
    /**
791 32
     * Returns whether the scriptPubKey or redeemScript is P2WSH.
792 32
     *
793
     * @return bool
794
     */
795 32
    public function isP2WSH()
796 12
    {
797 12
        if ($this->redeemScript instanceof OutputData) {
798 12
            if ($this->redeemScript->getType() === ScriptType::P2WSH && ($this->witnessScript instanceof OutputData)) {
799 12
                return true;
800 12
            }
801 10
        }
802 12
803
        if ($this->scriptPubKey->getType() === ScriptType::P2WSH && ($this->witnessScript instanceof OutputData)) {
804
            return true;
805
        }
806
807 12
        return false;
808 12
    }
809
810
    /**
811
     * Sign the input using $key and $sigHashTypes
812
     *
813
     * @param PrivateKeyInterface $privateKey
814 52
     * @param int $sigHashType
815
     * @return $this
816
     */
817
    public function sign(PrivateKeyInterface $privateKey, $sigHashType = SigHash::ALL)
818
    {
819
        if ($this->isFullySigned()) {
820
            return $this;
821
        }
822
823 50
        if (SigHash::V1 === $this->sigVersion && !$privateKey->isCompressed()) {
824
            throw new \RuntimeException('Uncompressed keys are disallowed in segwit scripts - refusing to sign');
825 50
        }
826
827 50
        if ($this->signScript->getType() === ScriptType::P2PK) {
828 50
            if (!$this->pubKeySerializer->serialize($privateKey->getPublicKey())->equals($this->signScript->getSolution())) {
829
                throw new \RuntimeException('Signing with the wrong private key');
830
            }
831 50
            $this->signatures[0] = $this->calculateSignature($privateKey, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
832 50
        } else if ($this->signScript->getType() === ScriptType::P2PKH) {
833 22
            $publicKey = $privateKey->getPublicKey();
834
            if (!$publicKey->getPubKeyHash()->equals($this->signScript->getSolution())) {
835
                throw new \RuntimeException('Signing with the wrong private key');
836 50
            }
837
838
            if (!array_key_exists(0, $this->signatures)) {
839 50
                $this->signatures[0] = $this->calculateSignature($privateKey, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
840 50
            }
841
842 50
            $this->publicKeys[0] = $publicKey;
843 22
        } else if ($this->signScript->getType() === ScriptType::MULTISIG) {
844 22
            $signed = false;
845 22
            foreach ($this->publicKeys as $keyIdx => $publicKey) {
846 22
                if ($publicKey instanceof PublicKeyInterface) {
847
                    if ($privateKey->getPublicKey()->equals($publicKey)) {
848 2
                        $this->signatures[$keyIdx] = $this->calculateSignature($privateKey, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
849
                        $signed = true;
850
                    }
851
                }
852 22
            }
853
854
            if (!$signed) {
855 50
                throw new \RuntimeException('Signing with the wrong private key');
856
            }
857
        } else {
858
            throw new \RuntimeException('Unexpected error - sign script had an unexpected type');
859
        }
860
861
        return $this;
862
    }
863
864 52
    /**
865
     * Verifies the input using $flags for script verification
866 52
     *
867 52
     * @param int $flags
868 12
     * @return bool
869 12
     */
870
    public function verify($flags = null)
871 42
    {
872 32
        $consensus = ScriptFactory::consensus();
873 32
874
        if ($flags === null) {
875 10
            $flags = $this->flags;
876 10
        }
877 10
878 10
        $flags |= Interpreter::VERIFY_P2SH;
879 10
        if (SigHash::V1 === $this->sigVersion) {
880
            $flags |= Interpreter::VERIFY_WITNESS;
881
        }
882
883
        $sig = $this->serializeSignatures();
884
885
        // Take serialized signatures, and use mutator to add this inputs sig data
886 52
        $mutator = TransactionFactory::mutate($this->tx);
887
        $mutator->inputsMutator()[$this->nInput]->script($sig->getScriptSig());
888
889
        if (SigHash::V1 === $this->sigVersion) {
890
            $witness = [];
891
            for ($i = 0, $j = count($this->tx->getInputs()); $i < $j; $i++) {
892
                if ($i === $this->nInput) {
893
                    $witness[] = $sig->getScriptWitness();
894 52
                } else {
895
                    $witness[] = new ScriptWitness([]);
896 52
                }
897 52
            }
898 52
899 2
            $mutator->witness($witness);
900 2
        }
901
902
        return $consensus->verify($mutator->done(), $this->txOut->getScript(), $flags, $this->nInput, $this->txOut->getValue());
903 52
    }
904 52
905 52
    /**
906 26
     * Produces the script stack that solves the $outputType
907
     *
908
     * @param string $outputType
909 52
     * @return BufferInterface[]
910 52
     */
911 52
    private function serializeSolution($outputType)
912 18
    {
913 18
        $result = [];
914 6
        if (ScriptType::P2PK === $outputType) {
915
            if (count($this->signatures) === 1) {
916 18
                $result = [$this->txSigSerializer->serialize($this->signatures[0])];
0 ignored issues
show
Compatibility introduced by
$this->signatures[0] of type object<BitWasp\Bitcoin\S...tionSignatureInterface> is not a sub-type of object<BitWasp\Bitcoin\S...e\TransactionSignature>. It seems like you assume a concrete implementation of the interface BitWasp\Bitcoin\Signatur...ctionSignatureInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
917
            }
918
        } else if (ScriptType::P2PKH === $outputType) {
919 52
            if (count($this->signatures) === 1 && count($this->publicKeys) === 1) {
920 8
                $result = [$this->txSigSerializer->serialize($this->signatures[0]), $this->pubKeySerializer->serialize($this->publicKeys[0])];
0 ignored issues
show
Compatibility introduced by
$this->signatures[0] of type object<BitWasp\Bitcoin\S...tionSignatureInterface> is not a sub-type of object<BitWasp\Bitcoin\S...e\TransactionSignature>. It seems like you assume a concrete implementation of the interface BitWasp\Bitcoin\Signatur...ctionSignatureInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
921 46
            }
922 14
        } else if (ScriptType::MULTISIG === $outputType) {
923 14
            $isFullySigned = $this->isFullySigned();
924 14
925
            $result[] = new Buffer();
926
            for ($i = 0, $nPubKeys = count($this->publicKeys); $i < $nPubKeys; $i++) {
927
                if (isset($this->signatures[$i])) {
928 52
                    $result[] = $this->txSigSerializer->serialize($this->signatures[$i]);
0 ignored issues
show
Compatibility introduced by
$this->signatures[$i] of type object<BitWasp\Bitcoin\S...tionSignatureInterface> is not a sub-type of object<BitWasp\Bitcoin\S...e\TransactionSignature>. It seems like you assume a concrete implementation of the interface BitWasp\Bitcoin\Signatur...ctionSignatureInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
929 18
                } else if ($this->padUnsignedMultisigs && !$isFullySigned) {
930
                    $result[] = new Buffer();
931
                }
932 52
            }
933
        } else {
934
            throw new \RuntimeException('Parameter 0 for serializeSolution was a non-standard input type');
935
        }
936
937
        return $result;
938
    }
939
940
    /**
941
     * Produces a SigValues instance containing the scriptSig & script witness
942
     *
943
     * @return SigValues
944
     */
945
    public function serializeSignatures()
946
    {
947
        static $emptyScript = null;
948
        static $emptyWitness = null;
949
        if (is_null($emptyScript) || is_null($emptyWitness)) {
950
            $emptyScript = new Script();
951
            $emptyWitness = new ScriptWitness([]);
952
        }
953
954
        $scriptSigChunks = [];
955
        $witness = [];
956
        if ($this->scriptPubKey->canSign()) {
957
            $scriptSigChunks = $this->serializeSolution($this->scriptPubKey->getType());
958
        }
959
960
        $solution = $this->scriptPubKey;
961
        $p2sh = false;
962
        if ($solution->getType() === ScriptType::P2SH) {
963
            $p2sh = true;
964
            if ($this->redeemScript->canSign()) {
965
                $scriptSigChunks = $this->serializeSolution($this->redeemScript->getType());
966
            }
967
            $solution = $this->redeemScript;
968
        }
969
970
        if ($solution->getType() === ScriptType::P2WKH) {
971
            $witness = $this->serializeSolution(ScriptType::P2PKH);
972
        } else if ($solution->getType() === ScriptType::P2WSH) {
973
            if ($this->witnessScript->canSign()) {
974
                $witness = $this->serializeSolution($this->witnessScript->getType());
975
                $witness[] = $this->witnessScript->getScript()->getBuffer();
976
            }
977
        }
978
979
        if ($p2sh) {
980
            $scriptSigChunks[] = $this->redeemScript->getScript()->getBuffer();
981
        }
982
983
        return new SigValues($this->pushAll($scriptSigChunks), new ScriptWitness($witness));
984
    }
985
}
986