Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to sign an transanction #166

Closed
cevin opened this issue Nov 1, 2015 · 11 comments
Closed

How to sign an transanction #166

cevin opened this issue Nov 1, 2015 · 11 comments

Comments

@cevin
Copy link

cevin commented Nov 1, 2015

$ecAdapter = Bitcoin::getEcAdapter();

$private_key = PrivateKeyFactory::fromWif('wif_private_key');

// from address1 to address2

$transaction = TransactionFactory::build()
->input('address1 unspet tx',$out)
->payToAddress('$amount',AddressFactory::fromString('address2'))
->payToAddress('$amount',AddressFactory::fromString('address1'))
->get();

And then ? How to sign it?

@afk11
Copy link
Member

afk11 commented Nov 1, 2015

https://github.com/Bit-Wasp/bitcoin-php/blob/master/examples/offlinetx.p2pkh.testnet.php#L34

You can probably just do:

$signed = TransactionFactory::sign($transaction)
  -> sign (0, $private_key, $output->getScript())
  -> get();

This assumes $output is a TransactionInterface.

@cevin
Copy link
Author

cevin commented Nov 2, 2015

If I have not full raw transaction hex , And only have transaction index hash like 7a792a2bdf06a861408a028aa1bc3767cd141d89b009d72c61bd9866ca15000f and Have lots of hashs. How to build a transaction and sign it ?

TransactionFactory::fromHex only allow full raw transaction.

@cevin
Copy link
Author

cevin commented Nov 2, 2015

$private_key = PrivateKeyFactory::fromWif('address1 wif private key');


try {
    $transaction = TransactionFactory::build()
    ->input('unspent hash of address1','2')// unspent hash value : 147900
    ->payToAddress('39479',AddressFactory::fromString('address2'))
    ->payToAddress('97200',AddressFactory::fromString('address1'))// Give change
    ->payToAddress('1221',AddressFactory::fromString('address3'))
    ->get();

    $hex = $transaction->getHex();

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


    $hex = TransactionFactory::sign($transaction)
      -> sign (0, $private_key, $myTx->getOutputs()->get(2)->getScript())
      -> get() -> getHex();

    echo ($hex);
} catch (Exception $e) {
    echo $e->getMessage().PHP_EOL;
}

https://blockexplorer.com/tx/send send to bitcoin network, get an error:

An error occured:
Transaction rejected by network (code -26). Reason: 16: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element)

@afk11
Copy link
Member

afk11 commented Nov 3, 2015

First, I created a test transaction, and broadcast using the same block explorer - it worked!

I think I see your problem however. $myTx is the transaction you just created, so you are passing the wrong output script to sign().

Just so it's clear, when you pass an output script to sign(), it needs to be the output script of the [txid,vout] you want to spend. Meaning TransactionFactory::fromHex() isn't called on your new transactions hex, but actually the transaction who's hash you are now spending.

@cevin
Copy link
Author

cevin commented Nov 3, 2015

@afk11 How to create a transaction and sign it on offline wallet ? only have from address's unspent hash (isn't full raw transaction). use TransactionFactory::build to create a transactoin, input is from address unspent hash, how to get the transaction output script?

/**
     * @param $inputToSign
     * @param PrivateKeyInterface $privateKey
     * @param ScriptInterface $outputScript
     * @param RedeemScript $redeemScript
     * @param int $sigHashType
     * @return $this
     */
    public function sign

$inputToSign : inputs index
$privateKey: input address private key
$outputScript: ???? what's is?

update

Maybe I know how to do.

the sign script it's unspent transaction scriptPubKey use ScriptFactory::fromHex to get ScriptInterface Object use the object as the sign script parameter.

~

use code :

function createTransaction(Array $inputs,$private_key,Array $outputs) {
    try {

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


        // Generate raw transaction
        $transaction = TransactionFactory::build();

        foreach ($inputs as $input) {
            $transaction->input($input['txid'],$input['vout'], ScriptFactory::fromHex($input['scriptPubKey']));
        }

        foreach ($outputs as $address=>$amount)
            $transaction->payToAddress($amount,AddressFactory::fromString($address));

        // Make transaction
        $transaction = $transaction->get();



        // Sign transaction
        $signer = TransactionFactory::sign($transaction);

        foreach ($transaction->getInputs() as $index=>$input) {
            $signer->sign($index,$private_key,$input->getScript());
        }

        // Signing and get output
        $hex = $signer -> get() -> getHex();

        return $hex;
    } catch(Exception $e) {
        self::$error = $e->getMessage();
        return false;
    }
}

get raw transaction hex code:

