/**
  * @param $hex
  * @return PublicKey
  * @throws \Exception
  */
 public function parse($hex)
 {
     $hex = Buffer::hex($hex);
     if (!in_array($hex->getSize(), [PublicKey::LENGTH_COMPRESSED, PublicKey::LENGTH_UNCOMPRESSED])) {
         throw new \Exception('Invalid hex string, must match size of compressed or uncompressed public key');
     }
     return $this->ecAdapter->publicKeyFromBuffer($hex);
 }
예제 #2
0
 /**
  * @param $mnemonic
  * @return Buffer
  */
 public function mnemonicToEntropy($mnemonic)
 {
     $math = $this->ecAdapter->getMath();
     $words = explode(" ", $mnemonic);
     if ($math->mod(count($words), 3) != 0) {
         throw new \InvalidArgumentException('Invalid mnemonic');
     }
     $bits = array();
     foreach ($words as $word) {
         $idx = $this->wordList->getIndex($word);
         $bits[] = str_pad($math->baseConvert($idx, 10, 2), 11, '0', STR_PAD_LEFT);
     }
     $bits = implode("", $bits);
     $CS = strlen($bits) / 33;
     $ENT = strlen($bits) - $CS;
     $csBits = substr($bits, -1 * $CS);
     $entBits = substr($bits, 0, -1 * $CS);
     $binary = '';
     $bitsInChar = 8;
     for ($i = 0; $i < $ENT; $i += $bitsInChar) {
         // Extract 8 bits at a time, convert to hex, pad, and convert to binary.
         $eBits = substr($entBits, $i, $bitsInChar);
         $binary .= hex2bin(str_pad($math->baseConvert($eBits, 2, 16), 2, '0', STR_PAD_LEFT));
     }
     $entropy = new Buffer($binary);
     if ($csBits !== $this->calculateChecksum($entropy, $CS)) {
         throw new \InvalidArgumentException('Checksum does not match');
     }
     return $entropy;
 }
예제 #3
0
 /**
  * @param NetworkInterface $network
  * @return string
  */
 public function toWif(NetworkInterface $network = null)
 {
     $network = $network ?: Bitcoin::getNetwork();
     $wifSerializer = new WifPrivateKeySerializer($this->ecAdapter->getMath(), new HexPrivateKeySerializer($this->ecAdapter));
     $wif = $wifSerializer->serialize($network, $this);
     return $wif;
 }
예제 #4
0
 /**
  * @param string $mnemonic
  * @return Buffer
  */
 public function mnemonicToEntropy($mnemonic)
 {
     $math = $this->ecAdapter->getMath();
     $wordList = $this->wordList;
     $words = explode(" ", $mnemonic);
     $n = count($wordList);
     $out = '';
     $thirdWordCount = count($words) / 3;
     for ($i = 0; $i < $thirdWordCount; $i++) {
         list($word1, $word2, $word3) = array_slice($words, $math->mul(3, $i), 3);
         $index1 = $wordList->getIndex($word1);
         $index2 = $wordList->getIndex($word2);
         $index3 = $wordList->getIndex($word3);
         $x = $math->add($index1, $math->add($math->mul($n, $math->mod($index2 - $index1, $n)), $math->mul($n, $math->mul($n, $math->mod($index3 - $index2, $n)))));
         $out .= str_pad($math->decHex($x), 8, '0', STR_PAD_LEFT);
     }
     return Buffer::hex($out);
 }
