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