はじめに
ビットコインの取引を自作のC#プログラムで行います。
2017年に来日していた NicolasDorier さんからレクチャーを受けた内容をベースにしています。
これを応用すればこれまで一度もダウンしていないビットコインブロックチェーン上に永遠に残るメッセージを書き込んだり、独自のコインやウォレットを作成することができます。
ここで紹介する方法は筆者自身がブロックチェーンの可能性に初めて触れた内容であり、その後の研究や仕事に大きな影響を与えたものです。
古いPCの中で埋れつつあるのが勿体なかったのでここに載せておきたいと思います。
使用環境
実施方法
Visual Studio Codeで以下のコマンドを実行し、プロジェクトを開始する。
# 任意のプロジェクトフォルダを用意
mkdir sendBtc
cd sendBtc
# プロジェクトの開始と必要パッケージの追加
# 執筆時点のバージョン
# - NBitcoin: Version="5.0.65"
# - QBitNinja.Client: Version="1.0.3.52"
dotnet new console
dotnet add package NBitcoin
dotnet add package QBitNinja.Client
dotnet restore
秘密鍵、公開鍵、ビットコインアドレスを生成する。
- ビットコインの取引に必要な秘密鍵、公開鍵、ビットコインアドレスはNBitcoinを使用したC#プログラムで簡単に生成することができます。
- 生成方法は以下の投稿を参考にしてください。
→C#でビットコインの秘密鍵、公開鍵、ビットコインアドレスを生成する
ビットコインを取得する。
- 今回は実験・情報共有用のためMainnetではなくTestnetを使用します。Testnetは開発やテスト用のネットワークであり、そこで取引されるビットコインは無価値です。
- Testnetを使用するため、Testnet用に生成したビットコインアドレス
n1LkPbkfFT9xSUmSNZ4gBak1Mqe8AKs1n5
を使用します。 - Testnet用のビットコインはFaucet(蛇口)サイトを利用することで取得できます(Mainnetではできません)。
- Faucetには色々ありますが、ここでは [bitcoinfaucet.uo1.net] (https://bitcoinfaucet.uo1.net/) を利用します。
- Faucetサイトにアクセスし、ビットコインアドレスを入力して"Send testnet bitcoins"を選択します。
Testnet用ビットコインが指定のアドレスに送付されていることをブロックチェーンを参照して確認する。
- ブロックチェーンエクスプローラにアクセスします。
ブロックチェーンエクスプローラには色々ありますが、ここでは BlockCypher を利用します。 - 0.0003 BTCが取得できていることを確認できます。
- トランザクションの内容も確認できます。
- 下のイメージの左側はFaucetサイトが管理している送り元のアドレス、右側は送り先のアドレス。
- 右側にある 2.12175313 BTCは送り元へのおつりです。
- このトランザクションのIDは
4c82922f0e3f04f98ac7809a15ce15d66505bc1627c86ede82341fd5d5089a2f
であることが確認できます。
Visual Studio Codeで以下のコマンドを実行し、オリジナルメッセージ付きの送金を行う。
using System;
using System.Collections.Generic;
using System.Text;
using NBitcoin;
using QBitNinja.Client;
namespace sendBtc
{
class Program
{
static void Main(string[] args)
{
// 自アドレスの秘密鍵(WIF形式)からBitcoinSecretを取得する
var bitcoinPrivateKey = new BitcoinSecret("cPoEcLELGdXjHK5HB4tFxWf11UabeBJX4muBZSTZoavL8ZbDpBGa");
var network = bitcoinPrivateKey.Network;
// これから送金するためのトランザクションの左側(TxIn)をつくる
// - UTXO = Unspent Transaction Output(自アドレス宛の未使用トランザクション)の確認
// - トランザクションインプットの作成
var client = new QBitNinjaClient(network);
var transactionId = uint256.Parse("4c82922f0e3f04f98ac7809a15ce15d66505bc1627c86ede82341fd5d5089a2f");
var transactionResponse = client.GetTransaction(transactionId).Result;
var receivedCoins = transactionResponse.ReceivedCoins;
OutPoint outPointToSpend = null;
foreach (var coin in receivedCoins)
{
if (coin.TxOut.ScriptPubKey == bitcoinPrivateKey.ScriptPubKey)
{
outPointToSpend = coin.Outpoint;
}
}
if (outPointToSpend == null)
{
throw new Exception("TxOut doesn't contain our ScriptPubKey");
}
var transaction = Transaction.Create(network);
transaction.Inputs.Add(new TxIn()
{
PrevOut = outPointToSpend
});
// これから送金するためのトランザクションの右側(TxOut)をつくる
// - BitcoinAddressの生成
// - 送金額の設定
var destinationAddress = BitcoinAddress.Create("mhk39wXeb1ZsDegtPb6xnrUquctp6Rusko", network);
var spendAmount = new Money(0.0001m, MoneyUnit.BTC);
var minerFee = new Money(0.00008m, MoneyUnit.BTC);
var txInAmount = (Money)receivedCoins[(int)outPointToSpend.N].Amount;
var changeAmount = txInAmount - spendAmount - minerFee;
transaction.Outputs.Add(spendAmount, destinationAddress.ScriptPubKey);
transaction.Outputs.Add(changeAmount, bitcoinPrivateKey.ScriptPubKey);
// メッセージ編集
// ブロックチェーンに永遠に残るオリジナルメッセージを付けて送金する
var message = "From Qiita, Japan, @ysskjck.";
var bytes = Encoding.UTF8.GetBytes(message);
transaction.Outputs.Add(Money.Zero, TxNullDataTemplate.Instance.GenerateScriptPubKey(bytes));
// これから送金するためのトランザクションに署名する
// (TxInにある自アドレス宛の送金額の所有権を証明する必要がある)
transaction.Inputs[0].ScriptSig = bitcoinPrivateKey.ScriptPubKey;
transaction.Sign(bitcoinPrivateKey, receivedCoins.ToArray());
// トランザクションの出力(送金確定前の内容確認)
Console.WriteLine(transaction.ToString());
// 送金確定
// (ブロックチェーンへの書き込み)
var broadcastResponse = client.Broadcast(transaction).Result;
if (!broadcastResponse.Success)
{
Console.Error.WriteLine("Error! ErrorCode: " + broadcastResponse.Error.ErrorCode);
Console.Error.WriteLine("ErrorMsg: " + broadcastResponse.Error.Reason);
}
else
{
Console.WriteLine("Success! You can check out the hash of the transaciton in any block explorer:");
Console.WriteLine("TransactionID: " + transaction.GetHash());
}
}
}
}
- コード内でセットしている下記は任意のものをセットしてください。
- bitcoinPrivateKey
- transactionId
- destinationAddress
- message
- コードの実行は以下のコマンドで行います。
dotnet run
- コードの実行により出力された内容は以下の通りです。
トランザクションの構成をJSON形式で確認できます。
ブロックチェーンへの書き込みが成功したことと、成功したトランザクションのIDが確認できます。
トランザクションの構成
{
"hash": "0afe7c45fafa334ea9beb48166f582d7e9e8fed41a67b24738202ceb53e39d7d",
"ver": 1,
"vin_sz": 1,
"vout_sz": 3,
"lock_time": 0,
"size": 264,
"in": [
{
"prev_out": {
"hash": "4c82922f0e3f04f98ac7809a15ce15d66505bc1627c86ede82341fd5d5089a2f",
"n": 0
},
"scriptSig": "3044022019ae8c12df7d27c89ba7eeb4111435451e766819270db9342cf71ee4bb88e3ed0220596b3d6c820d6d92b4033f8154ed1c8e39e096aff11ec8c8a017b4b38d98a6dd01 02ce3ff023c56e25354574f4f04451d4c2947847360495e490a726f6ce2ac0f15a"
}
],
"out": [
{
"value": "0.00010000",
"scriptPubKey": "OP_DUP OP_HASH160 1869b751f67d31f7f3a9f362cddc74b176630adc OP_EQUALVERIFY OP_CHECKSIG"
},
{
"value": "0.00012000",
"scriptPubKey": "OP_DUP OP_HASH160 d9746b354ba771ee86a24ae4ff69c03d8de058af OP_EQUALVERIFY OP_CHECKSIG"
},
{
"value": "0.00000000",
"scriptPubKey": "OP_RETURN 46726f6d2051696974612c204a6170616e2c20407973736b6a636b2e"
}
]
}
処理結果とトランザクションIDの出力内容
Success! You can check out the hash of the transaciton in any block explorer:
TransactionID: 0afe7c45fafa334ea9beb48166f582d7e9e8fed41a67b24738202ceb53e39d7d
オリジナルメッセージ付きの送金内容をブロックチェーンを参照して確認する。
- ブロックチェーンエクスプローラで先ほど成功したトランザクションのIDを入力して検索します。
(トランザクションID:0afe7c45fafa334ea9beb48166f582d7e9e8fed41a67b24738202ceb53e39d7d
)
- トランザクションがブロックチェーン上に存在していることを確認できます。
- String:欄で
From Qiita, Japan, @ysskjck.
が書き込まれていることを確認できます。 - CONFIRMATIONSが1/6であることが確認できます。
これはこのトランザクションを承認しているマイナーが1人(1ノード)いるということを意味しています。ビットコインについては6承認をとるとほぼ間違いなく安全であるといわれています。なお、2020/11/09 22:20時点では1承認でしたが、その数分後には6以上の承認が得られていました。
- トランザクションが所属するブロックの情報(ブロックのハッシュ値、ブロックチェーン上のブロックの高さ)やトランザクションのサイズなどを確認することもできます。
- Faucetサイトで取得した 0.0003 BTCのうち 0.0001 BTCが
mhk39wXeb1ZsDegtPb6xnrUquctp6Rusko
に送付されていることを確認できます。 - おつりの 0.00012 BTCが
n1LkPbkfFT9xSUmSNZ4gBak1Mqe8AKs1n5
に送付されています。 - 0.00008 BTCはマイナーへのマイニング手数料です。
これでビットコインにオリジナルメッセージを添えて送金できました。