Never Too Late

いつだってゼロからやり直そう

ビットコイントランザクションの中身を詳しく見てみる

(2017年10月3日にQiitaにて執筆した記事をこちらに転載しています。)

前回やったこと

前回の記事では堀江貴文さんのビットコインアドレスを例に、あるビットコインアドレスに関連するトランザクションの一覧を取得しました。

今回はその中のひとつのトランザクションに着目し、トランザクショの中身をより詳しく見ていきたいと思います。

今回はC#とNBitcoinというビットコインライブラリ、QBitNinjaというブロックチェーンAPIのクライアントライブラリを使用したいと思います。

環境の準備

Visual Studio Communityをインストールした後、コンソールアプリ( .NET Coreでも .NET Frameworkでもどちらでも構いません)のプロジェクトを作成し、NuGetパッケージの管理画面でNBitcoinとQBitNinjaをそれぞれ検索し、プロジェクトに追加して下さい。

今回例として参照するトランザクション

前回取得した堀江さんのビットコインアドレスの一番古いトランザクションであるこちらのトランザクションを例に使いましょう。トランザクションハッシュは以下の値になっています。

7008807adb713205d01d378f3d25f1bc5e06bda7d30b526c2b9d2c018cfa08b4

ちなみにこちらは堀江さんがビットコインを受け取ったときのトランザクションです。

ブロックチェーンAPIを使って該当のトランザクションを取得してみる

まずはブロックチェーンAPIにアクセスして該当のトランザクションを取得してみます。その為にQBitNinjaというクライアントライブラリを使用します。

using NBitcoin;
using QBitNinja.Client;
using QBitNinja.Client.Models;
using System;

namespace NBitcoinTest1
{
    class Program
    {
        static void Main(string[] args)
        {
            QBitNinjaClient client = new QBitNinjaClient(Network.Main);
            var transactionId = uint256.Parse("7008807adb713205d01d378f3d25f1bc5e06bda7d30b526c2b9d2c018cfa08b4");
            GetTransactionResponse transactionResponse = client.GetTransaction(transactionId).Result;

            Console.WriteLine(transactionResponse.TransactionId);
        }
    }
}

これでコンソールの出力にトランザクションハッシュが出力されるはずです。

ちなみに以下で使用しているNetwork.Mainというのは、ビットコインのテスト用ネットワークではなく、本番用のネットワークからデータを取得する為に指定しているものです。

QBitNinjaClient client = new QBitNinjaClient(Network.Main);

トランザクション内のビットコインの量を見てみる

このトランザクションのアウトプットに含まれるビットコインの量を見てみます。これはつまり堀江さんが受け取ったビットコインの量ということになります。

今回のトランザクションにはアウトプットが一つしかありませんが、アウトプットがひとつのトランザクションにひとつとは限りません。例えばビットコインを支払いに使いおつりが発生した場合などには、おつりがひとつのアウトプットとなり、支払者宛てに送られます。

transactionResponse.ReceivedCoins.ForEach((coin) =>
{
    Money amount = (Money)coin.Amount;
    Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
});

出力は以下のようになります。

0.09020979

同じ要領でインプットの量も見てみます。こちらもひとつとは限りませんし、アウトプットと同量であるとも限りません。

decimal total = 0;
transactionResponse.SpentCoins.ForEach((coin) =>
{
    Money amount = (Money)coin.Amount;
    Console.WriteLine(amount.ToDecimal(MoneyUnit.BTC));
    total += amount.ToDecimal(MoneyUnit.BTC);
});

Console.WriteLine(string.Format("Total input is {0} BTC", total));

出力は以下のようになりました。

0.001
0.01914202
0.00077777
0.0001
0.01
0.01
0.001
0.001
0.04649
0.001
Total input is 0.09050979 BTC

インプットが10個存在すること。それらのビットコインが集められて、インプットのビットコインの総量の方がアウトプットのビットコインの総量(0.09020979)よりも多いことが確認できます。

このインプットとアウトプットの差はビットコインのトランザクション手数料として、所謂マイナーさんがこのトランザクションをブロックチェーンのブロックとして正式に追加したときに彼らに払われるものです。

ScriptPubKeyを見てみる

ここで詳しくは触れませんが、ビットコインの所有権を示す為の根幹となるScriptPubKeyを見てみます。すごく簡単に言うと、このスクリプトは所有者にしか解けないパズルのようなものだと言えます。

transactionResponse.ReceivedCoins.ForEach((coin) =>
{
    Console.WriteLine(coin.TxOut.ScriptPubKey);
});

出力は以下のようになります。

OP_DUP OP_HASH160 a4de1901ca3dd7167d655214c21baaa7fe554d3f OP_EQUALVERIFY OP_CHECKSIG

これは知識がないとチンプンカンプンだと思いますが、以下のようにNBitcoinを使用すると、実はここから堀江さんのビットコインアドレスである1G2jt5WeGhqWtDKEkcKY2GrZKjfYsuiVxXが抜き出せます。

using NBitcoin;
using QBitNinja.Client;
using QBitNinja.Client.Models;
using System;

namespace NBitcoinTest1
{
    class Program
    {
        static void Main(string[] args)
        {
            QBitNinjaClient client = new QBitNinjaClient(Network.Main);
            var transactionId = uint256.Parse("7008807adb713205d01d378f3d25f1bc5e06bda7d30b526c2b9d2c018cfa08b4");
            GetTransactionResponse transactionResponse = client.GetTransaction(transactionId).Result;

            var tx = transactionResponse.Transaction;
            tx.Outputs.ForEach((txOut) =>
            {
                var script = txOut.ScriptPubKey;
                Console.WriteLine(script);
                Console.WriteLine(script.GetDestinationAddress(Network.Main));
            });
        }
    }
}
OP_DUP OP_HASH160 a4de1901ca3dd7167d655214c21baaa7fe554d3f OP_EQUALVERIFY OP_CHECKSIG
1G2jt5WeGhqWtDKEkcKY2GrZKjfYsuiVxX

ビットコインアドレスというのは公開鍵から作られているものです(正確に言うと公開鍵のハッシュから作られています。)

これでこのスクリプトは受け取り手である堀江さんの公開鍵で鍵をかけられ、彼の秘密鍵でしか解けないものであることが何となくイメージできると思います。

次回にやること

ちょっと長くなってきてしまったので今回はここまでにします。

次回は、上で10個あったこのトランザクションへのインプットがどこからやってきたのかを見たいと思います。

参照