Op-stack'te libp2p kullanımı

Joohhnnn tarafından yazıldı.

İyimserlikte Libp2P uygulamaları

Bu bölümde temel amaç, Optimism'in Op-Node'da P2P ağlarının kurulumunu tamamlamak için Libb2P'yi nasıl kullandığını açıklamaktır. P2P ağları esas olarak, Unsafe'in blok yapısını tamamladıktan sonra sıralayıcılar gibi farklı düğümlerde bilgi iletmek ve P2P'nin gossiphub'ının pub/sub'ı aracılığıyla yayılmak için kullanılır. Libb2P ayrıca P2P ağlarında ağ oluşturma, adresleme vb. gibi diğer altyapılarla da ilgilenir.

libp2p hakkında bilgi edinin

libp2p ("Library Peer-to-Peer" veya "Library Peer-to-Peer" olarak kısaltılır), P2P uygulamalarının geliştirilmesine yardımcı olan bir eşler arası (P2P) ağ çerçevesidir. Ağ katılımcıları ("eşler" veya "eşler" olarak da bilinir) arasındaki P2P iletişimini kolaylaştıran bir dizi protokol, özellik ve kitaplık içerir (kaynak[2] [3])。 Başlangıçta IPFS (Gezegenler Arası Dosya) projesinin bir parçası olan libp2p, dağıtılmış ağlar için modüler bir ağ yığını (kaynak) haline gelen bağımsız bir projeye dönüştü )。

libp2p, IPFS topluluğunun, spesifikasyonların yazılmasına, kod uygulamalarına ve örnekler ve öğreticiler oluşturmaya yardımcı olmak da dahil olmak üzere çok çeşitli toplulukların katkılarını memnuniyetle karşılayan açık kaynaklı bir projesidir[4] [5])。 libp2p, her biri çok net, belgelenmiş ve test edilmiş bir arayüze sahip birden fazla yapı taşından oluşur ve bu da onları birleştirilebilir, değiştirilebilir ve dolayısıyla yükseltilebilir kılar )。 libp2p'nin modüler yapısı, geliştiricilerin yalnızca uygulamaları için gerekli bileşenleri seçmelerine ve kullanmalarına olanak tanıyarak P2P web uygulamaları oluştururken esnekliği ve verimliliği artırır.

İlgili kaynaklar

  • libp2p için resmi belgeler[6]
  • libp2p'nin GitHub deposu[7]
  • ProtoSchool'da libp2p'ye giriş[8]

libp2p'nin modüler mimarisi ve açık kaynak yapısı, güçlü, ölçeklenebilir ve esnek P2P uygulamaları geliştirmek için iyi bir ortam sağlayarak onu dağıtılmış ağlar ve ağ uygulaması geliştirme alanında önemli bir oyuncu haline getirir.

libp2p uygulaması

libp2p kullanırken, P2P ağınızı oluşturmak için bazı temel bileşenleri uygulamanız ve yapılandırmanız gerekecektir. Uygulamada libp2p'nin ana uygulama yönlerinden bazıları şunlardır:

1. Düğüm Oluşturma ve Yapılandırma:

  • Bir libp2p düğümü oluşturmak ve yapılandırmak, düğümün ağ adresini, kimliğini ve diğer temel parametrelerini ayarlamayı içeren en temel adımdır. Anahtar kullanım kodları:

libp2p'yi seçin. Yeni()

2. Taşıma Protokolü:

Düğümler arasında iletişimi sağlamak için aktarım protokollerinizi (ör. TCP, WebSockets vb.) seçin ve yapılandırın. Anahtar kullanım kodları:

tcpTransport := tcp. YeniTCPTransport()

3. Çoğullama ve Akış Kontrolü:

  • Birden fazla eşzamanlı veri akışının tek bir bağlantıda işlenmesine izin vermek için çoğullama uygulayın.
  • Veri iletim hızını ve işleme hızını yönetmek için akış kontrolünü uygulayın. Anahtar kullanım kodları:

yamuxTransport := yamux. Yeni()

4. Güvenlik ve şifreleme:

  • İletişimin güvenliğini ve gizliliğini sağlamak için güvenli bir taşıma katmanı yapılandırın.
  • Verileri korumak ve iletişim kuran tarafların kimliğini doğrulamak için şifreleme ve kimlik doğrulama mekanizmaları uygulayın. Anahtar kullanım kodları:

