Completed
Pull Request — master (#451)
by thomas
71:01
created

Hasher::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 5
ccs 4
cts 4
cp 1
crap 2
rs 9.4285
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\SignatureHash;
4
5
use BitWasp\Bitcoin\Bitcoin;
6
use BitWasp\Bitcoin\Crypto\Hash;
7
use BitWasp\Bitcoin\Script\Script;
8
use BitWasp\Bitcoin\Script\ScriptInterface;
9
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializer;
10
use BitWasp\Bitcoin\Serializer\Transaction\TransactionSerializerInterface;
11
use BitWasp\Bitcoin\Transaction\Mutator\TxMutator;
12
use BitWasp\Bitcoin\Transaction\TransactionInterface;
13
use BitWasp\Buffertools\Buffer;
14
use BitWasp\Buffertools\BufferInterface;
15
16
class Hasher extends SigHash
17
{
18
    /**
19
     * @var TransactionSerializer
20
     */
21
    private $txSerializer;
22
23
    /**
24
     * Hasher constructor.
25
     * @param TransactionInterface $transaction
26
     * @param TransactionSerializerInterface|null $txSerializer
27 86
     */
28
    public function __construct(TransactionInterface $transaction, TransactionSerializerInterface $txSerializer = null)
29 86
    {
30 86
        $this->txSerializer = $txSerializer ?: new TransactionSerializer();
0 ignored issues
show
Documentation Bug introduced by
$txSerializer ?: new \Bi...TransactionSerializer() is of type object<BitWasp\Bitcoin\S...ionSerializerInterface>, but the property $txSerializer was declared to be of type object<BitWasp\Bitcoin\S...\TransactionSerializer>. Are you sure that you always receive this specific sub-class here, or does it make sense to add an instanceof check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a given class or a super-class is assigned to a property that is type hinted more strictly.

Either this assignment is in error or an instanceof check should be added for that assignment.

class Alien {}

class Dalek extends Alien {}

class Plot
{
    /** @var  Dalek */
    public $villain;
}

$alien = new Alien();
$plot = new Plot();
if ($alien instanceof Dalek) {
    $plot->villain = $alien;
}
Loading history...
31 86
        parent::__construct($transaction);
32 86
    }
33
34
    /**
35 86
     * Calculate the hash of the current transaction, when you are looking to
36 86
     * spend $txOut, and are signing $inputToSign. The SigHashType defaults to
37
     * SIGHASH_ALL, though SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ANYONECANPAY
38
     * can be used.
39 86
     *
40
     * @param ScriptInterface $txOutScript
41 86
     * @param int $inputToSign
42
     * @param int $sighashType
43 4
     * @return BufferInterface
44
     * @throws \Exception
45
     */
46 4
    public function calculate(ScriptInterface $txOutScript, $inputToSign, $sighashType = SigHash::ALL)
47 4
    {
48 4
        $math = Bitcoin::getMath();
49
        $tx = new TxMutator($this->tx);
50
        $inputs = $tx->inputsMutator();
51 82
        $outputs = $tx->outputsMutator();
52
53
        // Default SIGHASH_ALL procedure: null all input scripts
54 8
        foreach ($inputs as $input) {
55 8
            $input->script(new Script);
56 2
        }
57
58
        $inputs[$inputToSign]->script($txOutScript);
59
60 8
        if (($sighashType & 31) === SigHash::NONE) {
61 8
            // Set outputs to empty vector, and set sequence number of inputs to 0.
62 6
            $outputs->null();
63
64
            // Let the others update at will. Set sequence of inputs we're not signing to 0.
65
            foreach ($inputs as $i => $input) {
66 8
                if ($i !== $inputToSign) {
67 8
                    $input->sequence(0);
68 8
                }
69
            }
70
        } elseif (($sighashType & 31) === SigHash::SINGLE) {
71
            // Resize output array to $inputToSign + 1, set remaining scripts to null,
72
            // and set sequence's to zero.
73
            $nOutput = $inputToSign;
74 86
            if ($nOutput >= count($this->tx->getOutputs())) {
75 7
                return Buffer::hex('0100000000000000000000000000000000000000000000000000000000000000', 32, $math);
76 7
            }
77
78
            // Resize, set to null
79 86
            $outputs->slice(0, $nOutput + 1);
80
            for ($i = 0; $i < $nOutput; $i++) {
81 86
                $outputs[$i]->null();
82 86
            }
83 86
84 86
            // Let the others update at will. Set sequence of inputs we're not signing to 0
85
            foreach ($inputs as $i => $input) {
86
                if ($i !== $inputToSign) {
87
                    $input->sequence(0);
88
                }
89
            }
90
        }
91
92
        // This can happen regardless of whether it's ALL, NONE, or SINGLE
93
        if (($sighashType & SigHash::ANYONECANPAY) > 0) {
94
            $input = $inputs[$inputToSign]->done();
95
            $inputs->null()->add($input);
96
        }
97
98
        return Hash::sha256d(new Buffer(
99
            $this->txSerializer->serialize($tx->done(), TransactionSerializer::NO_WITNESS)->getBinary() .
100
            pack('V', $sighashType)
101
        ));
102
    }
103
}
104