0100000002c6ffff0d22d16e4595e1b2a43ea7e4cd2e80258928b1bef97bd33739efeb188c020000008b483045022100a5b23c9a3444d3be62a9cc40882c5b9b6c48841ec75cfd3663d4770c04d2c75202207340d77a279a2df823a98166e647a22bfa3b4447326553f7f758a2723ff5d63d0141048a8f8b00f3b73fa5cc6c1d4db0d9bccdd1a7f482e7b4402bc573cdd3b31962f11faaeb77df0b04b5569e24ce0fea2e3e193f5665b9d0437b5b2b69176375f7fd0000000004a022504dd94a0db47890b2d3f865036d26ee4466eb5eec3dc448c5a711e486000000008a4730440220450d41f551f6da69e11d20db40e10d6971752f6052b53c763c54482981ec339302206b100efd48415dc2fdbdf06da178723590786db8ccd4d6d121d1192d914177210141048a8f8b00f3b73fa5cc6c1d4db0d9bccdd1a7f482e7b4402bc573cdd3b31962f11faaeb77df0b04b5569e24ce0fea2e3e193f5665b9d0437b5b2b69176375f7fd0000000003379a0000000000001976a9149d108663348b241aa709a3e4c5587d9e98ab669f88acc5040000000000001976a91413bb16e7cbb20c8e4983b5c6e0e0a81b5236b9e688ac40510000000000001976a91416e2f94ff158c985cd3c37f6a648190ba3977cf188ac00000000

boardcast url: http://docs.coinprism.apiary.io/#reference/transaction-signing-and-broadcasting/push-a-signed-raw-transaction-to-the-network/post?console=1

got an error.

Other error

Warning: gmp_init(): Unable to convert variable to GMP - wrong type
If the $number is (float)4294967295

@afk11
Copy link
Member

afk11 commented Nov 3, 2015

ScriptFactory::scriptPubKey()->payToAddress() will make an output script for an address. You generally will store [txid, vout, scriptpubkey] together when you learn them.

The Gmp error is because you passed the transactions output value as an integer but they need to be strings, otherwise they get casted to floats.
Instead of $outputs = [...., 4294967295]; use $outputs = [...., '4294967295'];

@cevin
Copy link
Author

cevin commented Nov 3, 2015

@afk11

TxBuilder->input need parameters (String transaction,Int vout,Script scriptPubKey, Numeric nSequence)

So, I must loop all inputs to set transaction input for function TxBuilder::input like
$tx->input(txid, vout, ScriptFactory::fromHex(scriptPubKey in unspent transaction ), '4294967295') It's okay?

And in signing process, sign all inputs(unspent transactions) use the from address's private key and the input's scriptPubKey(from function $tx->input) It's okay?

Or in signing process should be sign all output and use $outputs address's scriptPubKey use ScriptFactory::scriptPubKey()->payToAddress(AddressFactory::fromString(to address)) ?

My custom function maybe like below ?

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


        // Generate raw transaction
        $transaction = TransactionFactory::build();

        foreach ($inputs as $input) {
            $transaction->input(
                                   $input['txid'],
                                   $input['vout'],
                                   ScriptFactory::fromHex($input['scriptPubKey']),
                                   '4294967295');
        }

        foreach ($outputs as $address=>$amount)
            $transaction->payToAddress($amount,AddressFactory::fromString($address));

        // Make transaction
        $transaction = $transaction->get();



        // Sign transaction
        $signer = TransactionFactory::sign($transaction);


        // Here!!
        foreach ($transaction->getInputs() as $index=>$input)
            $signer->sign($index,$private_key,$input->getScript());

        // Signing and get output
        $hex = $signer -> get() -> getHex();

        return $hex;

Platform error:

E..... Maybe it's the platform have some errors. I changed to other platform use the code, It's successfully.

Error platform:

Success platform:

Maybe Bitcoind have a new changes to verify transaction.

Last

default SEQUENCE is defined at here https://github.com/Bit-Wasp/bitcoin-php/blob/master/src/Transaction/TransactionInputInterface.php#L12.
I think you should update this constant to string value.

@afk11
Copy link
Member

afk11 commented Nov 24, 2015

        foreach ($transaction->getInputs() as $index=>$input)
            $signer->sign($index,$private_key,$input->getScript());

The script should be the output script, since that's what actually locked the input you are spending.

@afk11
Copy link
Member

afk11 commented Dec 26, 2015

Closing due to inactivity.

@afk11 afk11 closed this as completed Dec 26, 2015
@Gwinest
Copy link

Gwinest commented Feb 20, 2020

Updating topic...
Having the raw transaction (non-signed), how can I sign it using bitcoin-php version 1.0?

@afk11
Copy link
Member

afk11 commented Feb 20, 2020

@Gwinest Don't post questions on very old issues - I've created another one for your question.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants