Completed
Pull Request — master (#519)
by thomas
80:31 queued 75:30
created

InputSigner::getWitnessScript()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 2.0625

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
ccs 3
cts 4
cp 0.75
crap 2.0625
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\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 implements InputSignerInterface
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 bool
84
     */
85
    private $tolerateInvalidPublicKey = false;
86
87
    /**
88
     * @var SignData
89
     */
90
    private $signData;
91
92
    /**
93
     * @var int
94
     */
95
    private $sigVersion;
96
97
    /**
98
     * @var int
99
     */
100
    private $flags;
101
102
    /**
103
     * @var OutputData $witnessKeyHash
104
     */
105
    private $witnessKeyHash;
106
107
    /**
108
     * @var TransactionInterface
109
     */
110
    private $tx;
111
112
    /**
113
     * @var int
114
     */
115
    private $nInput;
116
117
    /**
118
     * @var TransactionOutputInterface
119
     */
120
    private $txOut;
121
122
    /**
123
     * @var PublicKeyInterface[]
124
     */
125
    private $publicKeys = [];
126
127
    /**
128
     * @var TransactionSignatureInterface[]
129
     */
130
    private $signatures = [];
131
132
    /**
133
     * @var int
134
     */
135
    private $requiredSigs = 0;
136
137
    /**
138
     * @var Interpreter
139
     */
140
    private $interpreter;
141
142
    /**
143
     * @var Checker
144
     */
145
    private $signatureChecker;
146
147
    /**
148
     * @var TransactionSignatureSerializer
149
     */
150
    private $txSigSerializer;
151
152
    /**
153
     * @var PublicKeySerializerInterface
154
     */
155
    private $pubKeySerializer;
156
157
    /**
158
     * InputSigner constructor.
159
     *
160
     * Note, the implementation of this class is considered internal
161
     * and only the methods exposed on InputSignerInterface should
162
     * be depended on to avoid BC breaks.
163
     *
164
     * The only recommended way to produce this class is using Signer::input()
165
     *
166
     * @param EcAdapterInterface $ecAdapter
167
     * @param TransactionInterface $tx
168
     * @param int $nInput
169
     * @param TransactionOutputInterface $txOut
170
     * @param SignData $signData
171
     * @param TransactionSignatureSerializer|null $sigSerializer
172
     * @param PublicKeySerializerInterface|null $pubKeySerializer
173
     */
174 94
    public function __construct(EcAdapterInterface $ecAdapter, TransactionInterface $tx, $nInput, TransactionOutputInterface $txOut, SignData $signData, TransactionSignatureSerializer $sigSerializer = null, PublicKeySerializerInterface $pubKeySerializer = null)
175
    {
176 94
        $this->ecAdapter = $ecAdapter;
177 94
        $this->tx = $tx;
178 94
        $this->nInput = $nInput;
179 94
        $this->txOut = $txOut;
180 94
        $this->signData = $signData;
181 94
        $this->publicKeys = [];
182 94
        $this->signatures = [];
183
184 94
        $this->txSigSerializer = $sigSerializer ?: new TransactionSignatureSerializer(EcSerializer::getSerializer(DerSignatureSerializerInterface::class, true, $ecAdapter));
185 94
        $this->pubKeySerializer = $pubKeySerializer ?: EcSerializer::getSerializer(PublicKeySerializerInterface::class, true, $ecAdapter);
186 94
        $this->interpreter = new Interpreter($this->ecAdapter);
187 94
    }
188
189
    /**
190
     * @return InputSigner
191
     */
192 94
    public function extract()
193
    {
194 94
        $defaultFlags = Interpreter::VERIFY_DERSIG | Interpreter::VERIFY_P2SH | Interpreter::VERIFY_CHECKLOCKTIMEVERIFY | Interpreter::VERIFY_CHECKSEQUENCEVERIFY | Interpreter::VERIFY_WITNESS;
195 94
        $checker = new Checker($this->ecAdapter, $this->tx, $this->nInput, $this->txOut->getValue(), $this->txSigSerializer, $this->pubKeySerializer);
196
197 94
        $this->flags = $this->signData->hasSignaturePolicy() ? $this->signData->getSignaturePolicy() : $defaultFlags;
198 94
        $this->signatureChecker = $checker;
199
200 94
        $witnesses = $this->tx->getWitnesses();
201 94
        $witness = array_key_exists($this->nInput, $witnesses) ? $witnesses[$this->nInput]->all() : [];
202
203 94
        return $this->solve(
204 94
            $this->signData,
205 94
            $this->txOut->getScript(),
206 94
            $this->tx->getInput($this->nInput)->getScript(),
207
            $witness
208
        );
209
    }
210
211
    /**
212
     * @param bool $setting
213
     * @return $this
214
     */
215 64
    public function tolerateInvalidPublicKey($setting)
216
    {
217 64
        $this->tolerateInvalidPublicKey = (bool) $setting;
218 64
        return $this;
219
    }
220
221
    /**
222
     * @param BufferInterface $vchPubKey
223
     * @return PublicKeyInterface|null
224
     * @throws \Exception
225
     */
226 62
    protected function parseStepPublicKey(BufferInterface $vchPubKey)
227
    {
228
        try {
229 62
            return $this->pubKeySerializer->parse($vchPubKey);
230 6
        } catch (\Exception $e) {
231 6
            if ($this->tolerateInvalidPublicKey) {
232 2
                return null;
233
            }
234
235 4
            throw $e;
236
        }
237
    }
238
239
    /**
240
     * A snipped from OP_CHECKMULTISIG - verifies signatures according to the
241
     * order of the given public keys (taken from the script).
242
     *
243
     * @param ScriptInterface $script
244
     * @param BufferInterface[] $signatures
245
     * @param BufferInterface[] $publicKeys
246
     * @param int $sigVersion
247
     * @return \SplObjectStorage
248
     */
249 10
    private function sortMultisigs(ScriptInterface $script, array $signatures, array $publicKeys, $sigVersion)
250
    {
251 10
        $sigCount = count($signatures);
252 10
        $keyCount = count($publicKeys);
253 10
        $ikey = $isig = 0;
254 10
        $fSuccess = true;
255 10
        $result = new \SplObjectStorage;
256
257 10
        while ($fSuccess && $sigCount > 0) {
258
            // Fetch the signature and public key
259 10
            $sig = $signatures[$isig];
260 10
            $pubkey = $publicKeys[$ikey];
261
262 10
            if ($this->signatureChecker->checkSig($script, $sig, $pubkey, $sigVersion, $this->flags)) {
263 10
                $result[$pubkey] = $sig;
264 10
                $isig++;
265 10
                $sigCount--;
266
            }
267
268 10
            $ikey++;
269 10
            $keyCount--;
270
271
            // If there are more signatures left than keys left,
272
            // then too many signatures have failed. Exit early,
273
            // without checking any further signatures.
274 10
            if ($sigCount > $keyCount) {
275
                $fSuccess = false;
276
            }
277
        }
278
279 10
        return $result;
280
    }
281
282
    /**
283
     * @param ScriptInterface $script
284
     * @return \BitWasp\Buffertools\BufferInterface[]
285
     */
286 74
    private function evalPushOnly(ScriptInterface $script)
287
    {
288 74
        $stack = new Stack();
289 74
        $this->interpreter->evaluate($script, $stack, SigHash::V0, $this->flags | Interpreter::VERIFY_SIGPUSHONLY, $this->signatureChecker);
290 74
        return $stack->all();
291
    }
292
293
    /**
294
     * Create a script consisting only of push-data operations.
295
     * Suitable for a scriptSig.
296
     *
297
     * @param BufferInterface[] $buffers
298
     * @return ScriptInterface
299
     */
300
    private function pushAll(array $buffers)
301
    {
302 50
        return ScriptFactory::sequence(array_map(function ($buffer) {
303 42
            if (!($buffer instanceof BufferInterface)) {
304
                throw new \RuntimeException('Script contained a non-push opcode');
305
            }
306
307 42
            $size = $buffer->getSize();
308 42
            if ($size === 0) {
309 4
                return Opcodes::OP_0;
310
            }
311
312 42
            $first = ord($buffer->getBinary());
313 42
            if ($size === 1 && $first >= 1 && $first <= 16) {
314
                return \BitWasp\Bitcoin\Script\encodeOpN($first);
315
            } else {
316 42
                return $buffer;
317
            }
318 50
        }, $buffers));
319
    }
320
321
    /**
322
     * Verify a scriptSig / scriptWitness against a scriptPubKey.
323
     * Useful for checking the outcome of certain things, like hash locks (p2sh)
324
     *
325
     * @param int $flags
326
     * @param ScriptInterface $scriptSig
327
     * @param ScriptInterface $scriptPubKey
328
     * @param ScriptWitnessInterface|null $scriptWitness
329
     * @return bool
330
     */
331 26
    private function verifySolution($flags, ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, ScriptWitnessInterface $scriptWitness = null)
332
    {
333 26
        return $this->interpreter->verify($scriptSig, $scriptPubKey, $flags, $this->signatureChecker, $scriptWitness);
334
    }
335
336
    /**
337
     * Evaluates a scriptPubKey against the provided chunks.
338
     *
339
     * @param ScriptInterface $scriptPubKey
340
     * @param array $chunks
341
     * @param int $sigVersion
342
     * @return bool
343
     */
344 56
    private function evaluateSolution(ScriptInterface $scriptPubKey, array $chunks, $sigVersion)
345
    {
346 56
        $stack = new Stack($chunks);
347 56
        if (!$this->interpreter->evaluate($scriptPubKey, $stack, $sigVersion, $this->flags, $this->signatureChecker)) {
348 2
            return false;
349
        }
350
351 54
        if ($stack->isEmpty()) {
352
            return false;
353
        }
354
355 54
        if (false === $this->interpreter->castToBool($stack[-1])) {
356 4
            return false;
357
        }
358
359 50
        return true;
360
    }
361
362
    /**
363
     * This function is strictly for $canSign types.
364
     * It will extract signatures/publicKeys when given $outputData, and $stack.
365
     * $stack is the result of decompiling a scriptSig, or taking the witness data.
366
     *
367
     * @param OutputData $outputData
368
     * @param array $stack
369
     * @param int $sigVersion
370
     * @return string
371
     */
372 70
    public function extractFromValues(OutputData $outputData, array $stack, $sigVersion)
373
    {
374 70
        $type = $outputData->getType();
375 70
        $size = count($stack);
376
377 70
        if (ScriptType::P2PKH === $type) {
378 38
            $this->requiredSigs = 1;
379 38
            if ($size === 2) {
380 34
                if (!$this->evaluateSolution($outputData->getScript(), $stack, $sigVersion)) {
381 2
                    throw new \RuntimeException('Existing signatures are invalid!');
382
                }
383 32
                $this->signatures = [$this->txSigSerializer->parse($stack[0])];
384 36
                $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...
385
            }
386 34
        } else if (ScriptType::P2PK === $type) {
387 14
            $this->requiredSigs = 1;
388 14
            if ($size === 1) {
389 12
                if (!$this->evaluateSolution($outputData->getScript(), $stack, $sigVersion)) {
390 2
                    throw new \RuntimeException('Existing signatures are invalid!');
391
                }
392 10
                $this->signatures = [$this->txSigSerializer->parse($stack[0])];
393
            }
394 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...
395 20
        } else if (ScriptType::MULTISIG === $type) {
396 20
            $info = new Multisig($outputData->getScript(), $this->pubKeySerializer);
397 20
            $this->requiredSigs = $info->getRequiredSigCount();
398
399 20
            $keyBuffers = $info->getKeyBuffers();
400 20
            $this->publicKeys = [];
401 20
            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...
402 20
                $this->publicKeys[$i] = $this->parseStepPublicKey($keyBuffers[$i]);
403
            }
404
405 16
            if ($size > 1) {
406
                // Check signatures irrespective of scriptSig size, primes Checker cache, and need info
407 12
                $check = $this->evaluateSolution($outputData->getScript(), $stack, $sigVersion);
408 12
                $sigBufs = array_slice($stack, 1, $size - 1);
409 12
                $sigBufCount = count($sigBufs);
410
411
                // If we seem to have all signatures but fail evaluation, abort
412 12
                if ($sigBufCount === $this->requiredSigs && !$check) {
413 2
                    throw new \RuntimeException('Existing signatures are invalid!');
414
                }
415
416 10
                $keyToSigMap = $this->sortMultiSigs($outputData->getScript(), $sigBufs, $keyBuffers, $sigVersion);
417
418
                // Here we learn if any signatures were invalid, it won't be in the map.
419 10
                if ($sigBufCount !== count($keyToSigMap)) {
420
                    throw new \RuntimeException('Existing signatures are invalid!');
421
                }
422
423 10
                foreach ($keyBuffers as $idx => $key) {
424 10
                    if (isset($keyToSigMap[$key])) {
425 14
                        $this->signatures[$idx] = $this->txSigSerializer->parse($keyToSigMap[$key]);
426
                    }
427
                }
428
            }
429
        } else {
430
            throw new \RuntimeException('Unsupported output type passed to extractFromValues');
431
        }
432
433 60
        return $type;
434
    }
