3

I have a unsigned transaction hex. Now I want to sign this transaction input with my private key in PHP. Bitwasp library looks nice. But I can't find how to sign my transaction. Can you give me any advices?

Transaction Hex

010000000414ae2264cdbe754b9ae4be18d84bfeb4f578af553b9b9e4f9cb2303d04ee9e45000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffffec15d27b74516fefd921cecbe043ea63124d28a3903aef8fb1682ccc926b1c62000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffff9878f76e1424c1d1eeb6b15d06902dd8f0c78f9bdb61263e4ca3ae68c571a292000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acfffffffffdac7f1c83b01a8924a8965d356b804c2608bc261fc18041116ddb4a143bc499000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffff0336150000000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88ac00000000000000001e6a1c246698efc5d81b78ceadf3179316b5eb6cc5c2c347c0b7b42121a94e92180000000000001976a91458b6e991b45487df810f4d96d5315da739637f1788ac00000000

Decoded Transaction

{
   "txid":"6c521937d8fa889d71609ebed2e23b694cbca89778c8b8d967167e5a4c9d5b78",
   "size":381,
   "version":1,
   "locktime":0,
   "vin":[
      {
         "txid":"459eee043d30b29c4f9e9b3b55af78f5b4fe4bd818bee49a4b75becd6422ae14",
         "vout":0,
         "scriptSig":{
            "asm":"OP_DUP OP_HASH160 58b6e991b45487df810f4d96d5315da739637f17 OP_EQUALVERIFY OP_CHECKSIG",
            "hex":"76a91458b6e991b45487df810f4d96d5315da739637f1788ac"
         },
         "sequence":4294967295
      },
      {
         "txid":"621c6b92cc2c68b18fef3a90a3284d1263ea43e0cbce21d9ef6f51747bd215ec",
         "vout":0,
         "scriptSig":{
            "asm":"OP_DUP OP_HASH160 58b6e991b45487df810f4d96d5315da739637f17 OP_EQUALVERIFY OP_CHECKSIG",
            "hex":"76a91458b6e991b45487df810f4d96d5315da739637f1788ac"
         },
         "sequence":4294967295
      },
      {
         "txid":"92a271c568aea34c3e2661db9b8fc7f0d82d90065db1b6eed1c124146ef77898",
         "vout":0,
         "scriptSig":{
            "asm":"OP_DUP OP_HASH160 58b6e991b45487df810f4d96d5315da739637f17 OP_EQUALVERIFY OP_CHECKSIG",
            "hex":"76a91458b6e991b45487df810f4d96d5315da739637f1788ac"
         },
         "sequence":4294967295
      },
      {
         "txid":"99c43b144adb6d114180c11f26bc08264c806b355d96a824891ab0831c7facfd",
         "vout":0,
         "scriptSig":{
            "asm":"OP_DUP OP_HASH160 58b6e991b45487df810f4d96d5315da739637f17 OP_EQUALVERIFY OP_CHECKSIG",
            "hex":"76a91458b6e991b45487df810f4d96d5315da739637f1788ac"
         },
         "sequence":4294967295
      }
   ],
   "vout":[
      {
         "value":5.43e-5,
         "n":0,
         "scriptPubKey":{
            "asm":"OP_DUP OP_HASH160 1485d9d03b41aaa9dca7d70d7f63ff4a0826100e OP_EQUALVERIFY OP_CHECKSIG",
            "hex":"76a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88ac",
            "reqSigs":1,
            "type":"pubkeyhash",
            "addresses":[
               "12sWrxRY7E7Nhmuyjbz4TtGE9jRewGqEZD"
            ]
         }
      },
      {
         "value":0,
         "n":1,
         "scriptPubKey":{
            "asm":"OP_RETURN 246698efc5d81b78ceadf3179316b5eb6cc5c2c347c0b7b42121a94e",
            "hex":"6a1c246698efc5d81b78ceadf3179316b5eb6cc5c2c347c0b7b42121a94e",
            "type":"nulldata"
         }
      },
      {
         "value":6.29e-5,
         "n":2,
         "scriptPubKey":{
            "asm":"OP_DUP OP_HASH160 58b6e991b45487df810f4d96d5315da739637f17 OP_EQUALVERIFY OP_CHECKSIG",
            "hex":"76a91458b6e991b45487df810f4d96d5315da739637f1788ac",
            "reqSigs":1,
            "type":"pubkeyhash",
            "addresses":[
               "1965areciqapsuL2hsia2yKkRLfAsH1smG"
            ]
         }
      }
   ]
}

composer.json

"require": {
    "bitwasp/bitcoin": "v0.0.29.1"
}

sample.php

<?php

require 'vendor/autoload.php';

use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Transaction\TransactionFactory;

// Load private key
$private_key = PrivateKeyFactory::fromWif('KEY');

// Transaction from Hex
$transaction = TransactionFactory::fromHex('HEX');

... what should I next...?

Update 1

Solved! Thank you @karimkorun.

composer.json

"require": {
  "bitwasp/bitcoin": "v0.0.29.1"
}

test.php

<?php

require 'vendor/autoload.php';

use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\Factory\Signer;
use BitWasp\Bitcoin\Transaction\OutPoint;
use BitWasp\Bitcoin\Utxo\Utxo;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Transaction\TransactionOutput;

$hex = '010000000414ae2264cdbe754b9ae4be18d84bfeb4f578af553b9b9e4f9cb2303d04ee9e45000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffffec15d27b74516fefd921cecbe043ea63124d28a3903aef8fb1682ccc926b1c62000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffff9878f76e1424c1d1eeb6b15d06902dd8f0c78f9bdb61263e4ca3ae68c571a292000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acfffffffffdac7f1c83b01a8924a8965d356b804c2608bc261fc18041116ddb4a143bc499000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffff0336150000000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88ac00000000000000001e6a1c246698efc5d81b78ceadf3179316b5eb6cc5c2c347c0b7b42121a94e92180000000000001976a91458b6e991b45487df810f4d96d5315da739637f1788ac00000000';
$privateKey = 'XXX';

