読者です 読者をやめる 読者になる 読者になる

Tech と Culture

テクノロジーとカルチャー

翻訳:Satoshiクライアントのノード発見

Bitcoindのノード発見の部分を勉強したいと思ったので、以下のページを翻訳してみた。少し情報が古いような気がするけど、各自自己責任でお願いします。
Satoshiクライアントのノード発見
 
1 概要
2 "getaddr"メッセージの取り扱い
3 発見方法
  3.1 ローカルクライアントの外部アドレス
  3.2 コールバックアドレスへの接続
  3.3 IRC アドレス
  3.4 DNS アドレス
  3.5 ハードコードされた "seed""アドレス
  3.6 "addr"アドバタイズメント
       3.6.1  アドレスリレー
       3.6.2   セルフブロードキャスト
       3.6.3   古いアドレスのクリーンアップ
  3.7 データベースに保持されたアドレス
  3.8 コマンドラインで与えられるアドレス
  3.9 テキストファイルで与えられるアドレス
4 他の情報
5 リファレンス
 
1 概要
Satoshiクライアントは数種類の異なる方法で、IPアドレスとノードのポートの発見を行います
1 ノードは様々な方法で自身の外部アドレスを発見する
2 ノードは接続する外部ノードのコールバックアドレスを受信する
3 ノードはIPアドレスを受信するためDNSリクエストを作る
4 ノードはソフトウェアにハードコードされたアドレスを使うことができる
5 ノードは他のノードとアドレス交換をする
6 ノードはデータベースにアドレスをストアし、スタートアップ時にそのデータベースを読む
7 ノードはコマンドラインの引数でアドレスを与えることができる
8 ノードはスタートアップ時にユーザーが与えるテキストファイルからアドレスを読む
 
Timestampはそれぞれのアドレスに対していつそのノードアドレスが最後に確認されたかを記録するために保持される。net.cpp中のAddressCurrentlyConnectedはメッセージをノードから受信した時に必ずTimestampをアップデートする。Timestampは20分以上古くなった時にアップデートされデータベースに保持される
実際にノードに接続する時にどのタイプのアドレスが上位に来るのかについてはNode Connectivityの項目を参照。
最初の項では、”getaddr”メッセージを通して、ノードがどのようにアドレスのリクエストを取り扱うのかについて記す。Timestampの役割を理解することによって、アドレスが発見された方法によってTimestampがなぜそれぞれの方法で保持されるのかがよりクリアになる。
 
2 "getaddr"メッセージの取り扱い"
ノードが”getaddr”リクエストを受信した時に、最初に過去3時間のTimestampを持つアドレスをいくつもっているかをはっきりさせる。次に、それらのアドレスを送信する。しかし、もし3時間以内に2500以上のアドレスがあった場合は、ランダムな選択で約2500個のアドレスを選択する。
 
さあ、ノードがノードアドレスを見つける方法について見てみよう
 
3 発見方法
 3.1 ローカルクライアントの外部アドレス
クライアントは自身のルーティング可能なIPアドレス情報を返すパブリックなWEBサービスを使用する
クライアントはnet.cpp中のThreadGetMyExternalIPと呼ばれるスレッドを走らせる。そこでは外部世界から見えるクライアントのIPアドレスを決定する。
最初に91.198.22.70のポート80(checkip.dyndns.org)に接続を試みる。もし接続が失敗した場合、checkip.dyndns.orgへのDNSリクエストが行われ、そのアドレスへの接続を試みる。次に74.208.43.192のポート80(www.showmyip.com)に接続を試みる。接続に失敗した場合、www.showmyip.comへのDNSリクエストが行われ、そのアドレスへの接続を試みる。
上記で試みられたそれぞれのアドレスに対して、クライアントは接続を試み、HTTPリクエストを送り、適切なレスポンスをリードし、そこからIPアドレスをパースする。もしこれが成功した場合、IPアドレスが返ってくる。IPアドレスは接続しているどれかのノードにアドバタイズされ、スレッドは終了する(???)
 3.2 コールバックアドレスへの接続
