Ejemplo n.º 1
0
 /**
  * @param integer $input
  * @return TxSignerContext
  * @throws BuilderNoInputState
  */
 public function inputState($input)
 {
     $this->transaction->getInput($input);
     if (!array_key_exists($input, $this->inputStates)) {
         throw new BuilderNoInputState('State not found for this input');
     }
     return $this->inputStates[$input];
 }
Ejemplo n.º 2
0
 /**
  * @param UtxoView $utxoView
  * @param TransactionInterface $tx
  * @return ScriptValidationInterface
  */
 public function queue(UtxoView $utxoView, TransactionInterface $tx)
 {
     for ($i = 0, $c = count($tx->getInputs()); $i < $c; $i++) {
         $output = $utxoView->fetchByInput($tx->getInput($i))->getOutput();
         $witness = isset($tx->getWitnesses()[$i]) ? $tx->getWitness($i) : null;
         $this->results[] = $this->consensus->verify($tx, $output->getScript(), $i, $output->getValue(), $witness);
     }
     return $this;
 }
Ejemplo n.º 3
0
 /**
  * @return $this
  */
 public function extractSignatures()
 {
     $type = (new OutputClassifier($this->txOut->getScript()))->classify();
     $scriptPubKey = $this->txOut->getScript();
     $scriptSig = $this->tx->getInput($this->nInput)->getScript();
     if ($type === OutputClassifier::PAYTOPUBKEYHASH || $type === OutputClassifier::PAYTOPUBKEY || $type === OutputClassifier::MULTISIG) {
         $values = [];
         foreach ($scriptSig->getScriptParser()->decode() as $o) {
             $values[] = $o->getData();
         }
         $this->extractFromValues($type, $scriptPubKey, $values, 0);
     }
     if ($type === OutputClassifier::PAYTOSCRIPTHASH) {
         $decodeSig = $scriptSig->getScriptParser()->decode();
         if (count($decodeSig) > 0) {
             $redeemScript = new Script(end($decodeSig)->getData());
             $p2shType = (new OutputClassifier($redeemScript))->classify();
             if (count($decodeSig) > 1) {
                 $decodeSig = array_slice($decodeSig, 0, -1);
             }
             $internalSig = [];
             foreach ($decodeSig as $operation) {
                 $internalSig[] = $operation->getData();
             }
             $this->redeemScript = $redeemScript;
             $this->extractFromValues($p2shType, $redeemScript, $internalSig, 0);
             $type = $p2shType;
         }
     }
     $witnesses = $this->tx->getWitnesses();
     if ($type === OutputClassifier::WITNESS_V0_KEYHASH) {
         $this->requiredSigs = 1;
         if (isset($witnesses[$this->nInput])) {
             $witness = $witnesses[$this->nInput];
             $this->signatures = [TransactionSignatureFactory::fromHex($witness[0], $this->ecAdapter)];
             $this->publicKeys = [PublicKeyFactory::fromHex($witness[1], $this->ecAdapter)];
         }
     } else {
         if ($type === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
             if (isset($witnesses[$this->nInput])) {
                 $witness = $witnesses[$this->nInput];
                 $witCount = count($witnesses[$this->nInput]);
                 if ($witCount > 0) {
                     $witnessScript = new Script($witness[$witCount - 1]);
                     $vWitness = $witness->all();
                     if (count($vWitness) > 1) {
                         $vWitness = array_slice($witness->all(), 0, -1);
                     }
                     $witnessType = (new OutputClassifier($witnessScript))->classify();
                     $this->extractFromValues($witnessType, $witnessScript, $vWitness, 1);
                     $this->witnessScript = $witnessScript;
                 }
             }
         }
     }
     return $this;
 }
Ejemplo n.º 4
0
 /**
  * Calculate the hash of the current transaction, when you are looking to
  * spend $txOut, and are signing $inputToSign. The SigHashType defaults to
  * SIGHASH_ALL, though SIGHASH_SINGLE, SIGHASH_NONE, SIGHASH_ANYONECANPAY
  * can be used.
  *
  * @param ScriptInterface $txOutScript
  * @param int $inputToSign
  * @param int $sighashType
  * @return BufferInterface
  * @throws \Exception
  */
 public function calculate(ScriptInterface $txOutScript, $inputToSign, $sighashType = SigHash::ALL)
 {
     $sighashType = (int) $sighashType;
     $hashPrevOuts = $this->hashPrevOuts($sighashType);
     $hashSequence = $this->hashSequences($sighashType);
     $hashOutputs = $this->hashOutputs($sighashType, $inputToSign);
     $input = $this->transaction->getInput($inputToSign);
     return Hash::sha256d(new Buffer(pack("V", $this->transaction->getVersion()) . $hashPrevOuts->getBinary() . $hashSequence->getBinary() . $input->getOutPoint()->getBinary() . ScriptFactory::create()->push($txOutScript->getBuffer())->getScript()->getBinary() . Buffer::int($this->amount, 8)->flip()->getBinary() . pack("V", $input->getSequence()) . $hashOutputs->getBinary() . pack("V", $this->transaction->getLockTime()) . pack("V", $sighashType)));
 }
