Bit-Wasp/bitcoin-php 的简单使用:创建钱包 + 进行交易

Bit-Wasp/bitcoin-php 的简单使用:

这里简单介绍了一下最最最基础的 Bit-Wasp/bitcoin-php 的用法,如果你对 bitcoin 还很陌生,推荐 【回形针PaperClip】区块链到底是什么? 了解 bitcoin 和 比特币交易如何防伪?私钥公钥地址啥意思?李永乐老师讲比特币(2) 大致了解 bitcoin 的交易流程。想进一步了解 bitcoin,推荐阅读 《精通比特币》

  1. 创建钱包

    不同于中心化的钱包( 支付宝 / 微信 ),创建 Bitcoin 钱包不需要网络,只需要一个足够随机的数字 n ( 1 < n < 1.158 * 10^77 -1 ,略小于 2^256 ,关于这个数是否足够安全,可以看这个视频:256位加密有多安全?),公钥和地址都是由这个 n 生成,它被称作 私钥

    创建钱包本质上就是找到一个随机数。

    私钥和公钥及地址的关系

    但是你不能用 php 自带的随机数生成函数!!!因为 php 自带的这些随机数生成函数并不是密码学安全的随机数生成器(它是基于 unix时间戳 / mac地址 / etc 生成的数字,并不是真正的随机数)。
    随机数生成器

    但是我们可以使用 Bit-Wasp/bitcoin-php 中密码学安全的随机数生成器生成一个随机数来作为我们的钱包:
    在 Laravel Job 中的实现

     <?php
     namespace App\Jobs\Bitcoin;
    
     use Illuminate\Bus\Queueable;
     use Illuminate\Contracts\Queue\ShouldQueue;
     use Illuminate\Foundation\Bus\Dispatchable;
     use Illuminate\Queue\InteractsWithQueue;
     use Illuminate\Queue\SerializesModels;
    
     use App\Models\Bitcoin\Wallet;
    
     use BitWasp\Bitcoin\Address\PayToPubKeyHashAddress;
     use BitWasp\Bitcoin\Bitcoin;
     use BitWasp\Bitcoin\Crypto\Random\Random;
     use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
    
     class GenerateWallet implements ShouldQueue
     {
         use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
         /**
          * Execute the job.
          *
          * @return void
          */
         public function handle()
         {
             $random = new Random();
             $private_key_factory = new PrivateKeyFactory();
             $private_key = $private_key_factory->generateCompressed($random);
             $public_key = $private_key->getPublicKey();
    
             // p2pkh 格式的地址
             $address = new PayToPubKeyHashAddress($public_key->getPubKeyHash());
    
             // 将生成的钱包保存到数据库中
             $wallet = new Wallet;
             $wallet->wif = $private_key->toWif($network);
             $wallet->address = $address->getAddress();
    
             $wallet->save();
    
             // 保存到日志文件中
             $log  = "wif: ". $wallet->wif . PHP_EOL .
                     "address: ". $wallet->address . PHP_EOL .
                     "-------------". date('Y-m-d Hs') ."------------" . PHP_EOL;
             file_put_contents('./wallets.log', $log, FILE_APPEND);
         }
     }

    下面就是我们创建的一个新钱包的示例:
    Bit-Wasp/bitcoin-php 的简单使用:创建钱包 + 交易
    wif 是一种私钥的格式,也是各个交易所、钱包所通用的私钥导入格式,原始的二进制数主要用于程序内部。

  2. 创建交易

    我们首先来看一下一个已完成的交易有那些内容:
    比特币交易
    此标准交易的主要组成部分采用颜色编码:

    • 交易ID(以黄色突出显示)

    • 描述符和元数据(蓝色花括号在右边详细说明)

    • 输入(粉色区域)

    • 输出(绿色区域)

      我们要做的就是要离线构造一个如上的数据,在通过第三方或者你自己的结点广播出去,待区块确认后,即可视为完成了交易。
      在这里需要注意以下几点:

    1. 交易信息里面,收款人的信息在输出里面,锁定脚本(scriptPubKey)将限制使用人,也就是地址所对的私钥。

    2. 交易时需要提供上次交易的 id,来证明你对 bitcoin 的所有权

      bitcoin 的地址存在几种类型,这里暂不讨论,这里将根据提供的 p2pkh 地址发送到 p2wpkh 地址
      在 Laravel Job 中的实现

      <?php
      namespace App\Jobs\Bitcoin;
      
      use Illuminate\Bus\Queueable;
      use Illuminate\Contracts\Queue\ShouldQueue;
      use Illuminate\Foundation\Bus\Dispatchable;
      use Illuminate\Queue\InteractsWithQueue;
      use Illuminate\Queue\SerializesModels;
      
      use BitWasp\Bitcoin\Address\PayToPubKeyHashAddress;
      use BitWasp\Bitcoin\Bitcoin;
      use BitWasp\Bitcoin\Key\Factory\PrivateKeyFactory;
      use BitWasp\Bitcoin\Script\Interpreter\InterpreterInterface as I;
      use BitWasp\Bitcoin\Script\ScriptFactory;
      use BitWasp\Bitcoin\Transaction\Factory\Signer;
      use BitWasp\Bitcoin\Transaction\Factory\TxBuilder;
      use BitWasp\Bitcoin\Transaction\OutPoint;
      use BitWasp\Bitcoin\Transaction\TransactionOutput;
      use BitWasp\Buffertools\Buffer;
      use BitWasp\Bitcoin\Transaction\Factory\SignData;
      use BitWasp\Bitcoin\Script\WitnessScript;
      use BitWasp\Bitcoin\Script\P2shScript;
      use BitWasp\Bitcoin\Address\SegwitAddress;
      use BitWasp\Bitcoin\Script\WitnessProgram;
      use BitWasp\Bitcoin\Address\AddressCreator;
      
      use GuzzleHttp\Client;
      use GuzzleHttp\Exception\ClientException;
      
      class Transfer implements ShouldQueue
      {
       use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
      
       /**
        * Execute the job.
        *
        * @return void
        */
       public function handle()
       {
           // 支付钱包的私钥(wif 格式)
           $wif = 'KxwUMCtXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXxEGooz';
           // 支付钱包上一次交易的 id
           $txid = '4c7a031a31fe794e64ef5ca2714bdd9dd10ceae44650bce025952282bdeeda8b';
           // 收款钱包地址(p2pkh 格式)
           $address = '1LRqwmjqLNNybJ3M2zQHiwvyA6ikbQQkyS';
      
           $privKeyFactory = new PrivateKeyFactory;
           $key = $privKeyFactory->fromWif($wif);
      
           $witnessScript = new WitnessScript(
               ScriptFactory::scriptPubKey()->payToPubKeyHash($key->getPubKeyHash())
           );
      
           $dest = new SegwitAddress(
               WitnessProgram::v0(
                   (new AddressCreator())->fromString($address)->getHash()
               )
           );
      
           // UTXO
           $outpoint = new OutPoint(Buffer::hex($txid, 32), 0);
           // 这里一共将支出 100000 聪(0.00000001 BTC = 1 聪)
           $txOut = new TransactionOutput(100000, $witnessScript);
      
           // 收款人将收到 90000 聪,中间的差将作为矿工的手续费
           $builder = (new TxBuilder())
               ->spendOutPoint($outpoint)
               ->payToAddress(90000, $dest);
      
           // 签署交易
           $signer = new Signer($builder->get(), Bitcoin::getEcAdapter());
           $input = $signer->input(0, $txOut);
           $input->sign($key);
      
           $signed = $signer->get();
      
           // 需要进行广播的交易
           $broadcast = $signed->getBaseSerialization()->getHex();
      
           // 我这里使用 blockchain.info 的接口进行广播,你可以使用你自己的结点,或者现成的 RPC 接口
           $client = new Client;
           try {
               $response = $client->request('POST', 'https://blockchain.info/pushtx', [
                   'form_params' => [
                       'tx' => $signed->getBaseSerialization()->getHex()
                   ]
               ]);
               var_dump(json_decode($response->getBody(), true));
           } catch (ClientException $e) {
               var_dump($e->getResponse());
           }
       }

      成功进行广播你将收到:
      成功广播

      待 10-15 分钟后,交易被写入区块,再等待后续生成 2-3 个区块,即可视为交易完成。
      你可以在 Electrum 这样的开源钱包中查看你收到的钱
      Bit-Wasp/bitcoin-php 的简单使用:创建钱包 + 交易

    关于手续费
    你完全可以一毛不拔,一聪都不给,但是就没有矿工愿意打包你的交易,你的交易将永远无法完成。bitcoin 矿工的收入来自打包完成的系统奖励(到 2024 年就没有了)和该区块中的所有交易的手续费,所以矿工总是优先打包手续费多的交易。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 5

大佬,测试网里怎么用呢 ,bitcoin core 和electrum 钱包 导出的私钥都不是wif格式的

1年前 评论
jiumengyichang (作者) 1年前
jiumengyichang (作者) 1年前

懵逼了 有个变量$network 没定义就直接用了?

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!