ノードが最初の”version”メッセージを受信して、コネクションを開始した時に、ノードは自分のアドレスをリモートにアドバタイズし、リモートが望んだ時にローカルのノードに接続することができるようにします。
もしリモートのバージョンが最新のものであったり、ローカルのノードが1000個のアドレスをまだ持っていない場合、自分のアドレスを送信したのちに、”getaddr”リクエストメッセージをリモートノードに送り、多数のアドレスを得ます。
 3.3 IRCアドレス
バージョン0.6.xからBitcoinクライアントはデフォルトではIRCブートストラッピングは使わなくなっているし、バージョン0.8.2からはIRCブートストラッピングは完全に取り除かれている。以下のドキュメントは過去のバージョンに対する正確性のために記されている
自身のアドレスを知ったりシェアするために、ノードはたのノードをIRCチャンネルを通して他のノードアドレスを知ることができる。 irc.cpp参照。
自分自身のアドレスを知ったのち、ノードは自身のアドレスをニックネームとして使用するための文字列にエンコードします。そして、#bitcoin00と#bitcoin99の間のIRCチャンネルにランダムに参加します。そして、WHOコマンドを発行します。それらが現れたチャンネルのスレッドはラインを読むとチャンネル内の他のノードのIPアドレスをデコードします。ノードがシャットダウンされるまでそれは繰り返します。
クライアントはIRCからアドレスを発見した時に、現在時刻をTimestampとしてアドレスにセットします。ただし、その時にペナルティとして51分が課されます。それはあたかも一時間前に発見されたかのように見えます。
 3.4 DNS Address
スタートアップにおいて、ピアノードの発見が必要な場合、クライアントは他のピアノードのアドレスを知るためにDNSリクエストを発行します。クライアントはDNSサービスのためのシードとなるホストネームを含んでいます。2012,5月17日現在、リストは以下のものを含んでいます(chainparams.cppより)
 
DNS応答は要求された名前に対して複数IPアドレスを含むことができます。
DNSによって発見されたアドレスは最初にTimestampゼロが与えられています。ですので、getaddrリクエストに対する応答でアドバタイズはされません。
 
3.5 ハードコードされたSeedアドレス
クライアントはBitcoinのノードを示すハードコードされたIPアドレスを含んでいます。
これらのアドレスは、他の方法によってアドレスが全く得られなかった時にラストリゾートとしてのみ使われます。接続をハンドリングするThreadOpenConnection2()のスレッドのループでアドレスマップが空だった時、”seed”IPアドレスがバックアップとして使用されます。
可能な時にシードノードを取り除くコードがあります。これらのノードの負荷を下げるためです。ひとたびローカルノードが十分なアドレスを得た時(おそらくシードノードから情報を得た)、シードノードとのコネクションは閉じられます。
シードアドレスは最初はゼロのTimestampが与えられます、それゆえ、”getaddr”リクエストに対する応答ではアドバタイズされません。
 
3.6 addr"アドバタイズメント
getaddrリクエストを送った後にやってくるaddrメッセージによってノードはアドレスを受け取ります。または、ノードが無償でアドレスをリレーするときに要求なしにやってくるaddrメッセージによってノードはアドレスを受け取ります。
もし、アドレスが古いバージョンのリレーからのものであった場合は無視します。もし、そこまで古いバージョンでない場合、もし1000個のアドレスをすでに持っていたら無視します。
または、1000以上のアドレスが送られてきた時も全て無視されます。
addrメッセージで受信したアドレスはTimestampを持っていますが、Timestampを直接使用はしません。
 
メッセージの中のそれぞれのアドレスに対して
      1.もしTimestampが非常に小さかったり、大きかったりした場合、5日前に設定されます
      2.Timestampから2時間を差し引いて、アドレスを追加します。
