استخدام libp2p في مكدس التشغيل

كتبه Joohhnnn

تطبيقات Libp2P في التفاؤل

في هذا القسم ، الغرض الرئيسي هو شرح كيفية استخدام Optimism Libb2P لإكمال إنشاء شبكات P2P في Op-Node. تستخدم شبكات P2P بشكل أساسي لتمرير المعلومات في عقد مختلفة ، مثل أجهزة التسلسل بعد الانتهاء من بناء كتلة غير آمنة ، والانتشار من خلال الحانة / الفرعية لثرثرة P2P. يتعامل Libb2P أيضا مع البنى التحتية الأخرى مثل الشبكات والعنونة وما إلى ذلك في شبكات P2P.

تعرف على libp2p

libp2p (يختصر من "مكتبة نظير إلى نظير" أو "مكتبة نظير إلى نظير") هو إطار عمل شبكات نظير إلى نظير (P2P) يساعد في تطوير تطبيقات P2P. يحتوي على مجموعة من البروتوكولات والمواصفات والمكتبات التي تجعل اتصال P2P بين المشاركين في الشبكة (المعروف أيضا باسم "الأقران" أو "الأقران") أسهل (المصدر[2] [3])。 في الأصل جزء من مشروع IPFS (ملف بين الكواكب) ، تطور libp2p إلى مشروع قائم بذاته أصبح مكدس شبكة معياري (مصدر) للشبكات الموزعة )。

libp2p هو مشروع مفتوح المصدر لمجتمع IPFS يرحب بالمساهمات من مجموعة واسعة من المجتمعات ، بما في ذلك المساعدة في كتابة المواصفات وتنفيذ التعليمات البرمجية وإنشاء أمثلة وبرامج تعليمية[4] [5])。 يتكون libp2p من كتل بناء متعددة ، لكل منها واجهة واضحة للغاية وموثقة ومختبرة تجعلها قابلة للتركيب والاستبدال وبالتالي قابلة للترقية )。 تسمح الطبيعة المعيارية ل libp2p للمطورين بتحديد واستخدام المكونات الضرورية فقط لتطبيقاتهم ، مما يعزز المرونة والكفاءة عند إنشاء تطبيقات الويب P2P.

موارد ذات صلة

  • الوثائق الرسمية ل libp2p[6]
  • مستودع جيثب libp2p[7]
  • مقدمة إلى libp2p على ProtoSchool[8]

توفر البنية المعيارية وطبيعة المصدر المفتوح ل libp2p بيئة جيدة لتطوير تطبيقات P2P قوية وقابلة للتطوير ومرنة ، مما يجعلها لاعبا مهما في مجال الشبكات الموزعة وتطوير تطبيقات الشبكة.

تنفيذ libp2p

عند استخدام libp2p ، ستحتاج إلى تنفيذ وتكوين بعض المكونات الأساسية لبناء شبكة P2P الخاصة بك. فيما يلي بعض جوانب التنفيذ الرئيسية ل libp2p في التطبيق:

1. ** إنشاء العقدة وتكوينها **:

  • يعد إنشاء عقدة libp2p وتكوينها الخطوة الأساسية ، والتي تتضمن تعيين عنوان شبكة العقدة والهوية والمعلمات الأساسية الأخرى. رموز الاستخدام الرئيسية:

ليب 2 ص. جديد()

2. بروتوكول النقل:

اختر بروتوكولات النقل الخاصة بك وقم بتكوينها (مثل TCP و WebSockets وما إلى ذلك) لضمان الاتصال بين العقد. رموز الاستخدام الرئيسية:

tcpالنقل: = tcp. جديدTCPTransport()

3. ** تعدد الإرسال والتحكم في التدفق **:

  • تنفيذ تعدد الإرسال للسماح بمعالجة تدفقات البيانات المتزامنة المتعددة على اتصال واحد.
  • تنفيذ التحكم في التدفق لإدارة معدل نقل البيانات ومعدل المعالجة. رموز الاستخدام الرئيسية:

yamuxالنقل: = ياموكس. جديد()

4. الأمان والتشفير:

  • تكوين طبقة نقل آمنة لضمان أمن وخصوصية الاتصالات.
  • تنفيذ آليات التشفير والتوثيق لحماية البيانات ومصادقة الأطراف المتصلة. رموز الاستخدام الرئيسية:

tlsالنقل: = tls. جديد()

5. البروتوكولات ومعالجة الرسائل:

تحديد وتنفيذ بروتوكولات مخصصة للتعامل مع عمليات شبكة محددة وتبادل الرسائل.

  • معالجة الرسائل المستلمة وإرسال الردود حسب الحاجة. رموز الاستخدام الرئيسية:

