/** * @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; }
/** * @param ScriptInterface $script * @param BufferInterface $sigBuf * @param BufferInterface $keyBuf * @param int $sigVersion * @param int $flags * @return bool * @throws ScriptRuntimeException */ public function checkSig(ScriptInterface $script, BufferInterface $sigBuf, BufferInterface $keyBuf, $sigVersion, $flags) { $this->checkSignatureEncoding($sigBuf, $flags)->checkPublicKeyEncoding($keyBuf, $flags); try { $txSignature = TransactionSignatureFactory::fromHex($sigBuf->getHex()); $publicKey = PublicKeyFactory::fromHex($keyBuf->getHex()); if ($sigVersion === 1) { $hasher = new V1Hasher($this->transaction, $this->amount); } else { $hasher = new Hasher($this->transaction); } $hash = $hasher->calculate($script, $this->nInput, $txSignature->getHashType()); return $this->adapter->verify($hash, $publicKey, $txSignature->getSignature()); } catch (\Exception $e) { return false; } }
/** * @param ScriptInterface $script * @param Buffer $sigBuf * @param Buffer $keyBuf * @return bool * @throws ScriptRuntimeException * @throws \Exception */ private function checkSig(ScriptInterface $script, Buffer $sigBuf, Buffer $keyBuf) { $this->checkSignatureEncoding($sigBuf)->checkPublicKeyEncoding($keyBuf); try { $txSignature = TransactionSignatureFactory::fromHex($sigBuf->getHex()); $publicKey = PublicKeyFactory::fromHex($keyBuf->getHex()); return $this->ecAdapter->verify($this->transaction->getSignatureHash()->calculate($script, $this->inputToSign, $txSignature->getHashType()), $publicKey, $txSignature->getSignature()); } catch (\Exception $e) { return false; } }
/** * @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()); }
/** * @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; }
/** * @param TransactionInterface $tx * @param $inputToExtract * @return $this */ public function extractSigs(TransactionInterface $tx, $inputToExtract) { $inputs = $tx->getInputs(); $parsed = $inputs[$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(); $keys = $this->scriptInfo->getKeys(); foreach ($keys as $idx => $key) { $this->setSignature($idx, null); } if ($size > 2 && $size <= $this->scriptInfo->getKeyCount() + 2) { $sigs = []; foreach ($keys as $key) { $sigs[$key->getPubKeyHash()->getHex()] = []; } // Extract Signatures (as buffers), then compile arrays of [pubkeyHash => signature] $sigHash = new Hasher($tx); 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); $linked = $this->ecAdapter->associateSigs([$txSig->getSignature()], $sigHash->calculate($redeemScript, $inputToExtract, $txSig->getHashType()), $keys); if (count($linked)) { $key = array_keys($linked)[0]; $sigs[$key] = array_merge($sigs[$key], [$txSig]); } } } // We have all the signatures from the tx now. array_shift the sigs for a public key, as it's encountered. foreach ($keys as $idx => $key) { $hash = $key->getPubKeyHash()->getHex(); $this->setSignature($idx, isset($sigs[$hash]) ? array_shift($sigs[$hash]) : null); } } break; } return $this; }