/** * @param int $inputToSign * @param PrivateKeyInterface $privateKey * @param ScriptInterface $outputScript * @param ScriptInterface|null $redeemScript * @param int $sigHashType * @return $this */ public function sign($inputToSign, PrivateKeyInterface $privateKey, ScriptInterface $outputScript, ScriptInterface $redeemScript = null, $sigHashType = SigHash::ALL) { // If the input state hasn't been set up, do so now. try { $inputState = $this->inputState($inputToSign); } catch (BuilderNoInputState $e) { $inputState = $this->createInputState($inputToSign, $outputScript, $redeemScript); } // If it's PayToPubkey / PayToPubkeyHash, TransactionBuilderInputState needs to know the public key. if ($inputState->getScriptType() === OutputClassifier::PAYTOPUBKEYHASH) { $inputState->setPublicKeys([$privateKey->getPublicKey()]); } // loop over the publicKeys to find the key to sign with foreach ($inputState->getPublicKeys() as $idx => $publicKey) { if ($privateKey->getPublicKey()->getBinary() === $publicKey->getBinary()) { $signature = $this->makeSignature($privateKey, $this->signatureHash->calculate($redeemScript ?: $outputScript, $inputToSign, $sigHashType), $sigHashType); //if ($inputState->getPrevOutType() === OutputClassifier::WITNESS) { // $inputState->setWitness($idx, $signature); //} else { $inputState->setSignature($idx, $signature); //} } } 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 PrivateKeyInterface $key * @param ScriptInterface $scriptCode * @param int $sigVersion * @return TransactionSignature */ public function calculateSignature(PrivateKeyInterface $key, ScriptInterface $scriptCode, $sigVersion) { if ($sigVersion == 1) { $hasher = new V1Hasher($this->tx, $this->txOut->getValue()); } else { $hasher = new Hasher($this->tx); } $hash = $hasher->calculate($scriptCode, $this->nInput, $this->sigHashType); return new TransactionSignature($this->ecAdapter, $this->ecAdapter->sign($hash, $key, new Rfc6979($this->ecAdapter, $key, $hash, 'sha256')), $this->sigHashType); }
/** * @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; }