Việc sử dụng libp2p trong op-stack

Viết bởi Joohhnnn

Ứng dụng Libp2P trong Lạc quan

Trong phần này, mục đích chính là giải thích cách Optimism sử dụng Libb2P để hoàn thành việc thiết lập mạng P2P trong Op-Node. Mạng P2P chủ yếu được sử dụng để truyền thông tin trong các nút khác nhau, chẳng hạn như trình tự sau khi hoàn thành việc xây dựng khối Không an toàn và lan truyền qua pub / sub của gossiphub của P2P. Libb2P cũng xử lý các cơ sở hạ tầng khác như mạng, địa chỉ, v.v. trong mạng P2P.

Tìm hiểu về libp2p

libp2p (viết tắt từ "library peer-to-peer" hoặc "library peer-to-peer") là một khung mạng ngang hàng (P2P) giúp phát triển các ứng dụng P2P. Nó chứa một tập hợp các giao thức, thông số kỹ thuật và thư viện giúp giao tiếp P2P giữa những người tham gia mạng (còn được gọi là "ngang hàng" hoặc "ngang hàng") dễ dàng hơn (nguồn[2] [3])。 Ban đầu là một phần của dự án IPFS (InterPlanetary File), libp2p đã phát triển thành một dự án độc lập trở thành một ngăn xếp mạng mô-đun (nguồn) cho các mạng phân tán )。

libp2p là một dự án mã nguồn mở của cộng đồng IPFS hoan nghênh sự đóng góp từ nhiều cộng đồng, bao gồm giúp viết thông số kỹ thuật, triển khai mã và tạo các ví dụ và hướng dẫn[4] [5])。 libp2p được tạo thành từ nhiều khối xây dựng, mỗi khối có giao diện rất rõ ràng, được ghi lại và đã được thử nghiệm giúp chúng có thể kết hợp, thay thế và do đó có thể nâng cấp )。 Bản chất mô-đun của libp2p cho phép các nhà phát triển chỉ chọn và sử dụng các thành phần cần thiết cho ứng dụng của họ, thúc đẩy tính linh hoạt và hiệu quả khi xây dựng các ứng dụng web P2P.

Tài nguyên liên quan

  • Tài liệu chính thức cho libp2p[6]
  • Kho lưu trữ GitHub của libp2p[7]
  • Giới thiệu về libp2p trên ProtoSchool[8]

Kiến trúc mô-đun và bản chất nguồn mở của libp2p cung cấp một môi trường tốt để phát triển các ứng dụng P2P mạnh mẽ, có thể mở rộng và linh hoạt, làm cho nó trở thành một người chơi quan trọng trong lĩnh vực mạng phân tán và phát triển ứng dụng mạng.

triển khai libp2p

Khi sử dụng libp2p, bạn sẽ cần triển khai và cấu hình một số thành phần cốt lõi để xây dựng mạng P2P của mình. Dưới đây là một số khía cạnh triển khai chính của libp2p trong ứng dụng:

1. **Tạo và cấu hình nút **:

  • Tạo và cấu hình nút libp2p là bước cơ bản nhất, bao gồm thiết lập địa chỉ mạng, danh tính và các tham số cơ bản khác của nút. Mã sử dụng chính:

libp2p. Mới()

2. Giao thức vận chuyển:

Chọn và cấu hình các giao thức truyền tải của bạn (ví dụ: TCP, WebSockets, v.v.) để đảm bảo giao tiếp giữa các nút. Mã sử dụng chính:

tcpGiao thông vận tải := tcp. NewTCPTransport()

3. ** Ghép kênh và kiểm soát dòng chảy **:

  • Thực hiện ghép kênh để cho phép xử lý nhiều luồng dữ liệu đồng thời trên một kết nối duy nhất.
  • Thực hiện kiểm soát luồng để quản lý tốc độ truyền dữ liệu và tốc độ xử lý. Mã sử dụng chính:

yamuxGiao thông vận tải := yamux. Mới()

4. Bảo mật và mã hóa:

  • Cấu hình một lớp vận chuyển an toàn để đảm bảo an ninh và quyền riêng tư của thông tin liên lạc.
  • Thực hiện các cơ chế mã hóa và xác thực để bảo vệ dữ liệu và xác thực các bên giao tiếp. Mã sử dụng chính:

tlsGiao thông vận tải := tls. Mới()

5. Giao thức và xử lý tin nhắn:

Xác định và triển khai các giao thức tùy chỉnh để xử lý các hoạt động mạng và trao đổi tin nhắn cụ thể.

  • Xử lý tin nhắn đã nhận và gửi phản hồi khi cần thiết. Mã sử dụng chính:

