Completed
Push — master ( f684d6...af1b10 )
by thomas
25:38
created

V1Hasher::hashSequences()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 5.0488

Importance

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