Bitcoin
Blockchain

Blockchain 解析用ソフトウェア: BlockSci を使ってみる

この記事は、ブロックチェーン Advent Calendar 2017 の 20 日目の記事です。

概要

BlockSci というのは、Blockchain 解析用のソフトウェアです。
Blockchain を Jupyter Notebook 使って解析したいな〜というときにか〜なり便利です。

注意

インメモリデータベースなので、メモリをかなり食います。AMI をもとに、EC2 にインスタンスをサクッと立ち上げるのが良いでしょう。
公式が公開している AMI を使うと、立ち上げて ssh トンネリングするだけで Jupyter Notebook が使える状態になります。メチャクチャ楽です。
だいたい 3.5 時間後にすべてのデータが読み込まれ、クエリの処理速度が最高速に達します。

導入

Readme にある最新の AMI を元に EC2 インスタンスを立ち上げます。
2017/12/19 現在では ami-7cf38706 が最新となっています。
メモリ領域が 60GB 以上あることが期待されており, r4.2xlarge が推奨されています。

r4.2xlarge は $0.532 / hour なので, スポットインスタンス使わず一ヶ月回すと $383.04 程度かかります。

自分もこの記事のために昨日から回していて、$10 が失われました。
立ち上がると, Jupyter Notebook も起きているので,

ssh -i .ssh/your_private_key.pem -N -L 8888:localhost:8888 ubuntu@your_url.amazonaws.com

したら localhost:8888 へ見に行くだけです。

利用例

事前準備

import blocksci
import matplotlib.pyplot as plt
import matplotlib.ticker
import collections
import pandas as pd
import numpy as np
%matplotlib notebook
# parser_data_directory should be set to the data-directory which the blocksci_parser output
chain = blocksci.Blockchain("/home/ubuntu/bitcoin/")

Class: Block

ref: https://citp.github.io/BlockSci/chain/block.html

# 最新の block を取得
recent_block = chain[len(chain) - 1]
# coinbase
recent_block.coinbase_tx
# hash: ハッシュ
print("hash is", recent_block.hash)
#  height: ブロック高さ
print("height is", recent_block.height)
# input_count: ブロックに含まれるトランザクションインプットの数
print("input count is", recent_block.input_count)
# input_value: ブロックに含まれるトランザクションインプットに含まれる satoshi の総数
print("input value is", recent_block.input_value)
#  inputs: ブロックに含まれるトランザクションインプットを取得
recent_block.inputs
#  net_address_type_value: Returns a set of the net change in the utxo pool after this block split up by address type
print("net_address_type_value is", recent_block.net_address_type_value)
# net_full_type_value(self: blocksci.blocksci_interface.Block)
print("net_full_type_value", recent_block.net_full_type_value)
# next_block: 次のブロック
recent_block.next_block
# nonce: nonce の数値
print("nonce is", recent_block.nonce)
# output_count: ブロックに含まれるトランザクションアウトプットの数
print("output count is", recent_block.output_count)
# output_value: ブロックに含まれるトランザクションアウトプットに含まれる satoshi の総数
print("output value is", recent_block.output_value)
# outsputs: ブロックに含まれるトランザクションアウトプットを取得
recent_block.outputs
# prev_block: 前のブロック
recent_block.prev_block
# size_bytes: ブロックサイズ
print("size bytes is", recent_block.size_bytes)
# time: タイム
print("time is", recent_block.time)
# timestamp: ブロックヘッダのタイムスタンプ
print("timestamp is", recent_block.timestamp)
# total_spent_of_ages: Returns a list of sum of all the outputs in the block that were spent within a certain of blocks, up to the max age given
print("total_spent_of_ages is", recent_block.total_spent_of_ages)
# txes: ブロックに含まれるトランザクションを取得
recent_block.txes
# version: ブロックヘッダのプロトコルバージョン
print("version is", recent_block.version)
hash is 0000000000000000006271a8c189b9591ad06a369e29d24dbe25e6a1819d6cee
height is 498919
input count is 4913
input value is 1290872675640
net_address_type_value is <bound method PyCapsule.net_address_type_value of Block(numTxes=3031, height=498919, header_hash=0000000000000000006271a8c189b9591ad06a369e29d24dbe25e6a1819d6cee, version=536870912, timestamp=1513094055, bits=402698477, nonce=231235938)>
net_full_type_value <bound method PyCapsule.net_full_type_value of Block(numTxes=3031, height=498919, header_hash=0000000000000000006271a8c189b9591ad06a369e29d24dbe25e6a1819d6cee, version=536870912, timestamp=1513094055, bits=402698477, nonce=231235938)>
nonce is 231235938
output count is 6720
output value is 1292122675640
size bytes is 1050481
time is 2017-12-12 15:54:15
timestamp is 1513094055
total_spent_of_ages is <bound method PyCapsule.total_spent_of_ages of Block(numTxes=3031, height=498919, header_hash=0000000000000000006271a8c189b9591ad06a369e29d24dbe25e6a1819d6cee, version=536870912, timestamp=1513094055, bits=402698477, nonce=231235938)>
version is 536870912