chủ nhà. SetStreamHandler("/my-protocol/1.0.0", myProtocolHandler)

6. Khám phá và định tuyến:

  • Thực hiện cơ chế khám phá nút để tìm các nút khác trong mạng.
  • Thực hiện logic định tuyến để xác định cách tin nhắn được định tuyến đến các nút chính xác trong mạng. Mã sử dụng chính:

DHT := Kaddht. NewDHT (ctx, máy chủ, kho dữ liệu. NewMapDatastore())

7. Hành vi và chính sách mạng:

Xác định và thực hiện các hành vi và chính sách mạng, chẳng hạn như quản lý kết nối, xử lý lỗi và logic thử lại. Mã sử dụng chính:

connManager := connmgr. NewConnManager (lowWater, highWater, gracePeriod)

8. Quản lý và lưu trữ nhà nước:

Quản lý trạng thái của các nút và mạng, bao gồm trạng thái kết nối, danh sách nút và lưu trữ dữ liệu. Mã sử dụng chính:

Peerstore := pstoremem. NewPeerstore()

9. Kiểm tra và gỡ lỗi:

  • Viết các bài kiểm tra cho ứng dụng libp2p của bạn để đảm bảo tính chính xác và độ tin cậy của nó.
  • Sử dụng các công cụ gỡ lỗi và nhật ký để chẩn đoán và giải quyết các sự cố mạng. Mã sử dụng chính:

Đăng nhập. SetLogLevel ("libp2p", "GỠ LỖI")

10. Tài liệu và Hỗ trợ Cộng đồng:

  • Tham khảo tài liệu của libp2p để tìm hiểu về các thành phần và API khác nhau của nó.
  • Giao tiếp với cộng đồng libp2p để được hỗ trợ và giải quyết vấn đề.

}

Trên đây là một số khía cạnh chính cần xem xét và thực hiện khi sử dụng libp2p. Việc triển khai cụ thể của từng dự án có thể khác nhau, nhưng những khía cạnh cơ bản này là cần thiết để xây dựng và chạy các ứng dụng libp2p. Khi triển khai các chức năng này, bạn có thể tham khảo tài liệu chính thức về libp2p[9] [10]và kho lưu trữ GitHub Mã mẫu và hướng dẫn.

Sử dụng libp2p trong nút OP

Để tìm ra mối quan hệ giữa op-node và libp2p, chúng ta phải tìm ra một số câu hỏi

  • Tại sao libp2p? Tại sao không chọn devp2p (geth sử dụng devp2p)
  • Dữ liệu hoặc quy trình nào có liên quan chặt chẽ đến mạng P2P trong nút OP
  • Làm thế nào các tính năng này được thực hiện ở lớp mã

Tại sao các nút op cần mạng libp2p

Trước tiên, chúng ta cần hiểu tại sao Optimism yêu cầu mạng P2P ** LibbP2P là một giao thức mạng mô-đun cho phép các nhà phát triển xây dựng các ứng dụng ngang hàng phi tập trung cho nhiều trường hợp sử dụng (nguồn[11] [12])(nguồn [13])。 Mặt khác, DevP2P chủ yếu được sử dụng trong hệ sinh thái Ethereum và được thiết kế riêng cho các ứng dụng Ethereum (Nguồn )。 Tính linh hoạt và khả năng ứng dụng rộng rãi của libp2p có thể làm cho nó trở thành lựa chọn hàng đầu cho các nhà phát triển.

Op-Node chủ yếu sử dụng các điểm chức năng libp2p

  • Được sử dụng bởi trình sắp xếp để chuyển khối không an toàn kết quả đến các nút không trình tự khác
  • Đối với các nút khác ở chế độ không trình tự để đồng bộ hóa nhanh khi xảy ra khoảng cách (đồng bộ hóa chuỗi ngược)
  • Một môi trường tốt để áp dụng một hệ thống danh tiếng tích hợp để điều chỉnh nút tổng thể

Triển khai mã

khởi tạo tùy chỉnh máy chủ lưu trữ

Host có thể hiểu là một P2P node, khi mở node này, bạn cần thực hiện một số cấu hình khởi tạo đặc biệt cho project của riêng mình

Bây giờ chúng ta hãy xem phương thức Host trong tập tin op-node/p2p/host.go.

