/**
  * Composes a send transaction
  * @param  string $asset                     A counterparty asset name or BTC
  * @param  mixed  $quantity                  Quantity of asset to send.  Accepts a float or a Tokenly\CounterpartyTransactionComposer\Quantity or Tokenly\CryptoQuantity\CryptoQuantity object.  Use a Quantity object for indivisible assets.
  * @param  mixed  $destination               A single destination bitcoin address.  For BTC sends an array of [[address, amount], [address, amount]] is also allowed.  Amounts should be float values.
  * @param  string $private_key_wif           The private key in ASCII WIF format.  This can be null to compose an unsigned transaction.
  * @param  array  $utxos                     An array of UTXOs.  Each UTXO should be ['txid' => txid, 'n' => n, 'amount' => amount (in satoshis), 'script' => script hexadecimal string]
  * @param  mixed  $change_address_collection a single address string to receive all change. Or an array of [[address, amount], [address, amount], [address]].  Amounts should be float values.  An address with no amount for the last entry will send the remaining change to that address.
  * @param  float  $fee                       A fee
  * @param  float  $btc_dust                  Amount of BTC dust to send with the Counterparty transaction.
  * @return Array returns a ComposedTransaction object
  */
 public function composeSend($asset, $quantity, $destination, $private_key_wif, $utxos, $change_address_collection = null, $fee = null, $btc_dust = null)
 {
     if ($asset == 'BTC') {
         return $this->composeBTCSend($quantity, $destination, $private_key_wif, $utxos, $change_address_collection, $fee);
     }
     $fee_satoshis = $fee === null ? self::DEFAULT_FEE : intval(round($fee * self::SATOSHI));
     $btc_dust_satoshis = $btc_dust === null ? self::DEFAULT_BTC_DUST : intval(round($btc_dust * self::SATOSHI));
     // get total and change amount
     $change_amounts = $this->calculateAndValidateChange($utxos, $btc_dust_satoshis, $fee_satoshis, $change_address_collection);
     $tx_builder = TransactionFactory::build();
     // add the UTXO inputs
     $transaction_outputs = $this->addInputsAndReturnPreviousOutputs($utxos, $tx_builder);
     // pay the btc_dust to the destination
     if (is_array($destination)) {
         throw new Exception("Multiple destinations are not supported for counterparty sends", 1);
     }
     $tx_builder->payToAddress($btc_dust_satoshis, AddressFactory::fromString($destination));
     // build the OP_RETURN script
     $op_return_builder = new OpReturnBuilder();
     $op_return = $op_return_builder->buildOpReturn($quantity, $asset, $utxos[0]['txid']);
     $script = ScriptFactory::create()->op('OP_RETURN')->push(Buffer::hex($op_return, 28))->getScript();
     $tx_builder->output(0, $script);
     // pay the change to self
     $this->payChange($change_amounts, $tx_builder);
     // sign
     if ($private_key_wif !== null) {
         $signed_transaction = $this->signTx($private_key_wif, $tx_builder, $transaction_outputs);
         return $this->buildReturnValuesFromTransactionAndInputs($signed_transaction, $utxos, true);
     }
     return $this->buildReturnValuesFromTransactionAndInputs($tx_builder->get(), $utxos, false);
 }
Пример #2
0
 /**
  * @param int $m
  * @param PublicKeyInterface[] $keys
  * @param bool|true $sort
  * @return ScriptCreator|Script
  */
 public function multisig($m, array $keys = [], $sort = true)
 {
     $n = count($keys);
     if ($m > $n) {
         throw new \LogicException('Required number of sigs exceeds number of public keys');
     }
     if ($n > 16) {
         throw new \LogicException('Number of public keys is greater than 16');
     }
     if ($sort) {
         $keys = Buffertools::sort($keys);
     }
     $opM = \BitWasp\Bitcoin\Script\encodeOpN($m);
     $opN = \BitWasp\Bitcoin\Script\encodeOpN($n);
     $script = ScriptFactory::create();
     foreach ($keys as $key) {
         if (!$key instanceof PublicKeyInterface) {
             throw new \LogicException('Values in $keys[] must be a PublicKey');
         }
         $script->push($key->getBuffer());
     }
     $keyBuf = $script->getScript()->getBuffer();
     $script = new Script(new Buffer(chr($opM) . $keyBuf->getBinary() . chr($opN) . chr(Opcodes::OP_CHECKMULTISIG)));
     return $script;
 }
