تطوير Blockchain للصناعة على Go. الجزء الأول

منذ أربعة أشهر ، أعمل على مشروع يسمى "تطوير أدوات الأمان وإدارة البيانات المستندة إلى blockchain في الحكومة والصناعة".
أود الآن أن أتحدث عن كيفية بدء هذا المشروع ، والآن سأصف رمز البرنامج بالتفصيل.

صورة

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

لكن الجزء الثاني يتعلق ببنى بيانات سلسلة الكتل والمعاملات ، وكذلك حول الحزمة التي تنفذ التفاعل مع قاعدة البيانات.

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

يتم التطوير في Go ، وقاعدة البيانات التي يتم تخزين الكتل فيها هي LevelDB.
الأجزاء الرئيسية هي البروتوكول ، الخادم (الذي يقوم بتشغيل TCP و WebSocket - الأول لمزامنة blockchain ، والثاني لربط العملاء ، وإرسال المعاملات والأوامر من JavaScript ، على سبيل المثال.

كما ذكرنا ، فإن سلسلة الكتل هذه ضرورية في المقام الأول لأتمتة وحماية تبادل المنتجات بين الموردين والعملاء ، أو كليهما في شخص واحد. إنهم ليسوا في عجلة من أمرهم ليثقوا ببعضهم البعض. لكن المهمة ليست فقط إنشاء "دفتر شيكات" مع آلة حاسبة مدمجة ، ولكن نظام بأتمتة معظم المهام الروتينية التي تنشأ عند العمل مع دورة حياة المنتج. يتم تخزين كود البايت المسؤول عن هذا العمل ، كما هو معتاد في blockchains ، في مدخلات ومخرجات المعاملات (المعاملات نفسها في كتل ، يتم ترميز الكتل في LevelDB مسبقًا بتنسيق GOB). أولاً ، لنتحدث عن البروتوكول والخادم (المعروف أيضًا بالعقدة).

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

تسمى المجلدات (الدلائل كما يطلق عليها Linux) الحزم في فهم المبرمجين Go ، لذلك في بداية كل ملف برمز Go من هذا الدليل في البداية يكتبون حزمة folder_name_where_sit_this_file. خلاف ذلك ، لا يمكن تغذية الحزمة إلى المترجم. حسنا ، هذا ليس سرا بالنسبة لأولئك الذين يعرفون هذه اللغة. هذه الباقات هي:

  • الشبكات (الخادم ، العميل ، البروتوكول)
  • هياكل البيانات المخزنة والمرسلة (الكتلة ، المعاملة)
  • (blockchain)
  • (consensus)
  • (xvm)
  • (crypto, types) .

إليكم رابط إلى github.

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

أولاً ، ألق نظرة على الخادم.

يعمل روتين الخادم كملقم بيانات يعمل فوق بروتوكول TCP باستخدام هياكل البيانات من حزمة البروتوكول.

يستخدم روتين الحزم التالية: الخادم ، بروتوكول ، أنواع . تحتوي الحزمة نفسها على خدمة بنية بيانات tcp_server.go.

type Serve struct {
	Port string
	BufSize int
	ST *types.Settings
}

يمكن أن يستغرق المعلمات التالية:

  • منفذ شبكة يتم من خلاله تبادل البيانات
  • ملف تهيئة خادم JSON
  • تشغيل العلم في وضع التصحيح (blockchain خاص)

التقدم:

  • قراءة التكوين من ملف JSON
  • يتم فحص علامة وضع التصحيح: إذا تم تعيينها ، فلن يتم تشغيل برنامج جدولة مزامنة الشبكة ولا يتم تحميل سلسلة الكتل
  • تهيئة هيكل بيانات التكوين وبدء الخادم

الخادم


  • يبدأ اتصال خادم TCP والشبكة وفقًا للبروتوكول.
  • يحتوي على بنية بيانات Serve تتكون من رقم المنفذ وحجم المخزن المؤقت ومؤشر للأنواع.
  • Run ( , handle )
  • handle , protocol.Choice
  • protocol.Choice result . result protocol.Interprete, intrprInterpreteData,
  • ثم يتم تنفيذ التبديل عن طريق أوامر مقدمة [0] يتم فيها تحديد أحد الخيارات التالية: النتيجة ، رقم الفاتورة ، الخطأ ، وهناك قسم افتراضي
  • في نتيجة القسم، هناك مفتاح من قيمة intrpr.Commands [1] الذي يتحقق قيم bufferlength و نسخة (في كل حالة يتم استدعاء الدالة المقابلة)

و GetVersion و ظائف BufferLength تقع في srvlib.go ملف من حزمة الخدمة.

GetVersion(conn net.Conn, version string)

فقط يطبع على وحدة التحكم ويرسل للعميل النسخة التي تم تمريرها في المعلمة:

conn.Write([]byte("result:" + version))
.
وظيفة

BufferLength(conn net.Conn, intrpr *protocol.InterpreteData)

يقوم بتحميل كتلة أو معاملة أو بيانات محددة أخرى على النحو التالي:

  • يطبع على وحدة التحكم نوع البيانات المحددة في البروتوكول الذي يجب قبوله:

    fmt.Println("DataType:", intrpr.Commands[2])
  • يقرأ الجسم المقدّم في المتغير الرقمي buf_len
  • إنشاء مخزن مؤقت newbuf بالحجم المحدد:

    make([]byte, buf_len)
  • يرسل استجابة جيدة:

    conn.Write([]byte("result:ok"))
  • ينتج مخزن مؤقت كامل من دفق القراءة:

    io.ReadFull(conn, newbuf)
    .
  • يطبع محتويات المخزن المؤقت على وحدة التحكم

    fmt.Println(string(newbuf))

    وعدد وحدات البايت المقروءة

    fmt.Println("Bytes length:", n)
  • يرسل استجابة جيدة:

    conn.Write([]byte("result:ok"))

يتم تكوين الطرق من حزمة الخادم بطريقة تعالج البيانات المستلمة بوظائف من حزمة البروتوكول .

بروتوكول


يعمل البروتوكول كأداة تقدم البيانات أثناء اتصال الشبكة.

الاختيار (سلسلة نصية) (سلسلة ، خطأ) ينفذ المعالجة الأولية للبيانات التي يتلقاها الخادم ، ويتلقى تمثيل سلسلة للبيانات ويعيد السلسلة المعدة لـ Interprete :

  • يتم تقسيم سلسلة الإدخال إلى الرأس والجسم باستخدام ReqParseN2 (str)
  • يتم تقسيم الرأس إلى عناصر ويوضع في شريحة الأوامر باستخدام ReqParseHead (الرأس)
  • في التبديل (أوامر [0]) ، نختار الأمر المستلم (يتم تشغيل cmd أو المفتاح أو العنوان أو القسم الافتراضي )
  • في كمد ، يتم التحقق من أوامر التبديل 2 (أوامر [1]) - الطول و getversion .
  • يقوم الطول بفحص نوع البيانات في الأوامر [2] وتخزينه في نوع البيانات
  • الشيكات التي الجسم يحتوي على قيمة السلسلة

    len(body) < 1
  • إرجاع سلسلة استجابة:

    "result:bufferlength:" + datatype + "/" + body
  • إرجاع getversion سلسلة

    return "result:version/auto"

تفسير


يحتوي على بنية InterpreteData وينفذ معالجة ثانوية للسلسلة التي تم إرجاعها من اختيار وتشكيل كائن InterpreteData .

type InterpreteData struct {
	Head string
	Commands []string
	Body string
	IsErr bool
	ErrCode int 
	ErrMessage string
}

وظيفة

Interprete(str string) (*InterpreteData, error)

يقبل نتيجة السلسلة ويقوم بإنشاء مرجع إلى كائن InterpreteData .

التقدم:

  • وبالمثل ، يستعيد Choice الرأس والجسم باستخدام ReqParseN2 (str)
  • يتم تقسيم الرأس إلى عناصر باستخدام ReqParseHead (الرأس)
  • تتم تهيئة كائن InterpreteData ويتم إرجاع مؤشر إليه:

res := &InterpreteData{
	Head: head,
	Commands: commands,
	Body: body,
}
return res, nil

يستخدم هذا الكائن في server.go من الحزمة الرئيسية.

عميل


تحتوي حزمة العميل على الدالات TCPConnect و TCPResponseData .

وظيفة

TCPConnect(s *types.Settings, data []byte, payload []byte)

يعمل على النحو التالي:

  • يتم إجراء اتصال بالاتصال المحدد في كائن الإعدادات المنقولة

    net.Dial("tcp", s.Host + ":" + s.Port)
  • يتم نقل البيانات التي تم تمريرها في معلمة البيانات:

    conn.Write(data)
  • اقرأ الجواب

    resp, n, _ := TCPResponseData(conn, s.BufSize)

    وطباعتها على وحدة التحكم

    fmt.Println(string(resp[:n]))
  • إذا تم إرسال الحمولة ، يتم إرسالها

    conn.Write(payload)

    ويقرأ أيضًا استجابة الخادم ، ويطبعها على وحدة التحكم

وظيفة

 TCPResponseData(conn net.Conn, bufsiz int) ([]byte, int, error)

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

روتين العميل


يعمل على إرسال الأوامر إلى خوادم العقدة ، وكذلك للحصول على إحصاءات واختبارات موجزة.

يمكن أن يأخذ المعلمات التالية: ملف تكوين بتنسيق JSON ، البيانات التي يتم إرسالها إلى الخادم كسلسلة ، مسار إلى الملف لإرساله إلى الحمولة ، إشارة محاكاة جدولة العقدة ، نوع البيانات التي سيتم نقلها كقيمة رقمية.

  • احصل على التكوين

    st := types.ParseConfig(*config)
  • إذا تم إعطاؤك علم emu يبدأ sheduler
  • إذا تم تحديد العلامة f للإشارة إلى المسار إلى الملف ، فقم بتحميل بياناته في fdb وإرسال المحتويات إلى الخادم

    client.TCPConnect(st, []byte(CMD_BUFFER_LENGTH + ":" + strconv.Itoa(*t) + "/" + strconv.Itoa(fdblen)), fdb)
  • إذا لم يتم تحديد الملف ، فسيتم ببساطة إرسال البيانات من العلامة -d :

    client.TCPConnect(st, []byte(*data), nil)

كل هذا عرض مبسط يوضح بنية البروتوكول. أثناء التطوير ، يتم إضافة الوظائف اللازمة إلى هيكلها.

في الجزء الثاني سأتحدث عن هياكل البيانات للكتل والمعاملات ، في 3 حول خادم WebSocket للاتصال من JavaScript ، في 4 سننظر في جدولة التزامن ، ثم آلة مكدس تقوم بمعالجة الرمز الثانوي من المدخلات والمخرجات ، والتشفير ومخرجات المخرجات.

All Articles