435
436
    /**
437
     * Checks $chunks (a decompiled scriptSig) for it's last element,
438
     * or defers to SignData. If both are provided, it checks the
439
     * value from $chunks against SignData.
440
     *
441
     * @param BufferInterface[] $chunks
442
     * @param SignData $signData
443
     * @return ScriptInterface
444
     */
445 30
    private function findRedeemScript(array $chunks, SignData $signData)
446
    {
447 30
        if (count($chunks) > 0) {
448 22
            $redeemScript = new Script($chunks[count($chunks) - 1]);
449 22
            if ($signData->hasRedeemScript()) {
450 22
                if (!$redeemScript->equals($signData->getRedeemScript())) {
451 22
                    throw new \RuntimeException('Extracted redeemScript did not match sign data');
452
                }
453
            }
454
        } else {
455 26
            if (!$signData->hasRedeemScript()) {
456 2
                throw new \RuntimeException('Redeem script not provided in sign data or scriptSig');
457
            }
458 24
            $redeemScript = $signData->getRedeemScript();
459
        }
460
461 26
        return $redeemScript;
462
    }
463
464
    /**
465
     * Checks $witness (a witness structure) for it's last element,
466
     * or defers to SignData. If both are provided, it checks the
467
     * value from $chunks against SignData.
468
     *
469
     * @param BufferInterface[] $witness
470
     * @param SignData $signData
471
     * @return ScriptInterface
472
     */