Chức năng này chủ yếu được sử dụng để thiết lập máy chủ libp2p và tạo các cấu hình khác nhau. Dưới đây là các phần chính của hàm và mô tả đơn giản bằng tiếng Trung về từng phần:

  1. ** Kiểm tra xem P2P có bị tắt không **
    Nếu P2P bị tắt, hàm sẽ trả về trực tiếp.
  2. Nhận Peer ID từ Public Key
    Sử dụng khóa công khai trong cấu hình để tạo Peer ID.
  3. Khởi tạo Peerstore
    Tạo cửa hàng Peerstore cơ sở.
  4. Khởi tạo tiện ích mở rộng Peerstore
    Trên đầu Peerstore cơ sở, hãy tạo Peerstore mở rộng.
  5. **Thêm khóa riêng và khóa công khai vào Peerstore **
    Lưu trữ khóa riêng tư và khóa công khai của đồng nghiệp trong peerstore.
  6. Khởi tạo Connection Gater
    Được sử dụng để điều khiển các kết nối mạng.
  7. Khởi tạo Trình quản lý kết nối
    Được sử dụng để quản lý các kết nối mạng.
  8. ** Đặt địa chỉ truyền và nghe **
    Đặt giao thức truyền tải mạng và địa chỉ nghe của máy chủ.
  9. **Tạo máy chủ libp2p **
    Sử dụng tất cả các cài đặt trước đó để tạo một máy chủ libp2p mới.
  10. Khởi tạo ngang hàng tĩnh
    Nếu bạn đã cấu hình một peer tĩnh, hãy khởi tạo nó.
  11. Quay lại máy chủ
    Cuối cùng, hàm trả về máy chủ libp2p đã tạo.

Các phần chính này chịu trách nhiệm khởi tạo và thiết lập máy chủ libp2p và mỗi phần chịu trách nhiệm về một khía cạnh cụ thể của cấu hình máy chủ.