مضيف. SetStreamHandler ("/ my-protocol / 1.0.0" ، myProtocolHandler)

6. الاكتشاف والتوجيه:

  • تنفيذ آلية اكتشاف العقدة للعثور على العقد الأخرى في الشبكة.
  • تنفيذ منطق التوجيه لتحديد كيفية توجيه الرسائل إلى العقد الصحيحة في الشبكة. رموز الاستخدام الرئيسية:

DHT := قدحت. NewDHT (ctx ، المضيف ، مخزن البيانات. NewMapDatastore())

7. سلوك الشبكة وسياساتها:

تحديد سلوكيات الشبكة ونهجها وتنفيذها، مثل إدارة الاتصال ومعالجة الأخطاء ومنطق إعادة المحاولة. رموز الاستخدام الرئيسية:

كونماناجر: = كونمجر. NewConnManager (مياه منخفضة ، مياه عالية ، فترة سماح)

8. إدارة الدولة والتخزين:

إدارة حالة العقد والشبكات، بما في ذلك حالة الاتصال وقائمة العقد وتخزين البيانات. رموز الاستخدام الرئيسية:

بيرستور: = بستورميم. نيوبيرستور()

9. ** الاختبار والتصحيح **:

  • اكتب اختبارات لتطبيق libp2p الخاص بك للتأكد من صحته وموثوقيته.
  • استخدم أدوات تصحيح الأخطاء والسجلات لتشخيص مشكلات الشبكة وحلها. رموز الاستخدام الرئيسية:

تسجيل. SetLogLevel ("libp2p" ، "DEBUG")

10. التوثيق ودعم المجتمع:

  • راجع وثائق libp2p للتعرف على مكوناته المختلفة وواجهات برمجة التطبيقات.
  • التواصل مع مجتمع libp2p للحصول على الدعم وحل المشكلات.

}

ما سبق هو بعض الجوانب الرئيسية التي يجب مراعاتها وتنفيذها عند استخدام libp2p. قد يختلف التنفيذ المحدد لكل مشروع ، ولكن هذه الجوانب الأساسية ضرورية لبناء وتشغيل تطبيقات libp2p. عند تنفيذ هذه الوظائف ، يمكنك الرجوع إلى الوثائق الرسمية ل libp2p[9] [10]ومستودعات GitHub نموذج التعليمات البرمجية والبرامج التعليمية.

استخدام libp2p في عقدة OP

من أجل معرفة العلاقة بين op-node و libp2p ، يتعين علينا معرفة العديد من الأسئلة

  • لماذا libp2p؟ لماذا لا تختار devp2p (geth يستخدم devp2p)
  • ما هي البيانات أو العمليات التي ترتبط ارتباطا وثيقا بشبكة P2P في عقدة OP
  • كيف يتم تنفيذ هذه الميزات في طبقة التعليمات البرمجية

لماذا تحتاج العقد التشغيلية إلى شبكة libp2p

نحتاج أولا إلى فهم سبب تطلب التفاؤل شبكة P2P ** LibbP2P هو بروتوكول شبكة معياري يسمح للمطورين ببناء تطبيقات نظير إلى نظير لامركزية لحالات استخدام متعددة (المصادر[11] [12])(المصدر [13])。 من ناحية أخرى ، يستخدم DevP2P بشكل أساسي في النظام البيئي Ethereum وهو مصمم لتطبيقات Ethereum (المصدر )。 قد تجعله المرونة والتطبيق الواسع ل libp2p الخيار الأفضل للمطورين.

تستخدم Op-Node بشكل أساسي نقاط وظيفة libp2p

  • يستخدم من قبل جهاز التسلسل لتمرير الكتلة غير الآمنة الناتجة إلى العقد الأخرى غير التسلسلية
  • للعقد الأخرى في وضع غير التسلسل للمزامنة السريعة عند حدوث فجوة (مزامنة السلسلة العكسية)
  • بيئة جيدة لاعتماد نظام سمعة متكامل لتنظيم العقدة الشاملة

تنفيذ التعليمات البرمجية

تهيئة المضيف المخصصة

يمكن فهم المضيف على أنه عقدة P2P ، عند فتح هذه العقدة ، تحتاج إلى إجراء بعض تكوين التهيئة الخاص لمشروعك الخاص

الآن دعونا نلقي نظرة على طريقة المضيف في ملف op-node / p2p / host.go.