CurrencyConverter

converter = blocksci.CurrencyConverter()
converter.satoshi_to_currency(100, recent_block.time.date())
0.0171781025

サンプル

手数料のヒストグラム

USD_JPY_RATE = 112
fees = [converter.satoshi_to_currency(fee, recent_block.time) * USD_JPY_RATE for fee in recent_block.txes.fee]
plt.hist(fees, bins=20, range=(0, 6000))
(array([   6.,    8.,   20.,  217.,  870.,  897.,  464.,  126.,   22.,
         137.,   39.,   21.,   54.,   11.,   12.,   21.,   18.,   14.,
          10.,    4.]),
 array([    0.,   300.,   600.,   900.,  1200.,  1500.,  1800.,  2100.,
         2400.,  2700.,  3000.,  3300.,  3600.,  3900.,  4200.,  4500.,
         4800.,  5100.,  5400.,  5700.,  6000.]),
 <a list of 20 Patch objects>)

output_9_1.png

トランザクションから手数料などを取り出してみる

all_tx = recent_block.txes.all
sample_tx = all_tx[1]
# https://blockchain.info/ja/tx/4fe8cca3318ac086eaf4dcb3958dbd1db324b446591fdb03dbec1342e664570c
print(sample_tx.hash)
fee = sample_tx.fee
input_value = sum(sample_tx.inputs.value)
output_value = sum(sample_tx.outputs.value)
print(fee, input_value, output_value)
print(input_value - output_value == fee)
converter.satoshi_to_currency(fee, recent_block.time.date())
4fe8cca3318ac086eaf4dcb3958dbd1db324b446591fdb03dbec1342e664570c
700000 832822188 832122188
True





120.2467175

最大の手数料を探す

tx = sorted(recent_block.txes.all, key=lambda tx: - tx.fee)[0]
tx.fee
10000000
# それぞれのブロックの最大の手数料を持つ TX のリスト
txes = [sorted(block.txes.all, key=lambda tx: - tx.fee)[0] for block in chain]
fees = [tx.fee for tx in txes]
df = pd.DataFrame({"Fees":fees})
ax = df.plot(legend=False)
ax.set_ylim(ymin=0)
plt.tight_layout()

output_15_0.png

# 最大の手数料を持つ TX
tx = sorted(txes, key=lambda tx: - tx.fee)[0]
print("Maximum fee is %s JPY" % (converter.satoshi_to_currency(tx.fee, recent_block.time) * USD_JPY_RATE))
print("hash is", tx.hash)
print("time is", tx.block.time)
Maximum fee is 560332195.6279321 JPY
hash is cc455ae816e6cdafdb58d54e35d4f46d860047458eacf1c7405dc634631c570d
time is 2016-04-26 14:15:22

I smell a "Where did my money go?" moment : Bitcoin

まあこんな感じで遊べるので, 楽しい。$0.532/hour 失いながら遊んでいきましょう。