473 26
    private function findWitnessScript(array $witness, SignData $signData)
474
    {
475 26
        if (count($witness) > 0) {
476 18
            $witnessScript = new Script($witness[count($witness) - 1]);
477 18
            if ($signData->hasWitnessScript()) {
478 18
                if (!$witnessScript->equals($signData->getWitnessScript())) {
479 18
                    throw new \RuntimeException('Extracted witnessScript did not match sign data');
480
                }
481
            }
482
        } else {
483 22
            if (!$signData->hasWitnessScript()) {
484 4
                throw new \RuntimeException('Witness script not provided in sign data or witness');
485
            }
486 18
            $witnessScript = $signData->getWitnessScript();
487
        }
488
489 18
        return $witnessScript;
490
    }
491
492
    /**
493
     * Needs to be called before using the instance. By `extract`.
494
     *
495
     * It ensures that violating the following prevents instance creation
496
     *  - the scriptPubKey can be directly signed, or leads to P2SH/P2WSH/P2WKH
497
     *  - the P2SH script covers signable types and P2WSH/P2WKH
498
     *  - the witnessScript covers signable types only
499
     *
500
     * @param SignData $signData
501
     * @param ScriptInterface $scriptPubKey
502
     * @param ScriptInterface $scriptSig
503
     * @param BufferInterface[] $witness
504
     * @return $this
505
     */
