كيف رسول اللامركزية على blockchain

في بداية عام 2017 ، بدأنا في إنشاء messenger على blockchain [الاسم والرابط موجودان في الملف الشخصي] من خلال مناقشة المزايا التي توفرها برامج messenger P2P الكلاسيكية.

مرت 2.5 عام وتمكنا من تأكيد مفهومنا: أصبحت تطبيقات المراسلة الفورية لنظام التشغيل iOS و Web PWA و Windows و GNU / Linux و Mac OS و Android متاحة الآن.

سنخبرك اليوم كيف يتم ترتيب برنامج المراسلة على blockchain وكيف يمكن أن تعمل تطبيقات العميل مع API الخاص به.


أردنا أن تعمل blockchain على حل مشكلات الأمان والخصوصية الخاصة برسائل P2P الكلاسيكية:

  • بنقرة واحدة لإنشاء حساب - لا الهواتف ورسائل البريد الإلكتروني ، لا يمكن الوصول إلى دفاتر العناوين والمواقع الجغرافية.
  • لا يقوم المحاورون مطلقًا بإنشاء اتصالات مباشرة ، فكل الاتصالات تمر عبر نظام موزّع من العقد. لا يمكن الوصول إلى عناوين IP الخاصة بالمستخدمين.
  • يتم تشفير كل الرسائل منحنى من طرف إلى طرف منحنى 2519xsalsa20poly1305. يبدو أنك لن تفاجئ أحدًا ، لكن لدينا شفرة مفتوحة المصدر.
  • تم استبعاد هجوم MITM - كل رسالة عبارة عن معاملة ويتم توقيعها بواسطة Ed25519 EdDSA.
  • الرسالة تقع في كتلة لها. لا يمكن إصلاح تسلسل الكتل timestamp ، وبالتالي ترتيب الرسائل.
  • "لم أقل هذا" لن يعمل مع الرسائل الموجودة على blockchain.
  • لا يوجد هيكل مركزي يقوم بالتحقق من "صحة" الرسالة. يتم ذلك عن طريق نظام العقدة الموزعة القائم على الإجماع ، وهو يخص المستخدمين.
  • استحالة الرقابة - لا يمكن حظر الحسابات وحذف الرسائل.
  • تعتبر 2FA blockchain بديلاً عن 2FA الجهنمية عن طريق الرسائل القصيرة ، والتي كسرت الكثير من الصحة.
  • القدرة على الحصول على جميع مربعات الحوار الخاصة بك من أي جهاز في أي وقت هي القدرة على عدم تخزين مربعات الحوار محليًا على الإطلاق.
  • رسالة تأكيد التسليم. ليس لجهاز المستخدم ، ولكن على الشبكة. في الحقيقة ، هذا تأكيد لقدرة المستلم على قراءة رسالتك. هذه ميزة مفيدة لإرسال الإشعارات الهامة.

من الكعك blockchain ، هناك أيضا تكامل وثيق مع cryptocurrency Ethereum ، و Dogecoin ، و Lisk ، و Dash ، و Bitcoin (هذا لا يزال جاريا) والقدرة على إرسال الرموز المميزة في الدردشات. لقد صنعنا حتى مبادل تشفير مدمج.

وبعد ذلك - كيف يعمل كل شيء.

الرسالة هي الصفقة


الجميع معتاد بالفعل على حقيقة أن المعاملات في الرموز المميزة لـ blockchain (العملات المعدنية) من مستخدم لآخر. مثل بيتكوين. لقد أنشأنا نوعًا خاصًا من المعاملات لإرسال الرسائل.

لإرسال رسالة في messenger على blockchain ، تحتاج إلى المرور عبر عدة مراحل:

  1. تشفير نص الرسالة
  2. وضع النص المشفر في المعاملات
  3. توقيع الصفقة
  4. إرسال معاملة إلى أي مضيف
  5. يحدد النظام الموزع للعقد "موثوقية" الرسالة
  6. إذا كان كل شيء على ما يرام ، يتم تضمين المعاملة مع الرسالة في الكتلة التالية.
  7. المستلم باسترداد المعاملة الرسالة وفك تشفيرها

يتم تنفيذ المرحلتين 1-3 و 7 محليًا على العميل و 5-6 على عقد الشبكة.

تشفير الرسائل


يتم تشفير الرسالة باستخدام المفتاح الخاص للمرسل والمفتاح العام للمستلم. سنأخذ المفتاح العمومي من الشبكة ، ولكن لهذا يجب أن تتم تهيئة حساب المستلم ، أي أن يكون لديه معاملة واحدة على الأقل. يمكنك استخدام طلب REST GET /api/accounts/getPublicKey?address={ADAMANT address} ، وعندما تقوم بتنزيل الدردشات ، ستكون المفاتيح العامة GET /api/accounts/getPublicKey?address={ADAMANT address} متاحة بالفعل.