Пример #3
0
 /**
  * @param ScriptInterface $inputScript
  * @param ScriptInterface $redeemScript
  * @return ScriptInterface
  */
 public function payToScriptHash(ScriptInterface $inputScript, ScriptInterface $redeemScript)
 {
     return ScriptFactory::create($inputScript->getBuffer())->push($redeemScript->getBuffer())->getScript();
 }
Пример #4
0
 /**
  * @return ScriptInterface
  */
 public function getScript()
 {
     return ScriptFactory::create()->int($this->version)->push($this->program)->getScript();
 }
Пример #5
0
 /**
  * @param Buffer $secret
  * @param PublicKeyInterface $a1
  * @param PublicKeyInterface $a2
  * @param PublicKeyInterface $b1
  * @param PublicKeyInterface $b2
  * @return ScriptInterface
  */
 public function payToLightningChannel(Buffer $secret, PublicKeyInterface $a1, PublicKeyInterface $a2, PublicKeyInterface $b1, PublicKeyInterface $b2)
 {
     return ScriptFactory::create()->op('OP_DEPTH')->op('OP_3')->op('OP_EQUAL')->op('OP_IF')->op('OP_HASH160')->push(Hash::sha256ripe160($secret))->op('OP_EQUALVERIFY')->concat(ScriptFactory::scriptPubKey()->multisig(2, [$a1, $b1]))->op('OP_ELSE')->concat(ScriptFactory::scriptPubKey()->multisig(2, [$a2, $b2]))->op('OP_ENDIF')->getScript();
 }
Пример #6
0
 /**
  * @return \BitWasp\Bitcoin\Block\BlockInterface
  */
 public function getGenesisBlock()
 {
     $timestamp = new Buffer('The Times 03/Jan/2009 Chancellor on brink of second bailout for banks', null, $this->math);
     $publicKey = Buffer::hex('04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f', null, $this->math);
     $inputScript = ScriptFactory::create()->push(Buffer::int('486604799', 4, $this->math)->flip())->push(Buffer::int('4', null, $this->math))->push($timestamp)->getScript();
     $outputScript = ScriptFactory::create()->push($publicKey)->op('OP_CHECKSIG')->getScript();
     return new Block($this->math, $this->getGenesisBlockHeader(), new TransactionCollection([(new TxBuilder())->version('1')->input(new Buffer('', 32), 0xffffffff, $inputScript)->output(5000000000, $outputScript)->locktime(0)->get()]));
 }
 /**
  * @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());
 }
Пример #8
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)));
 }
 /**
  * @param OutputBuf $outBuf
  * @return TransactionOutput
  */
 private function bufToOutput(OutputBuf $outBuf)
 {
     $script = ScriptFactory::create(new Buffer($outBuf->getScript()));
     $output = new TransactionOutput($outBuf->getAmount(), $script);
     return $output;
 }
Пример #10
0
 /**
  * @param array $signatures
  * @param array $publicKeys
  * @return ScriptInterface
  */
 public function makeScriptSig(array $signatures = [], array $publicKeys = [])
 {
     $inputScript = $this->handler->makeScriptSig($signatures, $publicKeys);
     return ScriptFactory::create($inputScript->getBuffer())->push($this->redeemScript->getBuffer())->getScript();
 }
