INVメッセージ
INVメッセージの概要
INVメッセージは他のノードに自ノードが持っているブロックやトランザクションなどの目録を伝えるために利用されるメッセージです。実際に送信されるペイロードはトランザクションやブロックといったデータそのものではなく、データ種別とそのハッシュ値です。
トランザクションやブロックの実データは inv で渡された目録を元に、本当に必要なデータだけを getheaders や getdata メッセージを使いリモートノードに要求し取得することになります。
ノードはINVメッセージを以下の2つの状況で受け取ることがあります。
1. ブロックやトランザクションのリレーのために他のノードから送られてくる
2. 初期ブロックダウンロード(Initial Block Download: IBD)や、久々に他ノードへ接続された場合に(何らかの理由でネットワークからしばらく切断されていたなど)getblocks メッセージへの応答として送られてくる
つまり、リモートノードからデータを取得する時に、愚直にすべてのデータを受け取るとネットワークなどのリソースを浪費してしまうため、目録情報だけ先にやり取りを行い、本当に必要な実データだけをリモートノードから受け取るようにするのが INV メッセージの役割だと思われます。
INVメッセージのペイロードのフォーマット
INV メッセージは以下の表のような構造をしています。
table:INVメッセージのフォーマット
フィールドサイズ 名称 データ型 コメント
1+ カウント var_int 目録データの数
36x? 目録 inv_vect[] 目録データのリスト
1つ目のフィールドは目録データの数で var_int 型です。var_int 型は可変長の整数型です。続くフィールドが実際の目録( inventory )データで1件あたり36bytes のサイズでカウントフィールドの数だけ目録データが連なります。 目録データのフォーマットは以下の表になります。
table: 目録データのフォーマット
フィールドサイズ 名称 データ型 コメント
4 タイプ uint32_t この目録が示すデータの種別
32 ハッシュ char32 この目録が示すデータのハッシュ値 table: 目録が示すデータのタイプ
値 名称 説明
0 ERROR この値のときはどんなデータも無視されます
1 MSG_TX ハッシュがトランザクションのものであることを示しています
2 MSG_BLOCK ハッシュがブロックのものであることを示しています
3 MSG_FILTERED_BLOCK Hash of a block header; identical to MSG_BLOCK. Only to be used in getdata message. Indicates the reply should be a merkleblock message rather than a block message; this only works if a bloom filter has been set.
4 MSG_CMPCT_BLOCK Hash of a block header; identical to MSG_BLOCK. Only to be used in getdata message. Indicates the reply should be a cmpctblock message. See BIP 152 for more info.
処理ステップ
invで送られてきたデータが最大サイズの50,000件を超えていたら終了する
fRelayTxes のセット
ピアとの接続確立前などでブルームフィルターが設定されていないうちにトランザクションを伝搬しないようにする。
versionメッセージを受け取る前(つまり、ハンドシェイクをしていない)ピアからのトランザクションを伝搬しない。
whitelist に含まれるネットワークのピアから伝搬されたトランザクションは必ず伝搬する。
witness を含むトランザクションがある場合、nFetchFlags にMSG_WITNESS_FLAGを立てる。
なんでこのフラグが必要?
Segwit関連の処理。
witness を含むトランザクションがあるかの条件は「相手ノードが Segwit 対応していて、かつ相手ノードのステートのfHaveWitnessがtrue」
fHaveWitness の意味は未確認。やり取りをしたノードの状態を管理しているのでそのあたりの処理の確認が必要。
INVに含むデータ1件1件についてループする
invデータが既に持っているものか確認する
invがトランザクションの場合、ピアがwitness 対応のノードであれば、MSG_WITNESS_FLAGをinvのtypeにセットする。
ピアがwitness 対応ノードかどうかという情報はハンドシェイク時にversionメッセージから得ている。
この witness のフラグは何のために設定しているかな?
invがブロックの場合
受け取ったブロックが、ピアが知っている最新ブロックよりchainworkが大きい場合、ピアが知っている最新ブロックを更新する。
受け取ったブロックハッシュが、まだ実体を持っていないブロックのものだった場合、state->hashLastUnknownBlock にセットする。
次はmapBlockInFlight のところから読む
invがブロックではない場合
AddInventoryKnownに inv を渡す
BlocksOnlyの場合
ログを吐く(エラーではないが... inv sent in violation of protocol )
持っていなくて、Inport中ではなく、Reinded中ではなく、InitialBlockDownload中ではない場合
AskForしてる
GetLocator宿題
fRelayTxesについて
目的1)versionメッセージを受け取る前のノードからトランザクションを受け取らないようにする
目的2)ブルームフィルターをロードせずにトランザクションを送ることを避ける。
疑問)ブルームフィルターってどのタイミングで設定するんだっけ?
whitelistについて
-whitelist=<IP address or network> Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times. Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway
-whitelistrelay Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: 1)
-whitelistforcerelay Force relay of transactions from whitelisted peers even if they violate local relay policy (default: 1)
INVの送信元が whitelist に含まれており、-whitelistrelay フラグがセットされていれば INV でブロック以外のデータ(トランザクション?)が渡された時に AskFor へ処理を渡す。
AskFor について
概要
mapAskFor というコンテナに inv で受け取ったTxの目録のなかで実データを取得したいものを入れる。専用スレッドで定期実行されているSendMessages 関数が mapAskFor を見て、1000件単位で getdata メッセージをつかってピアに実データを要求する。
読みながら書いたメモ↓
GetDataを投げるサムシング? このあたり を読んだらわかりそうな気がする。 なんか時間をゴニョゴニョしてそう?
ピアが1つのinvに対して複数のNon-Respondedなqueue-positionを持つことはない
mapAskFor を優先的なキューとして使用する(順序がある?)
キーはリクエストを送信できる最も早い時間である
mapAlreadyAskedForにinvのハッシュを渡してFind(mapAlreadyAskedForは何やってるの?)
見つかったら mapAlreadyAskedFor した結果を it にぶっ込み、その second を nRequestTime にセットする
見つからなかったら nRequestTime = 0 とする
同じ時刻をセットしないように?と注意書きあり
nNowには現在の時間 - 100万マイクロ秒(= 1秒)をセットしてる
nLastTimeをstaticで宣言して、インクリメントしてる
nLastTimeとnNowを比較して大きい方を nLastTimeに代入している
Mapをゴニョゴニョしているが?
ビットシフトを使ったフラグ制御について
nFetchFlags の処理でビットシフトを使ったフラグ制御をしている。フラグ制御はtrue, false の2値が扱えれば良いので、1ビットをフラグの値に利用する。bool 型を使うと環境にも寄るけど、1byte を消費することになる。
実際にセットしているのは以下の処理。nFetchFlags を 0 で初期化して、フラグを立てる場合には |= を使ってビット演算をしている。
code:cpp
uint32_t nFetchFlags = 0;
if ((pfrom->GetLocalServices() & NODE_WITNESS) && State(pfrom->GetId())->fHaveWitness) {
nFetchFlags |= MSG_WITNESS_FLAG;
}
return nFetchFlags;
`MSG_WITNESS_FLAG はビットシフトを使って、左から 31bit 目のビットを立てた値になっている。0とこの値の or をとることで、nFetchFlagsの31bit目にビットが立つ。
code:cpp
const uint32_t MSG_WITNESS_FLAG = 1 << 30;
実際にビット列を見ながらだとわかりやすい。MSG_WITNESS_FLAG の宣言の左ビットシフトは以下のようになる。
00000000000000000000000000000001 << 30
↓
01000000000000000000000000000000
さらに0で初期化された nFetchFlags と or を取ると
00000000000000000000000000000000 | 01000000000000000000000000000000
↓
01000000000000000000000000000000
さらに INVの処理にはでてきてないが、 nFetchFlags において MSG_WITNESS_FLAG が立っているかどうかの判定は以下のように書ける。
code:cpp
if (nFetchFlags & MSG_WITNESS_FLAG)
{
// フラグがセットされていればここに処理がはいる
}
ビットで表現するとこんなかんじ。
01000000000000000000000000000000 & 01000000000000000000000000000000
↓
01000000000000000000000000000000
ビット演算結果の値が0でないのでif文の中身が実行される。
mapBlocksInFlight について
定義
code:cpp
std::map<uint256, std::pair<NodeId, std::list<QueuedBlock>::iterator> > mapBlocksInFlight GUARDED_BY(cs_main);
net_processing.cpp L138
InFlight の意味
ブロックのやり取りをしているメッセージの処理の中で度々見かける。
peer から要求されているけど、まだpeer に渡していないブロックの事?
ブロックに取り込まれている?取り込まれていない?
送ってる途中のブロック?
だとして、送っている途中のブロックを管理する必要があるのはなぜ?
CNode
ピアから接続されるとCNodeのインスタンスが生成
CConnmannにポインタを渡す
CConnmanについて
Connection Managerのこと。
外部ノードとの接続をコントロールしている。:通信をするときはこれを経由する。
接続してる数ぶんのCNodeをもってる
PeerLogicValidation::SendMessage
定期的に呼び出されている
mapAskForを使ってるところがある
mapAskFor が空ではなく、 mapAskFor.begin の first が現在より古いときにループ
invを持ってたら
GetDataが1000よりおおきかったら
1000って何:1件ずつではなく1000件ためてドンッっと投げてる
initial block download
最新のブロックまでブロックチェーンデータをダウンロードする