本ハンズオンではDockerを用いて2角bitcoindノードを建て、それぞれに作成したwalletで送受金、JSON-RPCを用いてプログラムを書きながらBitcoinの動きを学ぶ。
$ git clone https://github.com/oshikawatkm/bitcoin_handson.git
Bitcoin CLIは以下のような構成で入力します。
$ bitcoin-cli [options] <command>
使用可能なコマンドは以下で確認できる。 https://bitcoincore.org/en/doc/0.20.0/rpc/
体で覚えるためにコピペをするのではなく手入力しましょう
dockerコンテナを起動
$ docker-compose up
bitcoin_1のコンテナ内に入って操作する。
$ docker exec -it bitcoin_1 bash
ブロック数を確認。もちろん"0"
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass getblockcount
0
wallet1という名前のwalletを作成。(一つのノードに複数のwalletを作成することもできます)
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass createwallet "wallet1"
{
"name": "wallet1",
"warning": ""
}
wallet1の所有BTCを確認。もちろんまだ0BTC。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 getbalance
0.00000000
wallet1のUTXOを確認。もちろん空。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 listunspent
[
]
wallet1でaddressを生成する。
何かしらの文字列の部分が表示される。それがwallet1のアドレス。(何度実行してもよい。毎回別のアドレスが返ってくるはず。)
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 getnewaddress
< wallet1のアドレス >
ターミナルで別のタブを開く。
bitcoin_2のコンテナ内に入って操作する。
$ docker exec -it bitcoin_2 bash
wallet2という名前のwallet作成。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass createwallet "wallet2"
{
"name": "wallet2",
"warning": ""
}
wallet2でaddressを生成する。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet2 getnewaddress
< wallet2のアドレス >
bitcoin_1のターミナルに戻る。
101ブロックの生成を行い、マイニング報酬をwallet1で受け取る。
実行すると大量(101個)の文字列が配列で帰ってくるはず。この文字列はブロックヘッダー。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 generatetoaddress 101 < wallet1のアドレス >
[
"4ece5b5701a76f99fb3d823447773f648baf0f61835f5e11f4585044889df07b",
"16be9944002f88d2d86a9601d38c374467e62cab53bc7b93cd7d46002e0c0e29",
"124b00eba3bff1b2a50cb6bcc0b6d5ff3d04b3aafee89958427974fcc6aa715a",
"6250b1f1bd7bdf63e4c7f74014ddfdfa75e04a87cb89803d0db1198cc883c8d3",
"6a7fb4ee42207b729423912c6561fcc0275fec052334623eec05c0978f05379f",
"17f0df8e506b4b07128d11f2d5f59677d6d2777a6687350e77e1e7889d20.............
ブロックカウントを確認してみる。101なはず。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass getblockcount
101
wallet1の残高を確認してみる。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 getbalance
50.00000000
wallet1のUTXOを確認。マイニング報酬を確認できる。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 listunspent
[
{
"txid": "25d4893b0d83ae2980bd3a82537420246fe9274f54fbabafa93bd8673b97c69c",
"vout": 0,
"address": "bcrt1q6m75utlsfgct7g2xcdtrghaynxc88532g55xjs",
"label": "",
"scriptPubKey": "0014d6fd4e2ff04a30bf2146c356345fa499b073d22a",
"amount": 50.00000000,
"confirmations": 101,
"spendable": true,
"solvable": true,
"desc": "wpkh([2bbfacf4/0'/0'/0']03fcc2873c31bfbb3d81b069d6abd3d4ffa43d6a1d1e93408e1d0bf7013ff4573b)#ez49ra2j",
"safe": true
}
]
Bitcoinの送金には以下の2つの方法がある。
- sendtoaddress: 使用するUTXOの選択などを自動で行い、transaction作成&ブロードキャストするお手軽な送金方法
- createrowtransaction & broadcasttransaction: UTXO, Scriptなどを指定しtransactionを作成。transactionを選択し、ブロードキャスト。
ここでは
sendtoaddressのみを確認。
wallet2のアドレスと送金額(0.01BTC)を指定し、sendtoaddressを実行.
実行すると文字列が帰ってくる。これはtransaction id。あとで使うので控えておく。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 sendtoaddress <wallet2のアドレス> 0.01
76e72b6683b7a408f884cd5dd4cee5050d360cb8f32f5bb8627c7c64d6b89926
bitcoin_2のターミナルを開き、まずはbitcoin_1で生成したブロックが同期されているかを確認する。
101なはず。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass getblockcount
101
wallet2の残高を確認する。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet2 getbalance
0.00000000
残高は0BTCのまま。 送金transactionはブロードキャストされているが、マイニングによる承認を受けていないため、まだ着金は確認できない
bitcoin_1のターミナルを開き、マイニングを行う。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 generatetoaddress 6 < wallet1のアドレス >
[
"4ece5b5701a76f99fb3d823447773f648baf0f61835f5e11f4585044889df07b",
"16be9944002f88d2d86a9601d38c374467e62cab53bc7b93cd7d46002e0c0e29",
"124b00eba3bff1b2a50cb6bcc0b6d5ff3d04b3aafee89958427974fcc6aa715a",
"6250b1f1bd7bdf63e4c7f74014ddfdfa75e04a87cb89803d0db1198cc883c8d3",
"6a7fb4ee42207b729423912c6561fcc0275fec052334623eec05c0978f05379f",
"17f0df8e506b4b07128d11f2d5f59677d6d2777a6687350e77e1e7889d20379f"
]
先頭に表示されたblockhashは後で使うためメモしておく。(上の例では: "4ece5b5701a76f99fb3d823447773f648baf0f61835f5e11f4585044889df07b")
bitcoin_2のターミナルを開き、wallet2の残高を確認する。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet2 getbalance
0.01000000
wallet2のUTXOを確認する。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet2 listunspent
[
{
"txid": "c3a1e3051156d9ef64af3070382023e214642461be2e00dca734ac361a8dc82d",
"vout": 1,
"address": "bcrt1qhhrhxveqhyhch8mu83jx08cszxd9d9wq3rr93n",
"label": "",
"scriptPubKey": "0014bdc7733320b92f8b9f7c3c64679f10119a5695c0",
"amount": 0.01000000,
"confirmations": 6,
"spendable": true,
"solvable": true,
"desc": "wpkh([f5462cff/0'/0'/0']0388bd7d31168cc8ec710ca8a920d49579f50b48cd5a1b71ac8704a3318bcc9ce6)#yf2mpas5",
"safe": true
}
]
ブロック情報の確認。 txの配列に送金トランザクションと報酬トランザクションの2つがあることを確認。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 getblock < 控えたblockhash >
{
"hash": "50470b6c06ec75d26786038ff1e77278ca84db8ff690e46f4209232abe7aa2d9",
"confirmations": 6,
"strippedsize": 327,
"size": 472,
"weight": 1453,
"height": 103,
"version": 536870912,
"versionHex": "20000000",
"merkleroot": "a1a218b96cd33aaac26905e3130e382311b1f0d3a80f32d564e3a984aada456b",
"tx": [
"dcf9f810aabce38e8564bea35cfba75fe7e0a735cbe69c6a8352461819a52e8f",
"c3a1e3051156d9ef64af3070382023e214642461be2e00dca734ac361a8dc82d"
],
"time": 1653716676,
"mediantime": 1653713107,
"nonce": 0,
"bits": "207fffff",
"difficulty": 4.656542373906925e-10,
"chainwork": "00000000000000000000000000000000000000000000000000000000000000d0",
"nTx": 2,
"previousblockhash": "7901b8276863095a735c85f57ebfe8da685c333f85dbc0b008ade5626ddeab93",
"nextblockhash": "64c4d29732f81325dfe538576a19843709566ce67a448f0818d1c9b02d079ee7"
}
トランザクション情報の確認
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 gettransaction < 控えたtransactionid >
{
"amount": -0.01000000,
"fee": -0.00002820,
"confirmations": 6,
"blockhash": "50470b6c06ec75d26786038ff1e77278ca84db8ff690e46f4209232abe7aa2d9",
"blockheight": 103,
"blockindex": 1,
"blocktime": 1653716676,
"txid": "c3a1e3051156d9ef64af3070382023e214642461be2e00dca734ac361a8dc82d",
"walletconflicts": [
],
"time": 1653716145,
"timereceived": 1653716145,
"bip125-replaceable": "no",
"details": [
{
"address": "bcrt1qhhrhxveqhyhch8mu83jx08cszxd9d9wq3rr93n",
"category": "send",
"amount": -0.01000000,
"vout": 1,
"fee": -0.00002820,
"abandoned": false
}
],
"hex": "020000000001015e9d203e02c89f087556537b91bf54e4ed4bc035e35c863fb513e761acb260aa0000000000feffffff02bca4f62901000000160014709e4bb0727dc45e24776ec57c789ff9996753b040420f0000000000160014bdc7733320b92f8b9f7c3c64679f10119a5695c00247304402202cf01f9b13caa725dcd7ae8008d7f5c861c7eaa574cacd2806853365e30d7f22022009d6c98e726dda3986bc69ef8d8946acae695aa39edd4ff9155e2e37aa2b409d012103fcc2873c31bfbb3d81b069d6abd3d4ffa43d6a1d1e93408e1d0bf7013ff4573b66000000"
}
bitcoindはJSON-RPC を用いたRPC (Remote Procedure Call) ができる。 ここではcurlコマンドと**プログラム(python)**を用いたRPCを試してみる。
curlを用いてbitcoin_1でgetblockchaininfo呼び出し。
$ curl --user user:pass --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "getblockchaininfo", "params": []}' -H 'content-type: text/plain;' http://0.0.0.0:18443/
rubyのコードを用いてトランザクションの生成。
handson_finished.rbをmain.rbに写経してください。
$ ruby handson_finished.rb
生成したトランザションbitcoin_1で確認。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass decoderawtransaction "<生成したトランザクション>"
トランザションをwallet1の鍵で署名。
hexに署名済みのトランザクションが表示される。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 signrawtransactionwithwallet "<生成したトランザクション>"
{
"hex": "010000000001012d5473b68d749532e093b30c5df2d7e351a8c0b0006bae662d9ef8565f8c62b30000000000ffffffff0164000000000000000451529387024730440220135a1a56b61e5e30c715397415fa084d07144f8ec243627d72687d234c531b6602207e715601428020da75e21a45bad99ac1f7c1991b5c4f3aa2d9e869b1e5922f4d012103fcc2873c31bfbb3d81b069d6abd3d4ffa43d6a1d1e93408e1d0bf7013ff4573b00000000",
"complete": true
}
ブロードキャストする。
$ bitcoin-cli -regtest -rpcuser=user -rpcpassword=pass -rpcwallet=wallet1 sendrawtransaction "<生成した署名済みトランザクション>"
{
"hex": "010000000001012d5473b68d749532e093b30c5df2d7e351a8c0b0006bae662d9ef8565f8c62b30000000000ffffffff0164000000000000000451529387024730440220135a1a56b61e5e30c715397415fa084d07144f8ec243627d72687d234c531b6602207e715601428020da75e21a45bad99ac1f7c1991b5c4f3aa2d9e869b1e5922f4d012103fcc2873c31bfbb3d81b069d6abd3d4ffa43d6a1d1e93408e1d0bf7013ff4573b00000000",
"complete": true
}
bitcoin_3というbitcoinノードのコンテナを作成する。このノードはまだbitcoin_1, bitcoin_2のコンテナネットワークに接続はしていない。
bitcoin_3でブロックを1000個生成した後、bitcoin_1, bitcoin_2ネットワークに接続する。
最新のブロックカウント、今までの取引はどうなるでしょう?
時間あったら試してみてください。
rubyのコードでは50BTCをインプットに取り、10BTCをアウトプットに指定しました。
残りの40BTCはどうなったでしょうか?
rubyで生成したトランザクションをインプットにしたトランザクションを作成、ブロードキャストしてみてください。
mainnetやtestnetはregtestと全く違った面白さがあるため、ぜひ別のネットワークでも試してみてほしい
以下の資料はBitcoinの操作を網羅的に解説している https://github.com/BlockchainCommons/Learning-Bitcoin-from-the-Command-Line