506 92
    private function solve(SignData $signData, ScriptInterface $scriptPubKey, ScriptInterface $scriptSig, array $witness)
507
    {
508 92
        $classifier = new OutputClassifier();
509 92
        $sigVersion = SigHash::V0;
510 92
        $sigChunks = [];
511 92
        $solution = $this->scriptPubKey = $classifier->decode($scriptPubKey);
512 92
        if ($solution->getType() !== ScriptType::P2SH && !in_array($solution->getType(), self::$validP2sh)) {
513 2
            throw new \RuntimeException('scriptPubKey not supported');
514
        }
515
516 90
        if ($solution->canSign()) {
517 44
            $sigChunks = $this->evalPushOnly($scriptSig);
518
        }
519
520 90
        if ($solution->getType() === ScriptType::P2SH) {
521 30
            $chunks = $this->evalPushOnly($scriptSig);
522 30
            $redeemScript = $this->findRedeemScript($chunks, $signData);
523 26
            if (!$this->verifySolution(Interpreter::VERIFY_SIGPUSHONLY, ScriptFactory::sequence([$redeemScript->getBuffer()]), $solution->getScript())) {
524 2
                throw new \RuntimeException('Redeem script fails to solve pay-to-script-hash');
525
            }
526
527 24
            $solution = $this->redeemScript = $classifier->decode($redeemScript);
528 24
            if (!in_array($solution->getType(), self::$validP2sh)) {
529 2
                throw new \RuntimeException('Unsupported pay-to-script-hash script');
530
            }
531
532 22
            $sigChunks = array_slice($chunks, 0, -1);
533
        }
534
535 82
        if ($solution->getType() === ScriptType::P2WKH) {
536 8
            $sigVersion = SigHash::V1;
537 8
            $solution = $this->witnessKeyHash = $classifier->decode(ScriptFactory::scriptPubKey()->payToPubKeyHash($solution->getSolution()));
538 8
            $sigChunks = $witness;
539 76
        } else if ($solution->getType() === ScriptType::P2WSH) {
540 26
            $sigVersion = SigHash::V1;
541 26
            $witnessScript = $this->findWitnessScript($witness, $signData);
542
543
            // Essentially all the reference implementation does
544 18
            if (!$witnessScript->getWitnessScriptHash()->equals($solution->getSolution())) {
545 2
                throw new \RuntimeException('Witness script fails to solve witness-script-hash');
546
            }
547
548 16
            $solution = $this->witnessScript = $classifier->decode($witnessScript);
549 16
            if (!in_array($this->witnessScript->getType(), self::$canSign)) {
550 2
                throw new \RuntimeException('Unsupported witness-script-hash script');
551
            }
552
553 14
            $sigChunks = array_slice($witness, 0, -1);
554
        }
555
556 70
        $this->sigVersion = $sigVersion;
557 70
        $this->signScript = $solution;
558
559 70
        $this->extractFromValues($solution, $sigChunks, $this->sigVersion);
560
561 60
        return $this;
562
    }
