Completed
Push — master ( ef7efa...b4d73d )
by thomas
24:48
created

Parser::validateSize()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 9
ccs 6
cts 6
cp 1
rs 9.6667
cc 2
eloc 6
nc 2
nop 1
crap 2
1
<?php
2
3
namespace BitWasp\Bitcoin\Script\Parser;
4
5
use BitWasp\Bitcoin\Script\Opcodes;
6
use BitWasp\Bitcoin\Math\Math;
7
use BitWasp\Bitcoin\Script\ScriptInterface;
8
use BitWasp\Buffertools\Buffer;
9
10
class Parser implements \Iterator
11
{
12
    /**
13
     * @var Math
14
     */
15
    private $math;
16
17
    /**
18
     * @var Buffer
19
     */
20
    private $empty;
21
22
    /**
23
     * @var ScriptInterface
24
     */
25
    private $script;
26
27
    /**
28
     * @var int
29
     */
30
    private $position = 0;
31
32
    /**
33
     * @var int
34
     */
35
    private $end = 0;
36
37
    /**
38
     * @var int
39
     */
40
    private $execPtr = 0;
41
42
    /**
43
     * @var string
44
     */
45
    private $data = '';
46
47
    /**
48
     * @var Operation[]
49
     */
50
    private $array = array();
51
52
    /**
53
     * ScriptParser constructor.
54
     * @param Math $math
55
     * @param ScriptInterface $script
56
     */
57 1113
    public function __construct(Math $math, ScriptInterface $script)
58
    {
59 1113
        $this->math = $math;
60 1113
        $buffer = $script->getBuffer();
61 1113
        $this->data = $buffer->getBinary();
62 1113
        $this->end = $buffer->getSize();
0 ignored issues
show
Documentation Bug introduced by
It seems like $buffer->getSize() can also be of type string. However, the property $end is declared as type integer. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
63 1113
        $this->script = $script;
64 1113
        $this->empty = new Buffer('', 0, $math);
65 1113
    }
66
67
    /**
68
     * @return int
69
     */
70
    public function getPosition()
71
    {
72
        return $this->position;
73
    }
74
75
    /**
76
     * @param string $packFormat
77
     * @param integer $strSize
78
     * @return array|bool
79
     */
80 54
    private function unpackSize($packFormat, $strSize)
81
    {
82 54
        if ($this->end - $this->position < $strSize) {
83
            return false;
84
        }
85
86 54
        $size = unpack($packFormat, substr($this->data, $this->position, $strSize));
87 54
        $size = $size[1];
88 54
        $this->position += $strSize;
89
90 54
        return $size;
91
    }
92
93
    /**
94
     * @param int $ptr
95
     * @return Operation
96
     */
97 1035
    private function doNext($ptr)
98
    {
99 1035
        if ($this->math->cmp($this->position, $this->end) >= 0) {
100
            throw new \RuntimeException('Position exceeds end of script!');
101
        }
102
103 1035
        $opCode = ord($this->data[$this->position++]);
104 1035
        $pushData = $this->empty;
105 1035
        $dataSize = 0;
106
107 1035
        if ($opCode <= Opcodes::OP_PUSHDATA4) {
108 771
            if ($opCode < Opcodes::OP_PUSHDATA1) {
109 747
                $dataSize = $opCode;
110 771
            } else if ($opCode === Opcodes::OP_PUSHDATA1) {
111 30
                $dataSize = $this->unpackSize('C', 1);
112 54
            } else if ($opCode === Opcodes::OP_PUSHDATA2) {
113 12
                $dataSize = $this->unpackSize('v', 2);
114 12
            } else {
115 12
                $dataSize = $this->unpackSize('V', 4);
116
            }
117
118 771
            $delta = ($this->end - $this->position);
119 771
            if ($dataSize === false || $delta < 0 || $delta < $dataSize) {
120
                throw new \RuntimeException('Failed to unpack data from Script');
121
            }
122
123 771
            if ($dataSize > 0) {
124 633
                $pushData = new Buffer(substr($this->data, $this->position, $dataSize), $dataSize, $this->math);
125 633
            }
126
127 771
            $this->position += $dataSize;
128 771
        }
129
130 1035
        $this->array[$ptr] = new Operation($opCode, $pushData, $dataSize);
131
132 1035
        return $this->array[$ptr];
133
    }
134
135
    /**
136
     *
137
     */
138 1071
    public function rewind()
139
    {
140 1071
        $this->execPtr = 0;
141 1071
    }
142
143
    /**
144
     * @return Operation
145
     */
146 1035
    public function current()
147
    {
148 1035
        if (isset($this->array[$this->execPtr])) {
149
            $exec = $this->array[$this->execPtr];
150
        } else {
151 1035
            $exec = $this->doNext($this->execPtr);
152
        }
153
154 1035
        return $exec;
155
    }
156
157
    /**
158
     * @return int
159
     */
160
    public function key()
161
    {
162
        return $this->execPtr;
163
    }
164
165
    /**
166
     * @return Operation
167
     */
168 1065
    public function next()
169
    {
170 1065
        $ptr = $this->execPtr;
171 1065
        if (isset($this->array[$ptr])) {
172 1029
            $this->execPtr++;
173 1029
            return $this->array[$ptr];
174
        }
175
176 36
        return null;
177
    }
178
179
    /**
180
     * @return bool
181
     */
182 1071
    public function valid()
183
    {
184 1071
        return isset($this->array[$this->execPtr]) || $this->position < $this->end;
185
    }
186
187
    /**
188
     * @return Operation[]
189
     */
190 999
    public function decode()
191
    {
192 999
        $result = [];
193 999
        foreach ($this as $operation) {
194 969
            $result[] = $operation;
195 999
        }
196
197 999
        return $result;
198
    }
199
200
    /**
201
     * @return string
202
     */
203 672
    public function getHumanReadable()
204
    {
205 672
        return implode(' ', array_map(
206 672
            function (Operation $operation) {
207 672
                return $operation->isPush()
208 672
                    ? $operation->getData()->getHex()
209 672
                    : $this->script->getOpcodes()->getOp($operation->getOp());
210 672
            },
211 672
            $this->decode()
212 672
        ));
213
    }
214
}
215