$tx = TransactionFactory::fromHex($hex);

$utxos = [];
foreach ($tx->getInputs() as $idx => $input) {
    $txid = $input->getOutPoint()->getTxId()->getHex();
    $vout = $input->getOutPoint()->getVout();
    $scriptPubKey = $input->getScript()->getBuffer()->getHex();

    $utxo = new Utxo(new OutPoint(Buffer::hex($txid, 32), $vout), new TransactionOutput(0, ScriptFactory::fromHex($scriptPubKey)));
    array_push($utxos, $utxo);
}

$priv = PrivateKeyFactory::fromWif($privateKey);
$signer = new Signer($tx, Bitcoin::getEcAdapter());

foreach ($utxos as $i => $utxo) {
    $signer->sign($i, $priv, $utxo->getOutput());
}

$signed = $signer->get();
echo $signed->getHex() . PHP_EOL;

Update 2

This is shorter. UTXO objects are not necessary.

$tx = TransactionFactory::fromHex($hex);

$transactionOutputs = [];
foreach ($tx->getInputs() as $idx => $input) {
    $transactionOutput = new TransactionOutput(0, ScriptFactory::fromHex($input->getScript()->getBuffer()->getHex()));
    array_push($transactionOutputs, $transactionOutput);
}

$priv = PrivateKeyFactory::fromWif($privateKey);
$signer = new Signer($tx, Bitcoin::getEcAdapter());

foreach ($transactionOutputs as $idx => $transactionOutput) {
    $signer->sign($idx, $priv, $transactionOutput);
}

$signed = $signer->get();
echo $signed->getHex() . PHP_EOL;
  • There's a folder with examples: github.com/Bit-Wasp/bitcoin-php/tree/master/examples check them out as I think your case is covered. On an unrelated note, your transaction input scriptSigs shouldn't have the scriptPubKey's from the outpoints being spent. What way did you generate it? – karimkorun Jul 30 '16 at 13:26
4

The library has a documentation folder, plus examples to boot! You should always check for these first :-)

As mentioned in the comment above, you should check your transaction creation procedure, as the scriptSig's are not correct for an unsigned transaction - they should just be empty - though the following should work regardless.

First, when signing, you always need the TransactionOutput available.

Since you provided the scriptPubKey inadvertently, I wrote up the following which should work if you substitute your private key. Usually these lookups are done using an API/bitcoind instance, etc.

For non-segwit transactions, you can store just the scriptPubKey (notice I just use 0 for the amount in the TxOut), but for segwit, you have to fill out the amount correctly.

use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Key\PrivateKeyFactory;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\Factory\Signer;
use BitWasp\Bitcoin\Transaction\OutPoint;
use BitWasp\Bitcoin\Utxo\Utxo;
use BitWasp\Buffertools\Buffer;
use BitWasp\Bitcoin\Transaction\TransactionOutput;

/** @var Utxo[] $utxos */
$utxos = array_map(function (array $utxo) {
    list ($txid, $vout, $scriptPubKey) = $utxo;
    return new Utxo(new OutPoint(Buffer::hex($txid, 32), $vout), new TransactionOutput(0, ScriptFactory::fromHex($scriptPubKey)));
}, [
    ['459eee043d30b29c4f9e9b3b55af78f5b4fe4bd818bee49a4b75becd6422ae14', 0, '76a91458b6e991b45487df810f4d96d5315da739637f1788ac'],
    ['621c6b92cc2c68b18fef3a90a3284d1263ea43e0cbce21d9ef6f51747bd215ec', 0, '76a91458b6e991b45487df810f4d96d5315da739637f1788ac'],
    ['92a271c568aea34c3e2661db9b8fc7f0d82d90065db1b6eed1c124146ef77898', 0, '76a91458b6e991b45487df810f4d96d5315da739637f1788ac'],
    ['99c43b144adb6d114180c11f26bc08264c806b355d96a824891ab0831c7facfd', 0, '76a91458b6e991b45487df810f4d96d5315da739637f1788ac'],
]);

$priv = PrivateKeyFactory::fromWif('yourprivkeyhere');
$hex = '010000000414ae2264cdbe754b9ae4be18d84bfeb4f578af553b9b9e4f9cb2303d04ee9e45000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffffec15d27b74516fefd921cecbe043ea63124d28a3903aef8fb1682ccc926b1c62000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffff9878f76e1424c1d1eeb6b15d06902dd8f0c78f9bdb61263e4ca3ae68c571a292000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acfffffffffdac7f1c83b01a8924a8965d356b804c2608bc261fc18041116ddb4a143bc499000000001976a91458b6e991b45487df810f4d96d5315da739637f1788acffffffff0336150000000000001976a9141485d9d03b41aaa9dca7d70d7f63ff4a0826100e88ac00000000000000001e6a1c246698efc5d81b78ceadf3179316b5eb6cc5c2c347c0b7b42121a94e92180000000000001976a91458b6e991b45487df810f4d96d5315da739637f1788ac00000000';
$tx = TransactionFactory::fromHex($hex);
$signer = new Signer($tx);
foreach ($utxos as $i => $utxo) {
    $signer->sign($i, $priv, $utxo->getOutput());
}

$signed = $signer->get();

echo $signed->getHex() . PHP_EOL;
  • (1) Thank you for your help. I'm trying to implement the code in my environment. (2) The tx is Counterparty's transaction and it was decoded by my own bitcoind. I will let you know the result both (1) and (2) – zono Aug 1 '16 at 17:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.