Ethereumでセルフィッシュマイニングしてみた

f:id:simplex-blog:20190130150535j:plain

Ethereumでセルフィッシュマイニングしてみた

ローカル環境に構築したEthereumのプライベートチェーン上で、Selfish Mining (Block Withholding Attack) と呼ばれる手法によりブロックを Reorganization させてみました。

手順

  1. 2つのgethノードを起動しノード間を接続する (ノードAが通常側、ノードBが攻撃側とする)
  2. ブロックが同期できたら、ノード間を切断する
  3. ノードAにおいて、送金トランザクションを作成する
  4. ノードAにおいて、ファイナリティーに必要な承認数までブロックを追加する (仮に承認数を3とする)
  5. ノードBにおいて、ノードAを超えるようブロックを追加する
  6. ノード間を接続して同期させる
  7. ノード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後のブロック高)