func (conf * Config) Máy chủ (nhật ký nhật ký. Logger, số liệu phóng viên. Phóng viên, số liệu HostMetrics) (máy chủ. Máy chủ, lỗi) {  
    nếu nhầm lẫn. Vô hiệu hóaP2P {  
        Trả lại không, không  
    }  
    pub := conf. Priv.GetPublic()  
    pid, err := ngang hàng. IDFromPublicKey (pub)  
    nếu err != nil {  
        Trả lại Nil, FMT. Errorf("không thể lấy pubkey từ khóa riêng tư mạng: %w", err)  
    }  
    basePs, err := pstoreds. NewPeerstore (ngữ cảnh. Bối cảnh(), conf. Lưu trữ, lưu trữ. DefaultOpts())  
    nếu err != nil {  
        Trả lại Nil, FMT. Errorf("không thể mở peerstore: %w", err)  
    }  
    peerScoreParams := conf. PeerScoringParams()  
     scoreThời gian lưu giữ. Trường độ  
    nếu peerScoreParams != nil {  
        Sử dụng cùng một khoảng thời gian lưu giữ như tin đồn nếu có  
        scoreRetention = peerScoreParams.PeerScoring.RetainScore  
    } else {  
        Vô hiệu hóa điểm GC nếu tính năng chấm điểm ngang hàng bị tắt  
        scoreRetention = 0  
    }  
    ps, err := cửa hàng. NewExtendedPeerstore (ngữ cảnh. Background(), log, đồng hồ. Đồng hồ, basePs, conf. Lưu trữ, điểm sốLưu giữ)  
    nếu err != nil {  
        Trả lại Nil, FMT. Errorf("không thể mở peerstore mở rộng: %w", err)  
    }  
    nếu err := ps. AddPrivKey (pid, conf. Tư nhân); err != nil {  
        Trả lại Nil, FMT. Errorf("không thể thiết lập peerstore với khóa priv: %w", err)  
    }  
    nếu err := ps. AddPubKey (pid, quán rượu); err != nil {  
        Trả lại Nil, FMT. Errorf("không thể thiết lập peerstore với khóa pub: %w", err)  
    }  
     connGtr ăn uống. ChặnConnectionGater  
    connGtr, err = gating. NewBlockingConnectionGater(conf. Cửa hàng)  
    nếu err != nil {  
        Trả lại Nil, FMT. Errorf("không thể mở cổng kết nối: %w", lỗi)  
    }  
    connGtr = ăn uống. AddBanExpiry(connGtr, ps, log, clock. Đồng hồ, hệ mét)  
    connGtr = ăn uống. AddMetering(connGtr, số liệu)  
    connMngr, err := DefaultConnManager(conf)  
    nếu err != nil {  
        Trả lại Nil, FMT. Errorf("không thể mở trình quản lý kết nối: %w", lỗi)  
    }  
    listenAddr, err := addrFromIPAndPort(conf. Nghe, nhầm lẫn. NgheTCPPort)  
    nếu err != nil {  
        Trả lại Nil, FMT. Errorf("không thể làm cho listen addr: %w", err)  
    }  
    tcpGiao thông vận tải := libp2p. Giao thông vận tải(  
        Tcp. NewTCPTransport,  
        Tcp. WithConnectionTimeout (thời gian. Phút * 60)) // phá vỡ các kết nối không sử dụng  
    TODO: về mặt kỹ thuật chúng ta cũng có thể chạy node trên websocket và QUIC transports Có lẽ trong tương lai?  
     Nat lconf. NATManagerC // bị vô hiệu hóa nếu không  
    nếu nhầm lẫn. NAT {  
        NAT = Basichost. NewNATManager  
    }  
    Chọn := []libp2p. Tùy chọn{  
        libp2p. Danh tính (conf. Tư nhân),  
        Đặt rõ ràng tác nhân người dùng, vì vậy chúng tôi có thể phân biệt với những người dùng Go libp2p khác.  
        libp2p. UserAgent(conf. UserAgent),  
        tcpGiao thông vận tải,  
        libp2p. WithDialTimeout(conf. TimeoutDial),  
        Không có dịch vụ chuyển tiếp, chỉ kết nối trực tiếp giữa các đồng nghiệp.  
        libp2p. DisableRelay(),  
        Host sẽ khởi động và lắng nghe Network trực tiếp sau khi xây dựng từ Config.  
        libp2p. ListenAddrs (listenAddr),  
        libp2p. ConnectionGater (connGtr),  
        libp2p. ConnectionManager (connMngr),  
        libp2p. ResourceManager (nil), // TODO sử dụng giao diện quản lý tài nguyên để quản lý tài nguyên cho mỗi đồng nghiệp tốt hơn.  
        libp2p. Ping (đúng),  
        libp2p. AutoNATServiceRateLimit(10, 5, thời gian. Thứ hai * 60),  
    }  
    nếu nhầm lẫn. NoTransportSecurity {  
    } else {  
        opts = append(opts, conf. Máy chủBảo mật...)  
    }  
    h, err := libp2p. Mới (chọn...)  
    nếu err != nil {  
    }  
    cho i, peerAddr := phạm vi conf. StaticPeers {  
        nếu err != nil {  
        }  
        tĩnh Đồng đẳng[i]  = addr  
    }  
    out := &extraHost{  
        Chủ nhà: h,  
        tĩnhĐồng đẳng: tĩnhPeers,  
    }  
        đi out.monitorStaticPeers()  
    }  






        ...  
        n.gs, err = NewGossipSub(resourcesCtx, n.host, rollupCfg, setup, n.scorer, metrics, log)  
        nếu err != nil {  
        }  
        ...  
  1. Tạo trình xác thực: ** Tạo tên chủ đề khối **:
  • Tạo blocksTopicName bằng hàm blocksTopicV1, định dạng một chuỗi dựa trên L2ChainID trong cấu hình (cfg). Các chuỗi được định dạng tuân theo một cấu trúc cụ thể: /optimism/{L2ChainID}/0/blocks.

  • Chức năng bằng cách gọi PS. Tham gia (blocksTopicName) cố gắng thêm một chủ đề tin đồn khối. Nếu xảy ra lỗi, nó sẽ trả về thông báo lỗi cho biết không thể nối chủ đề.

  • Tạo một goroutine mới để ghi lại các sự kiện chủ đề bằng cách sử dụng hàm LogTopicEvents.

  • Tạo thuê bao bằng chức năng MakeSubscriber, đóng gói một BlocksHandler xử lý các sự kiện OnUnsafeL2Payload từ gossipIn. Một goroutine mới đã được tạo ra để chạy subion được cung cấp.

    func JoinGossip(p2pCtx bối cảnh. Ngữ cảnh, tự peer.ID, ps *pubsub. PubSub, nhật ký nhật ký. Logger, cfg * rollup. Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, lỗi) {
    blocksTopicName := blocksTopicV1(cfg) // trả về fmt. Sprintf("/optimism/%s/0/blocks", cfg. L2ChainID.String())
    Trả lại Nil, FMT. Errorf("không thể đăng ký chặn chủ đề tin đồn: %w", err)
    }
    blocksTopic, err := ps. Tham gia (blocksTopicName)
    nếu err != nil {
    }
    nếu err != nil {
    }
    đi LogTopicEvents(p2pCtx, nhật ký. Mới("chủ đề", "khối"), blocksTopicEvents)
    nếu err != nil {
    }
    return &publisher{log: log, cfg: cfg, blocksTopic: blocksTopic, runCfg: runCfg}, nil

