Completed
Push — master ( 472018...1c05ea )
by thomas
76:34 queued 04:45
created

InputSigner::extract()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

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