protected function addressFromScriptHex($script_hex)
 {
     $address = null;
     try {
         $script = ScriptFactory::fromHex($script_hex);
         $classifier = new InputClassifier($script);
         if ($classifier->isPayToPublicKeyHash()) {
             $decoded = $script->getScriptParser()->decode();
             $public_key = PublicKeyFactory::fromHex($decoded[1]->getData());
             $address = $public_key->getAddress()->getAddress();
         } else {
             if ($classifier->isPayToScriptHash()) {
                 $decoded = $script->getScriptParser()->decode();
                 $hex_buffer = $decoded[count($decoded) - 1]->getData();
                 $sh_address = new ScriptHashAddress(ScriptFactory::fromHex($hex_buffer)->getScriptHash());
                 $address = $sh_address->getAddress();
             } else {
                 // unknown script type
                 Log::debug("Unable to classify script " . substr($hex, 0, 20) . "...");
             }
         }
     } catch (Exception $e) {
         Log::error("failed to get address from script. " . $e->getMessage());
     }
     return $address;
 }
예제 #2
0
 /**
  * @param ScriptInterface $script
  */
 public function __construct(ScriptInterface $script)
 {
     $publicKeys = [];
     $parse = $script->getScriptParser()->decode();
     if (count($parse) < 4) {
         throw new \InvalidArgumentException('Malformed multisig script');
     }
     $mCode = $parse[0]->getOp();
     $nCode = $parse[count($parse) - 2]->getOp();
     $this->m = (int) \BitWasp\Bitcoin\Script\decodeOpN($mCode);
     foreach (array_slice($parse, 1, -2) as $key) {
         /** @var \BitWasp\Bitcoin\Script\Parser\Operation $key */
         if (!$key->isPush()) {
             throw new \RuntimeException('Malformed multisig script');
         }
         $publicKeys[] = PublicKeyFactory::fromHex($key->getData());
     }
     $n = \BitWasp\Bitcoin\Script\decodeOpN($nCode);
     $this->n = count($publicKeys);
     if ($this->n === 0 || $this->n !== $n) {
         throw new \LogicException('No public keys found in script');
     }
     $this->script = $script;
     $this->keys = $publicKeys;
 }
예제 #3
0
 /**
  * @param ScriptInterface $script
  */
 public function __construct(ScriptInterface $script)
 {
     $this->script = $script;
     $chunks = $script->getScriptParser()->decode();
     if (count($chunks) < 1 || !$chunks[0]->isPush()) {
         throw new \InvalidArgumentException('Malformed pay-to-pubkey script');
     }
     $this->publicKey = PublicKeyFactory::fromHex($chunks[0]->getData());
 }
 /**
  * @param Parser $parser
  * @return HierarchicalKey
  * @throws ParserOutOfRange
  */
 public function fromParser(Parser &$parser)
 {
     try {
         list($bytes, $depth, $parentFingerprint, $sequence, $chainCode, $keyData) = $this->getTemplate()->parse($parser);
         $bytes = $bytes->getHex();
     } catch (ParserOutOfRange $e) {
         throw new ParserOutOfRange('Failed to extract HierarchicalKey from parser');
     }
     if ($bytes !== $this->network->getHDPubByte() && $bytes !== $this->network->getHDPrivByte()) {
         throw new \InvalidArgumentException("HD key magic bytes do not match network magic bytes");
     }
     $key = $this->network->getHDPrivByte() == $bytes ? PrivateKeyFactory::fromHex($keyData->slice(1)->getHex(), true, $this->ecAdapter) : PublicKeyFactory::fromHex($keyData->getHex(), $this->ecAdapter);
     return new HierarchicalKey($this->ecAdapter, $depth, $parentFingerprint, $sequence, $chainCode, $key);
 }
예제 #5
0
 /**
  * @param ScriptInterface $script
  * @param NetworkInterface $network
  * @return String
  * @throws \RuntimeException
  */
 public static function getAssociatedAddress(ScriptInterface $script, NetworkInterface $network = null)
 {
     $classifier = new OutputClassifier($script);
     $network = $network ?: Bitcoin::getNetwork();
     try {
         if ($classifier->isPayToPublicKey()) {
             $address = PublicKeyFactory::fromHex($script->getScriptParser()->decode()[0]->getData())->getAddress();
         } else {
             $address = self::fromOutputScript($script);
         }
         return Base58::encodeCheck(Buffer::hex($network->getAddressByte() . $address->getHash(), 21));
     } catch (\Exception $e) {
         throw new \RuntimeException('No address associated with this script type');
     }
 }
예제 #6
0
 /**
  * @param ScriptInterface $script
  * @return RedeemScript
  */
 public static function fromScript(ScriptInterface $script)
 {
     $publicKeys = [];
     $parse = $script->getScriptParser()->parse();
     $opCodes = $script->getOpcodes();
     $m = $opCodes->getOpByName($parse[0]) - $opCodes->getOpByName('OP_1') + 1;
     foreach (array_slice($parse, 1, -2) as $item) {
         if (!$item instanceof Buffer) {
             throw new \RuntimeException('Unable to load public key');
         }
         $publicKeys[] = PublicKeyFactory::fromHex($item->getHex());
     }
     if (count($publicKeys) == 0) {
         throw new \LogicException('No public keys found in script');
     }
     return new self($m, $publicKeys);
 }
예제 #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;
 }
예제 #8
0
 /**
  * @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;
     }
 }
예제 #9
0
 /**
  * @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;
     }
 }
예제 #10
0
 /**
  * @param PrivateKeyInterface $privateKey
  * @return \BitWasp\Bitcoin\Key\PublicKey
  * @throws \Exception
  */
 public function privateToPublic(PrivateKeyInterface $privateKey)
 {
     $publicKey = '';
     $ret = \secp256k1_ec_pubkey_create($privateKey->getBuffer()->getBinary(), (int) $privateKey->isCompressed(), $publicKey);
     if ($ret === 1) {
         $public = PublicKeyFactory::fromHex(bin2hex($publicKey), $this);
         return $public;
     }
     throw new \Exception('Unable to convert private to public key');
 }
예제 #11
0
 /**
  * The function only returns true when $scriptPubKey could be classified
  *
  * @param PrivateKeyInterface $key
  * @param ScriptInterface $scriptPubKey
  * @param string $outputType
  * @param BufferInterface[] $results
  * @param int $sigVersion
  * @return bool
  */
 private function doSignature(PrivateKeyInterface $key, ScriptInterface $scriptPubKey, &$outputType, array &$results, $sigVersion = 0)
 {
     $return = [];
     $outputType = (new OutputClassifier($scriptPubKey))->classify($return);
     if ($outputType === OutputClassifier::UNKNOWN) {
         throw new \RuntimeException('Cannot sign unknown script type');
     }
     if ($outputType === OutputClassifier::PAYTOPUBKEY) {
         $publicKeyBuffer = $return;
         $results[] = $publicKeyBuffer;
         $this->requiredSigs = 1;
         $publicKey = PublicKeyFactory::fromHex($publicKeyBuffer);
         if ($publicKey->getBinary() === $key->getPublicKey()->getBinary()) {
             $this->signatures[0] = $this->calculateSignature($key, $scriptPubKey, $sigVersion);
         }
         return true;
     }
     if ($outputType === OutputClassifier::PAYTOPUBKEYHASH) {
         /** @var BufferInterface $pubKeyHash */
         $pubKeyHash = $return;
         $results[] = $pubKeyHash;
         $this->requiredSigs = 1;
         if ($pubKeyHash->getBinary() === $key->getPublicKey()->getPubKeyHash()->getBinary()) {
             $this->signatures[0] = $this->calculateSignature($key, $scriptPubKey, $sigVersion);
             $this->publicKeys[0] = $key->getPublicKey();
         }
         return true;
     }
     if ($outputType === OutputClassifier::MULTISIG) {
         $info = new Multisig($scriptPubKey);
         foreach ($info->getKeys() as $publicKey) {
             $results[] = $publicKey->getBuffer();
         }
         $this->publicKeys = $info->getKeys();
         $this->requiredSigs = $info->getKeyCount();
         foreach ($this->publicKeys as $keyIdx => $publicKey) {
             if ($publicKey->getBinary() == $key->getPublicKey()->getBinary()) {
                 $this->signatures[$keyIdx] = $this->calculateSignature($key, $scriptPubKey, $sigVersion);
             }
         }
         return true;
     }
     if ($outputType === OutputClassifier::PAYTOSCRIPTHASH) {
         /** @var BufferInterface $scriptHash */
         $scriptHash = $return;
         $results[] = $scriptHash;
         return true;
     }
     if ($outputType === OutputClassifier::WITNESS_V0_KEYHASH) {
         /** @var BufferInterface $pubKeyHash */
         $pubKeyHash = $return;
         $results[] = $pubKeyHash;
         $this->requiredSigs = 1;
         if ($pubKeyHash->getBinary() === $key->getPublicKey()->getPubKeyHash()->getBinary()) {
             $script = ScriptFactory::sequence([Opcodes::OP_DUP, Opcodes::OP_HASH160, $pubKeyHash, Opcodes::OP_EQUALVERIFY, Opcodes::OP_CHECKSIG]);
             $this->signatures[0] = $this->calculateSignature($key, $script, 1);
             $this->publicKeys[0] = $key->getPublicKey();
         }
         return true;
     }
     if ($outputType === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
         /** @var BufferInterface $scriptHash */
         $scriptHash = $return;
         $results[] = $scriptHash;
         return true;
     }
     return false;
 }
예제 #12
0
 /**
  * @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;
 }