Exemplo n.º 1
0
 /**
  * @param $opCode
  * @param ScriptStack $mainStack
  * @throws \BitWasp\Bitcoin\Exceptions\ScriptStackException
  * @throws \Exception
  */
 public function op($opCode, ScriptStack $mainStack)
 {
     if ($mainStack->size() < 1) {
         throw new \Exception('Invalid stack operation');
     }
     $opCodes = $this->opCodes;
     $opName = $opCodes->getOp($opCode);
     $buffer = $mainStack->top(-1);
     if ($opCodes->cmp($opCode, 'OP_RIPEMD160') >= 0 && $opCodes->cmp($opCode, 'OP_HASH256') <= 0) {
         if ($opName == 'OP_RIPEMD160') {
             $hash = Hash::ripemd160($buffer);
         } elseif ($opName == 'OP_SHA1') {
             $hash = Hash::sha1($buffer);
         } elseif ($opName == 'OP_SHA256') {
             $hash = Hash::sha256($buffer);
         } elseif ($opName == 'OP_HASH160') {
             $hash = Hash::sha256ripe160($buffer);
         } else {
             // is hash256
             $hash = Hash::sha256d($buffer);
         }
         $mainStack->pop();
         $mainStack->push($hash);
     } else {
         throw new \Exception('Opcode not found');
     }
 }
Exemplo n.º 2
0
 /**
  * @param Buffer $entropy
  * @param integer $CSlen
  * @return string
  */
 private function calculateChecksum(Buffer $entropy, $CSlen)
 {
     $entHash = Hash::sha256($entropy);
     $math = $this->ecAdapter->getMath();
     // Convert byte string to padded binary string of 0/1's.
     $hashBits = str_pad($math->baseConvert($entHash->getHex(), 16, 2), 256, '0', STR_PAD_LEFT);
     // Take $CSlen bits for the checksum
     $checksumBits = substr($hashBits, 0, $CSlen);
     return $checksumBits;
 }
Exemplo n.º 3
0
 /**
  * @param int $sighashType
  * @param int $inputToSign
  * @return Buffer|BufferInterface
  */
 public function hashOutputs($sighashType, $inputToSign)
 {
     if (($sighashType & 0x1f) !== SigHash::SINGLE && ($sighashType & 0x1f) != SigHash::NONE) {
         $binary = '';
         foreach ($this->transaction->getOutputs() as $output) {
             $binary .= $output->getBinary();
         }
         return Hash::sha256d(new Buffer($binary));
     } elseif (($sighashType & 0x1f) == SigHash::SINGLE && $inputToSign < count($this->transaction->getOutputs())) {
         return Hash::sha256($this->transaction->getOutput($inputToSign)->getBuffer());
     }
     return new Buffer('', 32);
 }
