كود بلوكشين P2P بسيط في Go!

لاقت سلسلتنا التعليمية حول blockchain شهرة كبيرة. تمت قراءة البرامج التعليمية عشرات الآلاف من المرات وانضم عدة مئات من الأشخاص إلى مجتمعنا لتقديم ملاحظات لنا وطرح الأسئلة.

التعليقات الأكثر شيوعًا التي يقدمها الأشخاص هي أن مشاركات المدونة الخاصة بنا مفيدة في جعل مفاهيم blockchain المعقدة بسيطة ومفهومة. ومع ذلك ، فإنهم يفتقدون إلى وظيفة نظير إلى نظير.

لقد فعلنا كل شيء بدءًا من توضيح كيفية كتابة رمز إثبات العمل إلى إعلامك بكيفية عمل IPFS. ومع ذلك ، في كل من برامجنا التعليمية ، اعتمدنا على خادم مركزي لتوضيح هذه المفاهيم لك.

سنعرض لك الآن كيفية تشغيل blockchain بسيط بطريقة لا مركزية حقًا ، من نظير إلى نظير. فلنبدأ!

الخلفية

ما هو Peer-to-Peer (P2P)؟

في بنية نظير إلى نظير الحقيقية ، لا تحتاج إلى خادم مركزي للحفاظ على حالة blockchain. على سبيل المثال ، عندما ترسل لصديقك بعض Bitcoin ، يجب تحديث “حالة” Bitcoin blockchain بحيث يزيد رصيد صديقك ويقل رصيدك.

لا توجد سلطة مركزية مثل البنك الذي يحافظ على الدولة. بدلاً من ذلك ، تقوم جميع العقد في شبكة Bitcoin التي ترغب في الاحتفاظ بنسخة من Bitcoin blockchain بتحديث نسخها من blockchain لتضمين معاملتك. بهذه الطريقة ، طالما أن 51٪ من العقد في الشبكة “توافق” على حالة blockchain ، فإنها تحافظ على ولائها. اقرأ المزيد عن مفهوم التوافق هنا.

في هذا البرنامج التعليمي ، سنعيد صياغة منشورنا ، رمز blockchain الخاص بك في أقل من 200 سطر من Go! لذلك يستخدم بنية نظير إلى نظير بدلاً من خادم مركزي. نوصي بشدة بقراءته قبل المتابعة. سيساعدك على فهم الشفرة القادمة.

لنبدأ الترميز!

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

لحسن الحظ ، توجد مكتبة P2P ممتازة مكتوبة بلغة Go تسمى go-libp2p. من قبيل الصدفة ، أنها مصنوعة من قبل نفس الأشخاص الذين أنشأوا IPFS. إذا لم تكن قد قمت بفحص البرنامج التعليمي IPFS الخاص بنا ، ألق نظرة هنا (لا تقلق أنه ليس إلزاميًا لهذا البرنامج التعليمي).

< تحذير

بقدر ما نستطيع أن نقول ، فإن مكتبة go-libp2p هذه لها عيبان:

لا تقلق بشأن # 1. سنساعدك خلال ذلك. # 2 مشكلة أكبر ولكنها لن تؤثر على الكود الخاص بنا. ومع ذلك ، إذا لاحظت وجود سباقات البيانات ، فمن المرجح أنها تأتي من الكود الأساسي لهذه المكتبة. لذا تأكد من فتح مشكلة وإخبار هم .

يتوفر عدد قليل جدًا من مكتبات P2P الحديثة والمفتوحة المصدر ، لا سيما في Go. بشكل عام ، يعد go-libp2p أمرًا جيدًا ومناسبًا تمامًا لأهدافنا.

< الإعداد

أفضل طريقة لإعداد بيئة التعليمات البرمجية الخاصة بك هي استنساخ هذه المكتبة بأكملها وكتابة التعليمات البرمجية بداخلها. يمكنك التطوير خارج البيئة التي يوفرونها ولكن ذلك يتطلب معرفة كيفية العمل مع gx . سنوضح لك الطريق السهل. على افتراض أن لديك Go مثبتًا بالفعل:

يوفر لك هذا جميع الحزم والتبعيات التي تحتاجها من هذا الريبو من خلال مدير الحزم gx . مرة أخرى ، لا نحب gx لأنه يكسر كثيرًا من قواعد Go (إلى جانب ذلك ، لماذا لا تلتزم فقط بـ go get؟ ) ولكن الأمر يستحق الإزعاج لاستخدام هذا مكتبة جميلة خلاف ذلك.