يقوم المشفر بتشفير الرسائل باستخدام خوارزمية المنحنى 2519xsalsa20poly1305 ( NaCl Box ). نظرًا لأن الحساب يحتوي على مفاتيح Ed25519 ، من أجل تشكيل مربع ، يجب أولاً تحويل المفاتيح إلى Curve25519 Diffie-Hellman.

فيما يلي مثال في JavaScript:

 /** * Encodes a text message for sending to ADM * @param {string} msg message to encode * @param {*} recipientPublicKey recipient's public key * @param {*} privateKey our private key * @returns {{message: string, nonce: string}} */ adamant.encodeMessage = function (msg, recipientPublicKey, privateKey) { const nonce = Buffer.allocUnsafe(24) sodium.randombytes(nonce) if (typeof recipientPublicKey === 'string') { recipientPublicKey = hexToBytes(recipientPublicKey) } const plainText = Buffer.from(msg) const DHPublicKey = ed2curve.convertPublicKey(recipientPublicKey) const DHSecretKey = ed2curve.convertSecretKey(privateKey) const encrypted = nacl.box(plainText, nonce, DHPublicKey, DHSecretKey) return { message: bytesToHex(encrypted), nonce: bytesToHex(nonce) } } 

تشكيل معاملة مع رسالة


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

 { "id": "15161295239237781653", "height": 7585271, "blockId": "16391508373936326027", "type": 8, "block_timestamp": 45182260, "timestamp": 45182254, "senderPublicKey": "bd39cc708499ae91b937083463fce5e0668c2b37e78df28f69d132fce51d49ed", "senderId": "U16023712506749300952", "recipientId": "U17653312780572073341", "recipientPublicKey": "23d27f616e304ef2046a60b762683b8dabebe0d8fc26e5ecdb1d5f3d291dbe21", "amount": 204921300000000, "fee": 50000000, "signature": "3c8e551f60fedb81e52835c69e8b158eb1b8b3c89a04d3df5adc0d99017ffbcb06a7b16ad76d519f80df019c930960317a67e8d18ab1e85e575c9470000cf607", "signatures": [], "confirmations": 3660548, "asset": {} } 

بالنسبة لرسالة المعاملة ، يعد asset هو الأكثر أهمية - تحتاج إلى وضع الرسالة في كائن chat مع البنية:

  • message - حفظ الرسالة المشفرة
  • own_message - own_message
  • type - نوع الرسالة

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

نتيجة لذلك ، نقوم بتكوين المعاملة:

 { "transaction": { "type": 8, "amount": 0, "senderId": "U12499126640447739963", "senderPublicKey": "e9cafb1e7b403c4cf247c94f73ee4cada367fcc130cb3888219a0ba0633230b6", "asset": { "chat": { "message": "cb682accceef92d7cddaaddb787d1184ab5428", "own_message": "e7d8f90ddf7d70efe359c3e4ecfb5ed3802297b248eacbd6", "type": 1 } }, "recipientId": "U15677078342684640219", "timestamp": 63228087, "signature": "  " } } 

توقيع المعاملة


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

لكن التوقيع نفسه يتم تنفيذه فقط بواسطة المفتاح الخاص:



يمكن أن نرى من الشكل التوضيحي أننا قمنا أولاً بتجميع المعاملة مع SHA-256 ، ثم نوقع Ed25519 EdDSA ونحصل على توقيع signature ، ومعرف المعاملة هو جزء من التجزئة SHA-256.

مثال التنفيذ:


1 - نحن نشكل كتلة البيانات ، بما في ذلك رسالة

 /** * Calls `getBytes` based on transaction type * @see privateTypes * @implements {ByteBuffer} * @param {transaction} trs * @param {boolean} skipSignature * @param {boolean} skipSecondSignature * @return {!Array} Contents as an ArrayBuffer. * @throws {error} If buffer fails. */ adamant.getBytes = function (transaction) { ... switch (transaction.type) { case constants.Transactions.SEND: break case constants.Transactions.CHAT_MESSAGE: assetBytes = this.chatGetBytes(transaction) assetSize = assetBytes.length breakdefault: alert('Not supported yet') } var bb = new ByteBuffer(1 + 4 + 32 + 8 + 8 + 64 + 64 + assetSize, true) bb.writeByte(transaction.type) bb.writeInt(transaction.timestamp) ... bb.flip() var arrayBuffer = new Uint8Array(bb.toArrayBuffer()) var buffer = [] for (var i = 0; i < arrayBuffer.length; i++) { buffer[i] = arrayBuffer[i] } return Buffer.from(buffer) } 

2 - نحن نعتبر SHA-256 من كتلة البيانات

 /** * Creates hash based on transaction bytes. * @implements {getBytes} * @implements {crypto.createHash} * @param {transaction} trs * @return {hash} sha256 crypto hash */ adamant.getHash = function (trs) { return crypto.createHash('sha256').update(this.getBytes(trs)).digest() } 

