Completed
Pull Request — master (#285)
by thomas
21:56
created

V1Hasher   A

Complexity

Total Complexity 16

Size/Duplication

Total Lines 113
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 89.58%

Importance

Changes 6
Bugs 0 Features 1
Metric Value
wmc 16
lcom 1
cbo 10
dl 0
loc 113
ccs 43
cts 48
cp 0.8958
rs 10
c 6
b 0
f 1

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A hashPrevOuts() 0 12 3
B hashSequences() 0 12 5
B hashOutputs() 0 14 6
A calculate() 0 23 1
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 30
    public function __construct(TransactionInterface $transaction, $amount)
30
    {
31 30
        $this->transaction = $transaction;
32 30
        $this->amount = $amount;
33 30
    }
34
35
    /**
36
     * @param int $sighashType
37
     * @return Buffer|BufferInterface
38
     */
39 30
    public function hashPrevOuts($sighashType)
40
    {
41 30
        if (!($sighashType & SigHashInterface::ANYONECANPAY)) {
42 30
            $binary = '';
43 30
            foreach ($this->transaction->getInputs() as $input) {
44 30
                $binary .= $input->getOutPoint()->getBinary();
45 30
            }
46 30
            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 30
    public function hashSequences($sighashType)
57
    {
58 30
        if (!($sighashType & SigHashInterface::ANYONECANPAY) && ($sighashType & 0x1f) != SigHashInterface::SINGLE && ($sighashType & 0x1f) != SigHashInterface::NONE) {
59 30
            $binary = '';
60 30
            foreach ($this->transaction->getInputs() as $input) {
61 30
                $binary .= Buffer::int($input->getSequence())->flip()->getBinary();
62 30
            }
63 30
            return Hash::sha256d(new Buffer($binary));
64
        }
65
66
        return new Buffer('', 32);
67
    }
68
69
    /**
70
     * @param int $sighashType
71
     * @param int $inputToSign
72
     * @return Buffer|BufferInterface
73
     */
74 30
    public function hashOutputs($sighashType, $inputToSign)
75
    {
76 30
        if (($sighashType & 0x1f) !== SigHashInterface::SINGLE && ($sighashType & 0x1f) != SigHashInterface::NONE) {
77 30
            $binary = '';
78 30
            foreach ($this->transaction->getOutputs() as $output) {
79 30
                $binary .= $output->getBinary();
80 30
            }
81 30
            return Hash::sha256d(new Buffer($binary));
82
        } elseif (($sighashType & 0x1f) == SigHashInterface::SINGLE && $inputToSign < count($this->transaction->getOutputs())) {
83
            return Hash::sha256($this->transaction->getOutput($inputToSign)->getBuffer());
84
        }
85
86
        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 30
    public function calculate(ScriptInterface $txOutScript, $inputToSign, $sighashType = SigHashInterface::ALL)
102
    {
103 30
        $sighashType = (int) $sighashType;
104
105 30
        $hashPrevOuts = $this->hashPrevOuts($sighashType);
106 30
        $hashSequence = $this->hashSequences($sighashType);
107 30
        $hashOutputs = $this->hashOutputs($sighashType, $inputToSign);
108
109 30
        $input = $this->transaction->getInput($inputToSign);
110
111 30
        return Hash::sha256d(new Buffer(
112 30
            pack("V", $this->transaction->getVersion()) .
113 30
            $hashPrevOuts->getBinary() .
114 30
            $hashSequence->getBinary() .
115 30
            $input->getOutPoint()->getBinary() .
116 30
            ScriptFactory::create()->push($txOutScript->getBuffer())->getScript()->getBinary() .
117 30
            Buffer::int($this->amount, 8)->flip()->getBinary() .
118 30
            pack("V", $input->getSequence()) .
119 30
            $hashOutputs->getBinary() .
120 30
            pack("V", $this->transaction->getLockTime()) .
121 30
            pack("V", $sighashType)
122 30
        ));
123
    }
124
}
125