سنقوم بالتطوير داخل الدليل الفرعي الأمثلة . فلننشئ دليلًا تحت أمثلة يسمى p2p مع

ثم انتقل إلى مجلد p2p الجديد وأنشئ ملف main.go . سنقوم بكتابة جميع التعليمات البرمجية الخاصة بنا في هذا الملف.

يجب أن تبدو شجرة الدليل على النحو التالي:

افتح ملف main.go ودعنا نبدأ في كتابة الكود الخاص بنا!

الواردات

لنقم بإعلان الحزمة الخاصة بنا وسرد وارداتنا. معظم عمليات الاستيراد هذه عبارة عن حزم توفرها مكتبة go-libp2p . سوف تتعلم كيفية استخدام كل منها خلال هذا البرنامج التعليمي.

تعد حزمة spew حزمة ملائمة بسيطة لطباعة blockchain الخاصة بنا. تأكد مما يلي:

عناصر Blockchain

تذكر! اقرأ هذا البرنامج التعليمي قبل المتابعة. سيكون القسم التالي أسهل في الفهم بعد قراءته!

دعونا نعلن عن المتغيرات العالمية لدينا.

اكتب الوظائف التالية الخاصة بـ blockchain.

أشياء P2P

المضيف

الآن ننتقل إلى الجزء الأساسي من برنامجنا التعليمي. أول شيء نريد القيام به هو كتابة المنطق الذي يسمح بإنشاء مضيف. عندما تقوم العقدة بتشغيل برنامج Go الخاص بنا ، يجب أن تعمل كمضيف يمكن للعقد الأخرى (أو الأقران) الاتصال به. ها هو الرمز. لا تشدد ، سنرشدك خلال ذلك 🙂

تأخذ وظيفة makeBasicHost 3 وسيطات وتعيد المضيف وخطأ (وهو صفري إذا لم تحدث أخطاء).

تحدد أول عبارة إذا للوظيفة ما إذا كان قد تم توفير البذور الأولية وتقوم بإنشاء مفاتيح لمضيفنا وفقًا لذلك. ثم نقوم بإنشاء مفاتيحنا العامة والخاصة حتى يظل مضيفنا آمنًا. يبدأ قسم الخيارات في إنشاء العنوان الذي يمكن للأقران الآخرين الاتصال به.

يتجاوز جزء ! secio التشفير ولكننا سنستخدم secio للأمان لذلك لا ينطبق هذا السطر علينا في الوقت الحالي. من الجيد أن يكون لديك هذا الخيار بالرغم من ذلك.

نقوم بعد ذلك بإنشاء المضيف وإنهاء العناوين التي يمكن للأقران الآخرين الاتصال بها. الجزء log.Printf في النهاية هو رسالة وحدة التحكم المفيدة التي نطبعها والتي تخبر العقدة الجديدة بكيفية الاتصال بالمضيف الذي أنشأناه للتو.

ثم نعيد المضيف الذي تم إنشاؤه بالكامل إلى مستدعي الوظيفة. لدينا الآن مضيفنا!

معالج البث

نحتاج إلى السماح لمضيفنا بالتعامل مع تدفقات البيانات الواردة. عندما تتصل عقدة أخرى بمضيفنا وتريد اقتراح blockchain جديد لاستبدال عقدةنا ، نحتاج إلى منطق لتحديد ما إذا كان ينبغي لنا قبولها أم لا.

عندما نضيف كتلًا إلى blockchain الخاص بنا ، فإننا نريد بثه إلى نظيرنا المتصل ، لذلك نحتاج إلى منطق للقيام بذلك أيضًا.

لننشئ الهيكل العظمي لمعالجنا.

ننشئ ReadWriter جديدًا لأننا نحتاج إلى القراءة والكتابة ونقوم بتدوير إجراءات Go منفصلة للتعامل مع منطق القراءة والكتابة.

قراءة

لنقم بإنشاء وظيفة readData أولاً.

وظيفتنا هي حلقة لا نهائية لأنها تحتاج إلى أن تظل مفتوحة إلى سلاسل الكتل الواردة. نقوم بتحليل blockchain الوارد الخاص بنا من نظير والذي هو مجرد سلسلة من blob JSON مع ReadString . إذا لم يكن فارغًا (! = "\ n" ) فإننا غير منظم النقطة أولاً.