Ejemplo n.º 5
0
 /**
  * @param \BitWasp\Bitcoin\Script\Interpreter\Number $sequence
  * @return bool
  */
 private function checkSequence(Number $sequence)
 {
     $txSequence = $this->transaction->getInput($this->inputToSign)->getSequence();
     if ($this->transaction->getVersion() < 2) {
         return false;
     }
     if ($this->math->cmp($this->math->bitwiseAnd($txSequence, TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG), 0) !== 0) {
         return 0;
     }
     $mask = $this->math->bitwiseOr(TransactionInputInterface::SEQUENCE_LOCKTIME_TYPE_FLAG, TransactionInputInterface::SEQUENCE_LOCKTIME_MASK);
     return $this->verifyLockTime($this->math->bitwiseAnd($txSequence, $mask), TransactionInputInterface::SEQUENCE_LOCKTIME_TYPE_FLAG, Number::int($this->math->bitwiseAnd($sequence->getInt(), $mask)));
 }
Ejemplo n.º 6
0
 /**
  * @param \BitWasp\Bitcoin\Script\Interpreter\Number $sequence
  * @return bool
  */
 public function checkSequence(\BitWasp\Bitcoin\Script\Interpreter\Number $sequence)
 {
     $math = $this->adapter->getMath();
     $txSequence = $this->transaction->getInput($this->nInput)->getSequence();
     if ($this->transaction->getVersion() < 2) {
         return false;
     }
     if ($math->cmp($math->bitwiseAnd($txSequence, TransactionInputInterface::SEQUENCE_LOCKTIME_DISABLE_FLAG), 0) !== 0) {
         return 0;
     }
     $mask = $math->bitwiseOr(TransactionInputInterface::SEQUENCE_LOCKTIME_TYPE_FLAG, TransactionInputInterface::SEQUENCE_LOCKTIME_MASK);
     return $this->verifyLockTime($math->bitwiseAnd($txSequence, $mask), TransactionInputInterface::SEQUENCE_LOCKTIME_TYPE_FLAG, Number::int($math->bitwiseAnd($sequence->getInt(), $mask)));
 }
Ejemplo n.º 7
0
 /**
  * @param TransactionInterface $tx
  * @param int $inputToExtract
  * @return $this
  */
 public function extractSigs(TransactionInterface $tx, $inputToExtract)
 {
     $parsed = $tx->getInput($inputToExtract)->getScript()->getScriptParser()->decode();
     $size = count($parsed);
     switch ($this->getScriptType()) {
         case OutputClassifier::PAYTOPUBKEYHASH:
             // Supply signature and public key in scriptSig
             if ($size === 2) {
                 $this->signatures = [TransactionSignatureFactory::fromHex($parsed[0]->getData(), $this->ecAdapter)];
                 $this->publicKeys = [PublicKeyFactory::fromHex($parsed[1]->getData(), $this->ecAdapter)];
             }
             break;
         case OutputClassifier::PAYTOPUBKEY:
             // Only has a signature in the scriptSig
             if ($size === 1) {
                 $this->signatures = [TransactionSignatureFactory::fromHex($parsed[0]->getData(), $this->ecAdapter)];
             }
             break;
         case OutputClassifier::MULTISIG:
             $redeemScript = $this->getRedeemScript();
             $this->signatures = array_fill(0, count($this->publicKeys), null);
             if ($size > 2 && $size <= $this->scriptInfo->getKeyCount() + 2) {
                 $sigHash = $tx->getSignatureHash();
                 $sigSort = new SignatureSort($this->ecAdapter);
                 $sigs = new \SplObjectStorage();
                 foreach (array_slice($parsed, 1, -1) as $item) {
                     /** @var \BitWasp\Bitcoin\Script\Parser\Operation $item */
                     if ($item->isPush()) {
                         $txSig = TransactionSignatureFactory::fromHex($item->getData(), $this->ecAdapter);
                         $hash = $sigHash->calculate($redeemScript, $inputToExtract, $txSig->getHashType());
                         $linked = $sigSort->link([$txSig->getSignature()], $this->publicKeys, $hash);
                         foreach ($this->publicKeys as $key) {
                             if ($linked->contains($key)) {
                                 $sigs[$key] = $txSig;
                             }
                         }
                     }
                 }
                 // We have all the signatures from the input now. array_shift the sigs for a public key, as it's encountered.
                 foreach ($this->publicKeys as $idx => $key) {
                     $this->signatures[$idx] = isset($sigs[$key]) ? $sigs[$key] : null;
                 }
             }
             break;
     }
     return $this;
 }