تستخدم هذه الوظيفة بشكل أساسي لإعداد مضيف libp2p وإجراء تكوينات مختلفة. فيما يلي الأجزاء الرئيسية للوظيفة ووصف صيني بسيط لكل منها:

  1. ** تحقق مما إذا كان P2P معطلا **
    إذا تم تعطيل P2P ، تعود الوظيفة مباشرة.
  2. ** الحصول على معرف النظير من المفتاح العام **
    استخدم المفتاح العام في التكوين لإنشاء معرف النظير.
  3. ** تهيئة Peerstore **
    إنشاء مخزن Peerstore أساسي.
  4. ** تهيئة ملحق Peerstore **
    في الجزء العلوي من Peerstore الأساسي ، قم بإنشاء Peerstore موسع.
  5. ** إضافة مفاتيح خاصة وعامة إلى Peerstore **
    قم بتخزين مفاتيح النظير الخاصة والعامة في الأقران.
  6. ** تهيئة اتصال Gater **
    تستخدم للتحكم في اتصالات الشبكة.
  7. تهيئة إدارة الاتصال
    تستخدم لإدارة اتصالات الشبكة.
  8. ** تعيين عنوان الإرسال والاستماع **
    اضبط بروتوكول نقل الشبكة وعنوان الاستماع للمضيف.
  9. ** إنشاء مضيف libp2p **
    استخدم جميع الإعدادات السابقة لإنشاء مضيف libp2p جديد.
  10. ** تهيئة نظير ثابت **
    إذا كان لديك نظير ثابت تم تكوينه، فقم بتهيئته.
  11. ** العودة إلى المضيف **
    أخيرا ، ترجع الوظيفة مضيف libp2p الذي تم إنشاؤه.

هذه الأقسام الرئيسية مسؤولة عن تهيئة وإعداد مضيف libp2p ، وكل جزء مسؤول عن جانب معين من تكوين المضيف.