op-node / rollup / driver / state.go

func (s *Driver) eventLoop() {  
    cho(){  
        ...  
            payload, err := s.sequencer.RunNextSequencerAction(ctx)  
            nếu err != nil {  
                Lỗi không đủ nghiêm trọng để thay đổi / tạm dừng trình tự nhưng cần được ghi lại và đo lường.  
                nếu err := s.network.PublishL2Payload(ctx, payload); err != nil {  
            }  
            planSequencerAction() // lên lịch hành động trình tự tiếp theo để giữ vòng lặp trình tự  
            }  
    }  
    ...  

Khi có các khối bị thiếu, đồng bộ hóa nhanh qua P2P

Khi các nút được liên kết lại sau thời gian chết do các trường hợp đặc biệt, chẳng hạn như liên kết lại sau thời gian chết, một số khối (khoảng trống) có thể không được đồng bộ hóa và khi gặp phải tình huống này, chúng có thể nhanh chóng đồng bộ hóa thông qua chuỗi ngược của mạng P2P.

Chúng ta hãy xem hàm checkForGapInUnsafeQueue trong op-node/rollup/driver/state.go

Đoạn mã định nghĩa một phương thức gọi là checkForGapInUnsafeQueue, thuộc cấu trúc Trình điều khiển. Mục đích của nó là kiểm tra các khoảng trống dữ liệu trong một hàng đợi được gọi là "hàng đợi không an toàn" và cố gắng truy xuất các tải trọng bị thiếu thông qua một phương thức đồng bộ hóa thay thế được gọi là altSync. Điểm mấu chốt ở đây là phương pháp này là đảm bảo tính liên tục của dữ liệu và cố gắng truy xuất dữ liệu bị thiếu từ các phương pháp đồng bộ hóa khác khi phát hiện thiếu dữ liệu. Dưới đây là các bước chính của hàm:

  1. Hàm đầu tiên nhận được UnsafeL2Head và UnsafeL2SyncTarget từ s.derivation làm điểm bắt đầu và điểm kết thúc của phạm vi kiểm tra.

  2. Chức năng kiểm tra các khối bị thiếu giữa bắt đầu và kết thúc, được thực hiện bằng cách so sánh các giá trị Số của kết thúc và bắt đầu.

  3. Nếu phát hiện khoảng cách dữ liệu, hàm sẽ yêu cầu phạm vi dữ liệu bị thiếu bằng cách gọi s.altSync.RequestL2Range (ctx, start, end). Nếu kết thúc là một tham chiếu null (tức là eth. L2BlockRef{}), hàm sẽ yêu cầu đồng bộ hóa phạm vi mở, bắt đầu bằng bắt đầu.

  4. Khi yêu cầu dữ liệu, hàm sẽ ghi nhật ký gỡ lỗi cho biết phạm vi dữ liệu mà nó đang yêu cầu.

  5. Hàm cuối cùng trả về một giá trị lỗi. Nếu không có lỗi, nó trả về không

    checkForGapInUnsafeQueue kiểm tra xem có khoảng trống trong hàng đợi không an toàn hay không và cố gắng truy xuất các tải trọng bị thiếu từ phương thức alt-sync.
    CẢNH BÁO: Đây chỉ là tín hiệu đi, các khối không được đảm bảo sẽ được truy xuất.
    Kết quả được nhận thông qua OnUnsafeL2Payload.
    func (s * Trình điều khiển) checkForGapInUnsafeQueue (ngữ cảnh ctx. Ngữ cảnh) lỗi {
    start := s.derivation.UnsafeL2Head()
    end := s.derivation.UnsafeL2SyncTarget()
    Kiểm tra xem chúng tôi có thiếu khối giữa đầu và cuối hay không Yêu cầu chúng nếu chúng tôi làm vậy.
    if end == (ETH. L2BlockRef{}) {
    s.log.Debug("yêu cầu đồng bộ hóa với phạm vi kết thúc mở", "bắt đầu", bắt đầu)
    trả về s.altSync.RequestL2Range(ctx, start, eth. L2BlockRef{})
    } khác nếu kết thúc. Số > bắt đầu. Số+1 {
    s.log.Debug("yêu cầu thiếu phạm vi khối L2 không an toàn", "bắt đầu", bắt đầu, "kết thúc", kết thúc, "kích thước", kết thúc. Số-start.Number)
    trả về s.altSync.RequestL2Range(ctx, start, end)
    }
    Trả lại không
    }

Hàm RequestL2Range báo hiệu sự bắt đầu và kết thúc của khối yêu cầu đến kênh yêu cầu.

Yêu cầu sau đó được phân phối đến kênh peerRequests thông qua phương thức onRangeRequest và kênh peerRequests được chờ bởi vòng lặp được mở bởi nhiều peer, tức là chỉ có một peer sẽ xử lý yêu cầu cho mỗi bản phân phối.

func (s * SyncClient) onRangeRequest (ngữ cảnh ctx. Context, req rangeRequest) {  
        ...  
        với i := uint64(0); ; i++ {  
        num := req.end.Number - 1 - i  
        if num <= req.start {  
            về  
        }  
        Kiểm tra xem chúng tôi đã có thứ gì đó trong khu cách ly chưa  
        nếu h, ok := s.quarantineByNum[num] ; ok {  
            nếu s.trusted.Contains(h) { // nếu chúng ta tin tưởng nó, hãy cố gắng quảng bá nó.  
                s.tryPromote(h)  
            }  
            Đừng lấy những thứ mà chúng tôi đã có ứng cử viên.  
            Chúng tôi sẽ loại bỏ nó khỏi khu vực cách ly bằng cách tìm xung đột hoặc nếu chúng tôi đồng bộ hóa đủ các khối khác  
            tiếp tục  
        }  
        nếu _, ok := s.inFlight[num] ; ok {  
            .log. Gỡ lỗi ("yêu cầu vẫn đang bay, không đổi lịch yêu cầu đồng bộ hóa", "num", num)  
            Tiếp tục // Yêu cầu vẫn còn trong chuyến bay  
        }  
        pr := peerRequest{num: num, complete: new(atomic. Bool)}  
        .log. Gỡ lỗi("Lập lịch yêu cầu khối P2P", "num", num)  
        Số lịch trình  
        chọn {  
        trường hợp s.peerRequests <- pr:  
            s.inChuyến bay[num]  = pr.complete  
        Trường hợp <-CTX. Xong():  
            .log. Thông tin ("không lên lịch phạm vi đồng bộ hóa P2P đầy đủ", "hiện tại", num, "err", ctx. Lỗi())  
            về  
        Mặc định: // Tất cả các đồng nghiệp có thể đang bận xử lý yêu cầu  
            .log. Thông tin ("không có đồng nghiệp nào sẵn sàng xử lý các yêu cầu chặn cho nhiều yêu cầu P2P hơn cho lịch sử khối L2", "hiện tại", num)  
            về  
        }  
    }  
}

Hãy xem điều gì sẽ xảy ra khi peer nhận được yêu cầu này.

Điều đầu tiên chúng ta cần biết là liên kết giữa peer và nút yêu cầu, hoặc thông điệp truyền, được truyền qua luồng libp2p. Phương thức xử lý của luồng được thực hiện bởi nút ngang hàng nhận và việc tạo luồng được mở bởi nút gửi.

Chúng ta có thể thấy mã này trong hàm init trước đó, trong đó MakeStreamHandler trả về trình xử lý và SetStreamHandler liên kết ID giao thức với trình xử lý này, vì vậy bất cứ khi nào nút gửi tạo và sử dụng luồng này, trình xử lý trả về sẽ được kích hoạt.

n.syncSrv = NewReqRespServer(rollupCfg, l2Chain, số liệu)  
Đăng ký giao thức đồng bộ hóa với máy chủ libp2p  
payloadByNumber := MakeStreamHandler(resourcesCtx, log. Mới("phục vụ", "payloads_by_number"), n.syncSrv.HandleSyncRequest)  
n.host.SetStreamHandler(PayloadByNumberProtocolID(rollupCfg.L2ChainID), payloadByNumber)

Hãy xem cách nó được xử lý bên trong trình xử lý Trước tiên, hàm thực hiện kiểm tra giới hạn tỷ lệ toàn cầu và riêng lẻ để kiểm soát tốc độ xử lý yêu cầu. Sau đó, nó đọc và xác minh số khối được yêu cầu, đảm bảo rằng nó nằm trong một phạm vi hợp lý. Hàm sau đó nhận tải trọng đoạn của yêu cầu từ lớp L2 và ghi nó vào luồng phản hồi. Khi ghi dữ liệu phản hồi, nó đặt thời hạn ghi để tránh bị chặn bởi các kết nối ngang hàng chậm trong quá trình ghi. Cuối cùng, hàm trả về số khối được yêu cầu và các lỗi có thể xảy ra.

func (srv * ReqRespServer) handleSyncRequest (ctx context. Bối cảnh, mạng luồng. luồng) (uint64, lỗi) {  
    peerId := luồng. Conn(). RemotePeer()  
    lấy mã thông báo từ giới hạn tỷ giá toàn cầu,  
    Để đảm bảo không có quá nhiều công việc máy chủ đồng thời giữa các đồng nghiệp khác nhau.  
    nếu err := srv.globalRequestsRL.Wait(ctx); err != nil {  
        trả về 0, fmt. Errorf("đã hết thời gian chờ giới hạn tốc độ đồng bộ hóa chung: %w", lỗi)  
    }  
    Tìm dữ liệu giới hạn tỷ lệ của các đồng nghiệp hoặc thêm bằng cách khác  
    srv.peerStatsLock.Lock()  
    ps, _ := srv.peerRateLimits.Get(peerId)  
    nếu ps == nil {  
        ps = &peerStat{  
            Yêu cầu: tỷ lệ. NewLimiter (peerServerBlocksRateLimit, peerServerBlocksBurst),  
        }  
        srv.peerRateLimits.Add(peerId, ps)  
        Ps. Requests.Reserve() // đếm lượt truy cập, nhưng làm cho nó trì hoãn yêu cầu tiếp theo thay vì chờ đợi ngay lập tức  
    } else {  
        Chỉ đợi nếu đó là một đồng nghiệp hiện có, nếu không cuộc gọi Chờ giới hạn tốc độ tức thì luôn bị lỗi.  
        Nếu người yêu cầu nghĩ rằng chúng tôi mất quá nhiều thời gian, thì đó là vấn đề của họ và họ có thể ngắt kết nối.  
        Chúng ta sẽ ngắt kết nối với chính mình chỉ khi không đọc / viết,  
        nếu công việc không hợp lệ (xác thực phạm vi) hoặc khi các tác vụ phụ riêng lẻ hết thời gian chờ.  
        nếu err := ps. Yêu cầu.Chờ (ctx); err != nil {  
            trả về 0, fmt. Errorf("đã hết thời gian chờ giới hạn tốc độ đồng bộ hóa chung: %w", lỗi)  
        }  
    }  
    srv.peerStatsLock.Mở khóa()  
    Đặt thời hạn đọc, nếu có  
    _ = luồng. SetReadDeadline(thời gian. Bây giờ(). Thêm (serverReadRequestTimeout))  
    Đọc yêu cầu  
     REQ UINT64  
    nếu err := nhị phân. Đọc (luồng, nhị phân. LittleEndian, &req); err != nil {  
        trả về 0, fmt. Errorf("không thể đọc số khối được yêu cầu: %w", lỗi)  
    }  
    nếu err := luồng. CloseRead(); err != nil {  
        trả lại req, fmt. Errorf("không thể đóng phía đọc của cuộc gọi yêu cầu đồng bộ hóa P2P: %w", lỗi)  
    }  
    Kiểm tra xem yêu cầu có nằm trong phạm vi khối dự kiến không  
    if req < srv.cfg.Genesis.L2.Number {  
        trả lại req, fmt. Errorf("cannot serve request for L2 block %d before genesis %d: %w", req, srv.cfg.Genesis.L2.Number, invalidRequestErr)  
    }  
    max, err := srv.cfg.TargetBlockNumber(uint64(time. Bây giờ(). Unix()))  
    nếu err != nil {  
        trả lại req, fmt. Errorf("không thể xác định số khối đích tối đa để xác minh yêu cầu: %w", invalidRequestErr)  
    }  
    nếu req > max {  
        trả lại req, fmt. Errorf("cannot serve request for L2 block %d after max expected block (%v): %w", req, max, invalidRequestErr)  
    }  
    payload, err := srv.l2.PayloadByNumber (ctx, req)  
    nếu err != nil {  
        nếu có lỗi. Là(err, ethereum. NotFound) {  
            trả lại req, fmt. Errorf("peer request unknown block by number: %w", err)  
        } else {  
            trả lại req, fmt. Errorf("không thể truy xuất payload để phục vụ ngang hàng: %w", err)  
        }  
    }  
    Chúng tôi đặt thời hạn ghi, nếu có, để viết một cách an toàn mà không chặn kết nối ngang hàng điều tiết  
    _ = luồng. SetWriteDeadline(thời gian. Bây giờ(). Thêm (serverWriteChunkTimeout))  
    0 - resultCode: thành công = 0  
    1:5 - phiên bản: 0  
     .tmp [5] byte  
    nếu _, err := luồng. Viết (tmp[:]); err != nil {  
        trả lại req, fmt. Errorf("không thể ghi dữ liệu tiêu đề phản hồi: %w", err)  
    }  
    w := linh hoạt. NewBufferedWriter (luồng)  
    if _, err := payload. Nguyên soáiSSZ (w); err != nil {  
        trả lại req, fmt. Errorf("không thể ghi payload để đồng bộ hóa phản hồi: %w", err)  
    }  
    nếu err := w.Close(); err != nil {  
        trả lại req, fmt. Errorf("không thể hoàn thành việc ghi payload để đồng bộ hóa phản hồi: %w", err)  
    }  
    trả lại req, nil  
}

Tại thời điểm này, quy trình chung của yêu cầu và xử lý đồng bộ hóa chuỗi ngược đã được giải thích

Hệ thống danh tiếng điểm trong các nút p2p

Để ngăn chặn một số nút nhất định đưa ra các yêu cầu và phản hồi độc hại làm suy yếu tính bảo mật của toàn bộ mạng, Optimism cũng sử dụng hệ thống điểm.

Ví dụ: trong op-node / p2p / app_scores.go, có một loạt các hàm để đặt điểm số của một peer

func (s *peerApplicationScorer) onValidResponse(id peer.ID) {  
    _, err := s.scorebook.SetScore(id, store. IncrementValidResponses{Cap: s.params.ValidResponseCap})  
    nếu err != nil {  
        s.log.Error ("Không thể cập nhật điểm ngang hàng", "ngang hàng", id, "err", err)  
        về  
    }  
}  
func (s *peerApplicationScorer) onResponseError(id peer.ID) {  
    _, err := s.scorebook.SetScore(id, store. IncrementErrorResponses{Cap: s.params.ErrorResponseCap})  
    nếu err != nil {  
        s.log.Error ("Không thể cập nhật điểm ngang hàng", "ngang hàng", id, "err", err)  
        về  
    }  
}  
func (s *peerApplicationScorer) onRejectedPayload(id peer.ID) {  
    _, err := s.scorebook.SetScore(id, store. IncrementRejectedPayloads{Cap: s.params.RejectedPayloadCap})  
    nếu err != nil {  
        s.log.Error ("Không thể cập nhật điểm ngang hàng", "ngang hàng", id, "err", err)  
        về  
    }  
}

Trạng thái tích hợp của nút mới sau đó được kiểm tra trước khi nó được thêm vào

func AddScoring(gater BlockingConnectionGater, điểm số Scores, minScore float64) *ScoringConnectionGater {  
    return &ScoringConnectionGater{BlockingConnectionGater: gater, scores: scores, minScore: minScore}  
}  
func (g *ScoringConnectionGater) checkScore(p peer.ID) (cho phép bool) {  
    score, err := g.scores.GetPeerScore(p)  
    nếu err != nil {  
        Trả về FALSE  
    }  
    điểm trả về >= g.minScore  
}  
func (g *ScoringConnectionGater) InterceptPeerDial(p peer.ID) (cho phép bool) {  
    trả về g.BlockingConnectionGater.InterceptPeerDial(p) &&; g.checkScore(p)  
}  
func (g *ScoringConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr. Multiaddr) (cho phép bool) {  
    trả về g.BlockingConnectionGater.InterceptAddrDial(id, ma) &&; g.checkScore(id)  
}  
func (g * ScoringConnectionGater) InterceptSecure (dir mạng. Hướng, id peer.ID, mạng mas. ConnMultiaddrs) (cho phép bool) {  
    trả về g.BlockingConnectionGater.InterceptSecured(dir, id, mas) &&; g.checkScore(id)  
}

Tóm tắt

Khả năng cấu hình cao của libp2p làm cho toàn bộ dự án p2p có khả năng tùy biến và mô-đun cao, trên đây là logic chính của việc triển khai libp2p được cá nhân hóa của optimsim và các chi tiết khác có thể được tìm hiểu chi tiết bằng cách đọc mã nguồn trong thư mục p2p.

Xem bản gốc
Trang này có thể chứa nội dung của bên thứ ba, được cung cấp chỉ nhằm mục đích thông tin (không phải là tuyên bố/bảo đảm) và không được coi là sự chứng thực cho quan điểm của Gate hoặc là lời khuyên về tài chính hoặc chuyên môn. Xem Tuyên bố từ chối trách nhiệm để biết chi tiết.
  • Phần thưởng
  • Bình luận
  • Chia sẻ
Bình luận
0/400
Không có bình luận
  • Ghim
Giao dịch tiền điện tử mọi lúc mọi nơi
qrCode
Quét để tải xuống ứng dụng Gate
Cộng đồng
Tiếng Việt
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)