オペスタックでの libp2p の使用

脚本:ジョーンン

楽観主義におけるLibp2Pアプリケーション

このセクションでは、オプティミズムがLibb2Pを使用してOp-NodeでのP2Pネットワークの確立を完了する方法を説明することを主な目的としています。 P2Pネットワークは主に、Unsafeのブロック構築が完了した後のシーケンサーや、P2Pのgossiphubのpub/subを介して伝播するシーケンサーなど、さまざまなノードで情報を渡すために使用されます。 Libb2Pは、P2Pネットワークのネットワーキング、アドレス指定などの他のインフラストラクチャも扱います。

libp2p について学ぶ

libp2p (「ライブラリピアツーピア」または「ライブラリピアツーピア」と略される) は、P2P アプリケーションの開発を支援するピアツーピア (P2P) ネットワーキングフレームワークです。 これには、ネットワーク参加者(「ピア」または「ピア」とも呼ばれます)間のP2P通信を容易にする一連のプロトコル、仕様、およびライブラリが含まれています(出典)[2] [3])。 もともとIPFS(InterPlanetary File)プロジェクトの一部であったlibp2pは、分散ネットワーク用のモジュラーネットワークスタック(ソース)となるスタンドアロンプロジェクトに進化しました )。

libp2pはIPFSコミュニティのオープンソースプロジェクトであり、仕様書の作成、コード実装、サンプルやチュートリアルの作成など、幅広いコミュニティからの貢献を歓迎しています[4] [5])。 libp2p は複数のビルディングブロックで構成されており、それぞれが非常に明確で、文書化され、テスト済みのインターフェイスを備えているため、構成可能、置き換え可能、したがってアップグレード可能です )。 libp2pのモジュール性により、開発者はアプリケーションに必要なコンポーネントのみを選択して使用できるため、P2P Webアプリケーションを構築する際の柔軟性と効率が向上します。

関連リソース

  • libp2p の公式ドキュメント[6]
  • libp2p の GitHub リポジトリ[7]
  • プロトスクールでのlibp2pの紹介[8]

libp2pのモジュラーアーキテクチャとオープンソースの性質は、強力でスケーラブルで柔軟なP2Pアプリケーションを開発するための優れた環境を提供し、分散ネットワークおよびネットワークアプリケーション開発の分野で重要なプレーヤーになります。

libp2p の実装

libp2p を使用する場合、P2P ネットワークを構築するために、いくつかのコアコンポーネントを実装して設定する必要があります。 アプリケーションにおけるlibp2pの主な実装面のいくつかを次に示します。

1. ノードの作成と構成:

  • libp2p ノードの作成と構成は、ノードのネットワークアドレス、ID、およびその他の基本パラメーターの設定を含む、最も基本的なステップです。 キー使用法コード:

libp2p.新規()

2. トランスポート プロトコル:

トランスポートプロトコル(TCP、WebSocketなど)を選択して構成し、ノード間の通信を確保します。 キー使用法コード:

tcpTransport := tcp.新しいTCPトランスポーツ()

3. 多重化とフロー制御:

  • 多重化を実装して、複数の同時データ ストリームを 1 つの接続で処理できるようにします。 *フロー制御を実装して、データ転送速度と処理速度を管理します。 キー使用法コード:

yamuxトランスポート:=ヤムックス。新規()

4. セキュリティと暗号化:

*通信のセキュリティとプライバシーを確保するために、安全なトランスポート層を構成します。 *データを保護し、通信相手を認証するための暗号化および認証メカニズムを実装します。 キー使用法コード:

tlsTransport := tls.新規()

5. プロトコルとメッセージ処理:

特定のネットワーク操作とメッセージ交換を処理するためのカスタム プロトコルを定義および実装します。 *受信したメッセージを処理し、必要に応じて応答を送信します。 キー使用法コード:

ホスト。SetStreamHandler("/my-protocol/1.0.0", myProtocolHandler)

6. 検出とルーティング:

*ネットワーク内の他のノードを見つけるためのノード検出メカニズムを実装します。

  • ルーティング ロジックを実装して、メッセージがネットワーク内の正しいノードにルーティングされる方法を決定します。 キー使用法コード:

DHT := kaddht.NewDHT(ctx, host, datastore.NewMapDatastore())

7. ネットワークの動作とポリシー:

接続管理、エラー処理、再試行ロジックなどのネットワークの動作とポリシーを定義して実装します。 キー使用法コード:

connManager := connmgr.新穂店長(低水位、高水位、猶予期間)

8. 状態管理とストレージ:

接続ステータス、ノードリスト、データストレージなど、ノードとネットワークのステータスを管理します。 キー使用法コード:

ピアストア := pstoremem.新しいピアストア()

9. テストとデバッグ:

  • libp2p アプリケーションの正確性と信頼性を確認するためのテストを書いてください。 *デバッグツールとログを使用して、ネットワークの問題を診断および解決します。 キー使用法コード:

伐採。SetLogLevel("libp2p", "DEBUG")

10. ドキュメントとコミュニティサポート:

  • libp2p のドキュメントを参照して、さまざまなコンポーネントと API について学習してください。
  • サポートと問題解決のためにlibp2pコミュニティと通信します。

}

上記は、libp2pを使用する際に考慮および実装する主な側面の一部です。 各プロジェクトの具体的な実装は異なる場合がありますが、libp2pアプリケーションを構築して実行するには、これらの基本的な側面が必要です。 これらの関数を実装するときは、libp2pの公式ドキュメントを参照できます[9] [10]および GitHub リポジトリ サンプル コードとチュートリアル。

OPノードでのlibp2pの使用

オペノードとlibp2pの関係を理解するには、いくつかの質問を理解する必要があります

  • なぜlibp2pなのか? devp2p を選んでみませんか (geth は devp2p を使用しています)
  • OPノードのP2Pネットワークに密接に関連するデータまたはプロセス
  • これらの機能がコード層でどのように実装されているか

オペノードにlibp2pネットワークが必要な理由

まず、楽観主義がP2Pネットワークを必要とする理由を理解する必要があります** LibbP2Pは、開発者が複数のユースケース(ソース)向けに分散型ピアツーピアアプリケーションを構築できるようにするモジュラーネットワークプロトコルです。[11] [12])(出典 [13])。 一方、DevP2Pは主にイーサリアムエコシステムで使用され、イーサリアムアプリケーション向けに調整されています(出典 )。 libp2pの柔軟性と幅広い適用性は、開発者にとって最高の選択肢になる可能性があります。

オペノードは主にlibp2pファンクションポイントを使用します

  • シーケンサーが、結果の安全でないブロックを他の非シーケンサー ノードに渡すために使用されます
  • 非シーケンサモードの他のノードの場合、ギャップ発生時の高速同期(逆チェーン同期)
  • ノード全体を規制するために統合レピュテーションシステムを採用するための良好な環境

コードの実装

ホストのカスタム初期化

ホストはP2Pノードとして理解できますが、このノードを開くときは、独自のプロジェクト用に特別な初期化構成を行う必要があります

次に、op-node/p2p/host.go ファイルの Host メソッドを見てみましょう。

この機能は主にlibp2pホストのセットアップとさまざまな構成を行うために使用されます。 関数の重要な部分と、それぞれの簡単な中国語の説明を次に示します。

  1. P2Pが無効になっているかどうかを確認します
    P2P が無効になっている場合、関数は直接戻ります。
  2. 公開鍵からピアIDを取得する
    構成の公開キーを使用して、ピア ID を生成します。
  3. ピアストアの初期化
    基本ピアストア ストアを作成します。
  4. 拡張機能ピアストアを初期化する
    基本ピアストアの上に、拡張ピアストアを作成します。
  5. ピアストアに秘密キーと公開キーを追加する
    ピアの秘密キーと公開キーをピアストアに格納します。
  6. 接続ゲートの初期化
    ネットワーク接続を制御するために使用されます。
  7. 接続マネージャーの初期化
    ネットワーク接続の管理に使用します。
  8. 送信アドレスとリスニングアドレスの設定
    ネットワークトランスポートプロトコルとホストのリスニングアドレスを設定します。
  9. libp2p ホストの作成
    上記のすべての設定を使用して、新しい libp2p ホストを作成します。
  10. 静的ピアの初期化
    スタティック ピアが設定されている場合は、初期化します。
  11. ホストに戻る
    最後に、この関数は作成された libp2p ホストを返します。

