Completed
Pull Request — master (#255)
by thomas
90:53 queued 19:51
created

V1Hasher::hashPrevOuts()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 12
ccs 1
cts 1
cp 1
rs 9.4285
cc 3
eloc 7
nc 3
nop 1
crap 3
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
    public function __construct(TransactionInterface $transaction, $amount)
40
    {
41 30
        $this->transaction = $transaction;
42
        $this->nInputs = count($this->transaction->getInputs());
43 30
        $this->nOutputs = count($this->transaction->getOutputs());
44 30
        $this->amount = $amount;
45 30
    }
46 30
47 30
    /**
48
     * @param int $sighashType
49
     * @return Buffer|BufferInterface
50
     */
51
    public function hashPrevOuts($sighashType)
52
    {
53
        if (!($sighashType & SigHash::ANYONECANPAY)) {
54
            $binary = '';
55
            foreach ($this->transaction->getInputs() as $input) {
56
                $binary .= $input->getOutPoint()->getBinary();
57
            }
58
            return Hash::sha256d(new Buffer($binary));
59
        }
60
61 30
        return new Buffer('', 32);
62
    }
63 30
64 30
    /**
65
     * @param int $sighashType
66 30
     * @return Buffer|BufferInterface
67
     */
68 30
    public function hashSequences($sighashType)
69 30
    {
70 30
        if (!($sighashType & SigHash::ANYONECANPAY) && ($sighashType & 0x1f) != SigHash::SINGLE && ($sighashType & 0x1f) != SigHash::NONE) {
71
            $binary = '';
72 30
            foreach ($this->transaction->getInputs() as $input) {
73
                $binary .= Buffer::int($input->getSequence())->flip()->getBinary();
74 30
            }
75 30
            return Hash::sha256d(new Buffer($binary));
76 30
        }
77
78 30
        return new Buffer('', 32);
79 30
    }
80 30
81 30
    /**
82 30
     * @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 30
        } elseif (($sighashType & 0x1f) == SigHash::SINGLE && $inputToSign < count($this->transaction->getOutputs())) {
95 30
            return Hash::sha256($this->transaction->getOutput($inputToSign)->getBuffer());
96 30
        }
97 30
98 30
        return new Buffer('', 32);
99 30
    }
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
    public function calculate(ScriptInterface $txOutScript, $inputToSign, $sighashType = SigHash::ALL)
114
    {
115
        $sighashType = (int) $sighashType;
116
117
        $hashPrevOuts = $this->hashPrevOuts($sighashType);
118
        $hashSequence = $this->hashSequences($sighashType);
119
        $hashOutputs = $this->hashOutputs($sighashType, $inputToSign);
120
121
        $input = $this->transaction->getInput($inputToSign);
122
123
        return Hash::sha256d(new Buffer(
124
            pack("V", $this->transaction->getVersion()) .
125
            $hashPrevOuts->getBinary() .
126
            $hashSequence->getBinary() .
127
            $input->getOutPoint()->getBinary() .
128
            ScriptFactory::create()->push($txOutScript->getBuffer())->getScript()->getBinary() .
129
            Buffer::int($this->amount, 8)->flip()->getBinary() .
130
            pack("V", $input->getSequence()) .
131
            $hashOutputs->getBinary() .
132
            pack("V", $this->transaction->getLockTime()) .
133
            pack("V", $sighashType)
134
        ));
135
    }
136
}
137