ERC20トークンは送金(transfer / transferFrom関数が呼ばれる)とevent Transferによってログが発行されます。ログは、Bloomフィルタという構造で格納され、ブロックチェーン自体には刻まれませんが、トランザクションを受け取った相手から受領するTransaction Receiptに刻まれます。
また、Transaction Receiptのハッシュ値自体はブロック内に含まれますので、ログも検証されることになります。ノードに備わるeth_getLogsという機能を用いると、このログを抽出することができます。
ログを抽出して、取引をネットワーク図にしてみます。
eth_getLogs関数について
eth_getLogsは、Bloomフィルタオブジェクトを引数にして、イベントログを抽出します。Bloomフィルタというと難しそうですが、要は抽出条件を設定するということです。
Bloomフィルタオブジェクトの具体的なパラメータはこちらになります。検索範囲、やログを出したアドレス、対象イベントなどを設定します。
名前 | 説明 |
---|---|
fromBlock | 検索範囲ブロック(開始) |
toBlock | 検索範囲ブロック(終点) |
address | 検索条件アドレス |
topics | 対象となるイベントのハッシュ値(※) |
limit | 返却する検索結果上限 |
※イベント名、引数の型をsha3(keccak)-256にかけて計算して求めます。 Deep dive into Ethereum logs – codeburstから引用して、
To find out which event the topic refers to we need to compute a signature of each event and find the matching one. Signature is a sha3 hash of event name and input argument types,
で、今回のTransferイベントの例だと、
sha3('Transfer(address,address,uint256)') ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
となります。
JSON-RPCの定義(parity)はこちらをどうぞ
parityノードからERC20トークンのイベントログを抽出
EthereumのノードからERC20トークンのイベントログを抽出します。
今回はベータ版が公開されたALISのALISトークンを対象にしてみます。引数は以下の通りです。
- fromBlock 設定なし
- toBlock latest
- address 0xea610b1153477720748dc13ed378003941d84fab(ALISのコントラクトアドレス
- topics 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef(Transfer eventのハッシュ値)
- limit 設定なし
今回はparityでノードを立てています。parityノードの立て方はこちら
「eth_parity_node」という名前でDockerコンテナを立てています。
#dockerコンテナ内にbashで入る docker exec -it eth_parity_node bash pwd /build #イベントログをjsonrpcで抽出する curl 'http://127.0.0.1:8545' -H 'Content-Type: application/json' -H 'Accept: application/json' --data-binary '{"id":1,"jsonrpc":"2.0","params":[{"toBlock":"latest","address":"0xea610b1153477720748dc13ed378003941d84fab","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"]}],"method":"eth_getLogs"}' > event_logs.json #コンテナから出る exit #コンテナ内のevent_logs.jsonをホストにコピー docker cp eth_parity_node:/build/event_logs.json .
今回抽出したイベントログですが、直近のブロック分しか抽出できていません。おそらく、parityの起動時に古いtransaction receiptも保持するように設定すれば古いイベントログも網羅的に抽出できると思われます。 (原理的には、現在のブロックチェーンから再計算することもできるはずですが、大変なので今回は見送ります)
イベントログを加工
抽出したイベントログ「event_logs.json」を加工して必要なデータだけ抽出します。
# coding: utf-8 import json import pandas as pd def extract_vals(results): ''' results:イベントログのresult項目 ''' ret = [] for result in results: tmp = [] tmp.append(result['transactionHash']) tmp.append(int(result['blockNumber'],16)) tmp.append(hex(int(result['topics'][1],16))) # from_address 0x0...と0が続くのであえて一度hex変換でフォーマットしている tmp.append(hex(int(result['topics'][2],16))) # to_address tmp.append(int(result['data'],16) / (10**18)) # value ALIS は18桁なので10の18乗で割る ret.append(tmp) return ret def main(): # イベントログファイルの読込 f = open('event_logs.json', 'r') json_data = json.load(f) f.close() # トランザクションに加工 txs = extract_vals(json_data['result']) df = pd.DataFrame(txs,columns=['transactionHash','blockNumber','from','to','data']) df.to_csv('txs.csv',index=False) if __name__ == '__main__': main()
加工後のデータ「txs.csv」の一部です。 左からトランザクションのハッシュ値、ブロック番号、送信元アドレス、送信先アドレス、ALIS金額になっていることが確認できます。
ネットワーク図で可視化
Cytoscapeを使って可視化してみます。GUIで簡単にcsvファイルなどからネットワーク図を作成できます。(APIもあります)
まずは、csvファイルのインポート
インポート時に、ソースとする項目、ターゲットとする項目をそれぞれ設定します。
そうするとこのようなネットワーク図ができます。 アドレスがノード(丸)で、トランザクションがエッジ(線)です。
少し調整して、取引が多いアドレスを大きくします。
一番大きいアドレスが出てきたので、こちらをEtherscanで調べてみます。
どうやらCoinExchangeのアドレスのようです。ALISはこちらの取引所で上場しています。
CoinExchangeでは、このアドレスを通じて入出金や取引がされているようです。
ERC20トークンのログは簡単に抽出、取引の動きも簡単な分析ならできそうなことがわかりました。
今回抽出したALISトークンのログは、本当にごく一部の直近トランザクションです。全てのトランザクションを抽出するともっと広大なネットワーク図になるはずです。
関連
今回実験したソースコード
ERC20の定義を過去にまとめた記事