563
564
    /**
565
     * Pure function to produce a signature hash for a given $scriptCode, $sigHashType, $sigVersion.
566
     *
567
     * @param ScriptInterface $scriptCode
568
     * @param int $sigHashType
569
     * @param int $sigVersion
570
     * @return BufferInterface
571
     */
572 52
    public function calculateSigHashUnsafe(ScriptInterface $scriptCode, $sigHashType, $sigVersion)
573
    {
574 52
        if (!$this->signatureChecker->isDefinedHashtype($sigHashType)) {
575 2
            throw new \RuntimeException('Invalid sigHashType requested');
576
        }
577
578 50
        return $this->signatureChecker->getSigHash($scriptCode, $sigHashType, $sigVersion);
579
    }
580
581
    /**
582
     * Calculates the signature hash for the input for the given $sigHashType.
583
     *
584
     * @param int $sigHashType
585
     * @return BufferInterface
586
     */
587 2
    public function getSigHash($sigHashType)
588
    {
589 2
        return $this->calculateSigHashUnsafe($this->signScript->getScript(), $sigHashType, $this->sigVersion);
590
    }
591
592
    /**
593
     * Pure function to produce a signature for a given $key, $scriptCode, $sigHashType, $sigVersion.
594
     *
595
     * @param PrivateKeyInterface $key
596
     * @param ScriptInterface $scriptCode
597
     * @param int $sigHashType
598
     * @param int $sigVersion
599
     * @return TransactionSignatureInterface
600
     */
