Bit-Wasp / bitcoin-php

Bitcoin implementation in PHP
The Unlicense
1.05k stars 419 forks source link

problem with signed transaction. when call "sendRowTrasaction" #801

Open merlinkory opened 5 years ago

merlinkory commented 5 years ago

Hi! I using your library for creating and sign transaction. Now i working in regtest.

After i getting sign transaction i call "sendRowTransaction" on my node (regtest) and getting following error:

error code: -26 error message: mandatory-script-verify-flag-failed (Script evaluated without error but finished with a false/empty top stack element) (code 16)

This is sign transaction: "010000000101f61949c298a20c4a48f8e10da46221f7affda36d49892bfd5243c08a423526000000006b4830450221008407b2aca363ec6e552c2ca9f2ec9850a033946d88291ce2b9c62b40f9546daf022038b43bebc31dc8a6b921b4fa1b13227f47e05abced9b84f1dd7094ddfb84551a01210385379e41c714555988b4130a78ea9b617b0a150a5fef1ca30a14897ed5ceae2affffffff02802fa6040000000017a9146df977bd55c76c917dd49867ff21b57ba0097edc8760e401000000000017a9147f21fb14fd212302036c5b09ec304f820242c2b68700000000"

Code below:


<?php

namespace App\Classes;
use BitWasp\Bitcoin\Address\AddressCreator;
use BitWasp\Bitcoin\Transaction\TransactionOutput;
use BitWasp\Bitcoin\Transaction\TransactionFactory;
use BitWasp\Bitcoin\Transaction\Factory\Signer;
use BitWasp\Bitcoin\Script\ScriptFactory;
use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
use BitWasp\Bitcoin\Bitcoin;
use BitWasp\Bitcoin\Network\NetworkFactory;

use App\Classes\nodeCurl;

/**
 * Description of Transaction
 *
 * @author walek
 */
class Transaction {

    public function __construct($from, $to, $amount,$fee)
    {
        ob_start();
        try {
            Bitcoin::setNetwork(NetworkFactory::bitcoinRegtest());
            $network = Bitcoin::getNetwork();

            $privKeyFactory = new PrivateKeyFactory();
            $privateKey = $privKeyFactory->fromWif('cUsdwZ6NwcDVQ6KGx4ULKGpaa6sDM1MFT1W6UL7ED86w3P6NsbC6', $network);

            $unspent = nodeCurl::getUnspent($from)->result;

            $balance = 0;
            foreach ($unspent as $u){
                $balance+=$u->amount;
            }
            $balance*=1e8;
            $fee *= 1e8;
            $amount*=1e8;
            $real_sum = $amount+$fee;

            $addressCreator = new AddressCreator();
            $transaction = TransactionFactory::build();

            if($balance>$amount+$fee){
                $unspent_sum=0;
                $i = 0;
                do{
                    $cur = current($unspent);
                    $unspent_sum+=$cur->amount*1e8;
                    $transaction->input($cur->txid, 0);
                    $cur = next($unspent);
                }while($unspent_sum<$real_sum&&$cur);
                $transaction->payToAddress($amount, $addressCreator->fromString($to));
                $transaction->payToAddress($unspent_sum-$real_sum, $addressCreator->fromString($from));
                $transaction = $transaction->get();

                $program = ScriptFactory::scriptPubKey()->payToPubKeyHash($privateKey->getPubKeyHash());
                $txOut = new TransactionOutput($unspent_sum, $program);

                $signer = new Signer($transaction);
                foreach ($transaction->getInputs() as $key=>$input){
                    $signer->sign($key, $privateKey, $txOut);
                }
                $signed = $signer->get();

            }else{
                die('over balance');
            }
            dump($signed->getHex(),'');
            die();
            ob_clean();
        } catch (Exception $ex) {
            ob_clean();
            die($ex);
        }
    }
}
afk11 commented 5 years ago

Hi, I took a look through your code (thanks for providing it), and can see the following causing problems:

                     $transaction->input($cur->txid, 0);

Should $cur->vout or something not be passed instead of 0?

                 $txOut = new TransactionOutput($unspent_sum, $program);

You need to store each $txOut as you loop through $unspents and add each as an input. Then when signing, pass the $txOut used to create the input. Add something like this to the unspents loop:

                $txOuts[] = new TransactionOutput($cur->amount*1e8, $program);

and in the signing loop:

                    $signer->sign($key, $privateKey, $txOuts[$key]);