예제 #5
0
 /**
  * Decodes a BIP32 path into it's actual 32bit sequence numbers: ie, m/0/1'/2/3' -> m/0/2147483649/2/2147483651
  *
  * @param string $path
  * @return string
  */
 public function decodePath($path)
 {
     $pathPieces = explode("/", $path);
     if (strlen($path) == 0 || count($pathPieces) == 0) {
         throw new \InvalidArgumentException('Invalid path passed to decodePath()');
     }
     $newPath = array();
     $helper = new HierarchicalKeySequence($this->ecAdapter->getMath());
     foreach ($pathPieces as $c => $sequence) {
         $newPath[] = $helper->fromNode($sequence);
     }
     $path = implode("/", $newPath);
     return $path;
 }
 /**
  * @param TransactionInterface $tx
  * @param integer $inputToExtract
  * @throws \Exception
  */
 public function extractSigs(TransactionInterface $tx, $inputToExtract)
 {
     $parsed = $tx->getInputs()->getInput($inputToExtract)->getScript()->getScriptParser()->parse();
     $size = count($parsed);
     switch ($this->getScriptType()) {
         case OutputClassifier::PAYTOPUBKEYHASH:
             // Supply signature and public key in scriptSig
             if ($size == 2) {
                 $this->setSignatures([TransactionSignatureFactory::fromHex($parsed[0]->getHex(), $this->ecAdapter->getMath())]);
                 $this->setPublicKeys([PublicKeyFactory::fromHex($parsed[1]->getHex(), $this->ecAdapter)]);
             }
             break;
         case OutputClassifier::PAYTOPUBKEY:
             // Only has a signature in the scriptSig
             if ($size == 1) {
                 $this->setSignatures([TransactionSignatureFactory::fromHex($parsed[0]->getHex(), $this->ecAdapter->getMath())]);
             }
             break;
         case OutputClassifier::MULTISIG:
             $keys = $this->getRedeemScript()->getKeys();
             foreach ($keys as $idx => $key) {
                 $this->setSignature($idx, null);
             }
             if ($size > 2 && $size <= $this->getRedeemScript()->getKeyCount() + 2) {
                 $sigs = [];
                 foreach ($keys as $key) {
                     $sigs[$key->getPubKeyHash()->getHex()] = [];
                 }
                 // Extract Signatures (as buffers), then compile arrays of [pubkeyHash => signature]
                 $sigHash = new SignatureHash($tx);
                 foreach (array_slice($parsed, 1, -1) as $item) {
                     if ($item instanceof Buffer) {
                         $txSig = TransactionSignatureFactory::fromHex($item, $this->ecAdapter->getMath());
                         $linked = $this->ecAdapter->associateSigs([$txSig->getSignature()], $sigHash->calculate($this->getRedeemScript(), $inputToExtract, $txSig->getHashType()), $this->getRedeemScript()->getKeys());
                         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;
     }
 }
예제 #7
0
 /**
  * @param string $message
  * @param PrivateKeyInterface $privateKey
  * @return SignedMessage
  */
 public function sign($message, PrivateKeyInterface $privateKey)
 {
     $hash = $this->calculateMessageHash($message);
     return new SignedMessage($message, $this->ecAdapter->signCompact($hash, $privateKey, new Rfc6979($this->ecAdapter, $privateKey, $hash, 'sha256')));
 }
예제 #8
0
 /**
  * @param int $bytes
  * @return Buffer
  */
 public function bytes($bytes)
 {
     $integer = $this->hmac->generate($this->ecAdapter->getGenerator()->getOrder());
     return Buffer::hex($this->ecAdapter->getMath()->decHex($integer));
 }
 /**
  * @param PrivateKeyInterface $privKey
  * @param Buffer $hash
  * @param $sigHashType
  * @return TransactionSignature
  */
 public function sign(PrivateKeyInterface $privKey, Buffer $hash, $sigHashType)
 {
     return new TransactionSignature($this->ecAdapter->sign($hash, $privKey, $this->deterministicSignatures ? new Rfc6979($this->ecAdapter, $privKey, $hash, 'sha256') : new Random()), $sigHashType);
 }
예제 #10
0
 /**
  * @return Buffer
  */
 public function getMPK()
 {
     $math = $this->ecAdapter->getMath();
     $point = $this->getMasterPublicKey()->getPoint();
     return Buffertools::concat(Buffer::hex($math->decHex($point->getX()), 32), Buffer::hex($math->decHex($point->getY()), 32));
 }
 /**
  * @param PrivateKeyInterface $privateKey
  * @return Buffer
  */
 public function serialize(PrivateKeyInterface $privateKey)
 {
     $multiplier = $privateKey->getSecretMultiplier();
     return Buffer::hex($this->ecAdapter->getMath()->decHex($multiplier), 32);
 }
예제 #12
0
 /**
  * @return bool
  */
 public function run()
 {
     $math = $this->ecAdapter->getMath();
     $opcodes = $this->script->getOpCodes();
     $flags = $this->flags;
     $mainStack = $this->state->getMainStack();
     $altStack = $this->state->getAltStack();
     $vfStack = $this->state->getVfStack();
     $this->hashStartPos = 0;
     $this->opCount = 0;
     $parser = $this->script->getScriptParser();
     $_bn0 = Buffer::hex('00');
     $_bn1 = Buffer::hex('01');
     if ($this->script->getBuffer()->getSize() > 10000) {
         return false;
     }
     $checkFExec = function () use(&$vfStack) {
         $c = 0;
         $len = $vfStack->end();
         for ($i = 0; $i < $len; $i++) {
             if ($vfStack->top(0 - $len - $i) == true) {
                 $c++;
             }
         }
         return (bool) $c;
     };
     try {
         while ($parser->next($opCode, $pushData) === true) {
             $fExec = !$checkFExec();
             // If pushdata was written to,
             if ($pushData instanceof Buffer && $pushData->getSize() > InterpreterInterface::MAX_SCRIPT_ELEMENT_SIZE) {
                 throw new \Exception('Error - push size');
             }
             // OP_RESERVED should not count towards opCount
             if ($this->script->getOpcodes()->cmp($opCode, 'OP_16') > 0 && ++$this->opCount) {
                 $this->checkOpcodeCount();
             }
             if ($this->checkDisabledOpcodes) {
                 if ($this->isDisabledOp($opCode)) {
                     throw new \Exception('Disabled Opcode');
                 }
             }
             if ($fExec && $opCode >= 0 && $opcodes->cmp($opCode, 'OP_PUSHDATA4') <= 0) {
                 // In range of a pushdata opcode
                 if ($flags->checkFlags(InterpreterInterface::VERIFY_MINIMALDATA) && !$this->checkMinimalPush($opCode, $pushData)) {
                     throw new ScriptRuntimeException(InterpreterInterface::VERIFY_MINIMALDATA, 'Minimal pushdata required');
                 }
                 $mainStack->push($pushData);
                 //echo " - [pushed '" . $pushData->getHex() . "']\n";
             } elseif ($fExec || $opcodes->isOp($opCode, 'OP_IF') <= 0 && $opcodes->isOp($opCode, 'OP_ENDIF')) {
                 //echo " - [". $opcodes->getOp($opCode) . "]\n";
                 switch ($opCode) {
                     case $opcodes->getOpByName('OP_1NEGATE'):
                     case $opcodes->cmp($opCode, 'OP_1') >= 0 && $opcodes->cmp($opCode, 'OP_16') <= 0:
                         $pushInt = new PushIntOperation($opcodes);
                         $pushInt->op($opCode, $mainStack);
                         break;
                     case $opcodes->cmp($opCode, 'OP_NOP1') >= 0 && $opcodes->cmp($opCode, 'OP_NOP10') <= 0:
                         if ($flags->checkFlags(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS)) {
                             throw new ScriptRuntimeException(InterpreterInterface::VERIFY_DISCOURAGE_UPGRADABLE_NOPS, 'Upgradable NOPS found - this is discouraged');
                         }
                         break;
                     case $opcodes->getOpByName('OP_NOP'):
                     case $opcodes->isOp($opCode, 'OP_IF') || $opcodes->isOp($opCode, 'OP_NOTIF'):
                     case $opcodes->isOp($opCode, 'OP_ELSE') || $opcodes->isOp($opCode, 'OP_ENDIF'):
                     case $opcodes->getOpByName('OP_VERIFY'):
                     case $opcodes->getOpByName('OP_RETURN'):
                         $flowControl = new FlowControlOperation($opcodes, function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         });
                         $flowControl->op($opCode, $mainStack, $vfStack, $fExec);
                         break;
                     case $opcodes->getOpByName('OP_RESERVED'):
                         // todo
                         break;
                     case $opcodes->getOpByName('OP_TOALTSTACK'):
                     case $opcodes->getOpByName('OP_FROMALTSTACK'):
                     case $opcodes->cmp($opCode, 'OP_IFDUP') >= 0 && $opcodes->cmp($opCode, 'OP_TUCK') <= 0:
                     case $opcodes->cmp($opCode, 'OP_2DROP') >= 0 && $opcodes->cmp($opCode, 'OP_2SWAP') <= 0:
                         $stackOper = new StackOperation($opcodes, $this->ecAdapter->getMath(), function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         });
                         $stackOper->op($opCode, $mainStack, $altStack);
                         break;
                     case $opcodes->getOpByName('OP_SIZE'):
                         if ($mainStack->size() < 1) {
                             throw new \Exception('Invalid stack operation OP_SIZE');
                         }
                         // todo: Int sizes?
                         $vch = $mainStack->top(-1);
                         $size = Buffer::hex($math->decHex($vch->getSize()));
                         $mainStack->push($size);
                         break;
                     case $opcodes->getOpByName('OP_EQUAL'):
                         // cscriptnum
                     // cscriptnum
                     case $opcodes->getOpByName('OP_EQUALVERIFY'):
                         //case $this->isOp($opCode, 'OP_NOTEQUAL'): // use OP_NUMNOTEQUAL
                         if ($mainStack->size() < 2) {
                             throw new \Exception('Invalid stack operation OP_EQUAL');
                         }
                         $vch1 = $mainStack->top(-2);
                         $vch2 = $mainStack->top(-1);
                         $equal = $vch1->getBinary() === $vch2->getBinary();
                         // OP_NOTEQUAL is disabled
                         //if ($this->isOp($opCode, 'OP_NOTEQUAL')) {
                         //    $equal = !$equal;
                         //}
                         $mainStack->pop();
                         $mainStack->pop();
                         $mainStack->push($equal ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_EQUALVERIFY')) {
                             if ($equal) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('Error EQUALVERIFY');
                             }
                         }
                         break;
                         // Arithmetic operations
                     // Arithmetic operations
                     case $opcodes->cmp($opCode, 'OP_1ADD') >= 0 && $opcodes->cmp($opCode, 'OP_WITHIN') <= 0:
                         $arithmetic = new ArithmeticOperation($opcodes, $this->ecAdapter->getMath(), function (Buffer $buffer) {
                             return $this->castToBool($buffer);
                         }, $_bn0, $_bn1);
                         $arithmetic->op($opCode, $mainStack);
                         break;
                         // Hash operations
                     // Hash operations
                     case $opcodes->cmp($opCode, 'OP_RIPEMD160') >= 0 && $opcodes->cmp($opCode, 'OP_HASH256') <= 0:
                         $hash = new HashOperation($opcodes);
                         $hash->op($opCode, $mainStack);
                         break;
                     case $opcodes->getOpByName('OP_CODESEPARATOR'):
                         $this->hashStartPos = $parser->getPosition();
                         break;
                     case $opcodes->getOpByName('OP_CHECKSIG'):
                     case $opcodes->getOpByName('OP_CHECKSIGVERIFY'):
                         if ($mainStack->size() < 2) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $vchPubKey = $mainStack->top(-1);
                         $vchSig = $mainStack->top(-2);
                         $scriptCode = new Script($this->script->getBuffer()->slice($this->hashStartPos));
                         $success = $this->checkSig($scriptCode, $vchSig, $vchPubKey);
                         $mainStack->pop();
                         $mainStack->pop();
                         $mainStack->push($success ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_CHECKSIGVERIFY')) {
                             if ($success) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('Checksig verify');
                             }
                         }
                         break;
                     case $opcodes->getOpByName('OP_CHECKMULTISIG'):
                     case $opcodes->getOpByName('OP_CHECKMULTISIGVERIFY'):
                         $i = 1;
                         if ($mainStack->size() < $i) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $math = $this->ecAdapter->getMath();
                         $keyCount = $mainStack->top(-$i)->getInt();
                         if ($math->cmp($keyCount, 0) < 0 || $math->cmp($keyCount, 20) > 0) {
                             throw new \Exception('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;
                         if ($mainStack->size() < $i) {
                             throw new \Exception('Invalid stack operation');
                         }
                         $sigCount = $mainStack->top(-$i)->getInt();
                         // cscriptnum
                         if ($math->cmp($sigCount, 0) < 0 || $math->cmp($sigCount, $keyCount) > 0) {
                             throw new \Exception('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 = $mainStack->top(-$isig);
                             $pubkey = $mainStack->top(-$ikey);
                             // Erase the signature and public key.
                             $mainStack->erase(-$isig);
                             $mainStack->erase(-$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) {
                             $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 ($mainStack->size() < 1) {
                             throw new \Exception('Invalid stack operation');
                         }
                         if ($flags->checkFlags(InterpreterInterface::VERIFY_NULL_DUMMY) && $mainStack->top(-1)->getSize()) {
                             throw new ScriptRuntimeException(InterpreterInterface::VERIFY_NULL_DUMMY, 'Extra P2SH stack value should be OP_0');
                         }
                         $mainStack->pop();
                         $mainStack->push($fSuccess ? $_bn1 : $_bn0);
                         if ($opcodes->isOp($opCode, 'OP_CHECKMULTISIGVERIFY')) {
                             if ($fSuccess) {
                                 $mainStack->pop();
                             } else {
                                 throw new \Exception('OP_CHECKMULTISIG verify');
                             }
                         }
                         break;
                     default:
                         throw new \Exception('Opcode not found');
                 }
                 if ($mainStack->size() + $altStack->size() > 1000) {
                     throw new \Exception('Invalid stack size, exceeds 1000');
                 }
             }
         }
         if (!$vfStack->end() == 0) {
             throw new \Exception('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;
     }
 }
예제 #13
0
 /**
  * @param int $tweak
  * @return PublicKeyInterface
  */
 public function tweakMul($tweak)
 {
     return $this->ecAdapter->publicKeyMul($this, $tweak);
 }