func (conf * config) المضيف (سجل السجل. المسجل ، مقاييس المراسل. مراسل ، مقاييس HostMetrics) (المضيف. المضيف ، خطأ) {  
    إذا كونكت. ديسابليب 2 بي {  
        عودة لا شيء، لا شيء  
    }  
    حانة: = مؤتمر. Priv.GetPublic()  
    PID ، يخطئ: = نظير. IDFromPublicKey(pub)  
    إذا أخطأ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل اشتقاق pubkey من مفتاح priv للشبكة: ٪w" ، يخطئ)  
    }  
    basePs ، يخطئ: = pstoreds. NewPeerstore (السياق. الخلفية () ، conf. مخزن ، pstoreds. DefaultOpts())  
    إذا أخطأ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل فتح النظراء: ٪w" ، يخطئ)  
    }  
    PeerScoreParams := conf. PeerScoringParams ()  
     النتيجةوقت الاحتفاظ. مدة  
    إذا كان peerScoreParams != لا شيء {  
        استخدم نفس فترة الاحتفاظ مثل القيل والقال إذا كان ذلك متاحا  
        scoreRetention = peerScoreParams.PeerScoring.RetainScore  
    } آخر {  
        تعطيل درجة GC إذا تم تعطيل تسجيل الأقران  
        النتيجة الاحتفاظ = 0  
    }  
    PS ، يخطئ: = مخزن. NewExtendedPeerstore(السياق. الخلفية () ، السجل ، الساعة. على مدار الساعة ، basePs ، conf. Store, scoreRetention)  
    إذا أخطأ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل فتح النظراء الموسعين: ٪w" ، خطأ)  
    }  
    إذا أخطأ: = ملاحظة. AddPrivKey(pid, conf. بريف)؛ يخطئ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل في إعداد peerstore باستخدام مفتاح priv: ٪ w" ، يخطئ)  
    }  
    إذا أخطأ: = ملاحظة. AddPubKey (pid ، pub) ؛ يخطئ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل إعداد peerstore باستخدام مفتاح pub: ٪w" ، خطأ)  
    }  
     بوابات الاتصال. حظر الاتصالغاتر  
    connGtr ، يخطئ = البوابة. NewBlockingConnectionGater(conf. المتجر)  
    إذا أخطأ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل فتح بوابة الاتصال: ٪ w" ، يخطئ)  
    }  
    connGtr = بوابة. AddBanExpiry(connGtr, ps, log, clock. الساعة ، المقاييس)  
    connGtr = بوابة. AddMetering(connGtr, المقاييس)  
    connMngr, err:= DefaultConnManager(conf)  
    إذا أخطأ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل فتح إدارة الاتصال: ٪w" ، خطأ)  
    }  
    listenAddr ، يخطئ: = addrFromIPAndPort (conf. استمع، مؤتمر. استمعTCPPort)  
    إذا أخطأ!= لا شيء {  
        عودة لا شيء ، FMT. Errorf ("فشل في جعل الاستماع addr: ٪ w" ، يخطئ)  
    }  
    tcpالنقل: = libp2p. النقل(  
        تي سي بي. جديدTCPTransport,  
        تي سي بي. معمهلة الاتصال (الوقت. دقيقة * 60)) // قطع الاتصالات غير المستخدمة  
    TODO: من الناحية الفنية ، يمكننا أيضا تشغيل العقدة على مقبس الويب وعمليات نقل QUIC ربما في المستقبل؟  
     نات لكونف. NATManagerC // معطل إذا لم يكن هناك شيء  
    إذا كونكت. نات {  
        نات = المضيف الأساسي. نيوناتمانجر  
    }  
    يختار := []libp2p. الخيار{  
        ليب 2 ص. الهوية(conf. Priv) ،  
        قم بتعيين وكيل المستخدم بشكل صريح ، حتى نتمكن من التمييز عن مستخدمي Go libp2p الآخرين.  
        ليب 2 ص. وكيل المستخدم (conf. وكيل المستخدم) ،  
        tcpالنقل,  
        ليب 2 ص. WithDialTimeout(conf. TimeoutDial) ،  
        لا توجد خدمات ترحيل ، اتصالات مباشرة بين الأقران فقط.  
        ليب 2 ص. ديسابليريلاي(),  
        سيبدأ المضيف ويستمع إلى الشبكة مباشرة بعد البناء من التكوين.  
        ليب 2 ص. استمعأ (استمع) ،  
        ليب 2 ص. ConnectionGater (connGtr) ،  
        ليب 2 ص. ConnectionManager(connMngr),  
        ليب 2 ص. ResourceManager (لا شيء) ، // TODO استخدام واجهة إدارة الموارد لإدارة الموارد لكل نظير بشكل أفضل.  
        ليب 2 ص. بينغ (صحيح) ،  
        ليب 2 ص. AutoNATServiceRateLimit(10 ، 5 ، الوقت. الثانية * 60) ،  
    }  
    إذا كونكت. نو ترانسبورتسيكيوريتي {  
    } آخر {  
        opts = إلحاق (يختار ، مؤتمر. هوست سيكيوريتي...)  
    }  
    ح ، يخطئ: = libp2p. جديد (يختار...)  
    إذا أخطأ!= لا شيء {  
    }  
    ل i ، peerAddr: = نطاق conf. ستاتيكبيرز {  
        إذا أخطأ!= لا شيء {  
        }  
        ثابتالأقران[i]  = العد  
    }  
    خارج: = و extraHost {  
        المضيف: ح ،  
        ثابتالأقران: ثابتأقران,  
    }  
        الخروج.مونيتوراتيستيك بيرز()  
    }  






        ...  
        n.gs ، يخطئ = NewGossipSub (resourcesCtx ، n.host ، rollupCfg ، الإعداد ، n.scorer ، المقاييس ، السجل)  
        إذا أخطأ!= لا شيء {  
        }  
        ...  
  1. ** إنشاء المصادقة **: ** إنشاء اسم موضوع الكتلة **:
  • قم بإنشاء blocksTopicName باستخدام وظيفة blocksTopicV1 ، التي تقوم بتنسيق سلسلة تستند إلى L2ChainID في التكوين (cfg). تتبع السلاسل المنسقة بنية محددة: /optimism/{L2ChainID}/0/blocks.

  • وظيفة عن طريق استدعاء PS. يحاول Join(blocksTopicName) إضافة سمة ثرثرة كتلة. في حالة حدوث خطأ ، فإنه يرجع رسالة خطأ تشير إلى تعذر ضم الموضوع.

  • إنشاء goroutine جديد لتسجيل أحداث الموضوع باستخدام وظيفة LogTopicEvents.

  • إنشاء مشترك باستخدام وظيفة MakeSubscriber ، والتي تغلف BlocksHandler الذي يتعامل مع أحداث OnUnsafeL2Payload من القيل والقال. تم إنشاء goroutine جديد لتشغيل subion المقدمة.

    func JoinGossip(p2pCtx context. السياق ، peer.ID الذاتي ، ps * pubsub. PubSub ، سجل السجل. المسجل ، cfg * التراكم. Config, runCfg GossipRuntimeConfig, gossipIn GossipIn) (GossipOut, error) {
    كتل اسم الموضوع: = كتل TopicV1 (cfg) // إرجاع fmt. Sprintf ("/ التفاؤل / ٪ s / 0 / كتل" ، cfg. L2ChainID.String())
    عودة لا شيء ، FMT. Errorf ("فشل تسجيل موضوع القيل والقال كتلة: ٪ w" ، يخطئ)
    }
    كتل الموضوع ، يخطئ: = ملاحظة. الانضمام (كتل اسم الموضوع)
    إذا أخطأ!= لا شيء {
    }
    إذا أخطأ!= لا شيء {
    }
    انتقل إلى LogTopicEvents (p2pCtx ، log. جديد ("الموضوع" ، "الكتل") ، كتل الموضوعالأحداث)
    إذا أخطأ!= لا شيء {
    }
    إرجاع وناشر {سجل: سجل ، cfg: cfg ، كتل الموضوع: كتل الموضوع ، runCfg: runCfg} ، لا شيء

