BitcoinCash(BCH)の0承認についての検証
1. 序論
2017年8月1日にBitcoin (以下BTC) からハードフォークした BitcoinCash (以下BCH) は BTC の RBF (replace-by-fee) の機能を削除することにより、0承認を実現しようとしている。支払われた値が承認されるまでユーザが約10分間待たされるようでは、一般への普及は現実的ではないため、0承認は BCH のビジョンの Peer-to-Peer Electronic Cash[1] になるための非常に重要な機能の一つである。
しかし、現時点において、BCHの0承認のセキュリティについては多くの議論が繰り広げられている途中である。これについて、参加している人のBCHへの理解度がまばらであるために、出処の不明確な仕様を元にした思想の押し付け合いのようなものになっていることをよく見かける。そこで、我々は0承認についての疑問を検証し、確かなエビデンスを残すことで、議論の生産性を高め、BCHコミュニティの健全な発展の手助けになりたいと考えた。
1.1 RBFと0承認
上で述べたようにBCH には BTC に存在する RBF (replace-by-fee) の機能がない。RBF を簡単に説明すると、アドレスや値に誤りがあるトランザクションを送ってしまっても、以前のトランザクションより手数料を高く設定し再送することで、トランザクションが上書きされるというものである。これにより、BTC はトランザクションがブロックに取り込まれるまでの間であれば、トランザクションのキャンセルができる。これは、ブロックに取り込まれることをデータのコミットとするブロックチェーンの仕様を利用した良い機能である。しかし、ブロックに取り込まれないことには取引が完了しないので送金の即時性は失われることになる。
一方、BCH ではこの BRF を削除し、トランザクションがブロードキャストされた時点でコミットされたこととする0承認が可能であるとした。もっとも、ブロックチェーンである以上、最終的にブロックに取り込まれなければならないので、ブロックサイズを大幅に拡大することで全てのトランザクションが確実にブロックに取り込まれることを前提とした仕様である。
1.2 0承認の疑問点
本記事では、0承認に関する議論でよく挙げられる以下の5点に関して述べる。
- BCHネットワークにおいて、2重支払いが起きているのかどうかを検証する
- 実際に2重支払いが起きていた場合、何が原因でそれが起きたのかを検証する
- 0承認とトランザクション手数料の関係性について検証する
- 0承認とマイニングの関係性について検証する
- 2重支払いの実現可能性について考察する
2. 検証
以下に今回の検証に使用した環境を示す。
- OS: Ubuntu 16.04
- デーモン: Bitcoin ABC:0.17.1
- ネットワーク: testnet3
2.1 2重支払いの確認
0承認が可能とされるBCHにおいて2重支払いは起きていないのかどうかを検証する。
そのためには、0承認における2重支払いとはどういうことなのか理解する必要がある。図1に2重支払いの概略図を示す。
![double_spend1.png](https://steemitimages.com/DQmaRzD4QsZUPHu7YtZTEXzqCEhzW4afrZKBq3yJqsaTC6J/double_spend1.png)
図1には3つの登場人物がいる。それぞれユーザ、ショップ、マイナーとする。まず(図1-①)、ユーザはお店に代金としていくらかのBCHを支払うトランザクションAを作成し、それをショップのノードに送信する。ショップはトランザクションAを受取、その取引を承認する。次に(図1-②)、ユーザはトランザクションAの input
に含まれる UTXO
と同じ UTXO
を input
に含むトランザクションBを作成し、Minerに送信する。マイナーはトランザクションBをブロックに取り込み、ブロックをブロードキャストする。最後に(図1-③)、そのブロックを受け取ったショップは承認したはずのトランザクションAが消滅し、トランザクションBに置き換わったように見える。これが、0承認における2重支払いである。
本検証では、2重支払いを確認するために次に示すBCHの2重支払いのログを表示するWebサービスを用いた。
[2] を見る限り、4月には最低28回の2重支払いがあったことがわかる。BitInfoCharts から4月のトランザクション数を算出してみると約525kトランザクションであった。したがって、19kトランザクションに1回の確率で2重支払いが起きていることになる。クレジットカードなどの既存の決済システムのトランザクションの失敗率などが不明なため比較できないが、決済プロトコルに要求される最低限の信頼性について一度考える必要がある。
このことから、BCHは0承認における2重支払いが存在するということが分かった。
2.2 2重支払いの原因
2.1 にて、2重支払いが起こることは確認できたが、なぜこのようなことが起きるのか検証をする。まず、2重支払いの原因に関する記事として、以下の2つを参照されたい。
- Double Spending a 0-Conf Bitcoin Transaction [3]
- Why is this transaction won't be propagated to all nodes? [4]
これらの記事に共通していえるのは、あるトランザクションを送信したときに他ノードに伝播されるのを確認できなかったが、最終的にブロックに入ったということである。 [3] では 2.1 で説明した2重支払いがまさに起こっている。原因として考えられるのは [4] のコメントにある、「一定の手数料以下 (デフォルトでは 1satoshi/byte
) のトランザクションは伝播されない」である。以下 satoshi/byte
は s/b
とする。
では、なぜこの仕様が2重支払いの原因となってしまうのかを説明する。図2, 3, 4, 5に2重支払いが起きるときのネットワークの状態を示す。このネットワークには伝播させるトランザクションの最低手数料設定 (minimum relay transaction fee) が異なったノードが存在しているおり、ショップ、マイナーAが 1s/b
、ユーザ、ノードB、マイナーBが 0.1s/b
と設定しているとする。また、マイナーAは 1s/b
以上、マイナーBは 0.1s/b
以上のトランザクションを mempool
からブロックに取り込むものとする。
まず(図2を参照)、ユーザは手数料が 0.1s/ b
のトランザクションAをブロードキャストする。このトランザクションAが伝播されるのは最低手数料を 0.1s/b
に設定しているノードB、マイナーBだけである。したがって、ノードBとマイナーBのmempoolにはトランザクションAが存在し、ショップ、マイナーAのmempoolにはトランザクションAが存在しない状態となる。
![double_spend2-1.png](https://steemitimages.com/640x0/https://steemitimages.com/DQmZCoiLrjFyG24Bua6fgTo4tQQhwcNoUExpgvLWJ36ZjAB/double_spend2-1.png)
次に(図3を参照)、ユーザはトランザクションAと同じ UTXO
を input
に含み、手数料を 1s/b
に引き上げたトランザクションBをブロードキャストする。このトランザクションBは全てのノードの最低手数料設定を上回るため、ネットワーク全体に伝播されることが可能である。しかし、ノードBとマイナーBにはすでに同じ UTXO
をもったトランザクションAが mempool
に存在するため、これらのノードからは、トランザクションBは棄却されてしまう。したがって、ショップとマイナーAの mempool
にはトランザクションBが、ノードBとマイナーBの mempool
にはトランザクションAが存在する状態となる。これはネットワークが最低手数料設定によって分断されたような形になっているといえるだろう。
![double_spend2-2.png](https://steemitimages.com/DQmUqM8KczEqi6SfX9KxHzUp1UBsXpJKHTYpLHedckAnE6p/double_spend2-2.png)
ここで、マイナーBがマイナーAよりも先にブロックのマイニングに成功すると(図4)、そのブロックは最低手数料設定に関係なく全てのノードに伝搬される。
![double_spend2-3.png](https://steemitimages.com/640x0/https://steemitimages.com/DQmR8xK8AmaHacGrvJtqhgksxk52pbKni45EAkiQbEcrVQ1/double_spend2-3.png)
最終的に(図5)、ブロックが全てのノードのチェーンに取り込まれ、ショップ、マイナーAのmempoolに存在していたトランザクションBは消滅する。
![double_spend2-4.png](https://steemitimages.com/DQmR69AyfDdKLjDJnVVi7biTFAVppvTKEAAk9EhYjcXCaXj/double_spend2-4.png)
以上が最低手数料設定の仕様が2重支払いを引き起こす原因となるロジックである。
2.3 0承認とトランザクション手数料の関係性
ここで新たな疑問が生まれた。それはノードが最低手数料の設定によってどのような挙動を示すのかということだ。そこで、実際にノードを用意し検証してみることにした。用意したノードは2つでそれぞれをノードA、ノードBとする。まずは、両ノードとも最低手数料設定である -minrelaytxfee
オプションをデフォルトの 0.00001
(1s/b) とした。また、ノードAとノードBが確実にP2P接続されるように、-addnode
オプションでお互いのノードを指定した。さらに送金の前準備として Bitcoin Cash Testnet Faucet より、13BCHを引き出した。
2.3.1 手数料: 0satoshi/byte
まず、手数料を0satoshiとしてノードAに sendrawtransaction
でトランザクションを送ってみるとリクエストは成功し、トランザクションハッシュとして 65f02468ef35297bfa43f3b341d5f4d83b122d0bc700ac8646c7dab8906336a4
が返ってきた。さらに、ノードAに対してgetrawtransaction
で先のハッシュを取得すると正しいトランザクションデータが得られたので、ノードAの mempool
には、手数料が0satoshiのトランザクションが存在していることがわかる。以下にそのトランザクションデータを示す。
RawTransaction: 010000000132e342de2b36d684faae5f6ae5149e392692f7651c55c7ed7408e2f89ea9bc92000000006a473044022023f990ddd8a171169f4dff082c09eae867d2697c52a22aeedfff4aceaba2f04c0220550d039f6377fd82081a59b7e04566d38b994e19c38bde76ab7c7afacc3223a34121025b2c1ea20968be0d04d66186a34e6537355652ca6a9c5a5cea1e05adfd517688ffffffff01006d7c4d000000001976a914fcf1b5f4847caf32e3d1471a22bc9500b6ae7cc888ac00000000
30分後、このトランザクションを各ノードに問い合わせてみると、ノードAではリクエストに成功するが、ノードBでは以下のようなエラーとなった。
"No such mempool or blockchain transaction. Use gettransaction for wallet transactions."
![0sb.png](https://steemitimages.com/640x0/https://steemitimages.com/DQmTPvGJKn5iD2HdZAr2kT5x1Z3dndh6RhBuZ7Ch946viWL/0sb.png)
このことから、手数料が0satoshiのトランザクションはノードAの mempool
には存在するが、ノードBには伝播されず、ブロックにも取り込まれていないことがわかる。この状態が起こる原因として、以下が考えられる。
- 最低手数料が0satoshiのマイナーがいない
- 最低手数料が0satoshiのマイナーのハッシュレートが低い
- トランザクションが最低手数料が0satoshiのマイナーに届かなかった
- ノードAの接続先が、最低手数料が0satoshiのマイナーに接続されていなかった
これらの場合、このトランザクションがブロックに取り込まれることはないのでノードAの mempool
に残り続けることになる。これでは input
に含まれる UTXO
はロックされたと同じことになってしまう。これを解消するには、手数料をマイナーの最低手数料以上に上げて、ノードA以外のノードにトランザクションを送信する必要があるが、ノードを選択できないウォレットではこれを行うのは難しいだろう。ただし、ライブネットで検証した結果では、送信先ノードの最低手数料以下のトランザクションの場合エラーとなったので大きな問題ではなさそうである。ノードのプログラムには、このようなテストネットとライブネットで仕様が違う点がいくつか存在するため、把握せず開発を進めると思わぬバグを生む可能性があるので注意が必要だ。
2.3.2 手数料: 1satoshi/byte
次に、デフォルトの最低手数料である 1s/b
のトランザクションの場合を検証した。satoshi/byte
の単位から分かる通り、手数料分のsatoshiをトランザクションのバイトサイズで割ったものが基準となるため、トランザクションサイズ 192byte
に対して 192satoshi
を手数料とした。おおよそのトランザクションのサイズは以下の式で求められる。
148 * input数 + 34 * output数 ± 10
この式についての説明は本記事では割愛するが、トランザクションのデータ構造を知っていれば簡単に理解できるので、気になった方は調べてみてほしい。
この手数料が 1s/b
のトランザクションをノードAに送信してみるとトランザクションハッシュ 375e68bca5ae3803ada0c9dfa2ca962c56f5b234bb479f5c1eff5a3298e0d68f
が得られ、ノードAに対してこのハッシュによるgetrawtransaction
が成功した。その後、同ハッシュでノードBに対しての getrawtransaction
も成功したので正しくトランザクションが伝播されている。
# getrawtransactionのレスポンス
# ノードA
{"result":"0100000001712efe66381cec7c46a4539ab52dd9a8ed5cb239fe4d4828e0901abfb984733d000000006b483045022100f19914d637fbf8de663eb29f599de3421973be71f0978c241618f42e056cd92102200567838f42f028f3ea2bbc0df4610172bafd35199537c8fbbfb581dbb140ee30412103e12dda97990ced7f0e0b6966fca82f79fb3fda88e38d50697e916fccf2f40759ffffffff01fc687c4d000000001976a914f8ba5954cde0b3d9e65d2c6561b772ac3b69ecb188ac00000000","error":null,"id":"97588544-72b7-4824-857e-4823247c9375"}
# ノードB
{"result":"0100000001712efe66381cec7c46a4539ab52dd9a8ed5cb239fe4d4828e0901abfb984733d000000006b483045022100f19914d637fbf8de663eb29f599de3421973be71f0978c241618f42e056cd92102200567838f42f028f3ea2bbc0df4610172bafd35199537c8fbbfb581dbb140ee30412103e12dda97990ced7f0e0b6966fca82f79fb3fda88e38d50697e916fccf2f40759ffffffff01fc687c4d000000001976a914f8ba5954cde0b3d9e65d2c6561b772ac3b69ecb188ac00000000","error":null,"id":"48784a9d-3a18-4882-b12f-13ab91acc3bf"}
![1sb (1).png](https://steemitimages.com/DQmW2BiqxFYYrPKCvmDQnZJJHewkAV3HsNv5taVSG4fzD16/1sb%20(1).png)
2.3.3 手数料: 0.99satoshi/byte
0s/b
と 1s/b
の挙動は確認できたので、次は0以上1未満の場合も検証してみる。サイズ192byte
に対して 191satoshi
を手数料をとした 0.995s/b
のトランザクションを作成した。これも上記と同様にノードAに送信し、ノードBで確認できるか試したところ 0satoshi/byte
の結果と同じになった。
# ノードAへのsendtransaction
{ result: '01000000018fd6e098325aff1e5c9f47bb34b2f5562c96caa2dfc9a0ad0338aea5bc685e37000000006b483045022100be1da4b299a7fd9a19c2823f4eb398b63f38271b31e07e9a1a8b6e942a4138b4022031fb218c777cf49cdf53323c2e25fd8a2fd45837a831ff0444ce069dc81758a7412103e12dda97990ced7f0e0b6966fca82f79fb3fda88e38d50697e916fccf2f40759ffffffff013d687c4d000000001976a914f8ba5954cde0b3d9e65d2c6561b772ac3b69ecb188ac00000000',
error: null,
id: '4ef0d9e1-ed9f-489b-80f1-855442115632' }
# ノードBへのgetrawtransaction
{"result":null,"error":{"code":-5,"message":"No such mempool or blockchain transaction. Use gettransaction for wallet transactions."},"id":"930f56e1-dcfb-4507-a8bb-269244f951c3"}
この状態でノードBに対して手数料を 1s/b
に引き上げて送信すると成功となり、トランザクションハッシュが得られた。このトランザクションをノードAに問い合わせると、エラーとなった。これは、RBFの機能がないために、Bに送信したトランザクションはノードAで棄却されるからである。ノードAには 0.99s/b
トランザクションが、ノードBには 1s/b
のトランザクションが存在しており、図3と類似した状態となっている。ここで、2.3.1 の結果と同様に 0.995s/b
のトランザクションはブロックに取り込まれず、後から送信した 1s/b
のトランザクションがブロックに取り込まれた。その後、0.995s/b
のトランザクションを両ノードに問い合わせるとどちらもエラーとなったため、ブロックに取り込まれなかった方が正しく mempool
から削除されたことが確認できた。つまり、2重支払いを意図的に起こせたことになる。
# ノードA
{"result":null,"error":{"code":-5,"message":"No such mempool or blockchain transaction. Use gettransaction for wallet transactions."},"id":"239b2753-95c3-4eaf-9cef-95420a6726e3"}
# ノードB
{"result":null,"error":{"code":-5,"message":"No such mempool or blockchain transaction. Use gettransaction for wallet transactions."},"id":"6b3af65c-72f3-4914-9a68-032ad9849774"}
![0995sb (1).png](https://steemitimages.com/640x0/https://steemitimages.com/DQmPkxbLJ3R2EvQ5cFeUrzuFdVJQd67nyjgSKA3WuiRuieL/0995sb%20(1).png)
2.3.4 トランザクションレース
ここで気になるのが、図3-2のようにネットワーク上に同じinputを持つ複数のトランザクションが存在していた場合どちらが早く伝播されるのかである。これを検証するためにノードAの最低手数料を 0.1s/b
に設定し、 0.1s/b
のトランザクションを送信する。続いて、ノードBは最低手数料はデフォルトの 1s/b
に設定し、 1s/b
のトランザクションを送信する。そして、ブロックが取り込まれる前にノードBの最低手数料を 0.1s/b
に設定し、再起動することでノードBに 0.1/sb
と 1s/b
の両方が送られてくる状態を用意した。結果としては、10回中10回が 1s/b
のトランザクションが先に mempool
に入り、 0.1s/b
のトランザクションは観測できなかった。この結果については、 bootnode
に設定されているノードが 1s/b
であるため、ノードBに接続される前に 1s/b
のトランザクションが到達していると考えられる。特定のノードだけに接続されるようになる -connect
オプションを使って自前のノードだけで試してみると、先に接続されたノードが持つトランザクションが毎回 mempool
に入ったのでこの予測は正しかったといえる。以上のことを踏まえると、ネットワークを構成しているノードの最低手数料設定が伝播の速度に関係していると考えられる。つまり、ネットワーク内にどれだけ低い手数料を許容するノードが存在しているかが重要になってくるのである。
![race.png](https://steemitimages.com/DQmdNYykdWwYKoShg3Y1Rv7qgG113Y2ehCaq1pw1oJn6Pnr/race.png)
2.4 0承認とマイニングの関係性
0承認の理論的には、マイニングに関係なくトランザクションがそのノードで観測できた時点で承認となる。ただし、これは送信したトランザクションがもれなく全てブロックの入ることを前提としている。そのために、BCHはブロックサイズを大幅に拡大しており、それによって手数料が非常に低くなっている。 しかし、手数料が低いということはそれだけたくさんのトランザクションを送ることができるということだ。つまり、DOSアタックが低リスクで行えてしまう。そこで、DOSアタックを実際に行ってみて、BCHネットワークがどのような挙動をするのか検証してみた。検証方法は、あるトランザクションからUTXOを次々とチェインする形でトランザクションを作成し、それを一気にノードAに対して送信した。その結果を以下に示す。
No.1 : 1299988220 -> 1299988028 (fee: 192)
8b00931a95fd880cf2daa007ae355e4028c008279f295f0c7385a26ce5180b64
No.2 : 1299988028 -> 1299987836 (fee: 192)
b02dd75db57ea98349aa26da2250ab1a86c13faa522347bdcda6ff71484b43d6
No.3 : 1299987836 -> 1299987644 (fee: 192)
1f0e3269f6fa2c4f0d83f82a57cfdc691aabb1bc651acd322b6b809e6b69926a
No.4 : 1299987644 -> 1299987452 (fee: 192)
36277ae693b76ffedc1675273292556441405e5ff648d51548371de6bd259c35
No.5 : 1299987452 -> 1299987260 (fee: 192)
d44784bbeba0ce5bb795e362b16f139bfc8e38f48c91805dd40ba779765314e6
No.6 : 1299987260 -> 1299987068 (fee: 192)
a25869729031b7375a6ba36c3d2d7161970ab7796cd74c6160845710601b2dac
No.7 : 1299987068 -> 1299986876 (fee: 192)
2dbb164454e34c1ffe4805ed4534092e1894c3d4d021a03500b4c36779b01d78
No.8 : 1299986876 -> 1299986684 (fee: 192)
2268c09f54fb0891e3d73eb860d5aef867395a41020188ee6c220e7c7da21b0d
No.9 : 1299986684 -> 1299986492 (fee: 192)
40b475df74bec109b61f26ca77fb15b1b5c41c5507f51262c7924ead4dcbd65d
No.10 : 1299986492 -> 1299986300 (fee: 192)
5ae830205a71bcb44c0fca2a362281812e57c19c4272fc956ab9de315bcb2fac
No.11 : 1299986300 -> 1299986108 (fee: 192)
65071be3b2f596fdb96b4b3e6af9b2043dedc6e838bdc6d720f25caf3335f29e
No.12 : 1299986108 -> 1299985916 (fee: 192)
945df80d3473a886d1c1e8068611482f6719afcf33204d31b0ae637fffc8885c
No.13 : 1299985916 -> 1299985724 (fee: 192)
b99f87197b4c40dd8b13bdc5e501c95c74b60c143883bf85eb157de03e4f585e
No.14 : 1299985724 -> 1299985532 (fee: 192)
f6c8d067b10700c03d2a661e6f1920e40bace43147a1e30730b1f4611518f273
No.15 : 1299985532 -> 1299985340 (fee: 192)
64e393255ad6d309b0ddc88880d932252a3442589fe76928c66d73e1c4602626
No.16 : 1299985340 -> 1299985148 (fee: 192)
85337c90863ceaaef1cf0eb37bf76090c7622eaf16d986079bf337a5dd4cf2b0
No.17 : 1299985148 -> 1299984956 (fee: 192)
20f855ccf0eb1b5fee04e827a6d73b16216837419aef8bb73fbd93ce69570983
No.18 : 1299984956 -> 1299984764 (fee: 192)
0e12ac930b772f7407732a0aa181d7dcabc268889057246dc04d73f54fbe8b5f
No.19 : 1299984764 -> 1299984572 (fee: 192)
33c72b114cf7b845d928f854899a2f61870359cc870c6f970a834d1bad615bbd
No.20 : 1299984572 -> 1299984380 (fee: 192)
fe40a18a0ad1e607c8de741a26a9e8010d9826d09924f61521b3b24318a87268
No.21 : 1299984380 -> 1299984188 (fee: 192)
9867c2e852c8b4b0d9d725af6ad3acb91be02d2a8354e9b09c3ea07eb5bbd5cc
No.22 : 1299984188 -> 1299983996 (fee: 192)
9908c180688f0f34d9d9ac3e9e5c0711edc018b817c633efc274c1b3c6602e08
No.23 : 1299983996 -> 1299983804 (fee: 192)
b69fe1eaaec2c093ad6f1a1c6d255baf5bd87979793f3c7dfe1ae29f11401268
No.24 : 1299983804 -> 1299983612 (fee: 192)
62ddcbf9b2107e7cb40d68302ecc1907d4fd623a8e044b981599eaa337c14432
No.25 : 1299983612 -> 1299983420 (fee: 192)
655446acae704b1f6c98540492a41833d1cfccda1d2801e734abcdb421a603dd
No.26 : 1299983420 -> 1299983228 (fee: 192)
{"result":null,"error":{"code":-26,"message":"64: too-long-mempool-chain"},"id":"86ee6b7c-8284-477d-819a-1cef8e6ab5f6"}
この結果を見ると25個目までは順調に送信できたが、26個目で too-long-mempool-chan
というエラーが出た。調べてみると以下のソースを見つけた。
# bitcoin-abc/src/validation.h:69
/** Default for -limitancestorcount, max number of in-mempool ancestors */
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
# bitcoin-abc/src/validation.cpp:981
if (!pool.CalculateMemPoolAncestors(
entry, setAncestors, nLimitAncestors, nLimitAncestorSize,
nLimitDescendants, nLimitDescendantSize, errString)) {
return state.DoS(0, false, REJECT_NONSTANDARD,
"too-long-mempool-chain", false, errString);
}
これを見る限り、トランザクションのバリデーションで mempool
に存在する祖先トランザクションが25個以上の場合はDOSと判断され、エラーを返すようになっているようだ。-limitancestorcount
オプションでこれを変更できるようなので指定してみたが、我々の環境では25からは変更されなかった。ヘルプにも存在しないので消えたオプションなのだろうか。自前のノードだけ変更できたとしてもネットワーク全体がこの制限を一斉に上げない限り有効性は低いため、現状ではトランザクションをチェインした0承認はブロックに取り込まれなければ25回までしかできないということになる。したがって、0承認の理論的にはマイニングが関係ないはずではあるものの、現状のノードはDOS攻撃に備えた実装になっているため、注意が必要である。
BCHでは、マイナーによって1時間以上ブロックが採掘されないことが発生することもあり、大量のトランザクションを一定時間に集中して送るようなケースでは、この仕様を理解しておく必要があるだろう。
![dos.png](https://steemitimages.com/640x0/https://steemitimages.com/DQmekeuWzZzYCg96HB7e4uj1WAheCYPs1hBe9VwPB6GgLk6/dos.png)
2.5 2重支払いの実現可能性
上記の検証で意図的に2重支払いを引き起こすことはできたが、全てテストネットによるものである。ここからは、ライブネットで2重支払いを意図的に行えるのかどうか検証する。まず重要なのはデフォルトである最低手数料の 1s/b
以下のトランザクションを送信したときに、それがマイニングされるかどうかである。実際にライブネットで手数料を 0.95s/b
に設定して送信すると、約2時間でマイニングされた。以下にトランザクション詳細を示す。
これより、デフォルトの 1s/b
以下の手数料でもブロックに取り込むマイナーがいることが確認できた。つまり、ライブネットでも2重支払いができる可能性があるということだ。また、このトランザクションはブロックに取り込まれるまでは主要なブロックエクスプローラーでは観測できなかったため、これらのノードの最低手数料設定は 1s/b
だということが考えられる。2重支払いの成功率は 1s/b
以下のトランザクションを受け付けるマイナーが占めるハッシュレートに依存するため、1s/b
未満のトランザクションが含まれるブロックのマイナーを調べた。確認できたのは AntPool と Bitcoin.com の2つである。Pool Stats によるとAntPoolが11.17%、Bitcoin.comが8.36%なので約20%の確率で 1s/b
未満のトランザクションがブロックに取り込まれる。つまり、1s/b
未満のトランザクションを先に送信しておいて、決済では 1s/b
以上のトランザクションを送信すれば、約20%の確率で2重支払いを意図的に引き起こせてしまうことになる。[2] の記事内では、2重支払いには$60,000 (算出方法は不明) のコストがかかると書かれているが、実際にはもっと低いコストで2重支払いを起こせるのではないだろうか。
3. 結論
今回行った検証によってBCHの0承認では2重支払いが実際に起こっており、その原因がノードの最低手数料設定によって伝播されないトランザクションによるものであることが分かった。また、BCHネットワークの状況によっては意図的に2重支払いを起こせる可能性があることが分かった。
0承認は非常に便利なものであるが、マイナーやノードのプロトコルがまだ0承認に対応していないことを考えると、現状では少額 (悪事をはたらくインセンティブが無くなる程度) に留めておくのが良いだろう。そもそも0承認は受け取り手が許容するかどうかの話でもあるため、セキュリティと利便性をトレードオフできる今のファジーな仕様はある意味自立分散的なのかもしれないが、BCHの普及を考えるとしっかりとプロトコルを定めた上で、ハードフォークアップデートする必要があるだろう。
4. 最後に
BCHの0承認の使用感は素晴らしいものであり、慣れてしまうと承認時間が短いEthereumですら使うのを躊躇ってしまうほどである。今回のハードフォークによってオペコードが追加されたこともあり、更に使い勝手の良い通貨になると我々は期待しているため、今後も調査と開発を続けていきたいと考えている。
Congratulations @morinikiz! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Do not miss the last post from @steemitboard:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!