tlsTransport := tls. Yeni()

5. Protokoller ve Mesaj İşleme:

Belirli ağ işlemlerini ve mesaj alışverişlerini işlemek için özel protokoller tanımlayın ve uygulayın.

  • Alınan mesajları işleyin ve gerektiğinde yanıtları gönderin. Anahtar kullanım kodları:

ev sahibi. SetStreamHandler("/protokolüm/1.0.0", myProtocolHandler)

6. Keşif ve Yönlendirme:

  • Ağdaki diğer düğümleri bulmak için bir düğüm keşif mekanizması uygulayın.
  • İletilerin ağdaki doğru düğümlere nasıl yönlendirildiğini belirlemek için yönlendirme mantığı uygulayın. Anahtar kullanım kodları:

dht := kaddht. NewDHT(ctx, ana bilgisayar, veri deposu. NewMapDatastore())

7. Ağ davranışı ve politikaları:

Bağlantı yönetimi, hata işleme ve yeniden deneme mantığı gibi ağ davranışlarını ve ilkelerini tanımlayın ve uygulayın. Anahtar kullanım kodları:

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

8. Durum Yönetimi ve Depolama:

Bağlantı durumu, düğüm listesi ve veri depolama dahil olmak üzere düğümlerin ve ağların durumunu yönetin. Anahtar kullanım kodları:

peerstore := pstoremem. YeniEşler Deposu()

9. Test edin ve hata ayıklayın:

  • Doğruluğundan ve güvenilirliğinden emin olmak için libp2p uygulamanız için testler yazın.
  • Ağ sorunlarını tanılamak ve çözmek için hata ayıklama araçlarını ve günlüklerini kullanın. Anahtar kullanım kodları:

günlük tutmak. SetLogLevel("libp2p", "HATA AYIKLA")

10. Dokümantasyon ve Topluluk Desteği:

  • Çeşitli bileşenleri ve API'leri hakkında bilgi edinmek için libp2p'nin belgelerine bakın.
  • Destek ve problem çözme için libp2p topluluğuyla iletişim kurun.

}

Yukarıdakiler, libp2p kullanırken göz önünde bulundurulması ve uygulanması gereken ana hususlardan bazılarıdır. Her projenin özel uygulaması değişebilir, ancak bu temel yönler libp2p uygulamaları oluşturmak ve çalıştırmak için gereklidir. Bu işlevleri uygularken, libp2p'nin resmi belgelerine başvurabilirsiniz[9] [10]ve GitHub depoları Örnek kod ve öğreticiler.

OP düğümünde libp2p kullanımı

Op-node ve libp2p arasındaki ilişkiyi anlamak için birkaç soruyu çözmemiz gerekiyor

  • Neden libp2p? Neden devp2p'yi seçmiyorsunuz (geth devp2p kullanıyor)
  • OP düğümündeki P2P ağıyla hangi veriler veya süreçler yakından ilişkilidir?
  • Bu özellikler kod katmanında nasıl uygulanır?

Op-node'ların neden bir libp2p ağına ihtiyacı var?

Öncelikle Optimism'in neden bir P2P ağı gerektirdiğini anlamamız gerekiyor ** LibbP2P, geliştiricilerin birden fazla kullanım durumu için merkezi olmayan eşler arası uygulamalar oluşturmasına olanak tanıyan modüler bir ağ protokolüdür (kaynaklar[11] [12])(kaynak [13])。 DevP2P ise esas olarak Ethereum ekosisteminde kullanılır ve Ethereum uygulamaları için uyarlanmıştır (Kaynak )。 Libp2p'nin esnekliği ve geniş uygulanabilirliği, onu geliştiriciler için en iyi seçim haline getirebilir.

Op-Node esas olarak libp2p işlev noktalarını kullanır

  • Sıralayıcı tarafından, ortaya çıkan güvenli olmayan bloğu sıralayıcı olmayan diğer düğümlere iletmek için kullanılır
  • Bir boşluk oluştuğunda hızlı senkronizasyon için sıralayıcı olmayan moddaki diğer düğümler için (ters zincir senkronizasyonu)
  • Genel düğümü düzenlemek için entegre bir itibar sistemini benimsemek için iyi bir ortam

Kod uygulaması

ana bilgisayar özel başlatma

Ana bilgisayar bir P2P düğümü olarak anlaşılabilir, bu düğümü açarken kendi projeniz için bazı özel başlatma yapılandırmaları yapmanız gerekir