Exemplo n.º 4
0
 /**
  * @return bool
  */
 private function run()
 {
     $math = $this->math;
     $this->hashStartPos = 0;
     $this->opCount = 0;
     $parser = $this->script->getScriptParser();
     if ($this->script->getBuffer()->getSize() > 10000) {
         return false;
     }
     try {
         foreach ($parser as $operation) {
             $opCode = $operation->getOp();
             $pushData = $operation->getData();
             $fExec = $this->checkExec();
             // If pushdata was written to,
             if ($operation->isPush() && $operation->getDataSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
                 throw new \RuntimeException('Error - push size');
             }
             // OP_RESERVED should not count towards opCount
             if ($opCode > Opcodes::OP_16 && ++$this->opCount) {
                 $this->checkOpcodeCount();
             }
             if (in_array($opCode, $this->disabledOps, true)) {
                 throw new \RuntimeException('Disabled Opcode');
             }
             if ($fExec && $operation->isPush()) {
                 // In range of a pushdata opcode
                 if ($this->minimalPush && !$this->checkMinimalPush($opCode, $pushData)) {
                     throw new ScriptRuntimeException(self::VERIFY_MINIMALDATA, 'Minimal pushdata required');
                 }
                 $this->mainStack->push($pushData);
                 // echo " - [pushed '" . $pushData->getHex() . "']\n";
             } elseif ($fExec || $opCode !== Opcodes::OP_IF && $opCode !== Opcodes::OP_ENDIF) {
                 // echo "OPCODE - " . $this->script->getOpCodes()->getOp($opCode) . "\n";
                 switch ($opCode) {
                     case Opcodes::OP_1NEGATE:
                     case Opcodes::OP_1:
                     case Opcodes::OP_2:
                     case Opcodes::OP_3:
                     case Opcodes::OP_4:
                     case Opcodes::OP_5:
                     case Opcodes::OP_6:
                     case Opcodes::OP_7:
                     case Opcodes::OP_8:
                     case Opcodes::OP_9:
                     case Opcodes::OP_10:
                     case Opcodes::OP_11:
                     case Opcodes::OP_12:
                     case Opcodes::OP_13:
                     case Opcodes::OP_14:
                     case Opcodes::OP_15:
                     case Opcodes::OP_16:
                         $num = $opCode - (Opcodes::OP_1 - 1);
                         $this->mainStack->push(Number::int($num)->getBuffer());
                         break;
                     case Opcodes::OP_CHECKLOCKTIMEVERIFY:
                         if (!$this->flags->checkFlags(self::VERIFY_CHECKLOCKTIMEVERIFY)) {
                             if ($this->flags->checkFlags(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                                 throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
                             }
                             break;
                         }
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation - CLTV');
                         }
                         $lockTime = Number::buffer($this->mainStack[-1], $this->minimalPush, 5, $math);
                         if (!$this->checkLockTime($lockTime)) {
                             throw new ScriptRuntimeException(self::VERIFY_CHECKLOCKTIMEVERIFY, 'Unsatisfied locktime');
                         }
                         break;
                     case Opcodes::OP_CHECKSEQUENCEVERIFY:
                         if (!$this->flags->checkFlags(self::VERIFY_CHECKSEQUENCEVERIFY)) {
                             if ($this->flags->checkFlags(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                                 throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
                             }
                             break;
                         }
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation - CSV');
                         }
                         $sequence = Number::buffer($this->mainStack[-1], $this->minimalPush, 5, $math);
                         $nSequence = $sequence->getInt();
                         if ($math->cmp($nSequence, 0) < 0) {
                             throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Negative locktime');
                         }
                         if ($math->cmp($math->bitwiseAnd($nSequence, TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG), '0') !== 0) {
                             break;
                         }
                         if (!$this->checkSequence($sequence)) {
                             throw new ScriptRuntimeException(self::VERIFY_CHECKSEQUENCEVERIFY, 'Unsatisfied locktime');
                         }
                         break;
                     case Opcodes::OP_NOP1:
                     case Opcodes::OP_NOP4:
                     case Opcodes::OP_NOP5:
                     case Opcodes::OP_NOP6:
                     case Opcodes::OP_NOP7:
                     case Opcodes::OP_NOP8:
                     case Opcodes::OP_NOP9:
                     case Opcodes::OP_NOP10:
                         if ($this->flags->checkFlags(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                             throw new ScriptRuntimeException(self::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOP found - this is discouraged');
                         }
                         break;
                     case Opcodes::OP_NOP:
                         break;
                     case Opcodes::OP_IF:
                     case Opcodes::OP_NOTIF:
                         // <expression> if [statements] [else [statements]] endif
                         $value = false;
                         if ($fExec) {
                             if ($this->mainStack->isEmpty()) {
                                 throw new \RuntimeException('Unbalanced conditional');
                             }
                             // todo
                             $buffer = Number::buffer($this->mainStack->pop(), $this->minimalPush)->getBuffer();
                             $value = $this->castToBool($buffer);
                             if ($opCode === Opcodes::OP_NOTIF) {
                                 $value = !$value;
                             }
                         }
                         $this->vfStack->push($value ? $this->vchTrue : $this->vchFalse);
                         break;
                     case Opcodes::OP_ELSE:
                         if ($this->vfStack->isEmpty()) {
                             throw new \RuntimeException('Unbalanced conditional');
                         }
                         $this->vfStack[-1] = !$this->vfStack->end() ? $this->vchTrue : $this->vchFalse;
                         break;
                     case Opcodes::OP_ENDIF:
                         if ($this->vfStack->isEmpty()) {
                             throw new \RuntimeException('Unbalanced conditional');
                         }
                         break;
                     case Opcodes::OP_VERIFY:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $value = $this->castToBool($this->mainStack[-1]);
                         if (!$value) {
                             throw new \RuntimeException('Error: verify');
                         }
                         $this->mainStack->pop();
                         break;
                     case Opcodes::OP_RESERVED:
                         // todo
                         break;
                     case Opcodes::OP_TOALTSTACK:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_TOALTSTACK');
                         }
                         $this->altStack->push($this->mainStack->pop());
                         break;
                     case Opcodes::OP_FROMALTSTACK:
                         if ($this->altStack->isEmpty()) {
                             throw new \RuntimeException('Invalid alt-stack operation OP_FROMALTSTACK');
                         }
                         $this->mainStack->push($this->altStack->pop());
                         break;
                     case Opcodes::OP_IFDUP:
                         // If top value not zero, duplicate it.
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_IFDUP');
                         }
                         $vch = $this->mainStack[-1];
                         if ($this->castToBool($vch)) {
                             $this->mainStack->push($vch);
                         }
                         break;
                     case Opcodes::OP_DEPTH:
                         $num = count($this->mainStack);
                         if ($num === 0) {
                             $depth = $this->vchFalse;
                         } else {
                             $depth = Number::int($num)->getBuffer();
                         }
                         $this->mainStack->push($depth);
                         break;
                     case Opcodes::OP_DROP:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_DROP');
                         }
                         $this->mainStack->pop();
                         break;
                     case Opcodes::OP_DUP:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_DUP');
                         }
                         $vch = $this->mainStack[-1];
                         $this->mainStack->push($vch);
                         break;
                     case Opcodes::OP_NIP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_NIP');
                         }
                         unset($this->mainStack[-2]);
                         break;
                     case Opcodes::OP_OVER:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_OVER');
                         }
                         $vch = $this->mainStack[-2];
                         $this->mainStack->push($vch);
                         break;
                     case Opcodes::OP_ROT:
                         if (count($this->mainStack) < 3) {
                             throw new \RuntimeException('Invalid stack operation OP_ROT');
                         }
                         $this->mainStack->swap(-3, -2);
                         $this->mainStack->swap(-2, -1);
                         break;
                     case Opcodes::OP_SWAP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_SWAP');
                         }
                         $this->mainStack->swap(-2, -1);
                         break;
                     case Opcodes::OP_TUCK:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_TUCK');
                         }
                         $vch = $this->mainStack[-1];
                         $this->mainStack->add(count($this->mainStack) - 1 - 2, $vch);
                         break;
                     case Opcodes::OP_PICK:
                     case Opcodes::OP_ROLL:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_PICK');
                         }
                         $n = Number::buffer($this->mainStack[-1], $this->minimalPush, 4)->getInt();
                         $this->mainStack->pop();
                         if ($math->cmp($n, 0) < 0 || $math->cmp($n, count($this->mainStack)) >= 0) {
                             throw new \RuntimeException('Invalid stack operation OP_PICK');
                         }
                         $pos = (int) $math->sub($math->sub(0, $n), 1);
                         $vch = $this->mainStack[$pos];
                         if ($opCode === Opcodes::OP_ROLL) {
                             unset($this->mainStack[$pos]);
                         }
                         $this->mainStack->push($vch);
                         break;
                     case Opcodes::OP_2DROP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_2DROP');
                         }
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         break;
                     case Opcodes::OP_2DUP:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_2DUP');
                         }
                         $string1 = $this->mainStack[-2];
                         $string2 = $this->mainStack[-1];
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         break;
                     case Opcodes::OP_3DUP:
                         if (count($this->mainStack) < 3) {
                             throw new \RuntimeException('Invalid stack operation OP_3DUP');
                         }
                         $string1 = $this->mainStack[-3];
                         $string2 = $this->mainStack[-2];
                         $string3 = $this->mainStack[-1];
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         $this->mainStack->push($string3);
                         break;
                     case Opcodes::OP_2OVER:
                         if (count($this->mainStack) < 4) {
                             throw new \RuntimeException('Invalid stack operation OP_2OVER');
                         }
                         $string1 = $this->mainStack[-4];
                         $string2 = $this->mainStack[-3];
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         break;
                     case Opcodes::OP_2ROT:
                         if (count($this->mainStack) < 6) {
                             throw new \RuntimeException('Invalid stack operation OP_2ROT');
                         }
                         $string1 = $this->mainStack[-6];
                         $string2 = $this->mainStack[-5];
                         unset($this->mainStack[-6], $this->mainStack[-5]);
                         $this->mainStack->push($string1);
                         $this->mainStack->push($string2);
                         break;
                     case Opcodes::OP_2SWAP:
                         if (count($this->mainStack) < 4) {
                             throw new \RuntimeException('Invalid stack operation OP_2SWAP');
                         }
                         $this->mainStack->swap(-3, -1);
                         $this->mainStack->swap(-4, -2);
                         break;
                     case Opcodes::OP_SIZE:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation OP_SIZE');
                         }
                         // todo: Int sizes?
                         $vch = $this->mainStack[-1];
                         $this->mainStack->push(Number::int($vch->getSize())->getBuffer());
                         break;
                     case Opcodes::OP_EQUAL:
                     case Opcodes::OP_EQUALVERIFY:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation OP_EQUAL');
                         }
                         $vch1 = $this->mainStack[-2];
                         $vch2 = $this->mainStack[-1];
                         $equal = $vch1->getBinary() === $vch2->getBinary();
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->push($equal ? $this->vchTrue : $this->vchFalse);
                         if ($opCode === Opcodes::OP_EQUALVERIFY) {
                             if ($equal) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('Error EQUALVERIFY');
                             }
                         }
                         break;
                         // Arithmetic operations
                     // Arithmetic operations
                     case $opCode >= Opcodes::OP_1ADD && $opCode <= Opcodes::OP_0NOTEQUAL:
                         if ($this->mainStack->isEmpty()) {
                             throw new \Exception('Invalid stack operation 1ADD-OP_0NOTEQUAL');
                         }
                         $num = Number::buffer($this->mainStack[-1], $this->minimalPush)->getInt();
                         if ($opCode === Opcodes::OP_1ADD) {
                             $num = $math->add($num, '1');
                         } elseif ($opCode === Opcodes::OP_1SUB) {
                             $num = $math->sub($num, '1');
                         } elseif ($opCode === Opcodes::OP_2MUL) {
                             $num = $math->mul(2, $num);
                         } elseif ($opCode === Opcodes::OP_NEGATE) {
                             $num = $math->sub(0, $num);
                         } elseif ($opCode === Opcodes::OP_ABS) {
                             if ($math->cmp($num, '0') < 0) {
                                 $num = $math->sub(0, $num);
                             }
                         } elseif ($opCode === Opcodes::OP_NOT) {
                             $num = (int) $math->cmp($num, '0') === 0;
                         } else {
                             // is OP_0NOTEQUAL
                             $num = (int) ($math->cmp($num, '0') !== 0);
                         }
                         $this->mainStack->pop();
                         $buffer = Number::int($num)->getBuffer();
                         $this->mainStack->push($buffer);
                         break;
                     case $opCode >= Opcodes::OP_ADD && $opCode <= Opcodes::OP_MAX:
                         if (count($this->mainStack) < 2) {
                             throw new \Exception('Invalid stack operation (OP_ADD - OP_MAX)');
                         }
                         $num1 = Number::buffer($this->mainStack[-2], $this->minimalPush)->getInt();
                         $num2 = Number::buffer($this->mainStack[-1], $this->minimalPush)->getInt();
                         if ($opCode === Opcodes::OP_ADD) {
                             $num = $math->add($num1, $num2);
                         } else {
                             if ($opCode === Opcodes::OP_SUB) {
                                 $num = $math->sub($num1, $num2);
                             } else {
                                 if ($opCode === Opcodes::OP_BOOLAND) {
                                     $num = $math->cmp($num1, $this->int0->getInt()) !== 0 && $math->cmp($num2, $this->int0->getInt()) !== 0;
                                 } else {
                                     if ($opCode === Opcodes::OP_BOOLOR) {
                                         $num = $math->cmp($num1, $this->int0->getInt()) !== 0 || $math->cmp($num2, $this->int0->getInt()) !== 0;
                                     } elseif ($opCode === Opcodes::OP_NUMEQUAL) {
                                         $num = $math->cmp($num1, $num2) === 0;
                                     } elseif ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
                                         $num = $math->cmp($num1, $num2) === 0;
                                     } elseif ($opCode === Opcodes::OP_NUMNOTEQUAL) {
                                         $num = $math->cmp($num1, $num2) !== 0;
                                     } elseif ($opCode === Opcodes::OP_LESSTHAN) {
                                         $num = $math->cmp($num1, $num2) < 0;
                                     } elseif ($opCode === Opcodes::OP_GREATERTHAN) {
                                         $num = $math->cmp($num1, $num2) > 0;
                                     } elseif ($opCode === Opcodes::OP_LESSTHANOREQUAL) {
                                         $num = $math->cmp($num1, $num2) <= 0;
                                     } elseif ($opCode === Opcodes::OP_GREATERTHANOREQUAL) {
                                         $num = $math->cmp($num1, $num2) >= 0;
                                     } elseif ($opCode === Opcodes::OP_MIN) {
                                         $num = $math->cmp($num1, $num2) <= 0 ? $num1 : $num2;
                                     } else {
                                         $num = $math->cmp($num1, $num2) >= 0 ? $num1 : $num2;
                                     }
                                 }
                             }
                         }
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $buffer = Number::int($num)->getBuffer();
                         $this->mainStack->push($buffer);
                         if ($opCode === Opcodes::OP_NUMEQUALVERIFY) {
                             if ($this->castToBool($this->mainStack[-1])) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('NUM EQUAL VERIFY error');
                             }
                         }
                         break;
                     case Opcodes::OP_WITHIN:
                         if (count($this->mainStack) < 3) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $num1 = Number::buffer($this->mainStack[-3], $this->minimalPush)->getInt();
                         $num2 = Number::buffer($this->mainStack[-2], $this->minimalPush)->getInt();
                         $num3 = Number::buffer($this->mainStack[-1], $this->minimalPush)->getInt();
                         $value = $math->cmp($num2, $num1) <= 0 && $math->cmp($num1, $num3) < 0;
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->push($value ? $this->vchFalse : $this->vchTrue);
                         break;
                         // Hash operation
                     // Hash operation
                     case Opcodes::OP_RIPEMD160:
                     case Opcodes::OP_SHA1:
                     case Opcodes::OP_SHA256:
                     case Opcodes::OP_HASH160:
                     case Opcodes::OP_HASH256:
                         if ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $buffer = $this->mainStack[-1];
                         if ($opCode === Opcodes::OP_RIPEMD160) {
                             $hash = Hash::ripemd160($buffer);
                         } elseif ($opCode === Opcodes::OP_SHA1) {
                             $hash = Hash::sha1($buffer);
                         } elseif ($opCode === Opcodes::OP_SHA256) {
                             $hash = Hash::sha256($buffer);
                         } elseif ($opCode === Opcodes::OP_HASH160) {
                             $hash = Hash::sha256ripe160($buffer);
                         } else {
                             $hash = Hash::sha256d($buffer);
                         }
                         $this->mainStack->pop();
                         $this->mainStack->push($hash);
                         break;
                     case Opcodes::OP_CODESEPARATOR:
                         $this->hashStartPos = $parser->getPosition();
                         break;
                     case Opcodes::OP_CHECKSIG:
                     case Opcodes::OP_CHECKSIGVERIFY:
                         if (count($this->mainStack) < 2) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $vchPubKey = $this->mainStack[-1];
                         $vchSig = $this->mainStack[-2];
                         $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
                         $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey);
                         $this->mainStack->pop();
                         $this->mainStack->pop();
                         $this->mainStack->push($success ? $this->vchTrue : $this->vchFalse);
                         if ($opCode === Opcodes::OP_CHECKSIGVERIFY) {
                             if ($success) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('Checksig verify');
                             }
                         }
                         break;
                     case Opcodes::OP_CHECKMULTISIG:
                     case Opcodes::OP_CHECKMULTISIGVERIFY:
                         $i = 1;
                         if (count($this->mainStack) < $i) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $keyCount = Number::buffer($this->mainStack[-$i], $this->minimalPush)->getInt();
                         if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
                             throw new \RuntimeException('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;
                         /** @var int $i */
                         if (count($this->mainStack) < $i) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         $sigCount = Number::buffer($this->mainStack[-$i], $this->minimalPush)->getInt();
                         if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
                             throw new \RuntimeException('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 = $this->mainStack[-$isig];
                             $pubkey = $this->mainStack[-$ikey];
                             // Erase the signature and public key.
                             unset($this->mainStack[-$isig], $this->mainStack[-$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) {
                             $this->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 ($this->mainStack->isEmpty()) {
                             throw new \RuntimeException('Invalid stack operation');
                         }
                         if ($this->flags->checkFlags(self::VERIFY_NULL_DUMMY) && $this->mainStack[-1]->getSize()) {
                             throw new ScriptRuntimeException(self::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
                         }
                         $this->mainStack->pop();
                         $this->mainStack->push($fSuccess ? $this->vchTrue : $this->vchFalse);
                         if ($opCode === Opcodes::OP_CHECKMULTISIGVERIFY) {
                             if ($fSuccess) {
                                 $this->mainStack->pop();
                             } else {
                                 throw new \RuntimeException('OP_CHECKMULTISIG verify');
                             }
                         }
                         break;
                     default:
                         throw new \RuntimeException('Opcode not found');
                 }
                 if (count($this->mainStack) + count($this->altStack) > 1000) {
                     throw new \RuntimeException('Invalid stack size, exceeds 1000');
                 }
             }
         }
         if (!$this->vfStack->end() === 0) {
             throw new \RuntimeException('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;
     }
 }
Exemplo n.º 5
0
 /**
  * @param PrivateKeyInterface $key
  * @param ScriptInterface|null $redeemScript
  * @param ScriptInterface|null $witnessScript
  * @return bool
  */
 public function sign(PrivateKeyInterface $key, ScriptInterface $redeemScript = null, ScriptInterface $witnessScript = null)
 {
     /** @var BufferInterface[] $return */
     $type = null;
     $return = [];
     $solved = $this->doSignature($key, $this->txOut->getScript(), $type, $return, 0);
     if ($solved && $type === OutputClassifier::PAYTOSCRIPTHASH) {
         $redeemScriptBuffer = $return[0];
         if (!$redeemScript instanceof ScriptInterface) {
             throw new \InvalidArgumentException('Must provide redeem script for P2SH');
         }
         if (!$redeemScript->getScriptHash()->getBinary() === $redeemScriptBuffer->getBinary()) {
             throw new \InvalidArgumentException("Incorrect redeem script - hash doesn't match");
         }
         $results = [];
         // ???
         $solved = $solved && $this->doSignature($key, $redeemScript, $type, $results, 0) && $type !== OutputClassifier::PAYTOSCRIPTHASH;
         if ($solved) {
             $this->redeemScript = $redeemScript;
         }
     }
     if ($solved && $type === OutputClassifier::WITNESS_V0_KEYHASH) {
         $pubKeyHash = $return[0];
         $witnessScript = ScriptFactory::sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $pubKeyHash, Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG]);
         $subType = null;
         $subResults = [];
         $solved = $solved && $this->doSignature($key, $witnessScript, $subType, $subResults, 1);
     } else {
         if ($solved && $type === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
             $scriptHash = $return[0];
             if (!$witnessScript instanceof ScriptInterface) {
                 throw new \InvalidArgumentException('Must provide witness script for witness v0 scripthash');
             }
             if (!Hash::sha256($witnessScript->getBuffer())->getBinary() === $scriptHash->getBinary()) {
                 throw new \InvalidArgumentException("Incorrect witness script - hash doesn't match");
             }
             $subType = null;
             $subResults = [];
             $solved = $solved && $this->doSignature($key, $witnessScript, $subType, $subResults, 1) && $subType !== OutputClassifier::PAYTOSCRIPTHASH && $subType !== OutputClassifier::WITNESS_V0_SCRIPTHASH && $subType !== OutputClassifier::WITNESS_V0_KEYHASH;
             if ($solved) {
                 $this->witnessScript = $witnessScript;
             }
         }
     }
     return $solved;
 }
