Completed
Pull Request — master (#593)
by thomas
15:02
created

TxSigHashSerializer::serializeOutput()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 8
nc 2
nop 1
dl 0
loc 12
ccs 6
cts 7
cp 0.8571
crap 3.0261
rs 9.4285
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace BitWasp\Bitcoin\Transaction\SignatureHash;
6
7
use BitWasp\Bitcoin\Script\Opcodes;
8
use BitWasp\Bitcoin\Script\ScriptInterface;
9
use BitWasp\Bitcoin\Serializer\Types;
10
use BitWasp\Bitcoin\Transaction\TransactionInterface;
11
12
class TxSigHashSerializer
13
{
14
    /**
15
     * @var TransactionInterface
16
     */
17
    private $tx;
18
19
    /**
20
     * @var ScriptInterface
21
     */
22
    private $scriptCode;
23
24
    /**
25
     * @var
26
     */
27
    private $nIn;
28
29
    /**
30
     * @var \BitWasp\Buffertools\Types\VarInt
31
     */
32
    private $varint;
33
34
    /**
35
     * @var \BitWasp\Buffertools\Types\ByteString
36
     */
37
    private $bs32le;
38
39
    /**
40
     * @var bool
41
     */
42
    private $anyoneCanPay = false;
43
44
    /**
45
     * @var bool
46
     */
47
    private $hashSingle = false;
48
49
    /**
50
     * @var bool
51
     */
52
    private $hashNone = false;
53
54
    /**
55
     * @param TransactionInterface $tx
56
     * @param ScriptInterface $scriptCode
57
     * @param int $nIn
58
     * @param int $nHashTypeIn
59
     */
60 195
    public function __construct(TransactionInterface $tx, ScriptInterface $scriptCode, int $nIn, int $nHashTypeIn)
61
    {
62 195
        $this->tx = $tx;
63 195
        $this->scriptCode = $scriptCode;
64 195
        $this->nIn = $nIn;
65 195
        $this->anyoneCanPay = !!($nHashTypeIn & SigHash::ANYONECANPAY);
66
67 195
        $bits = $nHashTypeIn & 0x1f;
68 195
        $this->hashSingle = $bits === SigHash::SINGLE;
69 195
        $this->hashNone = $bits === SigHash::NONE;
70
71 195
        $this->varint = Types::varint();
72 195
        $this->bs32le = Types::bytestringle(32);
73 195
    }
74
75
    /**
76
     * @return string
77
     */
78 195
    private function serializeScript(): string
79
    {
80 195
        $script = $this->scriptCode;
81 195
        $parser = $script->getScriptParser();
82
83 195
        $nSize = $script->getBuffer()->getSize();
84 195
        $nSeparators = 0;
85 195
        foreach ($parser as $operation) {
86 195
            if ($operation->getOp() === Opcodes::OP_CODESEPARATOR) {
87 195
                $nSeparators++;
88
            }
89
        }
90
91 195
        $newSize = $nSize - $nSeparators;
92 195
        $out = $this->varint->write($newSize);
93 195
        $begin = $position = 0;
0 ignored issues
show
Unused Code introduced by
$position is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
94
95 195
        foreach ($parser as $operation) {
96 195
            if ($operation->getOp() === Opcodes::OP_CODESEPARATOR) {
97
                $position = $parser->getPosition();
98
                $out .= $parser->slice($position)->getBinary();
99 195
                $begin = $position;
100
            }
101
        }
102
103 195
        if ($begin !== $newSize) {
104 195
            $out .= $parser->slice($begin, $newSize - $begin)->getBinary();
105
        }
106
107 195
        return $out;
108
    }
109
110
    /**
111
     * @param int $nInput
112
     * @return string
113
     */
114 195
    public function serializeInput(int $nInput): string
115
    {
116 195
        if ($this->anyoneCanPay) {
117 4
            $nInput = $this->nIn;
118
        }
119
120 195
        $txIn = $this->tx->getInput($nInput);
121 195
        $outpoint = $txIn->getOutPoint();
122 195
        $out = $this->bs32le->write($outpoint->getTxId()) . pack('V', $outpoint->getVout());
0 ignored issues
show
Compatibility introduced by
$outpoint->getTxId() of type object<BitWasp\Buffertools\BufferInterface> is not a sub-type of object<BitWasp\Buffertools\Buffer>. It seems like you assume a concrete implementation of the interface BitWasp\Buffertools\BufferInterface to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
123
124 195
        if ($nInput !== $this->nIn) {
125
            // script length is zero
126
            $out .= "\x00";
127
        } else {
128 195
            $out .= $this->serializeScript();
129
        }
130
131 195
        if ($nInput !== $this->nIn && ($this->hashSingle || $this->hashNone)) {
132
            $out .= pack('V', 0);
133
        } else {
134 195
            $out .= pack('V', $txIn->getSequence());
135
        }
136
137 195
        return $out;
138
    }
139
140
    /**
141
     * @param int $nOutput
142
     * @return string
143
     */
144 192
    public function serializeOutput(int $nOutput): string
145
    {
146 192
        if ($this->hashSingle && $nOutput != $this->nIn) {
147
            $out = pack('P', -1) . "\x00";
148
        } else {
149 192
            $txOut = $this->tx->getOutput($nOutput);
150 192
            $scriptBuf = $txOut->getScript()->getBuffer();
151 192
            $out = pack('P', $txOut->getValue()) . $this->varint->write($scriptBuf->getSize()) . $scriptBuf->getBinary();
152
        }
153
154 192
        return $out;
155
    }
156
157
    /**
158
     * @return string
159
     */
160 195
    public function serializeTransaction(): string
161
    {
162 195
        $data = Types::int32le()->write($this->tx->getVersion());
163
164 195
        $nInputs = $this->anyoneCanPay ? 1 : count($this->tx->getInputs());
165 195
        $data .= $this->varint->write($nInputs);
166 195
        for ($i = 0; $i < $nInputs; $i++) {
167 195
            $data .= $this->serializeInput($i);
168
        }
169
170 195
        $nOutputs = $this->hashNone ? 0 : ($this->hashSingle ? $this->nIn + 1 : count($this->tx->getOutputs()));
171 195
        $data .= $this->varint->write($nOutputs);
172 195
        for ($i = 0; $i < $nOutputs; $i++) {
173 192
            $data .= $this->serializeOutput($i);
174
        }
175
176 195
        $data .= pack('V', $this->tx->getLockTime());
177 195
        return $data;
178
    }
179
}
180