Şimdi op-node/p2p/host.go dosyasındaki Host metoduna bakalım.

Bu işlev esas olarak libp2p ana bilgisayarını kurmak ve çeşitli yapılandırmalar yapmak için kullanılır. İşte işlevin temel kısımları ve her birinin basit bir Çince açıklaması:

  1. P2P'nin devre dışı olup olmadığını kontrol edin
    P2P devre dışı bırakılırsa, işlev doğrudan geri döner.
  2. Ortak Anahtardan Eş Kimliği Alın
    Eş Kimliği oluşturmak için yapılandırmadaki ortak anahtarı kullanın.
  3. Peerstore'u Başlat
    Temel bir Peerstore mağazası oluşturun.
  4. Peerstore uzantısını başlatın
    Temel Peerstore'un üstünde, genişletilmiş bir Peerstore oluşturun.
  5. Peerstore'a özel ve genel anahtarlar ekleyin
    Eşin özel ve genel anahtarlarını eş deposunda depolayın.
  6. Bağlantı Gater'ı Başlat
    Ağ bağlantılarını kontrol etmek için kullanılır.
  7. Bağlantı Yöneticisini Başlat
    Ağ bağlantılarını yönetmek için kullanılır.
  8. İletim ve Dinleme Adresini Ayarlayın
    Ağ aktarım protokolünü ve ana bilgisayarın dinleme adresini ayarlayın.
  9. libp2p ana bilgisayarı oluşturun
    Yeni bir libp2p ana bilgisayarı oluşturmak için önceki tüm ayarları kullanın.
  10. Statik eşi başlat
    Yapılandırılmış bir statik eşiniz varsa, bunu başlatın.
  11. Ev Sahibine Geri Dön
    Son olarak, işlev oluşturulan libp2p ana bilgisayarını döndürür.

Bu anahtar bölümler libp2p ana bilgisayarını başlatmaktan ve kurmaktan sorumludur ve her bölüm ana bilgisayar yapılandırmasının belirli bir yönünden sorumludur.