Exemplo n.º 6
0
<?php

require "../vendor/autoload.php";
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Script\Script;
use BitWasp\Bitcoin\Transaction\OutPoint;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Script\WitnessProgram;
use BitWasp\Bitcoin\Script\P2shScript;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Script\Opcodes;
use BitWasp\Bitcoin\Bitcoin;
$wif = 'QP3p9tRpTGTefG4a8jKoktSWC7Um8qzvt8wGKMxwWyW3KTNxMxN7';
$s = \BitWasp\Bitcoin\Network\NetworkFactory::bitcoinSegnet();
Bitcoin::setNetwork($s);
$ec = \BitWasp\Bitcoin\Bitcoin::getEcAdapter();
$key = PrivateKeyFactory::fromWif($wif);
echo $key->getPublicKey()->getAddress()->getAddress() . PHP_EOL;
$outpoint = new OutPoint(Buffer::hex('499c2bff1499bf84bc63058fda3ed112c2c663389f108353798a1bd6a6651afe', 32), 0);
$scriptPubKey = ScriptFactory::scriptPubKey()->payToPubKeyHash($key->getPublicKey());
$value = 100000000;
$txOut = new \BitWasp\Bitcoin\Transaction\TransactionOutput($value, $scriptPubKey);
$destination = new WitnessProgram(0, Hash::sha256($scriptPubKey->getBuffer()));
$tx = TransactionFactory::build()->spendOutPoint($outpoint)->output(99990000, $destination->getScript())->get();
$signed = new \BitWasp\Bitcoin\Transaction\Factory\Signer($tx, $ec);
$signed->sign(0, $key, $txOut);
$ss = $signed->get();
echo $ss->getBuffer()->getHex() . PHP_EOL;
Exemplo n.º 7
0
require "../vendor/autoload.php";
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Script\Script;
use BitWasp\Bitcoin\Transaction\OutPoint;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Script\WitnessProgram;
use BitWasp\Bitcoin\Script\P2shScript;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Script\Opcodes;
use BitWasp\Bitcoin\Bitcoin;
$wif = 'QP3p9tRpTGTefG4a8jKoktSWC7Um8qzvt8wGKMxwWyW3KTNxMxN7';
$s = \BitWasp\Bitcoin\Network\NetworkFactory::bitcoinSegnet();
Bitcoin::setNetwork($s);
$ec = \BitWasp\Bitcoin\Bitcoin::getEcAdapter();
$key = PrivateKeyFactory::fromWif($wif);
echo "Bitcoin address: " . $key->getPublicKey()->getAddress()->getAddress() . PHP_EOL;
$destScript = ScriptFactory::scriptPubKey()->payToPubKeyHash($key->getPublicKey());
$program = new WitnessProgram(0, Hash::sha256($destScript->getBuffer()));
$outpoint = new OutPoint(Buffer::hex('c2197f15d510304f1463230c0e61566bfb8dcadb7e1c510d3c0470bcfbca2194', 32), 0);
$txOut = new \BitWasp\Bitcoin\Transaction\TransactionOutput(99990000, $program->getScript());
$tx = TransactionFactory::build()->spendOutPoint($outpoint)->payToAddress(97900000, $key->getPublicKey()->getAddress())->get();
$signed = new \BitWasp\Bitcoin\Transaction\Factory\Signer($tx, $ec);
$signed->sign(0, $key, $txOut, null, $destScript);
$ss = $signed->get();
$consensus = ScriptFactory::consensus(\BitWasp\Bitcoin\Script\Interpreter\InterpreterInterface::VERIFY_P2SH | \BitWasp\Bitcoin\Script\Interpreter\InterpreterInterface::VERIFY_WITNESS);
echo "Script validation result: " . ($ss->validator()->checkSignature($consensus, 0, 99990000, $txOut->getScript()) ? "yay\n" : "nay\n");
echo PHP_EOL;
echo $ss->getWitnessBuffer()->getHex() . PHP_EOL . PHP_EOL;
//echo $ss->getHex() . PHP_EOL;
Exemplo n.º 8
0
<?php

require "../vendor/autoload.php";
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Crypto\Hash;
use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Script\ScriptFactory;
$ent = Hash::sha256(new Buffer('abc'));
$priv = PrivateKeyFactory::fromHex($ent);
$publicKey = $priv->getPublicKey();
$outputScript = ScriptFactory::scriptPubKey()->payToPubKeyHash($publicKey);
$tx = TransactionFactory::build()->input('10001234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234', 0)->payToAddress(50, $publicKey->getAddress())->get();
$signed = TransactionFactory::sign($tx)->sign(0, $priv, $outputScript)->get();
echo $signed->getHex();
$consensus = ScriptFactory::consensus();
$validator = $signed->validator();
var_dump($validator->checkSignatures($consensus, [$outputScript]));