المرجع العقدة/التراكمي/سائق/state.go

func (s *Driver) eventLoop() {  
    من أجل (){  
        ...  
            الحمولة ، يخطئ: = s.sequencer.RunNextSequencerAction (ctx)  
            إذا أخطأ!= لا شيء {  
                الأخطاء ليست خطيرة بما يكفي لتغيير / إيقاف التسلسل ولكن يجب تسجيلها وقياسها.  
                إذا أخطأ: = s.network.PublishL2Payload (ctx ، الحمولة) ؛ يخطئ!= لا شيء {  
            }  
            planSequencerAction() // جدولة إجراء التسلسل التالي للحفاظ على تكرار التسلسل  
            }  
    }  
    ...  

عندما تكون هناك كتل مفقودة ، مزامنة سريعة عبر P2P

عندما يتم إعادة ربط العقد بعد التوقف بسبب ظروف خاصة ، مثل إعادة الربط بعد التوقف ، قد لا تتم مزامنة بعض الكتل (الفجوات) ، وعند مواجهة هذا الموقف ، يمكن مزامنتها بسرعة من خلال السلسلة العكسية لشبكة P2P.

دعنا نلقي نظرة على وظيفة checkForGapInUnsafeQueue في op-node / rollup / driver / state.go

يحدد مقتطف التعليمات البرمجية طريقة تسمى checkForGapInUnsafeQueue ، والتي تنتمي إلى بنية برنامج التشغيل. والغرض منه هو التحقق من وجود فجوات في البيانات في قائمة انتظار تسمى "قائمة انتظار غير آمنة" ومحاولة استرداد الحمولات المفقودة من خلال طريقة مزامنة بديلة تسمى altSync. النقطة الأساسية هنا هي أن الطريقة هي ضمان استمرارية البيانات ومحاولة استرداد البيانات المفقودة من طرق المزامنة الأخرى عند اكتشاف البيانات المفقودة. فيما يلي الخطوات الرئيسية للوظيفة:

  1. تحصل الوظيفة أولا على UnsafeL2Head و UnsafeL2SyncTarget من s.derivation كنقاط بداية ونهاية لنطاق الفحص.

  2. تتحقق الوظيفة من الكتل المفقودة بين البداية والنهاية ، ويتم ذلك عن طريق مقارنة قيم الأرقام للنهاية والبدء.

  3. إذا تم اكتشاف فجوة في البيانات ، فستطلب الوظيفة نطاق البيانات المفقود عن طريق استدعاء s.altSync.RequestL2Range (ctx ، start ، end). إذا كانت النهاية مرجعا فارغا (أي eth. L2BlockRef{}) ، ستطلب الوظيفة مزامنة نطاق مفتوح النهاية ، بدءا من البداية.

  4. عند طلب البيانات ، تسجل الوظيفة سجل تصحيح الأخطاء الذي يشير إلى نطاق البيانات الذي تطلبه.

  5. ترجع الدالة في النهاية قيمة خطأ. إذا لم تكن هناك أخطاء ، فإنه يرجع لا شيء

    يتحقق checkForGapInUnsafeQueue مما إذا كانت هناك فجوة في قائمة الانتظار غير الآمنة ويحاول استرداد الحمولات المفقودة من أسلوب مزامنة بديلة.
    تحذير: هذه ليست سوى إشارة صادرة ، ولا يمكن ضمان استرداد الكتل.
    يتم استلام النتائج من خلال OnUnsafeL2Payload.
    func (s * Driver) checkForGapInUnsafeQueue(ctx context. السياق) خطأ {
    البداية: = s.derivation.UnsafeL2Head()
    النهاية: = s.derivation.UnsafeL2SyncTarget ()
    تحقق مما إذا كان لدينا كتل مفقودة بين البداية والنهاية اطلبها إذا فعلنا ذلك.
    إذا كانت النهاية == (ETH. L2BlockRef{}) {
    s.log.Debug ("طلب المزامنة مع نطاق النهاية المفتوحة" ، "البدء" ، البدء)
    إرجاع s.altSync.RequestL2Range(ctx, start, eth. L2BlockRef{})
    } آخر إذا انتهى. رقم > البداية. رقم +1 {
    s.log.Debug ("طلب نطاق كتلة L2 غير آمن مفقود" ، "بدء" ، بداية ، "نهاية" ، نهاية ، "حجم" ، نهاية. رقم-بداية.رقم)
    إرجاع s.altSync.RequestL2Range(ctx، ابدأ، انتهى)
    }
    عودة لا شيء
    }

تشير الدالة RequestL2Range إلى بداية كتلة الطلب ونهايتها إلى قناة الطلبات.

ثم يتم توزيع الطلب على قناة peerRequests عبر طريقة onRangeRequest ، ويتم انتظار قناة peerRequests بواسطة الحلقة التي يفتحها العديد من الأقران ، أي أن نظيرا واحدا فقط سيعالج الطلب لكل توزيع.

func (s *SyncClient) onRangeRequest(ctx context. السياق، نطاق الطلبات) {  
        ...  
        ل i: = uint64 (0); ; i++ {  
        num: = req.end.Number - 1 - i  
        إذا كان num <= req.start {  
            أعاد  
        }  
        تحقق مما إذا كان لدينا شيء في الحجر الصحي بالفعل  
        إذا كان h ، موافق: = s.quarantineByNum[num] ; حسنا {  
            إذا كان s.trusted.Contains(h) { // إذا كنا نثق به ، فحاول الترويج له.  
                s.tryالترويج (ح)  
            }  
            لا تجلب الأشياء التي لدينا مرشح لها بالفعل.  
            سنقوم بإخلائه من الحجر الصحي من خلال العثور على تعارض ، أو إذا قمنا بمزامنة ما يكفي من الكتل الأخرى  
            استمر  
        }  
        إذا _ ، موافق: = s.inFlight[num] ; حسنا {  
            .log. تصحيح ("الطلب لا يزال في الرحلة ، وليس إعادة جدولة طلب المزامنة" ، "num" ، num)  
            متابعة // الطلب لا يزال في الرحلة  
        }  
        العلاقات العامة: = peerRequest {num: num ، complete: new (atomic. منطق)}  
        .log. تصحيح ("جدولة طلب كتلة P2P" ، "num" ، num)  
        رقم الجدول  
        حدد {  
        حالة s.peerطلبات <- العلاقات العامة:  
            s.inflight[num]  = العلاقات العامة كاملة  
        القضية <-CTX. تم():  
            .log. معلومات ("لم تقم بجدولة نطاق مزامنة P2P الكامل" ، "الحالي" ، num ، "err" ، ctx. يخطئ ())  
            أعاد  
        افتراضي: // قد يكون جميع الأقران مشغولين بمعالجة الطلبات بالفعل  
            .log. معلومات ("لا يوجد أقران على استعداد للتعامل مع طلبات الحظر لمزيد من طلبات P2P لسجل كتلة L2" ، "الحالي" ، num)  
            أعاد  
        }  
    }  
}

دعونا نرى ما يحدث عندما يتلقى النظير هذا الطلب.

أول شيء نحتاج إلى معرفته هو أن الرابط بين النظير والعقدة الطالبة ، أو تمرير الرسالة ، يتم تمريره عبر دفق libp2p. يتم تنفيذ طريقة معالجة الدفق بواسطة عقدة النظير المستقبلة ، ويتم فتح إنشاء الدفق بواسطة عقدة الإرسال.

يمكننا رؤية هذه التعليمة البرمجية في دالة init السابقة ، حيث يقوم MakeStreamHandler بإرجاع معالج ، ويقوم SetStreamHandler بربط معرف البروتوكول بهذا المعالج ، لذلك كلما قامت عقدة الإرسال بإنشاء هذا الدفق واستخدامه ، يتم تشغيل المعالج الذي تم إرجاعه.

n.syncSrv = نيوريكريسبسيرفير(rollupCfg, l2Chain, metrics)  
تسجيل بروتوكول المزامنة مع مضيف Lipp2P  
الحمولة ByNumber: = 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. السياق ، شبكة الدفق. دفق) (uint64 ، خطأ) {  
    معرف النظير: = تيار. Conn(). ريموت بير ()  
    خذ رمزا مميزا من محدد المعدل العالمي ،  
    للتأكد من عدم وجود الكثير من عمل الخادم المتزامن بين الأقران المختلفين.  
    إذا أخطأ: = srv.globalRequestsRL.Wait(ctx); يخطئ!= لا شيء {  
        إرجاع 0 ، FMT. Errorf ("انتهت مهلة انتظار حد معدل المزامنة العالمي: ٪w" ، خطأ)  
    }  
    ابحث عن بيانات تحديد معدل النظير ، أو أضف خلاف ذلك  
    srv.peerStatsLock.Lock()  
    ps, _ := srv.peerRateLimits.Get(peerId)  
    إذا كان ps == لا شيء {  
        ps = &peerStat{  
            الطلبات: معدل. NewLimiter(peerServerBlocksRateLimit, peerServerBlocksBurst),  
        }  
        srv.peerRateLimits.Add(peerId, ps)  
        ملاحظة. Requests.Reserve() // عد النتيجة ، ولكن اجعلها تؤخر الطلب التالي بدلا من الانتظار الفوري  
    } آخر {  
        انتظر فقط إذا كان نظيرا موجودا ، وإلا فإن مكالمة الانتظار ذات الحد الفوري للمعدل تخطئ دائما.  
        إذا اعتقد مقدم الطلب أننا نستغرق وقتا طويلا ، فهذه مشكلته ويمكنه قطع الاتصال.  
        سنفصل أنفسنا فقط عندما نفشل في القراءة / الكتابة ،  
        إذا كان العمل غير صالح (التحقق من صحة النطاق) ، أو عند انتهاء مهلة المهام الفرعية الفردية.  
        إذا أخطأ: = ملاحظة. الطلبات.انتظر (ctx); يخطئ!= لا شيء {  
            إرجاع 0 ، FMT. Errorf ("انتهت مهلة انتظار حد معدل المزامنة العالمي: ٪w" ، خطأ)  
        }  
    }  
    srv.peerStatsLock.Unlock()  
    حدد موعدا نهائيا للقراءة، إن وجد  
    _ = تيار. SetReadDeadline(time. الآن(). Add(serverReadRequestTimeout))  
    اقرأ الطلب  
     طلب uint64  
    إذا أخطأ: = ثنائي. قراءة (تيار ، ثنائي. LittleEndian, &req); يخطئ!= لا شيء {  
        إرجاع 0 ، FMT. Errorf ("فشل قراءة رقم الكتلة المطلوب: ٪w" ، خطأ)  
    }  
    إذا أخطأ: = تيار. CloseRead (); يخطئ!= لا شيء {  
        عودة الطلب ، FMT. Errorf ("فشل إغلاق جانب القراءة لاستدعاء طلب مزامنة P2P: ٪w" ، خطأ)  
    }  
    تحقق من أن الطلب ضمن النطاق المتوقع للكتل  
    إذا كان req < srv.cfg.Genesis.L2.Number {  
        عودة الطلب ، FMT. Errorf ("لا يمكن تقديم طلب كتلة L2٪ d قبل سفر التكوين٪ d: ٪ w" ، req ، srv .cfg.Genesis.L2.Number ، invalidRequestErr)  
    }  
    max ، err: = srv .cfg.TargetBlockNumber (uint64 (time. الآن(). يونكس ()))  
    إذا أخطأ!= لا شيء {  
        عودة الطلب ، FMT. Errorf ("لا يمكن تحديد الحد الأقصى لرقم الكتلة الهدف للتحقق من الطلب: ٪w" ، invalidRequestErr)  
    }  
    إذا كان التكرار > كحد أقصى {  
        عودة الطلب ، FMT. Errorf ("لا يمكن تقديم طلب كتلة L2٪ d بعد الحد الأقصى للكتلة المتوقعة (٪ v): ٪ w" ، req ، max ، invalidRequestErr)  
    }  
    الحمولة ، يخطئ: = srv.l2.PayloadByNumber (ctx ، req)  
    إذا أخطأ!= لا شيء {  
        إذا كانت الأخطاء. هو (خطأ ، إيثريوم. لم يتم العثور على) {  
            عودة الطلب ، FMT. Errorf ("طلب النظير كتلة غير معروفة حسب الرقم: ٪w" ، خطأ)  
        } آخر {  
            عودة الطلب ، FMT. Errorf ("فشل استرداد الحمولة لعرضها على نظير: ٪w" ، خطأ)  
        }  
    }  
    لقد حددنا موعدا نهائيا للكتابة ، إذا كان متاحا ، للكتابة بأمان دون حظر اتصال نظير خانق  
    _ = تيار. SetWriteDeadline(الوقت. الآن(). Add(serverWriteChunkTimeout))  
    0 - رمز النتيجة: النجاح = 0  
    1:5 - الإصدار: 0  
     .tmp [5] بايت  
    إذا _ ، يخطئ: = تيار. الكتابة (tmp [:]) ؛ يخطئ!= لا شيء {  
        عودة الطلب ، FMT. Errorf ("فشل كتابة بيانات رأس الاستجابة: ٪w" ، خطأ)  
    }  
    w: = لاذع. NewBufferedWriter (تيار)  
    إذا _ ، يخطئ: = الحمولة. مارشال SSZ (ث) ؛ يخطئ!= لا شيء {  
        عودة الطلب ، FMT. Errorf ("فشل كتابة الحمولة لمزامنة الاستجابة: ٪w" ، خطأ)  
    }  
    إذا أخطأ: = w.Close (); يخطئ!= لا شيء {  
        عودة الطلب ، FMT. Errorf ("فشل إنهاء حمولة الكتابة لمزامنة الاستجابة: ٪w" ، خطأ)  
    }  
    إعادة الطلب ، لا شيء  
}