func (conf *Config) Host(log log. Kaydedici, muhabir metrikleri. Muhabir, metrikler HostMetrics) (ana bilgisayar. Ana bilgisayar, hata) {  
    eğer conf. DisableP2P {  
        Dönüş sıfır, sıfır  
    }  
    pub := conf. Priv.GetPublic()  
    pid, err := eş. IDFromPublicKey(pub)  
    eğer err != nil {  
        Dönüş sıfır, fmt. Errorf("ağ özel anahtarından pubkey türetilemedi: %w", hata)  
    }  
    basePs, err := pstoreds. NewPeerstore(bağlam. background(), conf. Depo, depolanmış. DefaultOpts())  
    eğer err != nil {  
        Dönüş sıfır, fmt. Errorf("peerstore açılamadı: %w", hata)  
    }  
    peerScoreParams := conf. PeerScoringParams()  
     scoreRetention süresi. Süre  
    if peerScoreParams != nil {  
        Varsa, dedikodularla aynı saklama süresini kullanın  
        scoreRetention = peerScoreParams.PeerScoring.RetainScore  
    } başka {  
        Eş puanlaması devre dışı bırakılmışsa GC puanını devre dışı bırak  
        scoreRetention = 0  
    }  
    ps, err := mağaza. NewExtendedPeerstore(bağlam. background(), log, clock. Saat, baseP'ler, conf. Mağaza, scoreRetention)  
    eğer err != nil {  
        Dönüş sıfır, fmt. Errorf("genişletilmiş eş deposu açılamadı: %w", hata)  
    }  
    eğer hata := ps. AddPrivKey(pid, conf. Priv); err != nil {  
        Dönüş sıfır, fmt. Errorf("priv anahtarıyla peerstore kurulamadı: %w", hata)  
    }  
    eğer hata := ps. AddPubKey(pid, pub); err != nil {  
        Dönüş sıfır, fmt. Errorf("pub anahtarıyla peerstore kurulamadı: %w", hata)  
    }  
     connGtr geçit. BlockingConnectionGater  
    connGtr, err = geçit. NewBlockingConnectionGater(conf. Mağaza)  
    eğer err != nil {  
        Dönüş sıfır, fmt. Errorf("bağlantı kapısı açılamadı: %w", hata)  
    }  
    connGtr = geçit. AddBanExpiry(connGtr, ps, günlük, saat. Saat, metrikler)  
    connGtr = geçit. AddMetering(connGtr, metrikler)  
    connMngr, err := DefaultConnManager(conf)  
    eğer err != nil {  
        Dönüş sıfır, fmt. Errorf("bağlantı yöneticisi açılamadı: %w", hata)  
    }  
    listenAddr, err := addrFromIPAndPort(conf. ListenIP, conf. ListenTCPPort)  
    eğer err != nil {  
        Dönüş sıfır, fmt. Errorf("dinleme yapılamadı addr: %w", hata)  
    }  
    tcpTransport := libp2p. Taşıma(  
        TCP. NewTCPTransport,  
        TCP. WithConnectionTimeout(zaman. Dakika*60)) // kullanılmayan bağlantıları kes  
    TODO: teknik olarak düğümü websocket ve QUIC aktarımlarında da çalıştırabiliriz Belki gelecekte?  
     nat lconf. NATManagerC // sıfır ise devre dışı  
    eğer conf. NAT {  
        nat = temel ana bilgisayar. YeniNATManager  
    }  
    opts := []libp2p. Seçenek{  
        libp2p'yi seçin. Kimlik(conf. Özel),  
        Diğer Go libp2p kullanıcılarından ayırt edebilmemiz için kullanıcı aracısını açıkça ayarlayın.  
        libp2p'yi seçin. UserAgent(conf. UserAgent),  
        tcpTransport,  
        libp2p'yi seçin. WithDialTimeout(conf. Zaman Aşımı Kadranı),  
        Aktarma hizmeti yok, yalnızca eşler arasında doğrudan bağlantılar.  
        libp2p'yi seçin. DisableRelay(),  
        Ana bilgisayar, yapılandırmadan oluşturulduktan hemen sonra ağı başlatacak ve dinleyecektir.  
        libp2p'yi seçin. ListenAddrs(listenAddr),  
        libp2p'yi seçin. ConnectionGater(connGtr),  
        libp2p'yi seçin. ConnectionManager(connMngr),  
        libp2p'yi seçin. ResourceManager(nil), // TODO, eş başına kaynakları daha iyi yönetmek için kaynak yöneticisi arabirimini kullanır.  
        libp2p'yi seçin. Ping(doğru),  
        libp2p'yi seçin. AutoNATServiceRateLimit(10, 5, zaman. İkinci*60),  
    }  
    eğer conf. NoTransportSecurity {  
    } başka {  
        opts = append(opts, conf. Ana Bilgisayar Güvenliği...)  
    }  
    h, err := libp2p. Yeni(isteğe bağlı...)  
    eğer err != nil {  
    }  
    for i, peerAddr := range conf. StaticPeers {  
        eğer err != nil {  
        }  
        staticPeers[i]  = addr  
    }  
    out := &extraHost{  
        Ev sahibi: h,  
        staticPeers: staticPeers,  
    }  
        dışarı çık.monitorStaticPeers()  
    }  






        ...  
        n.gs, err = NewGossipSub(resourcesCtx, n.host, rollupCfg, kurulum, n.scorer, metrikler, günlük)  
        eğer err != nil {  
        }  
        ...  
  1. Kimlik Doğrulayıcı Oluşturma: Blok Tema Adı Oluşturma:
  • Yapılandırmadaki (cfg) L2ChainID'ye göre bir dizeyi biçimlendiren blocksTopicV1 işlevini kullanarak blocksTopicName oluşturun. Biçimlendirilmiş dizeler belirli bir yapıyı izler: /optimism/{L2ChainID}/0/blocks.

  • ps'yi çağırarak işlev görür. Join(blocksTopicName) bir blok dedikodu teması eklemeye çalışır. Bir hata oluşursa, konunun birleştirilemediğini belirten bir hata iletisi döndürür.

  • LogTopicEvents işlevini kullanarak konu olaylarını günlüğe kaydetmek için yeni bir goroutine oluşturuldu.

  • GossipIn'den OnUnsafeL2Payload olaylarını işleyen bir BlocksHandler'ı kapsülleyen MakeSubscriber işlevini kullanarak bir abone oluşturuldu. Sağlanan alt iyonu çalıştırmak için yeni bir goroutine oluşturuldu.

    func JoinGossip(p2pCtx context. Bağlam, öz peer.ID, ps *pubsub. PubSub, günlük günlüğü. Kaydedici, cfg *toplama. Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, hata) {
    blocksTopicName := blocksTopicV1(cfg) // return fmt. Sprintf("/iyimserlik/%s/0/bloklar", cfg. L2ChainID.String())
    Dönüş sıfır, fmt. Errorf("bloklar kaydedilemedi dedikodu konusu: %w", hata)
    }
    blocksTopic, err := ps. Join(blocksTopicName)
    eğer err != nil {
    }
    eğer err != nil {
    }
    git LogTopicEvents(p2pCtx, günlük. New("konu", "bloklar"), blocksTopicEvents)
    eğer err != nil {
    }
    return &publisher{log: log, cfg: cfg, blocksTopic, runCfg: runCfg}, nil

op-node/rollup/driver/state.go

func (s *Sürücü) eventLoop() {  
    for(){  
        ...  
            payload, err := s.sequencer.RunNextSequencerAction(ctx)  
            eğer err != nil {  
                Hatalar, sıralamayı değiştirecek/durduracak kadar ciddi değildir, ancak günlüğe kaydedilmeli ve ölçülmelidir.  
                if err := s.network.PublishL2Payload(ctx, payload); err != nil {  
            }  
            planSequencerAction() // sıralama döngüsünü korumak için bir sonraki sıralayıcı eylemini planlayın  
            }  
    }  
    ...  

Eksik bloklar olduğunda, P2P ile hızlı senkronizasyon

Kesinti süresinden sonra yeniden bağlanma gibi özel durumlar nedeniyle düğümler kesinti süresinden sonra yeniden bağlandığında, bazı bloklar (boşluklar) senkronize edilemeyebilir ve bu durumla karşılaştıklarında P2P ağının ters zinciri üzerinden hızlı bir şekilde senkronize olabilirler.

Op-node/rollup/driver/state.go içindeki checkForGapInUnsafeQueue işlevine bir göz atalım

Kod parçacığı, Driver yapısına ait olan checkForGapInUnsafeQueue adlı bir yöntemi tanımlar. Amacı, "güvenli olmayan kuyruk" adı verilen bir kuyruktaki veri boşluklarını kontrol etmek ve altSync adlı alternatif bir senkronizasyon yöntemi aracılığıyla eksik yükleri almaya çalışmaktır. Buradaki kilit nokta, yöntemin veri sürekliliğini sağlamak ve eksik veri tespit edildiğinde eksik verileri diğer senkronizasyon yöntemlerinden almaya çalışmak olmasıdır. İşte fonksiyonun ana adımları:

  1. İşlev ilk olarak kontrol aralığının başlangıç ve bitiş noktaları olarak s.derivation'dan UnsafeL2Head ve UnsafeL2SyncTarget'ı alır.

  2. Fonksiyon, bitiş ve başlangıç Sayı değerlerini karşılaştırarak yapılan başlangıç ve bitiş arasındaki eksik blokları kontrol eder.

  3. Bir veri boşluğu algılanırsa, işlev s.altSync.RequestL2Range(ctx, start, end) öğesini çağırarak eksik veri aralığını isteyecektir. End boş bir referanssa (örn. L2BlockRef{}), işlev start ile başlayan bir açık uçlu aralık senkronizasyonu isteyecektir.

  4. Veri talep ederken, işlev hangi veri aralığını istediğini gösteren bir hata ayıklama günlüğü kaydeder.

  5. İşlev sonunda bir hata değeri döndürür. Herhangi bir hata yoksa, nil döndürür

    checkForGapInUnsafeQueue, güvenli olmayan kuyrukta bir boşluk olup olmadığını kontrol eder ve eksik yükleri bir alt-sync yönteminden almaya çalışır.
    UYARI: Bu sadece giden bir sinyaldir, blokların geri alınması garanti edilmez.
    Sonuçlar OnUnsafeL2Payload aracılığıyla alınır.
    func (s *Sürücü) checkForGapInUnsafeQueue(ctx context. Bağlam) hatası {
    start := s.derivation.UnsafeL2Head()
    end := s.derivation.UnsafeL2SyncTarget()
    Başlangıç ve bitiş arasında eksik bloklarımız olup olmadığını kontrol edin Varsa talep edin.
    if end == (eth. L2BlockRef{}) {
    s.log.Debug("açık uçlu aralıkla senkronizasyon isteniyor", "start", start)
    return s.altSync.RequestL2Range(ctx, start, eth. L2BlockRef{})
    } else if end. > numara başlar. Sayı+1 {
    s.log.Debug("eksik güvenli olmayan L2 blok aralığı isteniyor", "başlangıç", başlangıç, "bitiş", bitiş, "boyut", bitiş. Sayı-başlangıç.Sayı)
    return s.altSync.RequestL2Range(ctx, başlangıç, bitiş)
    }
    Dönüş sıfır
    }

RequestL2Range işlevi, istek bloğunun başlangıcını ve bitişini istekler kanalına bildirir.

İstek daha sonra onRangeRequest yöntemi aracılığıyla peerRequests kanalına dağıtılır ve peerRequests kanalı birden çok eş tarafından açılan döngü tarafından beklenir, yani her dağıtım için isteği yalnızca bir eş işleyecektir.

func (s *SyncClient) onRangeRequest(ctx context. Context, req rangeRequest) {  
        ...  
        i için := uint64(0); ; i++ {  
        num := req.end.Sayı - 1 - i  
        if num <= req.start {  
            dönmek  
        }  
        Karantinada bir şeyimiz olup olmadığını kontrol edin  
        if h, tamam := s.quarantineByNum[num] ; tamam {  
            if s.trusted.Contains(h) { // güveniyorsak tanıtmaya çalışın.  
                s.tryPromote(h)  
            }  
            Zaten adayımız olan şeyleri getirmeyin.  
            Bir çakışma bularak veya yeterince başka blokları senkronize edersek karantinadan çıkaracağız  
            devam etmek  
        }  
        if _, ok := s.inFlight[num] ; tamam {  
            .log. Debug("istek hala yolda, senkronizasyon isteğini yeniden zamanlama değil", "num", num)  
            Devamı // Talep Hala Uçuşta  
        }  
        pr := peerRequest{num: num, complete: new(atomic. Bool)}  
        .log. Debug("P2P blok isteğini zamanlama", "num", num)  
        Program numarası  
        select {  
        case s.peerRequests <- pr:  
            s.inFlight (Uçuş)[num]  = pr.tamamlandı  
        Vaka <-CTX. Bitti():  
            .log. Info("tam P2P senkronizasyon aralığı planlanmadı", "current", num, "err", ctx. Hata())  
            dönmek  
        Varsayılan: // eşlerin tümü zaten istekleri işlemekle meşgul olabilir  
            .log. Info("L2 blok geçmişi için daha fazla P2P isteği için blok isteklerini işlemeye hazır eş yok", "current", num)  
            dönmek  
        }  
    }  
}

Eş bu isteği aldığında ne olacağını görelim.

Bilmemiz gereken ilk şey, eş ve istekte bulunan düğüm arasındaki bağlantının veya iletilen mesajın libp2p akışından geçirildiğidir. Akışın işleme yöntemi, alıcı eş düğüm tarafından uygulanır ve akışın oluşturulması, gönderen düğüm tarafından açılır.

Bu kodu, MakeStreamHandler'ın bir işleyici döndürdüğü ve SetStreamHandler'ın protokol kimliğini bu işleyiciye bağladığı önceki init işlevinde görebiliriz, böylece gönderen düğüm bu akışı her oluşturup kullandığında, döndürülen işleyici tetiklenir.

n.syncSrv = NewReqRespServer(rollupCfg, l2Chain, metrikler)  
Eşitleme protokolünü libp2p ana bilgisayarına kaydedin  
payloadByNumber := MakeStreamHandler(resourcesCtx, log. New("serve", "payloads_by_number"), n.syncSrv.HandleSyncRequest)  
n.host.SetStreamHandler(PayloadByNumberProtocolID(rollupCfg.L2ChainID), payloadByNumber)

İşleyici içinde nasıl ele alındığına bakalım İşlev, isteklerin ne kadar hızlı işlendiğini denetlemek için önce genel ve bireysel hız sınırı denetimleri gerçekleştirir. Ardından, istenen blok numarasını okuyup doğrulayarak makul bir aralıkta olmasını sağlar. İşlev daha sonra L2 katmanından isteğin öbek yükünü alır ve yanıt akışına yazar. Yanıt verilerini yazarken, yazma işlemi sırasında yavaş eş bağlantıları tarafından engellenmeyi önlemek için bir yazma son tarihi ayarlar. Son olarak, fonksiyon istenen blok numarasını ve olası hataları döndürür.

func (srv *ReqRespServer) handleSyncRequest(ctx context. Bağlam, akış ağı. Akış) (uint64, hata) {  
    peerId := akış. Conn() öğesini seçin. RemotePeer()  
    küresel oran sınırlayıcıdan bir jeton alın,  
    Farklı eşler arasında çok fazla eşzamanlı sunucu çalışması olmadığından emin olmak için.  
    if err := srv.globalRequestsRL.Wait(ctx); err != nil {  
        0, fmt dönüşü. Errorf("genel eşitleme hızı sınırı beklenirken zaman aşımına uğradı: %w", hata)  
    }  
    Eşin hız sınırlayıcı verilerini bulun veya başka bir şekilde ekleyin  
    srv.peerStatsLock.Lock()  
    ps, _ := srv.peerRateLimits.Get(peerId)  
    eğer ps == nil {  
        ps = &peerStat{  
            İstekler: oran. NewLimiter(peerServerBlocksRateLimit, peerServerBlocksBurst),  
        }  
        srv.peerRateLimits.Add(peerId, ps)  
        Ps. Requests.Reserve() // isabeti sayın, ancak hemen beklemek yerine bir sonraki isteği geciktirin  
    } başka {  
        Yalnızca mevcut bir eşse bekleyin, aksi takdirde anlık hız sınırı Bekle çağrısı her zaman hata verir.  
        Talepte bulunan kişi çok uzun sürdüğümüzü düşünüyorsa, bu onların sorunudur ve bağlantıyı kesebilir.  
        Sadece okumayı/yazmayı başaramadığımızda bağlantımızı keseceğiz,  
        Çalışma geçersizse (aralık doğrulaması) veya tek tek alt görevler zaman aşımına uğradığında.  
        eğer hata := ps. İstekler.Bekle(ctx); err != nil {  
            0, fmt dönüşü. Errorf("genel eşitleme hızı sınırı beklenirken zaman aşımına uğradı: %w", hata)  
        }  
    }  
    srv.peerStatsLock.Unlock()  
    Varsa son okuma tarihini ayarlayın  
    _ = akış. SetReadDeadline(zaman. Şimdi(). Add(serverReadRequestTimeout))  
    İsteği okuyun  
     req uint64  
    if err := ikili. Read(akış, ikili. LittleEndian, &req); err != nil {  
        0, fmt dönüşü. Errorf("istenen blok numarası okunamadı: %w", hata)  
    }  
    if err := akış. CloseRead(); err != nil {  
        Dönüş Gereksinimi, FMT. Errorf("P2P eşitleme isteği çağrısının okuma tarafı kapatılamadı: %w", hata)  
    }  
    İsteğin beklenen blok aralığında olup olmadığını kontrol edin  
    if req < srv.cfg.Genesis.L2.Number {  
        Dönüş Gereksinimi, FMT. Errorf("genesis %d: %w", req, srv.cfg.Genesis.L2.Number, invalidRequestErr'den önce L2 bloğu için istek sunulamıyor)  
    }  
    max, err := srv.cfg.TargetBlockNumber(uint64(time. Şimdi(). Unix()))  
    eğer err != nil {  
        Dönüş Gereksinimi, FMT. Errorf("isteği doğrulamak için maksimum hedef blok sayısı belirlenemiyor: %w", invalidRequestErr)  
    }  
    if req > max {  
        Dönüş Gereksinimi, FMT. Errorf("beklenen maksimum blok (%v): %w", req, max, invalidRequestErr'den sonra %d L2 bloğu için istek sunulamıyor)  
    }  
    payload, err := srv.l2.PayloadByNumber(ctx, req)  
    eğer err != nil {  
        hata verirse. Is(err, ethereum. Bulunamadı) {  
            Dönüş Gereksinimi, FMT. Errorf("eş, sayıya göre bilinmeyen blok istedi: %w", hata)  
        } başka {  
            Dönüş Gereksinimi, FMT. Errorf("eşe hizmet etmek için yük alınamadı: %w", hata)  
        }  
    }  
    Varsa, kısıtlamalı bir eş bağlantısını engellemeden güvenli bir şekilde yazmak için yazma son tarihini ayarlıyoruz  
    _ = akış. SetWriteDeadline(zaman. Şimdi(). Ekle(serverWriteChunkTimeout))  
    0 - resultCode: başarı = 0  
    1:5 - Sürüm: 0  
     .tmp [5] bayt  
    if _, err := akış. Yaz(tmp[:]); err != nil {  
        Dönüş Gereksinimi, FMT. Errorf("yanıt başlığı verileri yazılamadı: %w", hata)  
    }  
    w := çabuk. NewBufferedWriter(akış)  
    if _, err := yük. MareşalSSZ(w); err != nil {  
        Dönüş Gereksinimi, FMT. Errorf("yanıtı eşitlemek için yük yazılamadı: %w", hata)  
    }  
    if err := w.Close(); err != nil {  
        Dönüş Gereksinimi, FMT. Errorf("yanıtı eşitlemek için yükün yazılması tamamlanamadı: %w", hata)  
    }  
    dönüş gerekliliği, sıfır  
}

Bu noktada, ters zincir senkronizasyon isteği ve işlemenin genel süreci açıklanmıştır

P2p düğümlerinde puan itibar sistemi

Belirli düğümlerin tüm ağın güvenliğini baltalayan kötü niyetli istekler ve yanıtlar vermesini önlemek için Optimism ayrıca bir puan sistemi kullanır.

Örneğin, op-node/p2p/app_scores.go içinde, bir eşin puanını ayarlamak için bir dizi işlev vardır

func (s *peerApplicationScorer) onValidResponse(id peer.ID) {  
    _, err := s.scorebook.SetScore(id, mağaza. IncrementValidResponses{Cap: s.params.ValidResponseCap})  
    eğer err != nil {  
        s.log.Error("Eş puanı güncelleştirilemiyor", "eş", id, "hata", hata)  
        dönmek  
    }  
}  
func (s *peerApplicationScorer) onResponseError(id peer.ID) {  
    _, err := s.scorebook.SetScore(id, mağaza. IncrementErrorResponses{Cap: s.params.ErrorResponseCap})  
    eğer err != nil {  
        s.log.Error("Eş puanı güncelleştirilemiyor", "eş", id, "hata", hata)  
        dönmek  
    }  
}  
func (s *peerApplicationScorer) onRejectedPayload(id peer.ID) {  
    _, err := s.scorebook.SetScore(id, mağaza. IncrementRejectedPayloads{Cap: s.params.RejectedPayloadCap})  
    eğer err != nil {  
        s.log.Error("Eş puanı güncelleştirilemiyor", "eş", id, "hata", hata)  
        dönmek  
    }  
}

Yeni düğümün entegrasyon durumu daha sonra eklenmeden önce kontrol edilir

func AddScoring(gater BlockingConnectionGater, scores Scores, minScore float64) *ScoringConnectionGater {  
    return &ScoringConnectionGater{BlockingConnectionGater: gater, scores: scores, minScore: minScore}  
}  
func (g *ScoringConnectionGater) checkScore(p peer.ID) (bool'a izin ver) {  
    puan, hata := g.scores.GetPeerScore(p)  
    eğer err != nil {  
        false döndür  
    }  
    dönüş puanı >= g.minScore  
}  
func (g *ScoringConnectionGater) InterceptPeerDial(p peer.ID) (bool'a izin ver) {  
    return g.BlockingConnectionGater.InterceptPeerDial(p) & g.checkScore(p)  
}  
func (g *ScoringConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr. Multiaddr) (bool'a izin ver) {  
    return g.BlockingConnectionGater.InterceptAddrDial(id, ma) & g.checkScore(id)  
}  
func (g *ScoringConnectionGater) InterceptSecured(dir ağı. Yön, kimlik peer.ID, mas ağı. ConnMultiaddrs) (bool'a izin ver) {  
    return g.BlockingConnectionGater.InterceptSecured(dir, id, mas) & g.checkScore(id)  
}

Özet

Libp2p'nin yüksek yapılandırılabilirliği, tüm proje p2p'sini son derece özelleştirilebilir ve modüler hale getirir, yukarıdakiler optimsim'in kişiselleştirilmiş libp2p uygulamasının ana mantığıdır ve diğer ayrıntılar p2p dizinindeki kaynak kodunu okuyarak ayrıntılı olarak öğrenilebilir.

View Original
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Reward
  • Comment
  • Share
Comment
0/400
No comments
Trade Crypto Anywhere Anytime
qrCode
Scan to download Gate app
Community
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)