ثم نتحقق لمعرفة ما إذا كان طول السلسلة الواردة أطول من blockchain الذي نقوم بتخزينه بأنفسنا. لأغراضنا ، نحن ببساطة نتبع طول blockchain لتحديد من سيفوز. إذا كانت السلسلة الواردة أطول من سلسلتنا ، فسنقبلها على أنها أحدث حالة للشبكة (أو أحدث سلسلة blockchain “حقيقية”).

سنعيد Marshal إلى تنسيق JSON بحيث يسهل قراءته ثم نطبعه على وحدة التحكم الخاصة بنا. تتم طباعة الأمر fmt.Printf بلون مختلف لذلك يسهل علينا معرفة أنها سلسلة جديدة.

لقد قبلنا الآن blockchain الخاص بنظرائنا ، إذا أضفنا كتلة جديدة إلى blockchain ، فنحن بحاجة إلى طريقة لإعلام نظيرنا المتصل بذلك ، حتى يتمكنوا من قبول كتلتنا. نفعل ذلك من خلال وظيفة writeData الخاصة بنا.

كتابة

نبدأ الوظيفة باستخدام روتين Go الذي يبث أحدث حالة من blockchain كل 5 ثوان لأقراننا. سيتلقونها ويرمونها بعيدًا إذا كان الطول أقصر من طولهم. سيقبلونها إذا طال وقتها. في كلتا الحالتين ، يتم تحديث سلاسل الكتل الخاصة بهم باستمرار من خلال أحدث حالة للشبكة.

نحتاج الآن إلى طريقة لإنشاء قالب جديد باستخدام معدل النبض (BPM) الذي أخذناه سابقًا. نقوم بإنشاء قارئ جديد باستخدام bufio.NewReader حتى يتمكن من قراءة stdin (إدخال وحدة التحكم). نريد أن نكون قادرين على إضافة كتل جديدة باستمرار حتى نضع هذا في حلقة لا نهائية.

نجري القليل من التلاعب بالسلسلة للتأكد من أن BPM التي نكتبها هي عدد صحيح ومنسق بشكل صحيح ليتم إضافته ككتلة جديدة. ننتقل إلى وظائف blockchain القياسية الخاصة بنا (راجع قسم “ عناصر Blockchain” أعلاه). ثم Marshal بحيث تبدو جميلة ، ونطبعها على وحدة التحكم الخاصة بنا للتحقق باستخدام spew.Dump . ثم نبثه إلى نظيرنا المتصل باستخدام rw.WriteString .

رائع! لقد انتهينا الآن من وظائف blockchain ومعظم وظائف P2P الخاصة بنا. لقد أنشأنا المعالج ومنطق القراءة والكتابة للتعامل مع البلوكشين الواردة والصادرة. من خلال هذه الوظائف ، أنشأنا طريقة لكل نظير للتحقق باستمرار من حالة blockchain الخاصة به مقابل بعضها البعض ، وبشكل جماعي ، يتم تحديثهم جميعًا إلى أحدث حالة (أطول سلسلة blockchain صالحة).

كل ما تبقى الآن هو توصيل وظيفتنا main .

الوظيفة الرئيسية

ها هي وظيفتنا الرئيسية. ألقِ نظرة عليها أولاً ، ثم سنتناولها خطوة بخطوة.

نبدأ بإنشاء كتلة Genesis ، وهي الكتلة الأولية الخاصة بنا من أجل blockchain. مرة أخرى ، إذا قرأت البرنامج التعليمي السابق ، فيجب مراجعة هذا.

نستخدم مسجل مكتبة go-libp2p للتعامل مع التسجيل باستخدام SetAllLoggers . هذا اختياري.

ثم نضع جميع أعلام سطر الأوامر.

نقوم بعد ذلك بإنشاء مضيف جديد باستخدام الوظيفة makeBasicHost التي أنشأناها سابقًا. إذا كنا فقط نتصرف كمضيف (أي أننا لا نتصل بمضيفين آخرين) نحدد ذلك باستخدام إذا * target == “” ، أطلق المعالج الخاص بنا باستخدام وظيفة setStreamHandle التي أنشأناها سابقًا وهذه هي نهاية كود المستمع.

إذا كنا نريد الاتصال بمضيف آخر ، فسننتقل إلى قسم else . لقد عيّننا معالجنا مرة أخرى ، لأننا نعمل كمضيف ونظير متصل.

