Completed
Pull Request — master (#331)
by thomas
34:30
created

V1Hasher::hashOutputs()   B

Complexity

Conditions 6
Paths 4

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 9
c 1
b 0
f 0
nc 4
nop 2
dl 0
loc 14
ccs 10
cts 10
cp 1
crap 6
rs 8.8571
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\SignatureHash;
4
5
use BitWasp\Bitcoin\Crypto\Hash;
6
use BitWasp\Bitcoin\Script\ScriptFactory;
7
use BitWasp\Bitcoin\Transaction\TransactionInterface;
8
use BitWasp\Buffertools\Buffer;
9
use BitWasp\Buffertools\BufferInterface;
10
use BitWasp\Bitcoin\Script\ScriptInterface;
11
12
class V1Hasher
13
{
14
    /**
15
     * @var TransactionInterface
16
     */
17
    private $transaction;
18
19
    /**
20
     * @var int|string
21
     */
22
    private $amount;
23
24
    /**
25
     * V1Hasher constructor.
26
     * @param TransactionInterface $transaction
27
     * @param int|string $amount
28
     */
29 75
    public function __construct(TransactionInterface $transaction, $amount)
30
    {
31 75
        $this->transaction = $transaction;
32 75
        $this->amount = $amount;
33 75
    }
34
35
    /**
36
     * @param int $sighashType
37
     * @return Buffer|BufferInterface
38
     */
39 75
    public function hashPrevOuts($sighashType)
40
    {
41 75
        if (!($sighashType & SigHashInterface::ANYONECANPAY)) {
42 75
            $binary = '';
43 75
            foreach ($this->transaction->getInputs() as $input) {
44 75
                $binary .= $input->getOutPoint()->getBinary();
45 50
            }
46 75
            return Hash::sha256d(new Buffer($binary));
47
        }
48
49
        return new Buffer('', 32);
50
    }
51
52
    /**
53
     * @param int $sighashType
54
     * @return Buffer|BufferInterface
55
     */
56 75
    public function hashSequences($sighashType)
57
    {
58 75
        if (!($sighashType & SigHashInterface::ANYONECANPAY) && ($sighashType & 0x1f) != SigHashInterface::SINGLE && ($sighashType & 0x1f) != SigHashInterface::NONE) {
59 69
            $binary = '';
60 69
            foreach ($this->transaction->getInputs() as $input) {
61 69
                $binary .= Buffer::int($input->getSequence())->flip()->getBinary();
62 46
            }
63 69
            return Hash::sha256d(new Buffer($binary));
64
        }
65
66 6
        return new Buffer('', 32);
67
    }
68
69
    /**
70
     * @param int $sighashType
71
     * @param int $inputToSign
72
     * @return Buffer|BufferInterface
73
     */
74 75
    public function hashOutputs($sighashType, $inputToSign)
75
    {
76 75
        if (($sighashType & 0x1f) !== SigHashInterface::SINGLE && ($sighashType & 0x1f) != SigHashInterface::NONE) {
77 69
            $binary = '';
78 69
            foreach ($this->transaction->getOutputs() as $output) {
79 69
                $binary .= $output->getBinary();
80 46
            }
81 69
            return Hash::sha256d(new Buffer($binary));
82 6
        } elseif (($sighashType & 0x1f) == SigHashInterface::SINGLE && $inputToSign < count($this->transaction->getOutputs())) {
83 3
            return Hash::sha256($this->transaction->getOutput($inputToSign)->getBuffer());
84
        }
85
86 3
        return new Buffer('', 32);
87
    }
88
89
    /**
90
     * Calculate the hash of the current transaction, when you are looking to
91
     * spend $txOut, and are signing $inputToSign. The SigHashType defaults to
92
     * SIGHASH_ALL, though SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ANYONECANPAY
93
     * can be used.
94
     *
95
     * @param ScriptInterface $txOutScript
96
     * @param int $inputToSign
97
     * @param int $sighashType
98
     * @return BufferInterface
99
     * @throws \Exception
100
     */
101 75
    public function calculate(ScriptInterface $txOutScript, $inputToSign, $sighashType = SigHashInterface::ALL)
102
    {
103 75
        $sighashType = (int) $sighashType;
104
105 75
        $hashPrevOuts = $this->hashPrevOuts($sighashType);
106 75
        $hashSequence = $this->hashSequences($sighashType);
107 75
        $hashOutputs = $this->hashOutputs($sighashType, $inputToSign);
108
109 75
        $input = $this->transaction->getInput($inputToSign);
110
111 75
        return Hash::sha256d(new Buffer(
112 75
            pack("V", $this->transaction->getVersion()) .
113 75
            $hashPrevOuts->getBinary() .
114 75
            $hashSequence->getBinary() .
115 75
            $input->getOutPoint()->getBinary() .
116 75
            ScriptFactory::create()->push($txOutScript->getBuffer())->getScript()->getBinary() .
117 75
            Buffer::int($this->amount, 8)->flip()->getBinary() .
118 75
            pack("V", $input->getSequence()) .
119 75
            $hashOutputs->getBinary() .
120 75
            pack("V", $this->transaction->getLockTime()) .
121 75
            pack("V", $sighashType)
122 50
        ));
123
    }
124
}
125