Ejemplo n.º 8
0
 /**
  * @param UtxoView $view
  * @param TransactionInterface $tx
  * @param $spendHeight
  * @return bool
  */
 public function checkContextualInputs(UtxoView $view, TransactionInterface $tx, $spendHeight)
 {
     $valueIn = 0;
     for ($i = 0; $i < count($tx->getInputs()); $i++) {
         $utxo = $view->fetchByInput($tx->getInput($i));
         /*if ($out->isCoinbase()) {
               // todo: cb / height
               if ($spendHeight - $out->getHeight() < $this->params->coinbaseMaturityAge()) {
                   return false;
               }
           }*/
         $value = $utxo->getOutput()->getValue();
         $valueIn = $this->math->add($value, $valueIn);
         if (!$this->consensus->checkAmount($valueIn) || !$this->consensus->checkAmount($value)) {
             throw new \RuntimeException('CheckAmount failed for inputs value');
         }
     }
     $valueOut = 0;
     foreach ($tx->getOutputs() as $output) {
         $valueOut = $this->math->add($output->getValue(), $valueOut);
         if (!$this->consensus->checkAmount($valueOut) || !$this->consensus->checkAmount($output->getValue())) {
             throw new \RuntimeException('CheckAmount failed for outputs value');
         }
     }
     if ($this->math->cmp($valueIn, $valueOut) < 0) {
         throw new \RuntimeException('Value-in is less than value out');
     }
     $fee = $this->math->sub($valueIn, $valueOut);
     if ($this->math->cmp($fee, 0) < 0) {
         throw new \RuntimeException('Fee is less than zero');
     }
     if (!$this->consensus->checkAmount($fee)) {
         throw new \RuntimeException('CheckAmount failed for fee');
     }
     return true;
 }
 /**
  * @param TransactionInterface $tx
  * @return Transaction
  */
 public function fixTransaction(Peer $sender, TransactionInterface $tx, &$wasMalleated = false)
 {
     $c = count($tx->getInputs());
     $new = new TransactionInputCollection();
     for ($i = 0; $i < $c; $i++) {
         $input = $tx->getInput($i);
         $script = $input->getScript();
         $classify = ScriptFactory::scriptSig()->classify($input->getScript());
         $this->inputs++;
         if ($classify->isPayToPublicKeyHash()) {
             $parsed = $input->getScript()->getScriptParser()->parse();
             $txSig = TransactionSignatureFactory::fromHex($parsed[0]);
             $txSig = $this->fixSig($sender, $txSig, $wasMalleated);
             $script = ScriptFactory::create()->push($txSig->getBuffer())->push($parsed[1])->getScript();
         }
         $new->addInput(new TransactionInput($input->getTransactionId(), $input->getVout(), $script, $input->getSequence()));
     }
     return new Transaction($tx->getVersion(), $new, $tx->getOutputs(), $tx->getLockTime());
 }
Ejemplo n.º 10
0
 /**
  * @param UtxoView $view
  * @param TransactionInterface $tx
  * @param int $spendHeight
  * @return $this
  */
 public function checkContextualInputs(UtxoView $view, TransactionInterface $tx, $spendHeight)
 {
     $valueIn = gmp_init(0);
     for ($i = 0, $nInputs = count($tx->getInputs()); $i < $nInputs; $i++) {
         /*if ($out->isCoinbase()) {
               // todo: cb / height
               if ($spendHeight - $out->getHeight() < $this->params->coinbaseMaturityAge()) {
                   return false;
               }
           }*/
         $value = gmp_init($view->fetchByInput($tx->getInput($i))->getOutput()->getValue(), 10);
         $valueIn = $this->math->add($valueIn, $value);
         $this->consensus->checkAmount($valueIn);
     }
     $valueOut = gmp_init(0);
     foreach ($tx->getOutputs() as $output) {
         $valueOut = $this->math->add($valueOut, gmp_init($output->getValue(), 10));
         $this->consensus->checkAmount($valueOut);
     }
     if ($this->math->cmp($valueIn, $valueOut) < 0) {
         throw new \RuntimeException('Value-in is less than value-out');
     }
     $fee = $this->math->sub($valueIn, $valueOut);
     $this->consensus->checkAmount($fee);
     return $this;
 }