Пример #11
0
 /**
  * @return SigValues
  */
 public function serializeSignatures()
 {
     static $emptyScript = null;
     static $emptyWitness = null;
     if (is_null($emptyScript) || is_null($emptyWitness)) {
         $emptyScript = new Script();
         $emptyWitness = new ScriptWitness([]);
     }
     /** @var BufferInterface[] $return */
     $outputType = (new OutputClassifier($this->txOut->getScript()))->classify();
     /** @var SigValues $answer */
     $answer = new SigValues($emptyScript, $emptyWitness);
     $serialized = $this->serializeSimpleSig($outputType, $answer);
     $p2sh = false;
     if (!$serialized && $outputType === OutputClassifier::PAYTOSCRIPTHASH) {
         $p2sh = true;
         $outputType = (new OutputClassifier($this->redeemScript))->classify();
         $serialized = $this->serializeSimpleSig($outputType, $answer);
     }
     if (!$serialized && $outputType === OutputClassifier::WITNESS_V0_KEYHASH) {
         $answer = new SigValues($emptyScript, new ScriptWitness([$this->signatures[0]->getBuffer(), $this->publicKeys[0]->getBuffer()]));
     } else {
         if (!$serialized && $outputType === OutputClassifier::WITNESS_V0_SCRIPTHASH) {
             $outputType = (new OutputClassifier($this->witnessScript))->classify();
             $serialized = $this->serializeSimpleSig($outputType, $answer);
             if ($serialized) {
                 $data = [];
                 foreach ($answer->getScriptSig()->getScriptParser()->decode() as $o) {
                     $data[] = $o->getData();
                 }
                 $data[] = $this->witnessScript->getBuffer();
                 $answer = new SigValues($emptyScript, new ScriptWitness($data));
             }
         }
     }
     if ($p2sh) {
         $answer = new SigValues(ScriptFactory::create($answer->getScriptSig()->getBuffer())->push($this->redeemScript->getBuffer())->getScript(), $answer->getScriptWitness());
     }
     return $answer;
 }
Пример #12
0
<?php

require_once "../vendor/autoload.php";
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Transaction\Transaction;
$ec = \BitWasp\Bitcoin\Bitcoin::getEcAdapter();
$script = ScriptFactory::create()->op('OP_1')->op('OP_1')->op('OP_ADD')->op('OP_2')->op('OP_EQUALVERIFY');
echo "Formed script: " . $script->getHex() . "\n";
print_r($script->getScriptParser()->parse());
$factory = new \BitWasp\Bitcoin\Script\Interpreter\InterpreterFactory($ec);
$flags = $factory->flags(0);
$i = $factory->getNativeInterpreter(new Transaction(), $flags);
$result = $i->setScript($script)->run();
echo "Script result: " . ($result ? 'true' : 'false') . "\n";
 /**
  * @return \BitWasp\Bitcoin\Script\Script
  */
 public function regenerateScript()
 {
     // todo: this is worrisome, should have some way to fail and defer to the original script
     $signatures = array_filter($this->getSignatures());
     $script = $this->execForInputTypes(function () use(&$signatures) {
         return count($signatures) == 1 ? ScriptFactory::scriptSig()->payToPubKeyHash($signatures[0], $this->publicKeys[0]) : ScriptFactory::create();
     }, function () use(&$signatures) {
         return count($signatures) == 1 ? ScriptFactory::scriptSig()->payToPubKey($signatures[0]) : ScriptFactory::create();
     }, function () use(&$signatures) {
         return count($signatures) > 0 ? ScriptFactory::scriptSig()->multisigP2sh($this->getRedeemScript(), array_filter($this->signatures)) : ScriptFactory::create();
     });
     return $script;
 }
Пример #14
0
<?php

require_once "../vendor/autoload.php";
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Transaction\Transaction;
use BitWasp\Bitcoin\Script\Script;
$ec = \BitWasp\Bitcoin\Bitcoin::getEcAdapter();
$scriptSig = ScriptFactory::create()->int(1)->int(1)->getScript();
$scriptPubKey = ScriptFactory::create()->op('OP_ADD')->int(2)->op('OP_EQUAL')->getScript();
echo "Formed script: " . $scriptSig->getHex() . " " . $scriptPubKey->getHex() . "\n";
$flags = new \BitWasp\Bitcoin\Flags(0);
$i = new \BitWasp\Bitcoin\Script\Interpreter\Interpreter($ec, new Transaction(), $flags);
$result = $i->verify($scriptSig, $scriptPubKey, 0);
echo "Script result: " . ($result ? 'true' : 'false') . "\n";
Пример #15
0
<?php