いかなるアドレスが追加される時でも、いかなる理由によっても、コードはAddAddress()をコールして、すでにアドレスが存在しているかどうかはチェックしません。net.cpp内のAddAddress()関数がそれを実行します。すでにアドレスが存在していても、アドレスのレコードをアップデートします。もし、アドレスのアドバタイズされたサービスが変わっていても(?)、それがアップデートされストアされます。
もしアドレスが過去24時間以内に見られて、Timestampが60分以上古い場合、60分過去にアップデートされます。
もしアドレスが過去24時間以内に見られておらず、Timestampが24時間以上古い場合、24時間過去にアップデートされます。
 
 3.6.1 アドレスリレー
一度アドレスが”addr”メッセージ(上記参照)によって加えられたら、それらは他のノードにリレーされます。最初に以下のような基準が設定されます。
     1.処理が行われた後、アドレスTimestampが現在時刻から60分以内である
  1.       “addr”メッセージは10個、もしくはそれ以下のアドレスを含む
  2. fGetAddrがノードにセットされていない。fGetAddrはfalseからスタートし、ノードにアドレスをリクエストした時にtrueにセットされます。そして、1000以下のアドレスをノードから受け取ったときにクリアされます
  3. アドレスはルーティング可能でないといけない
上記の基準にマッチする全てのアドレスに対して、ノードはアドレスと現在の日にち(Integerで表される)と256bitの乱数(クライアントのスタートアップ時に生成される)をハッシュする。ノードは最もハッシュの小さい値のアドレスを二つ選び、”addr”メッセージをそれらにリレーする。これはそれぞれのノードが”addr”メッセージを与えられたタイミングで二つの他のノードのみにリレーすることを保証する。そしてその二つはランダムに選択されており、ランダムな選択は少なくとも24時間毎に行われることが保証される。
 3.6.2 セルフブロードキャスト
24時間毎に、ノードは自分自身のアドレスを接続している全てのノードにアドバタイズする。
これはまた、リモートノードが持っていると思われるアドレスのリストをクリアーし、ノードのリフレッシュのトリガーとなる(?)。このコードはmain.cppのSendMessages()の中にある。
 3.6.3 古いアドレスのクリーンアップ
main.cppの中のSendMessages()において、古いアドレスをリムーブするコードが存在している。
これは3つのアクティブなコネクションがある限り、10分毎に行われる。
ノードは1000アドレスがマップされ、20秒以上消去プロセスが行われていない限り、14日以上使われていないメッセージを消去する(?)。
 
3.7 データベースに保持されたアドレス
アドレスはAddAddress()がコールされた時にデータベースに保持される。
アドレスははスタートアップの時、AppInit2()がdb.cppに存在するLoadAddresses()をコールした際に読み出される。
現在は、どれか一つのアドレスがストアされるかアップデートされる時は必ず、全てのアドレスが一度に同時にストアされるようになっています。実際のところ、AddAddressはは様々なテストで、0.01秒ほど使用することが分かっており、クライアント実行の最初の12時間で典型的には数万回呼び出されます。
 
3.8 コマンドラインで与えられるアドレス
ユーザーは接続するノードを
-addnode <ip>
のようにコマンドラインの引数で与えることができます。複数のノードが指定できます。
コマンドラインで与えられるアドレスは当初はTimestampゼロが与えられます。それゆえ、”getaddr”リクエストに対する応答でアドバタイズされません。
ユーザは、-connect <ip>コマンドライン引数でも接続するアドレスを与えることができる。複数のノードが指定できます。
-connect引数と -addnode引数の違いは、-connectが指定されたとき、アドレスはアドレスデータベースへ追加されず、ただ単に使用されます。
 
3.9 テキストファイルで与えられるアドレス
クライアントは自動的に bitcoinデータディレクトリにある“addr.txt”という名前のファイルを読み、その中にある、ノードアドレスを追加します。これらのノードは他のアドレスと比較して何の特権もありません。ただ単にプールに追加されます。
テキストファイルからロードされるアドレスは最初にTimestampゼロが与えられます。それゆえ、”getaddr”リクエストに対する応答でアドバタイズされません。
 
  1. 他の参照
Network - Bootstrapping