Ethereumでセルフィッシュマイニングしてみた
Ethereumでセルフィッシュマイニングしてみた
ローカル環境に構築したEthereumのプライベートチェーン上で、Selfish Mining (Block Withholding Attack) と呼ばれる手法によりブロックを Reorganization させてみました。
手順
- 2つのgethノードを起動しノード間を接続する (ノードAが通常側、ノードBが攻撃側とする)
- ブロックが同期できたら、ノード間を切断する
- ノードAにおいて、送金トランザクションを作成する
- ノードAにおいて、ファイナリティーに必要な承認数までブロックを追加する (仮に承認数を3とする)
- ノードBにおいて、ノードAを超えるようブロックを追加する
- ノード間を接続して同期させる
- ノードAのブロックが置き替えられ、トランザクションが未承認となる
ブロックの置き替えを成功させるには
ノードBのtotalDifficultyがノードAを上回ることで、Reorg時にノードBのブロックが採用される。 ブロック高が同じでも置き替えは成功する。
ブロック⾼もtotalDifficultyも超えた状態
ノードA ノードB [100] [100] (同期された状態) [101/a] [101/b] [102/a] [102/b] [103/a] [103/b] [104/b] ※ 当該トランザクションは[101/a]に含まれ、必要な承認数を満たしている
現在の状態:
- ノードA のブロック高: 103
- ノードB のブロック高: 104
- totalDifficulty: ノードA < ノードB とする
ここで同期するとノードBのブロックが採用され下記の状態になる
ノードA ノードB [100] [100] [101/b] [101/b] [102/b] [102/b] [103/b] [103/b] [104/b] [104/b] (同期された状態) ※ [101/a]が無効となり、当該トランザクションはトランザクションプールに戻される
ブロック⾼は同じだがtotalDifficultyは超えている状態
ノードA ノードB [100] [100] (同期された状態) [101/a] [101/b] [102/a] [102/b] [103/a] [103/b] ※ 当該トランザクションは[101/a]に含まれ、必要な承認数を満たしている
現在の状態:
- ノードA のブロック高: 103
- ノードB のブロック高: 103
- totalDifficulty: ノードA < ノードB とする
ここで同期するとノードBのブロックが採用され下記の状態になる
ノードA ノードB [100] [100] [101/b] [101/b] [102/b] [102/b] [103/b] [103/b] (同期された状態) ※ [101/a]が無効となり、当該トランザクションはトランザクションプールに戻される
[Reorg時の出⼒]
Reorg時の挙動は下記の方法で確認できる。
geth の Log 出⼒:
Imported new chain segment, blocks=[XXX] number=[XXX] - Reorg後のブロック高と分岐以降の更新ブロック数が確認できる。
WebSocket による通知:
web3.eth.subscribe(‘newBlockHeaders’) - 追加ブロックのヘッダ情報が通知される。 - カレントブロックの更新および新たに追加されたブロックのみ。 ※ 旧カレントブロックより前のブロック更新は通知されない。 web3.eth.subscribe(‘syncing’) - Current Block: [XXX] (Reorg前のブロック高) - Highest Block: [XXX] (Reorg後のブロック高)
API による確認:
web3.eth.isSyncing() - Current Block: [XXX] (Reorg前のブロック高) - Highest Block: [XXX] (Reorg後のブロック高)