Completed
Pull Request — master (#474)
by thomas
23:00
created

TxSigHashSerializer::serializeOutput()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 2
nop 1
dl 0
loc 12
ccs 7
cts 7
cp 1
crap 3
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\SignatureHash;
4
5
6
use BitWasp\Bitcoin\Script\Opcodes;
7
use BitWasp\Bitcoin\Script\ScriptInterface;
8
use BitWasp\Bitcoin\Serializer\Types;
9
use BitWasp\Bitcoin\Transaction\TransactionInterface;
10
11
class TxSigHashSerializer
12
{
13
    /**
14
     * @var TransactionInterface
15
     */
16
    private $tx;
17
18
    /**
19
     * @var ScriptInterface
20
     */
21
    private $scriptCode;
22
23
    /**
24
     * @var
25
     */
26
    private $nIn;
27
28
    /**
29
     * @var bool
30
     */
31
    private $anyoneCanPay = false;
32
33
    /**
34
     * @var bool
35
     */
36
    private $hashSingle = false;
37
38
    /**
39
     * @var bool
40
     */
41
    private $hashNone = false;
42
43
    /**
44
     * @param TransactionInterface $tx
45
     * @param ScriptInterface $scriptCode
46
     * @param int $nIn
47
     * @param int $nHashTypeIn
48
     */
49 86
    public function __construct(TransactionInterface $tx, ScriptInterface $scriptCode, $nIn, $nHashTypeIn)
50
    {
51 86
        $this->tx = $tx;
52 86
        $this->scriptCode = $scriptCode;
53 86
        $this->nIn = $nIn;
54 86
        $this->anyoneCanPay = !!($nHashTypeIn & SigHash::ANYONECANPAY);
55
56 86
        $bits = $nHashTypeIn & 0x1f;
57 86
        $this->hashSingle = $bits === SigHash::SINGLE;
58 86
        $this->hashNone = $bits === SigHash::NONE;
59
60 86
        $this->varint = Types::varint();
0 ignored issues
show
Bug introduced by
The property varint does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
61 86
        $this->bs32le = Types::bytestringle(32);
0 ignored issues
show
Bug introduced by
The property bs32le does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
62 86
    }
63
64
    /**
65
     * @return string
66
     */
67 86
    private function serializeScript()
68
    {
69 86
        $script = $this->scriptCode;
70 86
        $parser = $script->getScriptParser();
71
72 86
        $nSize = $script->getBuffer()->getSize();
73 86
        $nSeparators = 0;
74 86
        foreach ($parser as $operation) {
75 86
            if ($operation->getOp() === Opcodes::OP_CODESEPARATOR) {
76 86
                $nSeparators++;
77
            }
78
        }
79
80 86
        $newSize = $nSize - $nSeparators;
81 86
        $out = $this->varint->write($newSize);
82 86
        $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...
83
84 86
        foreach ($parser as $operation) {
85 86
            if ($operation->getOp() === Opcodes::OP_CODESEPARATOR) {
86
                $position = $parser->getPosition();
87
                $out .= $parser->slice($position)->getBinary();
88 86
                $begin = $position;
89
            }
90
        }
91
92 86
        if ($begin !== $newSize) {
93 86
            $out .= $parser->slice($begin, $newSize - $begin)->getBinary();
94
        }
95
96 86
        return $out;
97
    }
98
99
    /**
100
     * @param int $nInput
101
     * @return string
102
     */
103 86
    public function serializeInput($nInput)
104
    {
105 86
        if ($this->anyoneCanPay) {
106 7
            $nInput = $this->nIn;
107
        }
108
109 86
        $txIn = $this->tx->getInput($nInput);
110 86
        $outpoint = $txIn->getOutPoint();
111 86
        $out = $this->bs32le->write($outpoint->getTxId())
112 86
            . pack('V', $outpoint->getVout());
113
114 86
        if ($nInput !== $this->nIn) {
115
            // script length is zero
116 10
            $out .= "\x00";
117
        } else {
118 86
            $out .= $this->serializeScript();
119
        }
120
121 86
        if ($nInput !== $this->nIn && ($this->hashSingle || $this->hashNone)) {
122 6
            $out .= pack('V', 0);
123
        } else {
124 86
            $out .= pack('V', $txIn->getSequence());
125
        }
126
127 86
        return $out;
128
    }
129
130
    /**
131
     * @param int $nOutput
132
     * @return string
133
     */
134 78
    public function serializeOutput($nOutput)
135
    {
136 78
        if ($this->hashSingle && $nOutput != $this->nIn) {
137 6
            $out = pack('P', -1) . "\x00";
138
        } else {
139 78
            $txOut = $this->tx->getOutput($nOutput);
140 78
            $scriptBuf = $txOut->getScript()->getBuffer();
141 78
            $out = pack('P', $txOut->getValue()) . $this->varint->write($scriptBuf->getSize()) . $scriptBuf->getBinary();
142
        }
143
144 78
        return $out;
145
    }
146
147
    /**
148
     * @return string
149
     */
150 86
    public function serializeTransaction()
151
    {
152 86
        $data = Types::int32le()->write($this->tx->getVersion());
153
154 86
        $nInputs = $this->anyoneCanPay ? 1 : count($this->tx->getInputs());
155 86
        $data .= $this->varint->write($nInputs);
156 86
        for ($i = 0; $i < $nInputs; $i++) {
157 86
            $data .= $this->serializeInput($i);
158
        }
159
160 86
        $nOutputs = $this->hashNone ? 0 : ($this->hashSingle ? $this->nIn + 1 : count($this->tx->getOutputs()));
161 86
        $data .= $this->varint->write($nOutputs);
162 86
        for ($i = 0; $i < $nOutputs; $i++) {
163 78
            $data .= $this->serializeOutput($i);
164
        }
165
166 86
        $data .= pack('V', $this->tx->getLockTime());
167 86
        return $data;
168
    }
169
}