これらのキーセクションは libp2p ホストの初期化とセットアップを担当し、各部分はホスト構成の特定の側面を担当します。

func (conf Config) Host(log log.ロガー、レポーターメトリクス。Reporter, metrics HostMetrics) (host.ホスト、エラー) { 会議の場合。DisableP2P { ゼロを返す、ゼロ } パブ := 会議。Priv.GetPublic() pid, err := peer.IDFromPublicKey(pub) if err != nil { ゼロ、FMTを返します。エラー ("ネットワーク特権キーから公開キーを派生できませんでした: %w", エラー) } basePs, err := pstoreds.新しいピアストア(コンテキスト。背景(), conf.ストア、保存済み。DefaultOpts()) if err != nil { ゼロ、FMTを返します。エラー f("ピアストアを開けませんでした: %w", エラー) } peerScoreParams := conf.PeerScoringParams() スコア保持時間。期間 if peerScoreParams != nil { ゴシップと同じ保持期間を使用する(可能な場合) scoreRetention = peerScoreParams.PeerScoring.RetainScore } else { ピアスコアリングが無効になっている場合は、スコア GC を無効にします スコア保持 = 0 } ps, err := store.NewExtendedPeerstore(context.バックグラウンド()、ログ、クロック。クロック、ベースP、会議。ストア、スコア保持) if err != nil { ゼロ、FMTを返します。エラー f("拡張ピアストアを開けませんでした: %w", エラー) } エラーの場合:= ps。AddPrivKey(pid, conf.プライベート);err != nil { ゼロ、FMTを返します。エラー f("特権キーでピアストアをセットアップできませんでした: %w", エラー) } エラーの場合:= ps。AddPubKey(pid, pub);err != nil { ゼロ、FMTを返します。エラーf("パブ キーでピアストアをセットアップできませんでした: %w", エラー) } connGtr ゲーティング。ブロッキング接続ゲイター connGtr, err = ゲーティング.NewBlockingConnectionGater(conf.ストア) if err != nil { ゼロ、FMTを返します。エラー f("接続ゲートを開くことができませんでした: %w", エラー) } connGtr = ゲーティング。AddBanExpiry(connGtr, ps, log, clock.時計、メトリック) connGtr = ゲーティング。AddMetering(connGtr, metrics) connMngr, err := DefaultConnManager(conf) if err != nil { ゼロ、FMTを返します。エラー f("接続マネージャを開けませんでした: %w", エラー) } listenAddr, err := addrFromIPAndPort(conf.聞いてくださいIP、会議。聞くTCPポート) if err != nil { ゼロ、FMTを返します。エラー(リッスンアドレスの作成に失敗しました: %w", エラー) } tcpTransport := libp2p.輸送( ティッカー新しいTCPトランスポーツ、 ティッカー接続タイムアウトあり(時間。分60)) // 未使用の接続を切断する TODO:技術的には、WebSocketおよびQUICトランスポートでノードを実行することもできます 多分将来的には? nat lconf.NATManagerC // nil の場合は無効 会議の場合。NAT { NAT = ベーシックホスト。新しいNAT製造者 } オプツ := []libp2p.オプション{ libp2p.アイデンティティ(コンファレンス。プライベート)、 ユーザーエージェントを明示的に設定して、他のGolibp2pユーザーと区別できるようにします。 libp2p.UserAgent(conf.ユーザーエージェント)、 tcpトランスポート、 libp2p.WithDialTimeout(conf.タイムアウトダイヤル)、 リレー サービスはなく、ピア間の直接接続のみ。 libp2p.ディセーブルリレー(), ホストは、コンフィグから構築後すぐにネットワークを起動してリッスンします。 libp2p.聞くアドル(聞くアドル)、 libp2p.ConnectionGater(connGtr), libp2p.ConnectionManager(connMngr), libp2p.ResourceManager(nil), // TODO は、リソース マネージャ インターフェイスを使用して、ピアごとのリソースをより適切に管理します。 libp2p.ピン(真)、 libp2p.AutoNAT サービスレート制限(10, 5, time.セカンド*60)、 } 会議の場合。NoTransportSecurity { } else { opts = append(opts, conf.ホストセキュリティ...) } h, err := libp2p.新規(オプト...) if err != nil { } i の場合、peerAddr := range conf.StaticPeers { if err != nil { } 静的ピア[i] = addr } out := &extraHost{ ホスト: h, 静的ピア: 静的ピア、 } out.monitorStaticPeers() }

... n.gs, err = NewGossipSub(resourcesCtx, n.host, rollupCfg, setup, n.scorer, metrics, log) if err != nil { } ...

  1. オーセンティケータの作成: テーマ名の生成をブロックする:
  • 構成 (cfg) の L2ChainID に基づいて文字列をフォーマットする blocksTopicV1 関数を使用して、ブロックトピック名を生成します。 書式設定された文字列は、/optimism/{L2ChainID}/0/blocks という特定の構造に従います。

  • psを呼び出して関数します。 Join(blocksTopicName)は、ブロックゴシップテーマを追加しようとします。 エラーが発生した場合は、トピックに参加できなかったことを示すエラー メッセージが返されます。

  • LogTopicEvents 関数を使用してトピックイベントをログに記録する新しいゴルーチンを生成しました。

  • gossipIn からの OnUnsafeL2Payload イベントを処理する BlocksHandler をカプセル化する MakeSubscriber 関数を使用してサブスクライバを作成しました。 提供されたサブオンを実行するための新しいゴルーチンが生成されました。

func JoinGossip(p2pCtx context.Context, self peer.ID, ps *pubsub.PubSub、ログログ。ロガー、cfg *ロールアップ。Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, error) { blocksTopicName := blocksTopicV1(cfg) // return fmt.Sprintf("/optimism/%s/0/blocks", cfg.L2ChainID.String()) ゼロ、FMTを返します。エラー(ブロックゴシップトピックの登録に失敗しました:%w、エラー) } ブロックトピック、エラー:= ps。結合(ブロックトピック名) if err != nil { } if err != nil { } go LogTopicEvents(p2pCtx, log.New("topic", "blocks"), blocksTopicEvents) if err != nil { } return &publisher{log: log, cfg: cfg, blocksTopic: blocksTopic, runCfg: runCfg}, nil

op-node/rollup/driver/state.go

func (s *Driver) eventLoop() { for(){ ... payload, err := s.sequencer.RunNextSequencerAction(ctx) if err != nil { エラーは、シーケンスを変更/停止するほど重大ではありませんが、ログに記録して測定する必要があります。 if err := s.network.PublishL2Payload(ctx, payload);err != nil { } planSequencerAction() // シーケンスのループを維持するために次のシーケンサー アクションをスケジュールする } } ...

不足しているブロックがある場合、P2Pによる高速同期

ダウンタイム後の再リンクなど、特殊な事情によりダウンタイム後にノードが再リンクされると、一部のブロック(ギャップ)が同期されない場合があり、このような状況が発生した場合、P2Pネットワークのリバースチェーンを介して迅速に同期できます。

op-node/rollup/driver/state.go の checkForGapInUnsafeQueue 関数を見てみましょう。

このコード スニペットでは、ドライバー構造体に属する checkForGapInUnsafeQueue というメソッドを定義します。 その目的は、「安全でないキュー」と呼ばれるキュー内のデータギャップをチェックし、altSyncと呼ばれる代替同期メソッドを使用して欠落しているペイロードの取得を試みることです。 ここで重要な点は、データの継続性を確保し、欠落データが検出されたときに他の同期方法から欠落データを取得しようとする方法です。 関数の主な手順は次のとおりです。

  1. この関数は、最初に s.derivation から UnsafeL2Head と UnsafeL2SyncTarget をチェック範囲の開始点と終了点として取得します。 2.この関数は、終了と開始の数値を比較することによって、開始と終了の間に欠落しているブロックをチェックします。
  2. データギャップが検出された場合、関数はs.altSync.RequestL2Range(ctx、start、end)を呼び出して欠落しているデータ範囲を要求します。 end が null 参照の場合 (つまり、eth. L2BlockRef{}) の場合、関数は開始から始まるオープンエンドの範囲の同期を要求します。 4.データを要求すると、関数は要求しているデータの範囲を示すデバッグログをログに記録します。
  3. 関数は最終的にエラー値を返します。 エラーがない場合は、nil を返します

checkForGapInUnsafeQueue は、安全でないキューにギャップがあるかどうかをチェックし、代替同期メソッドから不足しているペイロードを取得しようとします。 警告:これは発信信号のみであり、ブロックの取得は保証されていません。 結果は OnUnsafeL2Payload を通じて受信されます。 func (s *Driver) checkForGapInUnsafeQueue(ctx context.コンテキスト) エラー { start := s.derivation.UnsafeL2Head() end := s.derivation.UnsafeL2SyncTarget() 開始と終了の間に欠落しているブロックがあるかどうかを確認します もしそうなら、それらを要求してください。 終了 == (eth.L2BlockRef{}) { s.log.Debug("オープンエンド範囲との同期を要求", "開始", 開始) return s.altSync.RequestL2Range(ctx, start, eth.L2BlockRef{}) } それ以外の場合は終了します。開始>番号。番号+1 { s.log.Debug("安全でない L2 ブロック範囲の欠落を要求しています", "開始", 開始, "終了", 終了, "サイズ", 終了.番号-開始.番号) return s.altSync.RequestL2Range(ctx, start, end) } ゼロを返す }

RequestL2Range 関数は、要求ブロックの開始と終了を要求チャネルに通知します。

その後、要求はonRangeRequestメソッドを介してpeerRequestsチャネルに配布され、peerRequestsチャネルは複数のピアによって開かれたループによって待機されます。

func (s *SyncClient) onRangeRequest(ctx context.Context, req rangeRequest) { ... i := uint64(0); ;i++ { 数値 := req.end.Number - 1 - i if num <= req.start { 帰る } すでに検疫されているものがあるかどうかを確認します h, ok := s.quarantineByNum[num] ;わかりました { もし s.trusted.Contains(h) { // もし私たちがそれを信頼するなら、それを宣伝してみてください。 s.tryプロモート(h) } すでに候補者がいるものを取得しないでください。 競合を見つけるか、他のブロックを十分に同期することで、検疫から削除されます 続ける } if _, ok := s.inFlight[num] ;わかりました { .log。デバッグ("要求はまだ処理中であり、同期要求を再スケジュールしていません", "num", num) 続行 // リクエストはまだ処理中です } pr := peerRequest{num: num, complete: new(atomic.ブール値)} .log。デバッグ("P2P ブロック要求のスケジューリング", "num", num) スケジュール番号 選択 { case s.peerRequests <- pr: s.インフライト[num] = PR.完了 ケース <-CTX.完了(): .log。情報("完全な P2P 同期範囲をスケジュールしませんでした", "現在", num, "err", ctx.エラー()) 帰る デフォルト: // ピアはすべて既に要求の処理でビジー状態である可能性があります .log。情報("L2ブロック履歴のより多くのP2P要求に対するブロック要求を処理する準備ができているピアがありません", "現在", num) 帰る } } }

ピアがこの要求を受信するとどうなるか見てみましょう。

最初に知っておく必要があるのは、ピアと要求ノードの間のリンク、またはメッセージの受け渡しがlibp2pストリームを介して渡されることです。 ストリームの処理メソッドは受信ピアノードによって実装され、ストリームの作成は送信ノードによって開かれます。

このコードは、前の init 関数で確認でき、MakeStreamHandler はハンドラーを返し、SetStreamHandler はプロトコル ID をこのハンドラーにバインドするため、送信ノードがこのストリームを作成して使用するたびに、返されたハンドラーがトリガーされます。

n.syncSrv = NewReqRespServer(rollupCfg, l2Chain, metrics) 同期プロトコルを libp2p ホストに登録する payloadByNumber := MakeStreamHandler(resourcesCtx, log.New("serve", "payloads_by_number"), n.syncSrv.HandleSyncRequest) n.host.SetStreamHandler(PayloadByNumberProtocolID(rollupCfg.L2ChainID), payloadByNumber)

ハンドラー内でどのように処理されるかを見てみましょう この関数は、最初にグローバルおよび個別のレート制限チェックを実行して、要求の処理速度を制御します。 次に、要求されたブロック番号を読み取って検証し、妥当な範囲内にあることを確認します。 次に、この関数は L2 レイヤーから要求のチャンク ペイロードを取得し、応答ストリームに書き込みます。 応答データを書き込むときは、書き込みプロセス中に低速のピア接続によってブロックされないように、書き込み期限を設定します。 最後に、この関数は要求されたブロック番号と考えられるエラーを返します。

func (srv *ReqRespServer) handleSyncRequest(ctx context.コンテキスト、ストリームネットワーク。Stream) (uint64, error) { ピア ID := ストリーム。Conn().リモートピア() グローバルレートリミッターからトークンを取得し、 異なるピア間での同時サーバー作業が多すぎないようにするため。 if err := srv.globalRequestsRL.Wait(ctx);err != nil { 0、FMT を返します。エラー("グローバル同期レート制限の待機中にタイムアウトしました: %w", エラー) } ピアのレート制限データを検索するか、それ以外の場合は追加します srv.peerStatsLock.Lock() ps, _ := srv.peerRateLimits.Get(peerId) ps == nil { ps = &peerStat{ 要求: レート。NewLimiter(peerServerBlocksRateLimit, peerServerBlocksBurst), } srv.peerRateLimits.Add(peerId, ps) 追伸。Requests.Reserve() // ヒットをカウントしますが、すぐに待つのではなく、次のリクエストを遅らせるようにします } else { 既存のピアである場合にのみ待機し、それ以外の場合は、インスタントレート制限待機呼び出しは常にエラーが発生します。 リクエスターが時間がかかりすぎると思った場合、それは彼らの問題であり、彼らは切断することができます。 読み取り/書き込みに失敗した場合にのみ切断します。 作業が無効 (範囲の検証) の場合、または個々のサブタスクがタイムアウトした場合。 エラーの場合:= ps。Requests.Wait(ctx);err != nil { 0、FMT を返します。エラー("グローバル同期レート制限の待機中にタイムアウトしました: %w", エラー) } } srv.peerStatsLock.Unlock() 読み取り期限を設定する (可能な場合) _ = ストリーム。SetReadDeadline(time.今()。Add(serverReadRequestTimeout)) 要求を読む Req uint64 エラーの場合:=バイナリ。読み取り(ストリーム、バイナリ。リトルエンディアン、&req);err != nil { 0、FMT を返します。エラー ("要求されたブロック番号 %w を読み取れませんでした", エラー) } エラーの場合:=ストリーム。CloseRead();err != nil { リターン要件、FMT。エラーf("P2P同期要求呼び出しの読み取り側を閉じることができませんでした:%w"、エラー) } 要求が予想されるブロック範囲内にあることを確認します if req < srv.cfg.Genesis.L2.Number { リターン要件、FMT。エラー("ジェネシス %d より前に L2 ブロック %d の要求を処理できません: %w", req, srv.cfg.Genesis.L2.Number, invalidRequestErr) } max, err := srv.cfg.TargetBlockNumber(uint64(time.今()。Unix())) if err != nil { リターン要件、FMT。エラー ("要求を検証するための最大ターゲット ブロック番号を決定できません: %w", 無効な要求エラー) } 最大>要求する場合 { リターン要件、FMT。エラー ("予想される最大ブロック (%v): %w" の後に L2 ブロック %d の要求を処理できません。" req, max, invalidRequestErr) } payload, err := srv.l2.PayloadByNumber(ctx, req) if err != nil { エラーの場合。です(エラー、イーサリアム。NotFound) { リターン要件、FMT。エラー(ピアが番号で不明なブロックを要求しました: %w", エラー) } else { リターン要件、FMT。エラーf("ピアに提供するペイロードを取得できませんでした: %w", エラー) } } 可能な場合は、スロットリングピア接続でブロックせずに安全に書き込むように書き込み期限を設定します _ = ストリーム。SetWriteDeadline(time.今()。Add(serverWriteChunkTimeout)) 0 - 結果コード: 成功 = 0 1:5 - バージョン: 0 .tmp[5] バイト _ の場合、err := ストリーム。書き込み(tmp[:]);err != nil { リターン要件、FMT。エラー ("応答ヘッダー データの書き込みに失敗しました: %w", エラー) } w := スッキリ。新しいバッファリングライター(ストリーム) _ の場合、err := ペイロード。元帥SSZ(w);err != nil { リターン要件、FMT。エラー ("応答を同期するためのペイロードの書き込みに失敗しました: %w", エラー) } if err := w.Close();err != nil { リターン要件、FMT。エラー ("応答を同期するためのペイロードの書き込みを完了できませんでした: %w", エラー) } 戻り値の要求、なし }

ここまでで、逆チェーン同期の要求と処理の一般的なプロセスについて説明しました

P2Pノードのポイントレピュテーションシステム

特定のノードがネットワーク全体のセキュリティを損なう悪意のある要求や応答を行うのを防ぐために、Optimismはポイントシステムも使用します。

たとえば、op-node/p2p/app_scores.go には、ピアのスコアを設定するための一連の関数があります

func (s *peerApplicationScorer) onValidResponse(id peer.ID) { _, err := s.scorebook.SetScore(id, store.IncrementValidResponses{Cap: s.params.ValidResponseCap}) if err != nil { s.log.Error("ピア スコアを更新できません", "ピア", ID, "エラー", エラー) 帰る } } func (s *peerApplicationScorer) onResponseError(id peer.ID) { _, err := s.scorebook.SetScore(id, store.IncrementErrorResponses{Cap: s.params.ErrorResponseCap}) if err != nil { s.log.Error("ピア スコアを更新できません", "ピア", ID, "エラー", エラー) 帰る } } func (s *peerApplicationScorer) onRejectedPayload(id peer.ID) { _, err := s.scorebook.SetScore(id, store.IncrementRejectedPayloads{Cap: s.params.RejectedPayloadCap}) if err != nil { s.log.Error("ピア スコアを更新できません", "ピア", ID, "エラー", エラー) 帰る } }

新しいノードの統合ステータスは、追加される前にチェックされます

func AddScoring(gater BlockingConnectionGater, scores Scores, minScore float64) *ScoreringConnectionGater { return &ScoringConnectionGater{BlockingConnectionGater: gater, scores: scores, minScore: minScore} } func (g *ScoreringConnectionGater) checkScore(p peer.ID) (allow bool) { score, err := g.scores.GetPeerScore(p) if err != nil { 偽を返す } リターンスコア>= g.minScore } func (g *ScoringConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) { return g.BlockingConnectionGater.InterceptPeerDial(p) && g.checkScore(p) } func (g *ScoringConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr.Multiaddr) (allow bool) { return g.BlockingConnectionGater.InterceptAddrDial(id, ma) && g.checkScore(id) } func (g *ScoreringConnectionGater) InterceptSecured(dir network.方向、ID peer.ID、ミリ秒ネットワーク。ConnMultiaddrs) (allow bool) { return g.BlockingConnectionGater.InterceptSecured(dir, id, mas) && g.checkScore(id) }

まとめ

libp2pの高い構成可能性により、プロジェクト全体のp2pが高度にカスタマイズ可能でモジュール化され、上記はoptimsimのlibp2pのパーソナライズされた実装の主なロジックであり、その他の詳細はp2pディレクトリのソースコードを読むことで詳細に学ぶことができます。

原文表示
このページには第三者のコンテンツが含まれている場合があり、情報提供のみを目的としております(表明・保証をするものではありません)。Gateによる見解の支持や、金融・専門的な助言とみなされるべきものではありません。詳細については免責事項をご覧ください。
  • 報酬
  • コメント
  • 共有
コメント
0/400
コメントなし
  • ピン
いつでもどこでも暗号資産取引
qrCode
スキャンしてGateアプリをダウンロード
コミュニティ
日本語
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)