601 50
    private function calculateSignature(PrivateKeyInterface $key, ScriptInterface $scriptCode, $sigHashType, $sigVersion)
602
    {
603 50
        $hash = $this->calculateSigHashUnsafe($scriptCode, $sigHashType, $sigVersion);
604 50
        $ecSignature = $this->ecAdapter->sign($hash, $key, new Rfc6979($this->ecAdapter, $key, $hash, 'sha256'));
605 50
        return new TransactionSignature($this->ecAdapter, $ecSignature, $sigHashType);
606
    }
607
608
    /**
609
     * Returns whether all required signatures have been provided.
610
     *
611
     * @return bool
612
     */
613 56
    public function isFullySigned()
614
    {
615 56
        return $this->requiredSigs !== 0 && $this->requiredSigs === count($this->signatures);
616
    }
617
618
    /**
619
     * Returns the required number of signatures for this input.
620
     *
621
     * @return int
622
     */
623 50
    public function getRequiredSigs()
624
    {
625 50
        return $this->requiredSigs;
626
    }
627
628
    /**
629
     * Returns an array where the values are either null,
630
     * or a TransactionSignatureInterface.
631
     *
632
     * @return TransactionSignatureInterface[]
633
     */
634 50
    public function getSignatures()
635
    {
636 50
        return $this->signatures;
637
    }
638
639
    /**
640
     * Returns an array where the values are either null,
641
     * or a PublicKeyInterface.
642
     *
643
     * @return PublicKeyInterface[]
644
     */
645 52
    public function getPublicKeys()
646
    {
647 52
        return $this->publicKeys;
648
    }
649
650
    /**
651
     * OutputData for the script to be signed (will be
652
     * equal to getScriptPubKey, or getRedeemScript, or
653
     * getWitnessScript.
654
     *
655
     * @return OutputData
656
     */
657 50
    public function getSignScript()
658
    {
659 50
        return $this->signScript;
660
    }
661
662
    /**
663
     * OutputData for the txOut script.
664
     *
665
     * @return OutputData
666
     */
667 24
    public function getScriptPubKey()
668
    {
669 24
        return $this->scriptPubKey;
670
    }
671
672
    /**
673
     * Returns OutputData for the P2SH redeemScript.
674
     *
675
     * @return OutputData
676
     */
677 18
    public function getRedeemScript()
678
    {
679 18
        if (null === $this->redeemScript) {
680
            throw new \RuntimeException("Input has no redeemScript, cannot call getRedeemScript");
681
        }
682
683 18
        return $this->redeemScript;
684
    }
685
686
    /**
687
     * Returns OutputData for the P2WSH witnessScript.
688
     *
689
     * @return OutputData
690
     */
691 14
    public function getWitnessScript()
692
    {
693 14
        if (null === $this->witnessScript) {
694
            throw new \RuntimeException("Input has no witnessScript, cannot call getWitnessScript");
695
        }
696
697 14
        return $this->witnessScript;
698
    }
699
700
    /**
701
     * Returns whether the scriptPubKey is P2SH.
702
     *
703
     * @return bool
704
     */
