Пример #1
0
 /**
  * returns a mix of Buffer objects and strings
  *
  * @return Buffer[]|string[]
  */
 public function parse()
 {
     $data = array();
     while ($this->next($opCode, $pushData)) {
         if ($opCode < 1) {
             $push = Buffer::hex('00');
         } elseif ($opCode <= 78) {
             $push = $pushData;
         } else {
             // None of these are pushdatas, so just an opcode
             $push = $this->script->getOpCodes()->getOp($opCode);
         }
         $data[] = $push;
     }
     $this->resetPosition();
     return $data;
 }
Пример #2
0
 /**
  * @return bool
  */
 public function run()
 {
     $math = $this->ecAdapter->getMath();
     $opcodes = $this->script->getOpCodes();
     $flags = $this->flags;
     $mainStack = $this->state->getMainStack();
     $altStack = $this->state->getAltStack();
     $vfStack = $this->state->getVfStack();
     $this->hashStartPos = 0;
     $this->opCount = 0;
     $parser = $this->script->getScriptParser();
     $_bn0 = Buffer::hex('00');
     $_bn1 = Buffer::hex('01');
     if ($this->script->getBuffer()->getSize() > 10000) {
         return false;
     }
     $checkFExec = function () use(&$vfStack) {
         $c = 0;
         $len = $vfStack->end();
         for ($i = 0; $i < $len; $i++) {
             if ($vfStack->top(0 - $len - $i) == true) {
                 $c++;
             }
         }
         return (bool) $c;
     };
     try {
         while ($parser->next($opCode, $pushData) === true) {
             $fExec = !$checkFExec();
             // If pushdata was written to,
             if ($pushData instanceof Buffer && $pushData->getSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
                 throw new \Exception('Error - push size');
             }
             // OP_RESERVED should not count towards opCount
             if ($this->script->getOpcodes()->cmp($opCode, 'OP_16') > 0 && ++$this->opCount) {
                 $this->checkOpcodeCount();
             }
             if ($this->checkDisabledOpcodes) {
                 if ($this->isDisabledOp($opCode)) {
                     throw new \Exception('Disabled Opcode');
                 }
             }
             if ($fExec && $opCode >= 0 && $opcodes->cmp($opCode, 'OP_PUSHDATA4') <= 0) {
                 // In range of a pushdata opcode
                 if ($flags->checkFlags(InterpreterInterface::VERIFY_MINIMALDATA) && !$this->checkMinimalPush($opCode, $pushData)) {
                     throw new ScriptRuntimeException(InterpreterInterface::VERIFY_MINIMALDATA, 'Minimal pushdata required');
                 }
                 $mainStack->push($pushData);
                 //echo " - [pushed '" . $pushData->getHex() . "']\n";
             } elseif ($fExec || $opcodes->isOp($opCode, 'OP_IF') <= 0 && $opcodes->isOp($opCode, 'OP_ENDIF')) {
                 //echo " - [". $opcodes->getOp($opCode) . "]\n";
                 switch ($opCode) {
                     case $opcodes->getOpByName('OP_1NEGATE'):
                     case $opcodes->cmp($opCode, 'OP_1') >= 0 && $opcodes->cmp($opCode, 'OP_16') <= 0:
                         $pushInt = new PushIntOperation($opcodes);
                         $pushInt->op($opCode, $mainStack);
                         break;
                     case $opcodes->cmp($opCode, 'OP_NOP1') >= 0 && $opcodes->cmp($opCode, 'OP_NOP10') <= 0:
                         if ($flags->checkFlags(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                             throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOPS found - this is discouraged');
                         }
                         break;
                     case $opcodes->getOpByName('OP_NOP'):
                     case $opcodes->isOp($opCode, 'OP_IF') || $opcodes->isOp($opCode, 'OP_NOTIF'):
                     case $opcodes->isOp($opCode, 'OP_ELSE') || $opcodes->isOp($opCode, 'OP_ENDIF'):
                     case $opcodes->getOpByName('OP_VERIFY'):
                     case $opcodes->getOpByName('OP_RETURN'):
                         $flowControl = new FlowControlOperation($opcodes, function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         });
                         $flowControl->op($opCode, $mainStack, $vfStack, $fExec);
                         break;
                     case $opcodes->getOpByName('OP_RESERVED'):
                         // todo
                         break;
                     case $opcodes->getOpByName('OP_TOALTSTACK'):
                     case $opcodes->getOpByName('OP_FROMALTSTACK'):
                     case $opcodes->cmp($opCode, 'OP_IFDUP') >= 0 && $opcodes->cmp($opCode, 'OP_TUCK') <= 0:
                     case $opcodes->cmp($opCode, 'OP_2DROP') >= 0 && $opcodes->cmp($opCode, 'OP_2SWAP') <= 0:
                         $stackOper = new StackOperation($opcodes, $this->ecAdapter->getMath(), function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         });
                         $stackOper->op($opCode, $mainStack, $altStack);
                         break;
                     case $opcodes->getOpByName('OP_SIZE'):
                         if ($mainStack->size() < 1) {
                             throw new \Exception('Invalid stack operation OP_SIZE');
                         }
                         // todo: Int sizes?
                         $vch = $mainStack->top(-1);
                         $size = Buffer::hex($math->decHex($vch->getSize()));
                         $mainStack->push($size);
                         break;
                     case $opcodes->getOpByName('OP_EQUAL'):
                         // cscriptnum
                     // cscriptnum
                     case $opcodes->getOpByName('OP_EQUALVERIFY'):
                         //case $this->isOp($opCode, 'OP_NOTEQUAL'): // use OP_NUMNOTEQUAL
                         if ($mainStack->size() < 2) {
                             throw new \Exception('Invalid stack operation OP_EQUAL');
                         }
                         $vch1 = $mainStack->top(-2);
                         $vch2 = $mainStack->top(-1);
                         $equal = $vch1->getBinary() === $vch2->getBinary();
                         // OP_NOTEQUAL is disabled
                         //if ($this->isOp($opCode, 'OP_NOTEQUAL')) {
                         //    $equal = !$equal;
                         //}
                         $mainStack->pop();
                         $mainStack->pop();
                         $mainStack->push($equal ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_EQUALVERIFY')) {
                             if ($equal) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('Error EQUALVERIFY');
                             }
                         }
                         break;
                         // Arithmetic operations
                     // Arithmetic operations
                     case $opcodes->cmp($opCode, 'OP_1ADD') >= 0 && $opcodes->cmp($opCode, 'OP_WITHIN') <= 0:
                         $arithmetic = new ArithmeticOperation($opcodes, $this->ecAdapter->getMath(), function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         }, $_bn0, $_bn1);
                         $arithmetic->op($opCode, $mainStack);
                         break;
                         // Hash operations
                     // Hash operations
                     case $opcodes->cmp($opCode, 'OP_RIPEMD160') >= 0 && $opcodes->cmp($opCode, 'OP_HASH256') <= 0:
                         $hash = new HashOperation($opcodes);
                         $hash->op($opCode, $mainStack);
                         break;
                     case $opcodes->getOpByName('OP_CODESEPARATOR'):
                         $this->hashStartPos = $parser->getPosition();
                         break;
                     case $opcodes->getOpByName('OP_CHECKSIG'):
                     case $opcodes->getOpByName('OP_CHECKSIGVERIFY'):
                         if ($mainStack->size() < 2) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $vchPubKey = $mainStack->top(-1);
                         $vchSig = $mainStack->top(-2);
                         $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
                         $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey);
                         $mainStack->pop();
                         $mainStack->pop();
                         $mainStack->push($success ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_CHECKSIGVERIFY')) {
                             if ($success) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('Checksig verify');
                             }
                         }
                         break;
                     case $opcodes->getOpByName('OP_CHECKMULTISIG'):
                     case $opcodes->getOpByName('OP_CHECKMULTISIGVERIFY'):
                         $i = 1;
                         if ($mainStack->size() < $i) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $math = $this->ecAdapter->getMath();
                         $keyCount = $mainStack->top(-$i)->getInt();
                         if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
                             throw new \Exception('OP_CHECKMULTISIG: Public key count exceeds 20');
                         }
                         $this->opCount += $keyCount;
                         $this->checkOpcodeCount();
                         // Extract positions of the keys, and signatures, from the stack.
                         $ikey = ++$i;
                         $i += $keyCount;
                         if ($mainStack->size() < $i) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $sigCount = $mainStack->top(-$i)->getInt();
                         // cscriptnum
                         if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
                             throw new \Exception('Invalid Signature count');
                         }
                         $isig = ++$i;
                         $i += $sigCount;
                         // Extract the script since the last OP_CODESEPARATOR
                         $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
                         $fSuccess = true;
                         while ($fSuccess && $sigCount > 0) {
                             // Fetch the signature and public key
                             $sig = $mainStack->top(-$isig);
                             $pubkey = $mainStack->top(-$ikey);
                             // Erase the signature and public key.
                             $mainStack->erase(-$isig);
                             $mainStack->erase(-$ikey);
                             // Decrement $i, since we are consuming stack values.
                             $i -= 2;
                             if ($this->checksig($scriptCode, $sig, $pubkey)) {
                                 $isig++;
                                 $sigCount--;
                             }
                             $ikey++;
                             $keyCount--;
                             // If there are more signatures left than keys left,
                             // then too many signatures have failed. Exit early,
                             // without checking any further signatures.
                             if ($sigCount > $keyCount) {
                                 $fSuccess = false;
                             }
                         }
                         while ($i-- > 1) {
                             $mainStack->pop();
                         }
                         // A bug causes CHECKMULTISIG to consume one extra argument
                         // whose contents were not checked in any way.
                         //
                         // Unfortunately this is a potential source of mutability,
                         // so optionally verify it is exactly equal to zero prior
                         // to removing it from the stack.
                         if ($mainStack->size() < 1) {
                             throw new \Exception('Invalid stack operation');
                         }
                         if ($flags->checkFlags(InterpreterInterface::VERIFY_NULL_DUMMY) && $mainStack->top(-1)->getSize()) {
                             throw new ScriptRuntimeException(InterpreterInterface::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
                         }
                         $mainStack->pop();
                         $mainStack->push($fSuccess ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_CHECKMULTISIGVERIFY')) {
                             if ($fSuccess) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('OP_CHECKMULTISIG verify');
                             }
                         }
                         break;
                     default:
                         throw new \Exception('Opcode not found');
                 }
                 if ($mainStack->size() + $altStack->size() > 1000) {
                     throw new \Exception('Invalid stack size, exceeds 1000');
                 }
             }
         }
         if (!$vfStack->end() == 0) {
             throw new \Exception('Unbalanced conditional at script end');
         }
         return true;
     } catch (ScriptRuntimeException $e) {
         //echo "\n Runtime: " . $e->getMessage() . "\n";
         // Failure due to script tags, can access flag: $e->getFailureFlag()
         return false;
     } catch (\Exception $e) {
         //echo "\n General: " . $e->getMessage() ;
         return false;
     }
 }