Completed
Push — master ( 31933a...221a1f )
by thomas
20:56
created

TxSigHashSerializer::serializeTransaction()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 19
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 12
nc 32
nop 0
dl 0
loc 19
ccs 12
cts 12
cp 1
crap 6
rs 8.8571
c 0
b 0
f 0
1
<?php
2
3
namespace BitWasp\Bitcoin\Transaction\SignatureHash;
4
5
use BitWasp\Bitcoin\Script\Opcodes;
6
use BitWasp\Bitcoin\Script\ScriptInterface;
7
use BitWasp\Bitcoin\Serializer\Types;
8
use BitWasp\Bitcoin\Transaction\TransactionInterface;
9
10
class TxSigHashSerializer
11
{
12
    /**
13
     * @var TransactionInterface
14
     */
15
    private $tx;
16
17
    /**
18
     * @var ScriptInterface
19
     */
20
    private $scriptCode;
21
22
    /**
23
     * @var
24
     */
25
    private $nIn;
26
27
    /**
28
     * @var bool
29
     */
30
    private $anyoneCanPay = false;
31
32
    /**
33
     * @var bool
34
     */
35
    private $hashSingle = false;
36
37
    /**
38
     * @var bool
39
     */
40
    private $hashNone = false;
41
42
    /**
43
     * @param TransactionInterface $tx
44
     * @param ScriptInterface $scriptCode
45
     * @param int $nIn
46
     * @param int $nHashTypeIn
47
     */
48 86
    public function __construct(TransactionInterface $tx, ScriptInterface $scriptCode, $nIn, $nHashTypeIn)
49
    {
50 86
        $this->tx = $tx;
51 86
        $this->scriptCode = $scriptCode;
52 86
        $this->nIn = $nIn;
53 86
        $this->anyoneCanPay = !!($nHashTypeIn & SigHash::ANYONECANPAY);
54
55 86
        $bits = $nHashTypeIn & 0x1f;
56 86
        $this->hashSingle = $bits === SigHash::SINGLE;
57 86
        $this->hashNone = $bits === SigHash::NONE;
58
59 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...
60 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...
61 86
    }
62
63
    /**
64
     * @return string
65
     */
66 86
    private function serializeScript()
67
    {
68 86
        $script = $this->scriptCode;
69 86
        $parser = $script->getScriptParser();
70
71 86
        $nSize = $script->getBuffer()->getSize();
72 86
        $nSeparators = 0;
73 86
        foreach ($parser as $operation) {
74 86
            if ($operation->getOp() === Opcodes::OP_CODESEPARATOR) {
75 86
                $nSeparators++;
76
            }
77
        }
78
79 86
        $newSize = $nSize - $nSeparators;
80 86
        $out = $this->varint->write($newSize);
81 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...
82
83 86
        foreach ($parser as $operation) {
84 86
            if ($operation->getOp() === Opcodes::OP_CODESEPARATOR) {
85
                $position = $parser->getPosition();
86
                $out .= $parser->slice($position)->getBinary();
87 86
                $begin = $position;
88
            }
89
        }
90
91 86
        if ($begin !== $newSize) {
92 86
            $out .= $parser->slice($begin, $newSize - $begin)->getBinary();
93
        }
94
95 86
        return $out;
96
    }
97
98
    /**
99
     * @param int $nInput
100
     * @return string
101
     */
102 86
    public function serializeInput($nInput)
103
    {
104 86
        if ($this->anyoneCanPay) {
105 7
            $nInput = $this->nIn;
106
        }
107
108 86
        $txIn = $this->tx->getInput($nInput);
109 86
        $outpoint = $txIn->getOutPoint();
110 86
        $out = $this->bs32le->write($outpoint->getTxId())
111 86
            . pack('V', $outpoint->getVout());
112
113 86
        if ($nInput !== $this->nIn) {
114
            // script length is zero
115 10
            $out .= "\x00";
116
        } else {
117 86
            $out .= $this->serializeScript();
118
        }
119
120 86
        if ($nInput !== $this->nIn && ($this->hashSingle || $this->hashNone)) {
121 6
            $out .= pack('V', 0);
122
        } else {
123 86
            $out .= pack('V', $txIn->getSequence());
124
        }
125
126 86
        return $out;
127
    }
128
129
    /**
130
     * @param int $nOutput
131
     * @return string
132
     */
133 78
    public function serializeOutput($nOutput)
134
    {
135 78
        if ($this->hashSingle && $nOutput != $this->nIn) {
136 6
            $out = pack('P', -1) . "\x00";
137
        } else {
138 78
            $txOut = $this->tx->getOutput($nOutput);
139 78
            $scriptBuf = $txOut->getScript()->getBuffer();
140 78
            $out = pack('P', $txOut->getValue()) . $this->varint->write($scriptBuf->getSize()) . $scriptBuf->getBinary();
141
        }
142
143 78
        return $out;
144
    }
145
146
    /**
147
     * @return string
148
     */
149 86
    public function serializeTransaction()
150
    {
151 86
        $data = Types::int32le()->write($this->tx->getVersion());
152
153 86
        $nInputs = $this->anyoneCanPay ? 1 : count($this->tx->getInputs());
154 86
        $data .= $this->varint->write($nInputs);
155 86
        for ($i = 0; $i < $nInputs; $i++) {
156 86
            $data .= $this->serializeInput($i);
157
        }
158
159 86
        $nOutputs = $this->hashNone ? 0 : ($this->hashSingle ? $this->nIn + 1 : count($this->tx->getOutputs()));
160 86
        $data .= $this->varint->write($nOutputs);
161 86
        for ($i = 0; $i < $nOutputs; $i++) {
162 78
            $data .= $this->serializeOutput($i);
163
        }
164
165 86
        $data .= pack('V', $this->tx->getLockTime());
166 86
        return $data;
167
    }
168
}
169