في هذه المرحلة ، تم شرح العملية العامة لطلب مزامنة السلسلة العكسية ومعالجتها

نظام سمعة النقاط في عقد P2P

من أجل منع بعض العقد من تقديم طلبات واستجابات ضارة تقوض أمان الشبكة بأكملها ، يستخدم Optimism أيضا نظام نقاط.

على سبيل المثال ، في op-node / p2p / app_scores.go ، هناك سلسلة من الوظائف لتعيين درجة نظير

func (s *peerApplicationScorer) onValidResponse(id peer.ID) {  
    _ ، يخطئ: = s.scorebook.SetScore (معرف ، متجر. IncrementValidResponses{Cap: s.params.ValidResponseCap})  
    إذا أخطأ!= لا شيء {  
        s.log.Error ("تعذر تحديث درجة الأقران" ، "نظير" ، معرف ، "خطأ" ، خطأ)  
        أعاد  
    }  
}  
func (s *peerApplicationScorer) onResponseError(id peer.ID) {  
    _ ، يخطئ: = s.scorebook.SetScore (معرف ، متجر. IncrementErrorResponses{Cap: s.params.ErrorResponseCap})  
    إذا أخطأ!= لا شيء {  
        s.log.Error ("تعذر تحديث درجة الأقران" ، "نظير" ، معرف ، "خطأ" ، خطأ)  
        أعاد  
    }  
}  
func (s *peerApplicationScorer) onRejectedPayload(id peer.ID) {  
    _ ، يخطئ: = s.scorebook.SetScore (معرف ، متجر. IncrementRejectedPayloads{Cap: s.params.RejectedPayloadCap})  
    إذا أخطأ!= لا شيء {  
        s.log.Error ("تعذر تحديث درجة الأقران" ، "نظير" ، معرف ، "خطأ" ، خطأ)  
        أعاد  
    }  
}