3 - نوقع المعاملة

 adamant.transactionSign = function (trs, keypair) { var hash = this.getHash(trs) return this.sign(hash, keypair).toString('hex') } /** * Creates a signature based on a hash and a keypair. * @implements {sodium} * @param {hash} hash * @param {keypair} keypair * @return {signature} signature */ adamant.sign = function (hash, keypair) { return sodium.crypto_sign_detached(hash, Buffer.from(keypair.privateKey, 'hex')) } 

إرسال معاملة مع رسالة إلى مضيف


نظرًا لأن الشبكة غير مركزية ، فإن أي من العقد ذات واجهة برمجة تطبيقات مفتوحة ستفعل. نقوم بتقديم طلب POST لنقطة نهاية api/transactions :

 curl 'api/transactions' -X POST \ -d 'TX_DATA' 

استجابة لذلك ، حصلنا على معرف المعاملة من النوع

 { "success": true, "nodeTimestamp": 63228852, "transactionId": "6146865104403680934" } 

التحقق من صحة المعاملات


يحدد نظام العقد الموزع القائم على الإجماع "الموثوقية" لمعاملة الرسائل. من ومن ولمن ومتى وما إذا كان قد تم استبدال الرسالة بأخرى وما إذا كان قد تم الإشارة إلى وقت الإرسال بشكل صحيح. هذه ميزة مهمة جدًا لسلسلة blockchain - لا يوجد هيكل مركزي مسؤول عن عمليات التحقق ، ولا يمكن تزوير تسلسل الرسائل ومحتوياتها.

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



يمكن الاطلاع على جزء من رمز المضيف المسؤول عن التحقق من الصحة على GitHub - validator.js و valid.js . نعم ، العقدة تعمل على Node.js.

تضمين المعاملة مع رسالة في كتلة


إذا تم التوصل إلى توافق في الآراء ، فستقع المعاملة التي تتضمن رسالتنا في المجموعة التالية ، إلى جانب المعاملات الأخرى الموثوقة.

الكتل لديها تسلسل صارم ، ويتم تشكيل كل كتلة لاحقة على أساس تجزئة الكتل السابقة.



خلاصة القول هي أن رسالتنا مدرجة أيضًا في هذا التسلسل ولا يمكن "إعادة ترتيبها". إذا وقعت عدة رسائل في الكتلة ، فسيتم تحديد ترتيبها حسب timestamp للرسائل.

قراءة الرسائل


يسترد تطبيق messenger المعاملات من blockchain التي يتم إرسالها إلى المرسل إليه. للقيام بذلك ، حققنا نقطة النهاية api/chatrooms .

جميع المعاملات متاحة للجميع - يمكنك تلقي رسائل مشفرة. ولكن يمكن للمستلم فقط فك تشفيره باستخدام مفتاحه الخاص والمفتاح العمومي للمرسل:

 ** * Decodes the incoming message * @param {any} msg encoded message * @param {string} senderPublicKey sender public key * @param {string} privateKey our private key * @param {any} nonce nonce * @returns {string} */ adamant.decodeMessage = function (msg, senderPublicKey, privateKey, nonce) { if (typeof msg === 'string') { msg = hexToBytes(msg) } if (typeof nonce === 'string') { nonce = hexToBytes(nonce) } if (typeof senderPublicKey === 'string') { senderPublicKey = hexToBytes(senderPublicKey) } if (typeof privateKey === 'string') { privateKey = hexToBytes(privateKey) } const DHPublicKey = ed2curve.convertPublicKey(senderPublicKey) const DHSecretKey = ed2curve.convertSecretKey(privateKey) const decrypted = nacl.box.open(msg, nonce, DHPublicKey, DHSecretKey) return decrypted ? decode(decrypted) : '' } 

ماذا بعد؟


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

لتخزين دفتر العناوين ، جعلنا KVS - Key-Value Storage نوعًا آخر من المعاملات التي يتم فيها تشفير asset وليس باستخدام NaCl-box ، ولكن مع NaCl-secretbox . وبالتالي فإن رسول يخزن البيانات الأخرى.

لا يزال نقل الملفات / الصور والدردشة الجماعية تتطلب الكثير من العمل. بالطبع ، في تنسيق tyap-bloop ، يمكن تثبيت هذا بسرعة ، لكننا نريد الحفاظ على نفس مستوى الخصوصية.

نعم ، لا يزال هناك عمل يتعين القيام به - من الناحية المثالية ، تعني الخصوصية الحقيقية أن المستخدمين لن يتصلوا بعُقد الشبكة العامة ، لكنهم سيثيرون عيوبهم. ما رأيك ، كم في المئة من المستخدمين القيام بذلك؟ هذا صحيح ، 0. جزئيًا ، تمكنا من حل هذه المشكلة باستخدام إصدار Tor من برنامج المراسلة.

لقد أثبتنا أن رسولا على blockchain يمكن أن توجد. في السابق ، كانت هناك محاولة واحدة فقط في عام 2012 - وهي رسالة bitmessage ، والتي فشلت بسبب وقت التسليم الطويل للرسائل وتحميل وحدة المعالجة المركزية ونقص التطبيقات المحمولة.

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

Source: https://habr.com/ru/post/ar467751/


All Articles