705 50
    public function isP2SH()
706
    {
707 50
        if ($this->scriptPubKey->getType() === ScriptType::P2SH && ($this->redeemScript instanceof OutputData)) {
708 18
            return true;
709
        }
710
711 32
        return false;
712
    }
713
714
    /**
715
     * Returns whether the scriptPubKey or redeemScript is P2WSH.
716
     *
717
     * @return bool
718
     */
719 50
    public function isP2WSH()
720
    {
721 50
        if ($this->redeemScript instanceof OutputData) {
722 18
            if ($this->redeemScript->getType() === ScriptType::P2WSH && ($this->witnessScript instanceof OutputData)) {
723 8
                return true;
724
            }
725
        }
726
727 42
        if ($this->scriptPubKey->getType() === ScriptType::P2WSH && ($this->witnessScript instanceof OutputData)) {
728 6
            return true;
729
        }
730
731 36
        return false;
732
    }
733
734
    /**
735
     * Sign the input using $key and $sigHashTypes
736
     *
737
     * @param PrivateKeyInterface $privateKey
738
     * @param int $sigHashType
739
     * @return $this
740
     */
741 56
    public function sign(PrivateKeyInterface $privateKey, $sigHashType = SigHash::ALL)
742
    {
743 56
        if ($this->isFullySigned()) {
744
            return $this;
745
        }
746
747 56
        if (SigHash::V1 === $this->sigVersion && !$privateKey->isCompressed()) {
748
            throw new \RuntimeException('Uncompressed keys are disallowed in segwit scripts - refusing to sign');
749
        }
750
751 56
        if ($this->signScript->getType() === ScriptType::P2PK) {
752 12
            if (!$this->pubKeySerializer->serialize($privateKey->getPublicKey())->equals($this->signScript->getSolution())) {
753 2
                throw new \RuntimeException('Signing with the wrong private key');
754
            }
755 10
            $this->signatures[0] = $this->calculateSignature($privateKey, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
756 46
        } else if ($this->signScript->getType() === ScriptType::P2PKH) {
757 34
            $publicKey = $privateKey->getPublicKey();
758 34
            if (!$publicKey->getPubKeyHash()->equals($this->signScript->getSolution())) {
759 2
                throw new \RuntimeException('Signing with the wrong private key');
760
            }
761
762 32
            if (!array_key_exists(0, $this->signatures)) {
763 32
                $this->signatures[0] = $this->calculateSignature($privateKey, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
764
            }
765
766 32
            $this->publicKeys[0] = $publicKey;
767 12
        } else if ($this->signScript->getType() === ScriptType::MULTISIG) {
768 12
            $signed = false;
769 12
            foreach ($this->publicKeys as $keyIdx => $publicKey) {
770 12
                if ($publicKey instanceof PublicKeyInterface) {
771 12
                    if ($privateKey->getPublicKey()->equals($publicKey)) {
772 10
                        $this->signatures[$keyIdx] = $this->calculateSignature($privateKey, $this->signScript->getScript(), $sigHashType, $this->sigVersion);
773 12
                        $signed = true;
774
                    }
775
                }
776
            }
777
778 12
            if (!$signed) {
779 12
                throw new \RuntimeException('Signing with the wrong private key');
780
            }
781
        } else {
782
            throw new \RuntimeException('Unexpected error - sign script had an unexpected type');
783
        }
784
785 50
        return $this;
786
    }
787
788
    /**
789
     * Verifies the input using $flags for script verification
790
     *
791
     * @param int $flags
792
     * @return bool
793
     */
794 50
    public function verify($flags = null)
795
    {
796 50
        $consensus = ScriptFactory::consensus();
797
798 50
        if ($flags === null) {
799 50
            $flags = $this->flags;
800
        }
801
802 50
        $flags |= Interpreter::VERIFY_P2SH;
803 50
        if (SigHash::V1 === $this->sigVersion) {
804 22
            $flags |= Interpreter::VERIFY_WITNESS;
805
        }
806
807 50
        $sig = $this->serializeSignatures();
808
809
        // Take serialized signatures, and use mutator to add this inputs sig data
810 50
        $mutator = TransactionFactory::mutate($this->tx);
811 50
        $mutator->inputsMutator()[$this->nInput]->script($sig->getScriptSig());
812
813 50
        if (SigHash::V1 === $this->sigVersion) {
814 22
            $witness = [];
815 22
            for ($i = 0, $j = count($this->tx->getInputs()); $i < $j; $i++) {
816 22
                if ($i === $this->nInput) {
817 22
                    $witness[] = $sig->getScriptWitness();
818
                } else {
819 2
                    $witness[] = new ScriptWitness([]);
820
                }
821
            }
822
823 22
            $mutator->witness($witness);
824
        }
825
826 50
        return $consensus->verify($mutator->done(), $this->txOut->getScript(), $flags, $this->nInput, $this->txOut->getValue());
827
    }
828
829
    /**
830
     * Produces the script stack that solves the $outputType
831
     *
832
     * @param string $outputType
833
     * @return BufferInterface[]
834
     */
835 50
    private function serializeSolution($outputType)
836
    {
837 50
        $result = [];
838 50
        if (ScriptType::P2PK === $outputType) {
839 10
            if (count($this->signatures) === 1) {
840 10
                $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...
841
            }
842 42
        } else if (ScriptType::P2PKH === $outputType) {
843 32
            if (count($this->signatures) === 1 && count($this->publicKeys) === 1) {
844 32
                $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...
845
            }
846 10
        } else if (ScriptType::MULTISIG === $outputType) {
847 10
            $result[] = new Buffer();
848 10
            for ($i = 0, $nPubKeys = count($this->publicKeys); $i < $nPubKeys; $i++) {
849 10
                if (isset($this->signatures[$i])) {
850 10
                    $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...
851
                }
852
            }
853
        } else {
854
            throw new \RuntimeException('Parameter 0 for serializeSolution was a non-standard input type');
855
        }
856
857 50
        return $result;
858
    }
859
860
    /**
861
     * Produces a SigValues instance containing the scriptSig & script witness
862
     *
863
     * @return SigValues
864
     */
865 50
    public function serializeSignatures()
866
    {
867 50
        static $emptyScript = null;
868 50
        static $emptyWitness = null;
869 50
        if (is_null($emptyScript) || is_null($emptyWitness)) {
870 2
            $emptyScript = new Script();
871 2
            $emptyWitness = new ScriptWitness([]);
872
        }
873
874 50
        $scriptSigChunks = [];
875 50
        $witness = [];
876 50
        if ($this->scriptPubKey->canSign()) {
877 24
            $scriptSigChunks = $this->serializeSolution($this->scriptPubKey->getType());
878
        }
879
880 50
        $solution = $this->scriptPubKey;
881 50
        $p2sh = false;
882 50
        if ($solution->getType() === ScriptType::P2SH) {
883 18
            $p2sh = true;
884 18
            if ($this->redeemScript->canSign()) {
885 6
                $scriptSigChunks = $this->serializeSolution($this->redeemScript->getType());
886
            }
887 18
            $solution = $this->redeemScript;
888
        }
889
890 50
        if ($solution->getType() === ScriptType::P2WKH) {
891 8
            $witness = $this->serializeSolution(ScriptType::P2PKH);
892 44
        } else if ($solution->getType() === ScriptType::P2WSH) {
893 14
            if ($this->witnessScript->canSign()) {
894 14
                $witness = $this->serializeSolution($this->witnessScript->getType());
895 14
                $witness[] = $this->witnessScript->getScript()->getBuffer();
896
            }
897
        }
898
899 50
        if ($p2sh) {
900 18
            $scriptSigChunks[] = $this->redeemScript->getScript()->getBuffer();
901
        }
902
903 50
        return new SigValues($this->pushAll($scriptSigChunks), new ScriptWitness($witness));
904
    }
905
}
906