تفكك الأسطر القليلة التالية السلسلة التي نقدمها إلى target حتى نتمكن من العثور على المضيف الذي نريد الاتصال به. وهذا ما يسمى أيضًا Decapsulation .

انتهى بنا الأمر مع peerID والعنوان الهدف targetAddr للمضيف الذي نريد الاتصال به وإضافة هذا السجل إلى “متجرنا” حتى نتمكن من تتبع من نحن على اتصال. نقوم بذلك باستخدام ha.Peerstore().AddAddr

نقوم بعد ذلك بإنشاء دفق الاتصال الخاص بنا إلى النظير الذي نريد الاتصال به باستخدام ha.NewStream . نريد أن نكون قادرين على تلقي وإرسال تدفقات البيانات (blockchain الخاصة بنا) إليهم ، لذا كما فعلنا في معالجنا ، نقوم بإنشاء ReadWriter ونقوم بتدوير إجراءات Go منفصلة لـ readData و writeData . ننتهي من الحظر باستخدام عبارة select فارغة حتى لا ينتهي برنامجنا وينتهي فقط.

واهو!

خمن ماذا؟ لقد انتهينا! أعلم أن ذلك كان قليلًا ولكن بالنظر إلى مدى تعقيد هندسة P2P ، يجب أن تفخر بأنك وصلت إلى النهاية! لم يكن ذلك سيئًا للغاية ، أليس كذلك؟

كود كامل

ها هي الشفرة كاملة

اختبار القيادة

دعنا الآن نصل إلى ما نحن هنا من أجله ونجرب ذلك! سنستخدم 3 محطات طرفية منفصلة للعمل كأقران فرديين.

قبل بدء التطبيق ، تفضل بنفسك وانتقل إلى الدليل الجذر لـ go-libp2p وشغل make deps مرة أخرى. هذا يضمن أن التبعيات الخاصة بك بالترتيب. مرة أخرى ، تستخدم هذه المكتبة مدير الحزم المزعج gx وعلينا القيام ببعض الأمور لجعلها تعمل بشكل جيد.

ارجع إلى دليل العمل الخاص بك.

في الجهاز الطرفي الأول ، انتقل إلى تشغيل main.go -l 10000 -secio

اتبع التعليمات التي تقول “تشغيل الآن…”. افتح طرفية ثانية ، وانتقل إلى نفس الدليل و اذهب قم بتشغيل main.go -l 10001 -d & lt ؛ العنوان المحدد في التعليمات & gt ؛ -Secio

سترى أول محطة طرفية اكتشفت الاتصال الجديد!

الآن باتباع الإرشادات الموجودة في المحطة الثانية ، افتح Terminal الثالث ، وانتقل إلى دليل العمل نفسه و اذهب إلى main.go -l 10002 -d & lt؛ العنوان المحدد في التعليمات & gt؛ -Secio

تحقق من المحطة الثانية ، التي اكتشفت الاتصال من المحطة الثالثة.

فلنبدأ الآن في إدخال بيانات BPM الخاصة بنا. اكتب “70” في المبنى الأول لدينا ، وامنحه بضع ثوانٍ وشاهد ما يحدث في كل محطة.



ماذا حدث هنا للتو؟ إنه حقًا رائعًا ، لذا دعونا نفكر مليًا في الأمر.

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

دعنا نختبرها مرة أخرى ولكن هذه المرة اسمح للمحطة 2 بإضافة كتلة. اكتب “80” في مبنى الركاب 2.



رائع! كما هو متوقع ، أضافت المحطة 2 هذه المرة كتلة جديدة وبثتها إلى بقية الشبكة. أجرى كل من النظراء فحوصات داخلية خاصة به وقاموا بتحديث blockchain الخاصة بهم إلى أحدث blockchain من الشبكة!

الخطوات التالية

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

هناك بعض التحسينات والمحاذير التي يجب إزالتها. تحدى نفسك وحاول التعامل مع واحد أو أكثر مما يلي:

هذا كل شيء الآن!

إلى أين ستنتقل رحلة P2P من هنا؟ بين هذا والبرامج التعليمية الأخرى لدينا ، لديك تقريبًا كل اللبنات الأساسية لإنشاء blockchain الخاص بك من البداية. هذه أمور خطيرة!

فيما يلي روابط برامجنا التعليمية الأخرى. اقرأهم جميعًا!

لمعرفة المزيد حول Coral Health وكيف نستخدم blockchain للنهوض بأبحاث الطب المخصص ، تفضل بزيارة موقع الويب وتابعنا على Twitter !