net_processing.cpp
語彙
ピア: 接続先の他ノードのこと
定数
CHAIN_SYNC_TIMEOUT
アウトバウンドで接続しているピアと chainwork を同期するためのタイムアウト時間。20分が指定されている。
MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT
定義
code: cpp
/** Protect at least this many outbound peers from disconnection due to slow/
* behind headers chain.
*/
static constexpr int32_t MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT = 4;
遅いまたは遅れているチェーンでも、少なくともこの数だけのアウトバウンドピアからの切断を保護する。
Struct
CNodeState
フィールド
const CBlockIndex *pindexBestKnownBlock;
このピアが自分が持ってるとアナウンスした最新ブロック
int64_t m_last_block_announcement;
最後に新しいブロック(自ノードの有効チェーンの先端ブロックよりも chainwork が大きいブロック)がアナウンスされた時間
uint256 hashLastUnknownBlock
このピアがアナウンスした最新の unknown ブロックのハッシュ(ここで言うunknownはどういう意味?)
INVメッセージでブロックのハッシュが送られてきた時に、そのブロックが mapBlockIndex にない場合にセットされる。
std::list<QueuedBlock> vBlocksInFlight;
ピアからダウンロード中のブロックを管理するキューだと思います(汗
int64_t nDownloadingSince;
vBLocksInFlight に入っているブロックの内、最初のものをダウンロードし始めた時間。
int nBlocksInFlight;
ダウンロード対象のキューに入れたブロックの数
int nBlocksInFlightValidHeaders;
ダウンロード対象のキューに入れたブロックの内、valid なヘッダーを持つブロックの数
int64_t nHeadersSyncTimeout;
ヘッダーのダウンロードが遅れた時に、ピアとの接続を切る可能性が出るタイムアウト時間
CNodeState::ChainSyncTimeoutState
ドキュメントの訳
CHAIN_SYNC_TIMEOUT によるピアとの切断の制御を実現するためのステートを保持する構造体です。アウトバウンドで non-manual connection で、 m_protect == false の場合に有効です。
アルゴリズム: もしピアについてわかっている最新ブロックが、自ノードの先端ブロックよりも chainwork が小さい場合、CHAIN_SYNC_TIMEOUT 秒後にタイムアウトするように設定します。
もしタイムアウトになった時に、ピアノ最新ブロックがその時点で、タイムアウトをセットしたときの自ノードのチップよりも大きな chainwork をもつ場合、タイムアウトをリセットするか、クリアします。(自ノードの現在の先端ブロックのchainwork に対して比較した上で)
タイムアウトした時、ピアの最新ブロックがまだタイムアウトがセットされた時の自ノードの先端ブロックより低い chainwork だった場合、getheaders メッセージを送り、より短いタイムアウト(HEADERS_RESPONSE_TIME)をセットする。さらにタイムアウトに到達した時、ピアの最新ブロックがまだ遅れていた時、切断する。
m_timeout:
ピアが十分に同期をしているか確認するためのタイムアウト時間。
この時間内に自ノードと同等以上のchainwork を持っていることをピアが示さない場合、切断の対象となる。
m_work_header
タイムアウトをセットしたときの chainActive の先端ブロックを記録する CBlockIndex のポインタ
m_sent_getheaders
getheaders メッセージを送ったかのフラグ。CHAIN_SYNC_TIMEOUT でタイムアウトした時に getheaders メッセージを投げるとセットされるんだと思います(実装未確認)
m_protect
ピアがbadまたはslowなチェーンだという理由で切断されることから保護するかどうかのフラグ
クラス
PeerLogicValidation
PeerLogicValidation::PeerLogicValidation(CConnman* connmanIn, CScheduler &scheduler, bool enable_bip61)
45秒に1回 CheckForStaleTipAndEvictPeers メソッドを実行するようにスケジューラーを設定している。
void PeerLogicValidation::CheckForStaleTipAndEvictPeers(const Consensus::Params &consensusParams)
45秒ごとに実行される
10分(STALE_CHECK_INTERVAL)ごとに自ノードの先端ブロックが古くなってないかチェックする
最後の先端ブロックの更新から30分以上経過している場合(これはTipMayBeStale関数でチェックしている)、今接続しているのとは別のアウトバウンドピアとの接続を試みるようにフラグをセットする。
void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
void PeerLogicValidation::EvictExtraOutboundPeers(int64_t time_in_seconds)
もし追加のアウトバウンドピアがある場合、アナウンスされてるブロックが最も古いものから接続を切る。
追加のアウトバウンドというのは、MAX_OUTBOUND_PEERS_TO_PROTECT_FROM_DISCONNECT に設定されている最低限プロテクトするピアの数よりも多い数のことかな?
void PeerLogicValidation::ConsiderEviction(CNode *pto, int64_t time_in_seconds)
chainActive の tip から遅れているアウトバウンドピアについて、接続を切るか判断する処理。sendMessageの中から呼ばれている。この処理の中で扱うピアはアウトバウンドで接続しているピアであることが前提となっている様子。
「CHAIN_SYNC_TIMEOUT(20分) + HEADER_RESPONSE_TIME の時間以内に、今の先端ブロックと同等の chainwork をもったブロックをアナウンスしない場合、このアウトバウンドピアは切断の対象となります。(ノート: もしピアのチェーンが自ノードのりも大きな chainwork をもつ場合、invalid でない限りそれを同期するべきです。invalid な場合は、それを見つけ出して接続を切る必要があります。)」(コメントから引用)
引数
pto: 接続先のピアへのポインタ
time_in_seconds: 現在時間(ピアへメッセージを送るときの時間の秒)
処理内容
ピアが自分よりも大きな chainwork のチェーンを持っている時。(つまり、自分よりも先行しているピアの場合)
timeout 時間が設定されている場合 0 にリセットする。(0の意味は未確認)
タイムアウト時間が 0 または work_header が pindexBestKnownBlock より先行している場合
Static 関数
UpdateBlockAvailability
ピアがどのブロックを持っているかについての追跡情報を更新する
処理内容
ProcessBlockAvailability() を実行する
その上で、引数で渡されたハッシュのブロックが、これまでピアの最新ブロックと思っていたものより chaiwork が大きい場合、そのブロックで pindexBestKnwonBlock を更新する
ハッシュに対応するブロックが見つからない場合、 hashLastUnknownBlock にハッシュをセットする。
ProcessBlockAvailability
概要
ピアのまだ実体を取得していなかった最新ブロックがあった場合、そのブロックを取得していないか確認する。すでに取得しており、それが自ノードの有効チェーンよりも有力な場合、ピアの最新ノードの情報を更新する。
処理内容
ピアの hashLastunknownBlock がnull でなく、hashLastUnkdonwBlock に対応するブロックを自ノードが持っている時、そのchainwork が pindexxBestKnownBlock のものよりも大きければ pindexBestKnownBlock を更新する。その後、 hashLastUnKnownBlock を null にセットする。
FindNextBlocksToDownload
概要
pindexLastCommonBlockを更新し、最大エントリ数になるまで、vBlockに未配送の不足している後続ブロックを追加します。(コメントの和訳)
SendMessages関数でブロックを要求する getdata メッセージを送る時に使われている。ダウンロードする必要があるブロックのリストを獲得するために使われている。
CanDirectFetch
自ノードの有効チェーンの先端ブロックのブロックタイムが200分(20ブロック分)以内の場合、trueを返す。
目的
推測混じりで述べると、自ノードの有効チェーンがある程度新しいと判断できるのであれば、自ノードが持っていない新しいブロックを持っているというアナウンスに素直に応じて、都度ブロックを取得していくようにしたい。その都度ブロックを取得するかどうかの条件になるのがこのCanDirectFetchだと思われる。
逆に、都度取得しない場合は、後述の parallel Download (または Faster Synchronization)によるダウンロードを試みるべきだということだと思われる。
使われている3つの場所
getdataメッセージで inv.type が MSG_CMPCT_BLOCK の時。つまり、コンパクトブロックの低帯域モードで動作しているピアにブロックデータを要求され、これから cmpctblock メッセージで応答するタイミング。CanDirectFetchがtrueならそのままcmpctblock メッセージを送る。falseなら block メッセージで、そのブロックの全てのデータをまるっと送る。
Headers メッセージを受け取った時、canDirectFetch が true ならうけとったヘッダに対応するブロックを極力取得する。
cmpctblock メッセージを受け取った時、canDirectFetch が false の場合、処理を続けずに、ブロックの取得を諦める。
Parallel Download とは
DirectFetch と並ぶ言葉として Parallel Download という言葉がコード中に登場する。
0.10.0 でリリースされた Faster synchronization のことを Parallel DownloadやParallel block Fetch などと読んでいるみたい。平たく言うと、最初に全てのblock header を取得して、検証したあと、ブロック本体は接続している複数のピアから並列で取得することで高速にダウンロードできるという仕組み。