require "vendor/autoload.php";
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Buffertools\Buffer;
$scriptPubKey = ScriptFactory::create()->op('OP_1')->op('OP_EQUAL')->getScript();
$scriptSig = ScriptFactory::create()->op('OP_1')->getScript();
$tx = TransactionFactory::build()->input(Buffer::hex('0000000000000000000000000000000000000000000000000000000000000001'), 0, $scriptSig)->output(1, new \BitWasp\Bitcoin\Script\Script())->get();
$t1 = ['txid' => 'a', 'tx' => $tx->getHex(), 'scripts' => [$scriptPubKey->getHex()]];
$msg = ['id' => 'asdfasdfsadf', 'flags' => 0, 'txs' => [$t1]];
echo 'sending';
$context = new \ZMQContext();
$push = $context->getSocket(\ZMQ::SOCKET_REQ);
$push->connect('tcp://127.0.0.1:6661');
$push->send(json_encode($msg));
$response = $push->recv();
echo $response . PHP_EOL;
Пример #16
0
 /**
  * @param TransactionSignatureInterface $signature
  * @return Script
  */
 public function payToPubKey(TransactionSignatureInterface $signature)
 {
     return ScriptFactory::create()->push($signature->getBuffer());
 }
Пример #17
0
 /**
  * @param ScriptInterface $scriptSig
  * @param ScriptInterface $scriptPubKey
  * @param int $flags
  * @param Checker $checker
  * @param ScriptWitnessInterface|null $witness
  * @return bool
  */
 public function verify(ScriptInterface $scriptSig, ScriptInterface $scriptPubKey, $flags, Checker $checker, ScriptWitnessInterface $witness = null)
 {
     static $emptyWitness = null;
     if ($emptyWitness === null) {
         $emptyWitness = new ScriptWitness([]);
     }
     $witness = $witness ?: $emptyWitness;
     if (($flags & self::VERIFY_SIGPUSHONLY) != 0 && !$scriptSig->isPushOnly()) {
         return false;
     }
     $stack = new Stack();
     if (!$this->evaluate($scriptSig, $stack, 0, $flags, $checker)) {
         return false;
     }
     $backup = [];
     if ($flags & self::VERIFY_P2SH) {
         foreach ($stack as $s) {
             $backup[] = $s;
         }
     }
     if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
         return false;
     }
     if ($stack->isEmpty()) {
         return false;
     }
     if (false === $this->castToBool($stack[-1])) {
         return false;
     }
     $program = null;
     if ($flags & self::VERIFY_WITNESS) {
         if ($scriptPubKey->isWitness($program)) {
             /** @var WitnessProgram $program */
             if ($scriptSig->getBuffer()->getSize() !== 0) {
                 return false;
             }
             if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
                 return false;
             }
             $stack->resize(1);
         }
     }
     if ($flags & self::VERIFY_P2SH && (new OutputClassifier($scriptPubKey))->isPayToScriptHash()) {
         if (!$scriptSig->isPushOnly()) {
             return false;
         }
         $stack = new Stack();
         foreach ($backup as $i) {
             $stack->push($i);
         }
         // Restore mainStack to how it was after evaluating scriptSig
         if ($stack->isEmpty()) {
             return false;
         }
         // Load redeemscript as the scriptPubKey
         $scriptPubKey = new Script($stack->bottom());
         $stack->pop();
         if (!$this->evaluate($scriptPubKey, $stack, 0, $flags, $checker)) {
             return false;
         }
         if ($stack->isEmpty()) {
             return false;
         }
         if (!$this->castToBool($stack->bottom())) {
             return false;
         }
         if ($flags & self::VERIFY_WITNESS) {
             if ($scriptPubKey->isWitness($program)) {
                 /** @var WitnessProgram $program */
                 if ($scriptSig != ScriptFactory::create()->push($scriptPubKey->getBuffer())->getScript()) {
                     return false;
                     // SCRIPT_ERR_WITNESS_MALLEATED_P2SH
                 }
                 if (!$this->verifyWitnessProgram($program, $witness, $flags, $checker)) {
                     return false;
                 }
                 $stack->resize(1);
             }
         }
     }
     if ($flags & self::VERIFY_CLEAN_STACK != 0) {
         if (!($flags & self::VERIFY_P2SH != 0 && $flags & self::VERIFY_WITNESS != 0)) {
             return false;
             // implied flags required
         }
         if (count($stack) != 1) {
             return false;
             // Cleanstack
         }
     }
     if ($flags & self::VERIFY_WITNESS) {
         if (!$flags & self::VERIFY_P2SH) {
             return false;
             //
         }
         if ($program === null && !$witness->isNull()) {
             return false;
             // SCRIPT_ERR_WITNESS_UNEXPECTED
         }
     }
     return true;
 }