1 | <?php |
||||||
2 | |||||||
3 | declare(strict_types=1); |
||||||
4 | |||||||
5 | namespace BitWasp\Bitcoin\Script\Interpreter; |
||||||
6 | |||||||
7 | use BitWasp\Bitcoin\Bitcoin; |
||||||
8 | use BitWasp\Bitcoin\Crypto\EcAdapter\Adapter\EcAdapterInterface; |
||||||
9 | use BitWasp\Bitcoin\Crypto\Hash; |
||||||
10 | use BitWasp\Bitcoin\Exceptions\ScriptRuntimeException; |
||||||
11 | use BitWasp\Bitcoin\Exceptions\SignatureNotCanonical; |
||||||
12 | use BitWasp\Bitcoin\Script\Classifier\OutputClassifier; |
||||||
13 | use BitWasp\Bitcoin\Script\Opcodes; |
||||||
14 | use BitWasp\Bitcoin\Script\Script; |
||||||
15 | use BitWasp\Bitcoin\Script\ScriptFactory; |
||||||
16 | use BitWasp\Bitcoin\Script\ScriptInterface; |
||||||
17 | use BitWasp\Bitcoin\Script\ScriptWitness; |
||||||
18 | use BitWasp\Bitcoin\Script\ScriptWitnessInterface; |
||||||
19 | use BitWasp\Bitcoin\Script\WitnessProgram; |
||||||
20 | use BitWasp\Bitcoin\Signature\TransactionSignature; |
||||||
21 | use BitWasp\Bitcoin\Transaction\SignatureHash\SigHash; |
||||||
22 | use BitWasp\Bitcoin\Transaction\TransactionInputInterface; |
||||||
23 | use BitWasp\Buffertools\Buffer; |
||||||
24 | use BitWasp\Buffertools\BufferInterface; |
||||||
25 | |||||||
26 | class Interpreter implements InterpreterInterface |
||||||
27 | { |
||||||
28 | |||||||
29 | /** |
||||||
30 | * @var \BitWasp\Bitcoin\Math\Math |
||||||
31 | */ |
||||||
32 | private $math; |
||||||
33 | |||||||
34 | /** |
||||||
35 | * @var BufferInterface |
||||||
36 | */ |
||||||
37 | private $vchFalse; |
||||||
38 | |||||||
39 | /** |
||||||
40 | * @var BufferInterface |
||||||
41 | */ |
||||||
42 | private $vchTrue; |
||||||
43 | |||||||
44 | /** |
||||||
45 | * @var array |
||||||
46 | */ |
||||||
47 | private $disabledOps = [ |
||||||
48 | Opcodes::OP_CAT, Opcodes::OP_SUBSTR, Opcodes::OP_LEFT, Opcodes::OP_RIGHT, |
||||||
49 | Opcodes::OP_INVERT, Opcodes::OP_AND, Opcodes::OP_OR, Opcodes::OP_XOR, |
||||||
50 | Opcodes::OP_2MUL, Opcodes::OP_2DIV, Opcodes::OP_MUL, Opcodes::OP_DIV, |
||||||
51 | Opcodes::OP_MOD, Opcodes::OP_LSHIFT, Opcodes::OP_RSHIFT |
||||||
52 | ]; |
||||||
53 | |||||||
54 | /** |
||||||
55 | * @param EcAdapterInterface $ecAdapter |
||||||
56 | */ |
||||||
57 | 2468 | public function __construct(EcAdapterInterface $ecAdapter = null) |
|||||
58 | { |
||||||
59 | 2468 | $ecAdapter = $ecAdapter ?: Bitcoin::getEcAdapter(); |
|||||
60 | 2468 | $this->math = $ecAdapter->getMath(); |
|||||
61 | 2468 | $this->vchFalse = new Buffer("", 0); |
|||||
62 | 2468 | $this->vchTrue = new Buffer("\x01", 1); |
|||||
63 | 2468 | } |
|||||
64 | |||||||
65 | /** |
||||||
66 | * Cast the value to a boolean |
||||||
67 | * |
||||||
68 | * @param BufferInterface $value |
||||||
69 | * @return bool |
||||||
70 | */ |
||||||
71 | 3518 | public function castToBool(BufferInterface $value): bool |
|||||
72 | { |
||||||
73 | 3518 | $val = $value->getBinary(); |
|||||
74 | 3518 | for ($i = 0, $size = strlen($val); $i < $size; $i++) { |
|||||
75 | 3237 | $chr = ord($val[$i]); |
|||||
76 | 3237 | if ($chr !== 0) { |
|||||
77 | 3236 | if (($i === ($size - 1)) && $chr === 0x80) { |
|||||
78 | return false; |
||||||
79 | } |
||||||
80 | 3236 | return true; |
|||||
81 | } |
||||||
82 | } |
||||||
83 | 867 | return false; |
|||||
84 | } |
||||||
85 | |||||||
86 | /** |
||||||
87 | * @param BufferInterface $signature |
||||||
88 | * @return bool |
||||||
89 | */ |
||||||
90 | public function isValidSignatureEncoding(BufferInterface $signature): bool |
||||||
91 | { |
||||||
92 | try { |
||||||
93 | TransactionSignature::isDERSignature($signature); |
||||||
94 | return true; |
||||||
95 | } catch (SignatureNotCanonical $e) { |
||||||
96 | /* In any case, we will return false outside this block */ |
||||||
97 | } |
||||||
98 | |||||||
99 | return false; |
||||||
100 | } |
||||||
101 | |||||||
102 | /** |
||||||
103 | * @param int $opCode |
||||||
104 | * @param BufferInterface $pushData |
||||||
105 | * @return bool |
||||||
106 | * @throws \Exception |
||||||
107 | */ |
||||||
108 | 461 | public function checkMinimalPush($opCode, BufferInterface $pushData): bool |
|||||
109 | { |
||||||
110 | 461 | $pushSize = $pushData->getSize(); |
|||||
111 | 461 | $binary = $pushData->getBinary(); |
|||||
112 | |||||||
113 | 461 | if ($pushSize === 0) { |
|||||
114 | 217 | return $opCode === Opcodes::OP_0; |
|||||
115 | 377 | } elseif ($pushSize === 1) { |
|||||
116 | 89 | $first = ord($binary[0]); |
|||||
117 | |||||||
118 | 89 | if ($first >= 1 && $first <= 16) { |
|||||
119 | 65 | return $opCode === (Opcodes::OP_1 + ($first - 1)); |
|||||
120 | 25 | } elseif ($first === 0x81) { |
|||||
121 | 25 | return $opCode === Opcodes::OP_1NEGATE; |
|||||
122 | } |
||||||
123 | 289 | } elseif ($pushSize <= 75) { |
|||||
124 | 281 | return $opCode === $pushSize; |
|||||
125 | 9 | } elseif ($pushSize <= 255) { |
|||||
126 | 5 | return $opCode === Opcodes::OP_PUSHDATA1; |
|||||
127 | 5 | } elseif ($pushSize <= 65535) { |
|||||
128 | 5 | return $opCode === Opcodes::OP_PUSHDATA2; |
|||||
129 | } |
||||||
130 | |||||||
131 | 21 | return true; |
|||||
132 | } |
||||||
133 | |||||||
134 | /** |
||||||
135 | * @param int $count |
||||||
136 | * @return $this |
||||||
137 | */ |
||||||
138 | 4409 | private function checkOpcodeCount(int $count) |
|||||
139 | { |
||||||
140 | 4409 | if ($count > 201) { |
|||||
141 | 20 | throw new \RuntimeException('Error: Script op code count'); |
|||||
142 | } |
||||||
143 | |||||||
144 | 4409 | return $this; |
|||||
145 | } |
||||||
146 | |||||||
147 | /** |
||||||
148 | * @param WitnessProgram $witnessProgram |
||||||
149 | * @param ScriptWitnessInterface $scriptWitness |
||||||
150 | * @param int $flags |
||||||
151 | * @param CheckerBase $checker |
||||||
152 | * @return bool |
||||||
153 | */ |
||||||
154 | 268 | private function verifyWitnessProgram(WitnessProgram $witnessProgram, ScriptWitnessInterface $scriptWitness, int $flags, CheckerBase $checker): bool |
|||||
155 | { |
||||||
156 | 268 | $witnessCount = count($scriptWitness); |
|||||
157 | |||||||
158 | 268 | if ($witnessProgram->getVersion() === 0) { |
|||||
159 | 264 | $buffer = $witnessProgram->getProgram(); |
|||||
160 | 264 | if ($buffer->getSize() === 32) { |
|||||
161 | // Version 0 segregated witness program: SHA256(Script) in program, Script + inputs in witness |
||||||
162 | 232 | if ($witnessCount === 0) { |
|||||
163 | // Must contain script at least |
||||||
164 | 4 | return false; |
|||||
165 | } |
||||||
166 | |||||||
167 | 228 | $scriptPubKey = new Script($scriptWitness[$witnessCount - 1]); |
|||||
168 | 228 | $stackValues = $scriptWitness->slice(0, -1); |
|||||
169 | 228 | if (!$buffer->equals($scriptPubKey->getWitnessScriptHash())) { |
|||||
170 | 228 | return false; |
|||||
171 | } |
||||||
172 | 32 | } elseif ($buffer->getSize() === 20) { |
|||||
173 | // Version 0 special case for pay-to-pubkeyhash |
||||||
174 | 28 | if ($witnessCount !== 2) { |
|||||
175 | // 2 items in witness - <signature> <pubkey> |
||||||
176 | 4 | return false; |
|||||
177 | } |
||||||
178 | |||||||
179 | 24 | $scriptPubKey = ScriptFactory::scriptPubKey()->payToPubKeyHash($buffer); |
|||||
180 | 24 | $stackValues = $scriptWitness; |
|||||
181 | } else { |
||||||
182 | 248 | return false; |
|||||
183 | } |
||||||
184 | 4 | } elseif ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM) { |
|||||
185 | 4 | return false; |
|||||
186 | } else { |
||||||
187 | // Unknown versions are always 'valid' to permit future soft forks |
||||||
188 | return true; |
||||||
189 | } |
||||||
190 | |||||||
191 | 244 | $mainStack = new Stack(); |
|||||
192 | 244 | foreach ($stackValues as $value) { |
|||||
193 | 208 | $mainStack->push($value); |
|||||
194 | } |
||||||
195 | |||||||
196 | 244 | if (!$this->evaluate($scriptPubKey, $mainStack, SigHash::V1, $flags, $checker)) { |
|||||
197 | 80 | return false; |
|||||
198 | } |
||||||
199 | |||||||
200 | 164 | if ($mainStack->count() !== 1) { |
|||||
201 | 56 | return false; |
|||||
202 | } |
||||||
203 | |||||||
204 | 108 | if (!$this->castToBool($mainStack->bottom())) { |
|||||
205 | 36 | return false; |
|||||
206 | } |
||||||
207 | |||||||
208 | 72 | return true; |
|||||
209 | } |
||||||
210 | |||||||
211 | /** |
||||||
212 | * @param ScriptInterface $scriptSig |
||||||
213 | * @param ScriptInterface $scriptPubKey |
||||||
214 | * @param int $flags |
||||||
215 | * @param CheckerBase $checker |
||||||
216 | * @param ScriptWitnessInterface|null $witness |
||||||
217 | * @return bool |
||||||
218 | */ |
||||||
219 | 4667 | public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, int $flags, CheckerBase $checker, ScriptWitnessInterface $witness = null): bool |
|||||
220 | { |
||||||
221 | 4667 | static $emptyWitness = null; |
|||||
222 | 4667 | if ($emptyWitness === null) { |
|||||
223 | 1 | $emptyWitness = new ScriptWitness(); |
|||||
224 | } |
||||||
225 | |||||||
226 | 4667 | $witness = is_null($witness) ? $emptyWitness : $witness; |
|||||
227 | |||||||
228 | 4667 | if (($flags & self::VERIFY_SIGPUSHONLY) !== 0 && !$scriptSig->isPushOnly()) { |
|||||
229 | 8 | return false; |
|||||
230 | } |
||||||
231 | |||||||
232 | 4659 | $stack = new Stack(); |
|||||
233 | 4659 | if (!$this->evaluate($scriptSig, $stack, SigHash::V0, $flags, $checker)) { |
|||||
234 | 221 | return false; |
|||||
235 | } |
||||||
236 | |||||||
237 | 4438 | $backup = []; |
|||||
238 | 4438 | if ($flags & self::VERIFY_P2SH) { |
|||||
239 | 3315 | foreach ($stack as $s) { |
|||||
240 | 2646 | $backup[] = $s; |
|||||
241 | } |
||||||
242 | } |
||||||
243 | |||||||
244 | 4438 | if (!$this->evaluate($scriptPubKey, $stack, SigHash::V0, $flags, $checker)) { |
|||||
245 | 1342 | return false; |
|||||
246 | } |
||||||
247 | |||||||
248 | 3096 | if ($stack->isEmpty()) { |
|||||
249 | 61 | return false; |
|||||
250 | } |
||||||
251 | |||||||
252 | 3035 | if (false === $this->castToBool($stack[-1])) { |
|||||
253 | 185 | return false; |
|||||
254 | } |
||||||
255 | |||||||
256 | 2850 | $program = null; |
|||||
257 | 2850 | if ($flags & self::VERIFY_WITNESS) { |
|||||
258 | 348 | if ($scriptPubKey->isWitness($program)) { |
|||||
259 | /** @var WitnessProgram $program */ |
||||||
260 | 152 | if ($scriptSig->getBuffer()->getSize() !== 0) { |
|||||
261 | 4 | return false; |
|||||
262 | } |
||||||
263 | |||||||
264 | 148 | if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) { |
|||||
265 | 112 | return false; |
|||||
266 | } |
||||||
267 | |||||||
268 | 36 | $stack->resize(1); |
|||||
269 | } |
||||||
270 | } |
||||||
271 | |||||||
272 | 2734 | if ($flags & self::VERIFY_P2SH && (new OutputClassifier())->isPayToScriptHash($scriptPubKey)) { |
|||||
273 | 246 | if (!$scriptSig->isPushOnly()) { |
|||||
274 | 13 | return false; |
|||||
275 | } |
||||||
276 | |||||||
277 | 233 | $stack = new Stack(); |
|||||
278 | 233 | foreach ($backup as $i) { |
|||||
279 | 233 | $stack->push($i); |
|||||
280 | } |
||||||
281 | |||||||
282 | // Restore mainStack to how it was after evaluating scriptSig |
||||||
283 | 233 | if ($stack->isEmpty()) { |
|||||
284 | return false; |
||||||
285 | } |
||||||
286 | |||||||
287 | // Load redeemscript as the scriptPubKey |
||||||
288 | 233 | $scriptPubKey = new Script($stack->bottom()); |
|||||
289 | 233 | $stack->pop(); |
|||||
290 | |||||||
291 | 233 | if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) { |
|||||
292 | 25 | return false; |
|||||
293 | } |
||||||
294 | |||||||
295 | 208 | if ($stack->isEmpty()) { |
|||||
296 | 20 | return false; |
|||||
297 | } |
||||||
298 | |||||||
299 | 188 | if (!$this->castToBool($stack->bottom())) { |
|||||
300 | 4 | return false; |
|||||
301 | } |
||||||
302 | |||||||
303 | 184 | if ($flags & self::VERIFY_WITNESS) { |
|||||
304 | 144 | if ($scriptPubKey->isWitness($program)) { |
|||||
305 | /** @var WitnessProgram $program */ |
||||||
306 | 124 | if (!$scriptSig->equals(ScriptFactory::sequence([$scriptPubKey->getBuffer()]))) { |
|||||
307 | 4 | return false; // SCRIPT_ERR_WITNESS_MALLEATED_P2SH |
|||||
308 | } |
||||||
309 | |||||||
310 | 120 | if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) { |
|||||
311 | 84 | return false; |
|||||
312 | } |
||||||
313 | |||||||
314 | 36 | $stack->resize(1); |
|||||
315 | } |
||||||
316 | } |
||||||
317 | } |
||||||
318 | |||||||
319 | 2584 | if ($flags & self::VERIFY_CLEAN_STACK) { |
|||||
320 | 12 | if (!($flags & self::VERIFY_P2SH !== 0) && ($flags & self::VERIFY_WITNESS !== 0)) { |
|||||
321 | return false; // implied flags required |
||||||
322 | } |
||||||
323 | |||||||
324 | 12 | if (count($stack) !== 1) { |
|||||
325 | 8 | return false; // Cleanstack |
|||||
326 | } |
||||||
327 | } |
||||||
328 | |||||||
329 | 2576 | if ($flags & self::VERIFY_WITNESS) { |
|||||
330 | 116 | if (!$flags & self::VERIFY_P2SH) { |
|||||
331 | return false; // |
||||||
332 | } |
||||||
333 | |||||||
334 | 116 | if ($program === null && !$witness->isNull()) { |
|||||
335 | 4 | return false; // SCRIPT_ERR_WITNESS_UNEXPECTED |
|||||
336 | } |
||||||
337 | } |
||||||
338 | |||||||
339 | 2572 | return true; |
|||||
340 | } |
||||||
341 | |||||||
342 | /** |
||||||
343 | * @param Stack $vfStack |
||||||
344 | * @param bool $value |
||||||
345 | * @return bool |
||||||
346 | */ |
||||||
347 | 4760 | public function checkExec(Stack $vfStack, bool $value): bool |
|||||
348 | { |
||||||
349 | 4760 | $ret = 0; |
|||||
350 | 4760 | foreach ($vfStack as $item) { |
|||||
351 | 1171 | if ($item === $value) { |
|||||
352 | 738 | $ret++; |
|||||
353 | } |
||||||
354 | } |
||||||
355 | |||||||
356 | 4760 | return (bool) $ret; |
|||||
357 | } |
||||||
358 | |||||||
359 | /** |
||||||
360 | * @param ScriptInterface $script |
||||||
361 | * @param Stack $mainStack |
||||||
362 | * @param int $sigVersion |
||||||
363 | * @param int $flags |
||||||
364 | * @param CheckerBase $checker |
||||||
365 | * @return bool |
||||||
366 | */ |
||||||
367 | 4660 | public function evaluate(ScriptInterface $script, Stack $mainStack, int $sigVersion, int $flags, CheckerBase $checker): bool |
|||||
368 | { |
||||||
369 | 4660 | $hashStartPos = 0; |
|||||
370 | 4660 | $opCount = 0; |
|||||
371 | 4660 | $zero = gmp_init(0, 10); |
|||||
372 | 4660 | $altStack = new Stack(); |
|||||
373 | 4660 | $vfStack = new Stack(); |
|||||
374 | 4660 | $minimal = ($flags & self::VERIFY_MINIMALDATA) !== 0; |
|||||
375 | 4660 | $parser = $script->getScriptParser(); |
|||||
376 | |||||||
377 | 4660 | if ($script->getBuffer()->getSize() > 10000) { |
|||||
378 | 4 | return false; |
|||||
379 | } |
||||||
380 | |||||||
381 | try { |
||||||
382 | 4660 | foreach ($parser as $operation) { |
|||||
383 | 4643 | $opCode = $operation->getOp(); |
|||||
384 | 4643 | $pushData = $operation->getData(); |
|||||
385 | 4643 | $fExec = !$this->checkExec($vfStack, false); |
|||||
386 | |||||||
387 | // If pushdata was written to |
||||||
388 | 4643 | if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) { |
|||||
389 | 9 | throw new \RuntimeException('Error - push size'); |
|||||
390 | } |
||||||
391 | |||||||
392 | // OP_RESERVED should not count towards opCount |
||||||
393 | 4642 | if ($opCode > Opcodes::OP_16 && ++$opCount) { |
|||||
394 | 4409 | $this->checkOpcodeCount($opCount); |
|||||
395 | } |
||||||
396 | |||||||
397 | 4642 | if (in_array($opCode, $this->disabledOps, true)) { |
|||||
398 | 96 | throw new \RuntimeException('Disabled Opcode'); |
|||||
399 | } |
||||||
400 | |||||||
401 | 4642 | if ($fExec && $operation->isPush()) { |
|||||
402 | // In range of a pushdata opcode |
||||||
403 | 3412 | if ($minimal && !$this->checkMinimalPush($opCode, $pushData)) { |
|||||
404 | 84 | throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required'); |
|||||
405 | } |
||||||
406 | |||||||
407 | 3328 | $mainStack->push($pushData); |
|||||
408 | // echo " - [pushed '" . $pushData->getHex() . "']\n"; |
||||||
409 | 4501 | } elseif ($fExec || (Opcodes::OP_IF <= $opCode && $opCode <= Opcodes::OP_ENDIF)) { |
|||||
410 | // echo "OPCODE - " . $script->getOpcodes()->getOp($opCode) . "\n"; |
||||||
411 | 4501 | switch ($opCode) { |
|||||
412 | case Opcodes::OP_1NEGATE: |
||||||
413 | case Opcodes::OP_1: |
||||||
414 | case Opcodes::OP_2: |
||||||
415 | case Opcodes::OP_3: |
||||||
416 | case Opcodes::OP_4: |
||||||
417 | case Opcodes::OP_5: |
||||||
418 | case Opcodes::OP_6: |
||||||
419 | case Opcodes::OP_7: |
||||||
420 | case Opcodes::OP_8: |
||||||
421 | case Opcodes::OP_9: |
||||||
422 | case Opcodes::OP_10: |
||||||
423 | case Opcodes::OP_11: |
||||||
424 | case Opcodes::OP_12: |
||||||
425 | case Opcodes::OP_13: |
||||||
426 | case Opcodes::OP_14: |
||||||
427 | case Opcodes::OP_15: |
||||||
428 | case Opcodes::OP_16: |
||||||
429 | 2832 | $num = \BitWasp\Bitcoin\Script\decodeOpN($opCode); |
|||||
430 | 2832 | $mainStack->push(Number::int($num)->getBuffer()); |
|||||
431 | 2832 | break; |
|||||
432 | |||||||
433 | case Opcodes::OP_CHECKLOCKTIMEVERIFY: |
||||||
434 | 24 | if (!($flags & self::VERIFY_CHECKLOCKTIMEVERIFY)) { |
|||||
435 | 24 | if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { |
|||||
436 | 4 | throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged'); |
|||||
437 | } |
||||||
438 | 20 | break; |
|||||
439 | } |
||||||
440 | |||||||
441 | if ($mainStack->isEmpty()) { |
||||||
442 | throw new \RuntimeException('Invalid stack operation - CLTV'); |
||||||
443 | } |
||||||
444 | |||||||
445 | $lockTime = Number::buffer($mainStack[-1], $minimal, 5, $this->math); |
||||||
446 | if (!$checker->checkLockTime($lockTime)) { |
||||||
447 | throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime'); |
||||||
448 | } |
||||||
449 | |||||||
450 | break; |
||||||
451 | |||||||
452 | case Opcodes::OP_CHECKSEQUENCEVERIFY: |
||||||
453 | 48 | if (!($flags & self::VERIFY_CHECKSEQUENCEVERIFY)) { |
|||||
454 | 24 | if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { |
|||||
455 | 4 | throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged'); |
|||||
456 | } |
||||||
457 | 20 | break; |
|||||
458 | } |
||||||
459 | |||||||
460 | 24 | if ($mainStack->isEmpty()) { |
|||||
461 | 4 | throw new \RuntimeException('Invalid stack operation - CSV'); |
|||||
462 | } |
||||||
463 | |||||||
464 | 20 | $sequence = Number::buffer($mainStack[-1], $minimal, 5, $this->math); |
|||||
465 | 16 | $nSequence = $sequence->getGmp(); |
|||||
466 | 16 | if ($this->math->cmp($nSequence, $zero) < 0) { |
|||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
467 | 4 | throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime'); |
|||||
468 | } |
||||||
469 | |||||||
470 | 12 | if ($this->math->cmp($this->math->bitwiseAnd($nSequence, gmp_init(TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG, 10)), $zero) !== 0) { |
|||||
0 ignored issues
–
show
It seems like
gmp_init(BitWasp\Bitcoin...KTIME_DISABLE_FLAG, 10) can also be of type resource ; however, parameter $other of Mdanter\Ecc\Math\GmpMath::bitwiseAnd() does only seem to accept GMP , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
471 | 4 | break; |
|||||
472 | } |
||||||
473 | |||||||
474 | 8 | if (!$checker->checkSequence($sequence)) { |
|||||
475 | 8 | throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied sequence'); |
|||||
476 | } |
||||||
477 | break; |
||||||
478 | |||||||
479 | case Opcodes::OP_NOP1: |
||||||
480 | case Opcodes::OP_NOP4: |
||||||
481 | case Opcodes::OP_NOP5: |
||||||
482 | case Opcodes::OP_NOP6: |
||||||
483 | case Opcodes::OP_NOP7: |
||||||
484 | case Opcodes::OP_NOP8: |
||||||
485 | case Opcodes::OP_NOP9: |
||||||
486 | case Opcodes::OP_NOP10: |
||||||
487 | 108 | if ($flags & self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS) { |
|||||
488 | 40 | throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged'); |
|||||
489 | } |
||||||
490 | 68 | break; |
|||||
491 | |||||||
492 | case Opcodes::OP_NOP: |
||||||
493 | 336 | break; |
|||||
494 | |||||||
495 | case Opcodes::OP_IF: |
||||||
496 | case Opcodes::OP_NOTIF: |
||||||
497 | // <expression> if [statements] [else [statements]] endif |
||||||
498 | 1268 | $value = false; |
|||||
499 | 1268 | if ($fExec) { |
|||||
500 | 1268 | if ($mainStack->isEmpty()) { |
|||||
501 | 48 | throw new \RuntimeException('Unbalanced conditional'); |
|||||
502 | } |
||||||
503 | 1220 | $vch = $mainStack[-1]; |
|||||
504 | |||||||
505 | 1220 | if ($sigVersion === SigHash::V1 && ($flags & self::VERIFY_MINIMALIF)) { |
|||||
506 | 80 | if ($vch->getSize() > 1) { |
|||||
507 | 16 | throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded'); |
|||||
508 | } |
||||||
509 | |||||||
510 | 64 | if ($vch->getSize() === 1 && $vch->getBinary() !== "\x01") { |
|||||
511 | 32 | throw new ScriptRuntimeException(self::VERIFY_MINIMALIF, 'Input to OP_IF/NOTIF should be minimally encoded'); |
|||||
512 | } |
||||||
513 | } |
||||||
514 | |||||||
515 | 1172 | $buffer = Number::buffer($mainStack->pop(), $minimal)->getBuffer(); |
|||||
516 | 1172 | $value = $this->castToBool($buffer); |
|||||
517 | 1172 | if ($opCode === Opcodes::OP_NOTIF) { |
|||||
518 | 164 | $value = !$value; |
|||||
519 | } |
||||||
520 | } |
||||||
521 | 1172 | $vfStack->push($value); |
|||||
522 | 1172 | break; |
|||||
523 | |||||||
524 | case Opcodes::OP_ELSE: |
||||||
525 | 460 | if ($vfStack->isEmpty()) { |
|||||
526 | 16 | throw new \RuntimeException('Unbalanced conditional'); |
|||||
527 | } |
||||||
528 | 452 | $vfStack->push(!$vfStack->pop()); |
|||||
529 | 452 | break; |
|||||
530 | |||||||
531 | case Opcodes::OP_ENDIF: |
||||||
532 | 780 | if ($vfStack->isEmpty()) { |
|||||
533 | 28 | throw new \RuntimeException('Unbalanced conditional'); |
|||||
534 | } |
||||||
535 | 764 | $vfStack->pop(); |
|||||
536 | 764 | break; |
|||||
537 | |||||||
538 | case Opcodes::OP_VERIFY: |
||||||
539 | 120 | if ($mainStack->isEmpty()) { |
|||||
540 | 4 | throw new \RuntimeException('Invalid stack operation'); |
|||||
541 | } |
||||||
542 | 116 | $value = $this->castToBool($mainStack[-1]); |
|||||
543 | 116 | if (!$value) { |
|||||
544 | 4 | throw new \RuntimeException('Error: verify'); |
|||||
545 | } |
||||||
546 | 112 | $mainStack->pop(); |
|||||
547 | 112 | break; |
|||||
548 | |||||||
549 | case Opcodes::OP_TOALTSTACK: |
||||||
550 | 32 | if ($mainStack->isEmpty()) { |
|||||
551 | 4 | throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK'); |
|||||
552 | } |
||||||
553 | 28 | $altStack->push($mainStack->pop()); |
|||||
554 | 28 | break; |
|||||
555 | |||||||
556 | case Opcodes::OP_FROMALTSTACK: |
||||||
557 | 20 | if ($altStack->isEmpty()) { |
|||||
558 | 8 | throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK'); |
|||||
559 | } |
||||||
560 | 12 | $mainStack->push($altStack->pop()); |
|||||
561 | 12 | break; |
|||||
562 | |||||||
563 | case Opcodes::OP_IFDUP: |
||||||
564 | // If top value not zero, duplicate it. |
||||||
565 | 24 | if ($mainStack->isEmpty()) { |
|||||
566 | 8 | throw new \RuntimeException('Invalid stack operation OP_IFDUP'); |
|||||
567 | } |
||||||
568 | 16 | $vch = $mainStack[-1]; |
|||||
569 | 16 | if ($this->castToBool($vch)) { |
|||||
570 | 12 | $mainStack->push($vch); |
|||||
571 | } |
||||||
572 | 16 | break; |
|||||
573 | |||||||
574 | case Opcodes::OP_DEPTH: |
||||||
575 | 301 | $num = count($mainStack); |
|||||
576 | 301 | $depth = Number::int($num)->getBuffer(); |
|||||
577 | 301 | $mainStack->push($depth); |
|||||
578 | 301 | break; |
|||||
579 | |||||||
580 | case Opcodes::OP_DROP: |
||||||
581 | 192 | if ($mainStack->isEmpty()) { |
|||||
582 | 8 | throw new \RuntimeException('Invalid stack operation OP_DROP'); |
|||||
583 | } |
||||||
584 | 184 | $mainStack->pop(); |
|||||
585 | 184 | break; |
|||||
586 | |||||||
587 | case Opcodes::OP_DUP: |
||||||
588 | 112 | if ($mainStack->isEmpty()) { |
|||||
589 | 8 | throw new \RuntimeException('Invalid stack operation OP_DUP'); |
|||||
590 | } |
||||||
591 | 104 | $vch = $mainStack[-1]; |
|||||
592 | 104 | $mainStack->push($vch); |
|||||
593 | 104 | break; |
|||||
594 | |||||||
595 | case Opcodes::OP_NIP: |
||||||
596 | 24 | if (count($mainStack) < 2) { |
|||||
597 | 12 | throw new \RuntimeException('Invalid stack operation OP_NIP'); |
|||||
598 | } |
||||||
599 | 12 | unset($mainStack[-2]); |
|||||
600 | 12 | break; |
|||||
601 | |||||||
602 | case Opcodes::OP_OVER: |
||||||
603 | 24 | if (count($mainStack) < 2) { |
|||||
604 | 12 | throw new \RuntimeException('Invalid stack operation OP_OVER'); |
|||||
605 | } |
||||||
606 | 12 | $vch = $mainStack[-2]; |
|||||
607 | 12 | $mainStack->push($vch); |
|||||
608 | 12 | break; |
|||||
609 | |||||||
610 | case Opcodes::OP_ROT: |
||||||
611 | 48 | if (count($mainStack) < 3) { |
|||||
612 | 16 | throw new \RuntimeException('Invalid stack operation OP_ROT'); |
|||||
613 | } |
||||||
614 | 32 | $mainStack->swap(-3, -2); |
|||||
615 | 32 | $mainStack->swap(-2, -1); |
|||||
616 | 32 | break; |
|||||
617 | |||||||
618 | case Opcodes::OP_SWAP: |
||||||
619 | 40 | if (count($mainStack) < 2) { |
|||||
620 | 12 | throw new \RuntimeException('Invalid stack operation OP_SWAP'); |
|||||
621 | } |
||||||
622 | 28 | $mainStack->swap(-2, -1); |
|||||
623 | 28 | break; |
|||||
624 | |||||||
625 | case Opcodes::OP_TUCK: |
||||||
626 | 24 | if (count($mainStack) < 2) { |
|||||
627 | 12 | throw new \RuntimeException('Invalid stack operation OP_TUCK'); |
|||||
628 | } |
||||||
629 | 12 | $vch = $mainStack[-1]; |
|||||
630 | 12 | $mainStack->add(- 2, $vch); |
|||||
631 | 12 | break; |
|||||
632 | |||||||
633 | case Opcodes::OP_PICK: |
||||||
634 | case Opcodes::OP_ROLL: |
||||||
635 | 116 | if (count($mainStack) < 2) { |
|||||
636 | 16 | throw new \RuntimeException('Invalid stack operation OP_PICK'); |
|||||
637 | } |
||||||
638 | |||||||
639 | 100 | $n = Number::buffer($mainStack[-1], $minimal, 4)->getGmp(); |
|||||
640 | 92 | $mainStack->pop(); |
|||||
641 | 92 | if ($this->math->cmp($n, $zero) < 0 || $this->math->cmp($n, gmp_init(count($mainStack))) >= 0) { |
|||||
642 | 20 | throw new \RuntimeException('Invalid stack operation OP_PICK'); |
|||||
643 | } |
||||||
644 | |||||||
645 | 72 | $pos = (int) gmp_strval($this->math->sub($this->math->sub($zero, $n), gmp_init(1)), 10); |
|||||
0 ignored issues
–
show
It seems like
gmp_init(1) can also be of type resource ; however, parameter $subtrahend of Mdanter\Ecc\Math\GmpMath::sub() does only seem to accept GMP , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() It seems like
$zero can also be of type resource ; however, parameter $minuend of Mdanter\Ecc\Math\GmpMath::sub() does only seem to accept GMP , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
646 | 72 | $vch = $mainStack[$pos]; |
|||||
647 | 72 | if ($opCode === Opcodes::OP_ROLL) { |
|||||
648 | 36 | unset($mainStack[$pos]); |
|||||
649 | } |
||||||
650 | 72 | $mainStack->push($vch); |
|||||
651 | 72 | break; |
|||||
652 | |||||||
653 | case Opcodes::OP_2DROP: |
||||||
654 | 36 | if (count($mainStack) < 2) { |
|||||
655 | 4 | throw new \RuntimeException('Invalid stack operation OP_2DROP'); |
|||||
656 | } |
||||||
657 | 32 | $mainStack->pop(); |
|||||
658 | 32 | $mainStack->pop(); |
|||||
659 | 32 | break; |
|||||
660 | |||||||
661 | case Opcodes::OP_2DUP: |
||||||
662 | 24 | if (count($mainStack) < 2) { |
|||||
663 | 12 | throw new \RuntimeException('Invalid stack operation OP_2DUP'); |
|||||
664 | } |
||||||
665 | 12 | $string1 = $mainStack[-2]; |
|||||
666 | 12 | $string2 = $mainStack[-1]; |
|||||
667 | 12 | $mainStack->push($string1); |
|||||
668 | 12 | $mainStack->push($string2); |
|||||
669 | 12 | break; |
|||||
670 | |||||||
671 | case Opcodes::OP_3DUP: |
||||||
672 | 44 | if (count($mainStack) < 3) { |
|||||
673 | 16 | throw new \RuntimeException('Invalid stack operation OP_3DUP'); |
|||||
674 | } |
||||||
675 | 28 | $string1 = $mainStack[-3]; |
|||||
676 | 28 | $string2 = $mainStack[-2]; |
|||||
677 | 28 | $string3 = $mainStack[-1]; |
|||||
678 | 28 | $mainStack->push($string1); |
|||||
679 | 28 | $mainStack->push($string2); |
|||||
680 | 28 | $mainStack->push($string3); |
|||||
681 | 28 | break; |
|||||
682 | |||||||
683 | case Opcodes::OP_2OVER: |
||||||
684 | 20 | if (count($mainStack) < 4) { |
|||||
685 | 12 | throw new \RuntimeException('Invalid stack operation OP_2OVER'); |
|||||
686 | } |
||||||
687 | 8 | $string1 = $mainStack[-4]; |
|||||
688 | 8 | $string2 = $mainStack[-3]; |
|||||
689 | 8 | $mainStack->push($string1); |
|||||
690 | 8 | $mainStack->push($string2); |
|||||
691 | 8 | break; |
|||||
692 | |||||||
693 | case Opcodes::OP_2ROT: |
||||||
694 | 40 | if (count($mainStack) < 6) { |
|||||
695 | 4 | throw new \RuntimeException('Invalid stack operation OP_2ROT'); |
|||||
696 | } |
||||||
697 | 36 | $string1 = $mainStack[-6]; |
|||||
698 | 36 | $string2 = $mainStack[-5]; |
|||||
699 | 36 | unset($mainStack[-6], $mainStack[-5]); |
|||||
700 | 36 | $mainStack->push($string1); |
|||||
701 | 36 | $mainStack->push($string2); |
|||||
702 | 36 | break; |
|||||
703 | |||||||
704 | case Opcodes::OP_2SWAP: |
||||||
705 | 20 | if (count($mainStack) < 4) { |
|||||
706 | 12 | throw new \RuntimeException('Invalid stack operation OP_2SWAP'); |
|||||
707 | } |
||||||
708 | 8 | $mainStack->swap(-3, -1); |
|||||
709 | 8 | $mainStack->swap(-4, -2); |
|||||
710 | 8 | break; |
|||||
711 | |||||||
712 | case Opcodes::OP_SIZE: |
||||||
713 | 124 | if ($mainStack->isEmpty()) { |
|||||
714 | 8 | throw new \RuntimeException('Invalid stack operation OP_SIZE'); |
|||||
715 | } |
||||||
716 | 116 | $size = Number::int($mainStack[-1]->getSize()); |
|||||
717 | 116 | $mainStack->push($size->getBuffer()); |
|||||
718 | 116 | break; |
|||||
719 | |||||||
720 | case Opcodes::OP_EQUAL: |
||||||
721 | case Opcodes::OP_EQUALVERIFY: |
||||||
722 | 1258 | if (count($mainStack) < 2) { |
|||||
723 | 16 | throw new \RuntimeException('Invalid stack operation OP_EQUAL'); |
|||||
724 | } |
||||||
725 | |||||||
726 | 1242 | $equal = $mainStack[-2]->equals($mainStack[-1]); |
|||||
727 | 1242 | $mainStack->pop(); |
|||||
728 | 1242 | $mainStack->pop(); |
|||||
729 | 1242 | $mainStack->push($equal ? $this->vchTrue : $this->vchFalse); |
|||||
730 | 1242 | if ($opCode === Opcodes::OP_EQUALVERIFY) { |
|||||
731 | 180 | if ($equal) { |
|||||
732 | 144 | $mainStack->pop(); |
|||||
733 | } else { |
||||||
734 | 36 | throw new \RuntimeException('Error EQUALVERIFY'); |
|||||
735 | } |
||||||
736 | } |
||||||
737 | |||||||
738 | 1210 | break; |
|||||
739 | |||||||
740 | // Arithmetic operations |
||||||
741 | case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL: |
||||||
742 | 444 | if ($mainStack->isEmpty()) { |
|||||
743 | 24 | throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL'); |
|||||
744 | } |
||||||
745 | |||||||
746 | 420 | $num = Number::buffer($mainStack[-1], $minimal)->getGmp(); |
|||||
747 | |||||||
748 | 328 | if ($opCode === Opcodes::OP_1ADD) { |
|||||
749 | 28 | $num = $this->math->add($num, gmp_init(1)); |
|||||
0 ignored issues
–
show
It seems like
gmp_init(1) can also be of type resource ; however, parameter $addend of Mdanter\Ecc\Math\GmpMath::add() does only seem to accept GMP , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
750 | 300 | } elseif ($opCode === Opcodes::OP_1SUB) { |
|||||
751 | 12 | $num = $this->math->sub($num, gmp_init(1)); |
|||||
752 | 288 | } elseif ($opCode === Opcodes::OP_2MUL) { |
|||||
753 | $num = $this->math->mul(gmp_init(2), $num); |
||||||
0 ignored issues
–
show
It seems like
gmp_init(2) can also be of type resource ; however, parameter $multiplier of Mdanter\Ecc\Math\GmpMath::mul() does only seem to accept GMP , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
754 | 288 | } elseif ($opCode === Opcodes::OP_NEGATE) { |
|||||
755 | 16 | $num = $this->math->sub($zero, $num); |
|||||
756 | 276 | } elseif ($opCode === Opcodes::OP_ABS) { |
|||||
757 | 20 | if ($this->math->cmp($num, $zero) < 0) { |
|||||
758 | 20 | $num = $this->math->sub($zero, $num); |
|||||
759 | } |
||||||
760 | 256 | } elseif ($opCode === Opcodes::OP_NOT) { |
|||||
761 | 232 | $num = gmp_init($this->math->cmp($num, $zero) === 0 ? 1 : 0); |
|||||
762 | } else { |
||||||
763 | // is OP_0NOTEQUAL |
||||||
764 | 24 | $num = gmp_init($this->math->cmp($num, $zero) !== 0 ? 1 : 0); |
|||||
765 | } |
||||||
766 | |||||||
767 | 328 | $mainStack->pop(); |
|||||
768 | |||||||
769 | 328 | $buffer = Number::int(gmp_strval($num, 10))->getBuffer(); |
|||||
770 | |||||||
771 | 328 | $mainStack->push($buffer); |
|||||
772 | 328 | break; |
|||||
773 | |||||||
774 | 2209 | case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX: |
|||||
775 | 656 | if (count($mainStack) < 2) { |
|||||
776 | 52 | throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)'); |
|||||
777 | } |
||||||
778 | |||||||
779 | 604 | $num1 = Number::buffer($mainStack[-2], $minimal)->getGmp(); |
|||||
780 | 536 | $num2 = Number::buffer($mainStack[-1], $minimal)->getGmp(); |
|||||
781 | |||||||
782 | 484 | if ($opCode === Opcodes::OP_ADD) { |
|||||
783 | 120 | $num = $this->math->add($num1, $num2); |
|||||
784 | 388 | } else if ($opCode === Opcodes::OP_SUB) { |
|||||
785 | 24 | $num = $this->math->sub($num1, $num2); |
|||||
786 | 364 | } else if ($opCode === Opcodes::OP_BOOLAND) { |
|||||
787 | 32 | $num = (int) ($this->math->cmp($num1, $zero) !== 0 && $this->math->cmp($num2, $zero) !== 0); |
|||||
788 | 332 | } else if ($opCode === Opcodes::OP_BOOLOR) { |
|||||
789 | 32 | $num = (int) ($this->math->cmp($num1, $zero) !== 0 || $this->math->cmp($num2, $zero) !== 0); |
|||||
790 | 300 | } elseif ($opCode === Opcodes::OP_NUMEQUAL) { |
|||||
791 | 112 | $num = (int) ($this->math->cmp($num1, $num2) === 0); |
|||||
792 | 220 | } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) { |
|||||
793 | 16 | $num = (int) ($this->math->cmp($num1, $num2) === 0); |
|||||
794 | 204 | } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) { |
|||||
795 | 20 | $num = (int) ($this->math->cmp($num1, $num2) !== 0); |
|||||
796 | 184 | } elseif ($opCode === Opcodes::OP_LESSTHAN) { |
|||||
797 | 32 | $num = (int) ($this->math->cmp($num1, $num2) < 0); |
|||||
798 | 152 | } elseif ($opCode === Opcodes::OP_GREATERTHAN) { |
|||||
799 | 32 | $num = (int) ($this->math->cmp($num1, $num2) > 0); |
|||||
800 | 120 | } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) { |
|||||
801 | 32 | $num = (int) ($this->math->cmp($num1, $num2) <= 0); |
|||||
802 | 88 | } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) { |
|||||
803 | 32 | $num = (int) ($this->math->cmp($num1, $num2) >= 0); |
|||||
804 | 56 | } elseif ($opCode === Opcodes::OP_MIN) { |
|||||
805 | 28 | $num = ($this->math->cmp($num1, $num2) <= 0) ? $num1 : $num2; |
|||||
806 | } else { |
||||||
807 | 28 | $num = ($this->math->cmp($num1, $num2) >= 0) ? $num1 : $num2; |
|||||
808 | } |
||||||
809 | |||||||
810 | 484 | $mainStack->pop(); |
|||||
811 | 484 | $mainStack->pop(); |
|||||
812 | 484 | $buffer = Number::int(gmp_strval($num, 10))->getBuffer(); |
|||||
813 | 484 | $mainStack->push($buffer); |
|||||
814 | |||||||
815 | 484 | if ($opCode === Opcodes::OP_NUMEQUALVERIFY) { |
|||||
816 | 16 | if ($this->castToBool($mainStack[-1])) { |
|||||
817 | 16 | $mainStack->pop(); |
|||||
818 | } else { |
||||||
819 | throw new \RuntimeException('NUM EQUAL VERIFY error'); |
||||||
820 | } |
||||||
821 | } |
||||||
822 | 484 | break; |
|||||
823 | |||||||
824 | 1553 | case Opcodes::OP_WITHIN: |
|||||
825 | 60 | if (count($mainStack) < 3) { |
|||||
826 | 4 | throw new \RuntimeException('Invalid stack operation'); |
|||||
827 | } |
||||||
828 | |||||||
829 | 56 | $num1 = Number::buffer($mainStack[-3], $minimal)->getGmp(); |
|||||
830 | 52 | $num2 = Number::buffer($mainStack[-2], $minimal)->getGmp(); |
|||||
831 | 48 | $num3 = Number::buffer($mainStack[-1], $minimal)->getGmp(); |
|||||
832 | |||||||
833 | 44 | $value = $this->math->cmp($num2, $num1) <= 0 && $this->math->cmp($num1, $num3) < 0; |
|||||
834 | 44 | $mainStack->pop(); |
|||||
835 | 44 | $mainStack->pop(); |
|||||
836 | 44 | $mainStack->pop(); |
|||||
837 | 44 | $mainStack->push($value ? $this->vchTrue : $this->vchFalse); |
|||||
838 | 44 | break; |
|||||
839 | |||||||
840 | // Hash operation |
||||||
841 | 1493 | case Opcodes::OP_RIPEMD160: |
|||||
842 | 1469 | case Opcodes::OP_SHA1: |
|||||
843 | 1437 | case Opcodes::OP_SHA256: |
|||||
844 | 1413 | case Opcodes::OP_HASH160: |
|||||
845 | 1183 | case Opcodes::OP_HASH256: |
|||||
846 | 415 | if ($mainStack->isEmpty()) { |
|||||
847 | 41 | throw new \RuntimeException('Invalid stack operation'); |
|||||
848 | } |
||||||
849 | |||||||
850 | 374 | $buffer = $mainStack[-1]; |
|||||
851 | 374 | if ($opCode === Opcodes::OP_RIPEMD160) { |
|||||
852 | 20 | $hash = Hash::ripemd160($buffer); |
|||||
853 | 358 | } elseif ($opCode === Opcodes::OP_SHA1) { |
|||||
854 | 24 | $hash = Hash::sha1($buffer); |
|||||
855 | 334 | } elseif ($opCode === Opcodes::OP_SHA256) { |
|||||
856 | 24 | $hash = Hash::sha256($buffer); |
|||||
857 | 318 | } elseif ($opCode === Opcodes::OP_HASH160) { |
|||||
858 | 298 | $hash = Hash::sha256ripe160($buffer); |
|||||
859 | } else { |
||||||
860 | 20 | $hash = Hash::sha256d($buffer); |
|||||
861 | } |
||||||
862 | |||||||
863 | 374 | $mainStack->pop(); |
|||||
864 | 374 | $mainStack->push($hash); |
|||||
865 | 374 | break; |
|||||
866 | |||||||
867 | 1155 | case Opcodes::OP_CODESEPARATOR: |
|||||
868 | 4 | $hashStartPos = $parser->getPosition(); |
|||||
869 | 4 | break; |
|||||
870 | |||||||
871 | 1151 | case Opcodes::OP_CHECKSIG: |
|||||
872 | 815 | case Opcodes::OP_CHECKSIGVERIFY: |
|||||
873 | 336 | if (count($mainStack) < 2) { |
|||||
874 | 8 | throw new \RuntimeException('Invalid stack operation'); |
|||||
875 | } |
||||||
876 | |||||||
877 | 328 | $vchPubKey = $mainStack[-1]; |
|||||
878 | 328 | $vchSig = $mainStack[-2]; |
|||||
879 | |||||||
880 | 328 | $scriptCode = new Script($script->getBuffer()->slice($hashStartPos)); |
|||||
881 | |||||||
882 | 328 | $success = $checker->checkSig($scriptCode, $vchSig, $vchPubKey, $sigVersion, $flags); |
|||||
883 | |||||||
884 | 232 | $mainStack->pop(); |
|||||
885 | 232 | $mainStack->pop(); |
|||||
886 | 232 | $mainStack->push($success ? $this->vchTrue : $this->vchFalse); |
|||||
887 | |||||||
888 | 232 | if (!$success && ($flags & self::VERIFY_NULLFAIL) && $vchSig->getSize() > 0) { |
|||||
889 | 4 | throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Signature must be zero for failed OP_CHECK(MULTIS)SIG operation'); |
|||||
890 | } |
||||||
891 | |||||||
892 | 228 | if ($opCode === Opcodes::OP_CHECKSIGVERIFY) { |
|||||
893 | if ($success) { |
||||||
894 | $mainStack->pop(); |
||||||
895 | } else { |
||||||
896 | throw new \RuntimeException('Checksig verify'); |
||||||
897 | } |
||||||
898 | } |
||||||
899 | 228 | break; |
|||||
900 | |||||||
901 | 815 | case Opcodes::OP_CHECKMULTISIG: |
|||||
902 | 495 | case Opcodes::OP_CHECKMULTISIGVERIFY: |
|||||
903 | 448 | $i = 1; |
|||||
904 | 448 | if (count($mainStack) < $i) { |
|||||
905 | 4 | throw new \RuntimeException('Invalid stack operation'); |
|||||
906 | } |
||||||
907 | |||||||
908 | 444 | $keyCount = Number::buffer($mainStack[-$i], $minimal)->getInt(); |
|||||
909 | 436 | if ($keyCount < 0 || $keyCount > 20) { |
|||||
910 | 8 | throw new \RuntimeException('OP_CHECKMULTISIG: Public key count exceeds 20'); |
|||||
911 | } |
||||||
912 | |||||||
913 | 428 | $opCount += $keyCount; |
|||||
914 | 428 | $this->checkOpcodeCount($opCount); |
|||||
915 | |||||||
916 | // Extract positions of the keys, and signatures, from the stack. |
||||||
917 | 428 | $ikey = ++$i; |
|||||
918 | 428 | $ikey2 = $keyCount + 2; |
|||||
919 | 428 | $i += $keyCount; |
|||||
920 | 428 | if (count($mainStack) < $i) { |
|||||
921 | 4 | throw new \RuntimeException('Invalid stack operation'); |
|||||
922 | } |
||||||
923 | |||||||
924 | 424 | $sigCount = Number::buffer($mainStack[-$i], $minimal)->getInt(); |
|||||
925 | 412 | if ($sigCount < 0 || $sigCount > $keyCount) { |
|||||
926 | 8 | throw new \RuntimeException('Invalid Signature count'); |
|||||
927 | } |
||||||
928 | |||||||
929 | 404 | $isig = ++$i; |
|||||
930 | 404 | $i += $sigCount; |
|||||
931 | |||||||
932 | // Extract the script since the last OP_CODESEPARATOR |
||||||
933 | 404 | $scriptCode = new Script($script->getBuffer()->slice($hashStartPos)); |
|||||
934 | |||||||
935 | 404 | $fSuccess = true; |
|||||
936 | 404 | while ($fSuccess && $sigCount > 0) { |
|||||
937 | // Fetch the signature and public key |
||||||
938 | 164 | $sig = $mainStack[-$isig]; |
|||||
939 | 160 | $pubkey = $mainStack[-$ikey]; |
|||||
940 | |||||||
941 | 160 | if ($checker->checkSig($scriptCode, $sig, $pubkey, $sigVersion, $flags)) { |
|||||
942 | 56 | $isig++; |
|||||
943 | 56 | $sigCount--; |
|||||
944 | } |
||||||
945 | |||||||
946 | 136 | $ikey++; |
|||||
947 | 136 | $keyCount--; |
|||||
948 | |||||||
949 | // If there are more signatures left than keys left, |
||||||
950 | // then too many signatures have failed. Exit early, |
||||||
951 | // without checking any further signatures. |
||||||
952 | 136 | if ($sigCount > $keyCount) { |
|||||
953 | 88 | $fSuccess = false; |
|||||
954 | } |
||||||
955 | } |
||||||
956 | |||||||
957 | 368 | while ($i-- > 1) { |
|||||
958 | // If the operation failed, we require that all signatures must be empty vector |
||||||
959 | 368 | if (!$fSuccess && ($flags & self::VERIFY_NULLFAIL) && !$ikey2 && $mainStack[-1]->getSize() > 0) { |
|||||
960 | 8 | throw new ScriptRuntimeException(self::VERIFY_NULLFAIL, 'Bad signature must be empty vector'); |
|||||
961 | } |
||||||
962 | |||||||
963 | 368 | if ($ikey2 > 0) { |
|||||
964 | 368 | $ikey2--; |
|||||
965 | } |
||||||
966 | |||||||
967 | 368 | $mainStack->pop(); |
|||||
968 | } |
||||||
969 | |||||||
970 | // A bug causes CHECKMULTISIG to consume one extra argument |
||||||
971 | // whose contents were not checked in any way. |
||||||
972 | // |
||||||
973 | // Unfortunately this is a potential source of mutability, |
||||||
974 | // so optionally verify it is exactly equal to zero prior |
||||||
975 | // to removing it from the stack. |
||||||
976 | 360 | if ($mainStack->isEmpty()) { |
|||||
977 | 4 | throw new \RuntimeException('Invalid stack operation'); |
|||||
978 | } |
||||||
979 | |||||||
980 | 360 | if ($flags & self::VERIFY_NULL_DUMMY && $mainStack[-1]->getSize() !== 0) { |
|||||
981 | 12 | throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0'); |
|||||
982 | } |
||||||
983 | |||||||
984 | 348 | $mainStack->pop(); |
|||||
985 | 348 | $mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse); |
|||||
986 | |||||||
987 | 348 | if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) { |
|||||
988 | 120 | if ($fSuccess) { |
|||||
989 | 120 | $mainStack->pop(); |
|||||
990 | } else { |
||||||
991 | throw new \RuntimeException('OP_CHECKMULTISIG verify'); |
||||||
992 | } |
||||||
993 | } |
||||||
994 | 348 | break; |
|||||
995 | |||||||
996 | default: |
||||||
997 | 367 | throw new \RuntimeException('Opcode not found'); |
|||||
998 | } |
||||||
999 | |||||||
1000 | 4066 | if (count($mainStack) + count($altStack) > 1000) { |
|||||
1001 | 8 | throw new \RuntimeException('Invalid stack size, exceeds 1000'); |
|||||
1002 | } |
||||||
1003 | } |
||||||
1004 | } |
||||||
1005 | |||||||
1006 | 4459 | if (count($vfStack) !== 0) { |
|||||
1007 | 24 | throw new \RuntimeException('Unbalanced conditional at script end'); |
|||||
1008 | } |
||||||
1009 | |||||||
1010 | 4439 | return true; |
|||||
1011 | 1665 | } catch (ScriptRuntimeException $e) { |
|||||
1012 | // echo "\n Runtime: " . $e->getMessage() . "\n" . $e->getTraceAsString() . PHP_EOL; |
||||||
1013 | // Failure due to script tags, can access flag: $e->getFailureFlag() |
||||||
1014 | 344 | return false; |
|||||
1015 | 1321 | } catch (\Exception $e) { |
|||||
1016 | // echo "\n General: " . $e->getMessage() . PHP_EOL . $e->getTraceAsString() . PHP_EOL; |
||||||
1017 | 1321 | return false; |
|||||
1018 | } |
||||||
1019 | } |
||||||
1020 | } |
||||||
1021 |