Completed
Pull Request — master (#514)
by thomas
70:16 queued 68:01
created

InputSigner::getPublicKeys()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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