ثم يتم التحقق من حالة تكامل العقدة الجديدة قبل إضافتها

func AddScorering(gater BlockingConnectionGater, عشرات الدرجات, minScore float64) *ScoreringConnectionGater {  
    Return &ScoreringConnectionGater{BlockingConnectionGater: gater, scores: scores, minScore: minScore}  
}  
func (g *ScoringConnectionGater) checkScore(p peer.ID) (allow bool) {  
    النتيجة ، يخطئ: = g.scores.GetPeerScore (ع)  
    إذا أخطأ!= لا شيء {  
        إرجاع خطأ  
    }  
    درجة العودة >= g.minScore  
}  
func (g *ScoringConnectionGater) InterceptPeerDial(p peer.ID) (allow bool) {  
    إرجاع g.BlockingConnectionGater.InterceptPeerDial(p) && g.checkScore(p)  
}  
func (g *ScoringConnectionGater) InterceptAddrDial(id peer.ID, ma multiaddr. Multiaddr) (السماح bool) {  
    إرجاع g.BlockingConnectionGater.InterceptAddrDial(id, ma)&& g.checkScore(id)  
}  
func (g *ScoringConnectionGater) InterceptSecured(dir network. الاتجاه ، معرف peer.ID ، شبكة ماس. ConnMultiaddrs) (السماح bool) {  
    إرجاع g.BlockingConnectionGater.InterceptSecured(dir, id, mas) && g.checkScore(id)  
}

ملخص

إن قابلية التكوين العالية ل libp2p تجعل المشروع بأكمله p2p قابلا للتخصيص والوحدات بدرجة كبيرة ، وما سبق هو المنطق الرئيسي لتنفيذ optimsim المخصص ل libp2p ، ويمكن تعلم تفاصيل أخرى بالتفصيل من خلال قراءة الكود المصدري في دليل p2p.

شاهد النسخة الأصلية
قد تحتوي هذه الصفحة على محتوى من جهات خارجية، يتم تقديمه لأغراض إعلامية فقط (وليس كإقرارات/ضمانات)، ولا ينبغي اعتباره موافقة على آرائه من قبل Gate، ولا بمثابة نصيحة مالية أو مهنية. انظر إلى إخلاء المسؤولية للحصول على التفاصيل.
  • أعجبني
  • تعليق
  • مشاركة
تعليق
0/400
لا توجد تعليقات
  • تثبيت