Πώς λειτουργεί ένας αποκεντρωμένος αγγελιοφόρος στο blockchain;

Στις αρχές του 2017, ξεκινήσαμε τη δημιουργία ενός messenger στο blockchain [το όνομα και ο σύνδεσμος βρίσκονται στο προφίλ] συζητώντας τα πλεονεκτήματα σε σχέση με τα κλασικά μηνύματα P2P.

Χαμένος 2.5 έτος, και μπορέσαμε να επιβεβαιώσουμε την ιδέα μας: οι εφαρμογές messenger είναι πλέον διαθέσιμες για iOS, Web PWA, Windows, GNU/Linux, Mac OS και Android.

Σήμερα θα σας πούμε πώς λειτουργεί το blockchain messenger και πώς μπορούν να λειτουργήσουν οι εφαρμογές πελατών με το API του.
Πώς λειτουργεί ένας αποκεντρωμένος αγγελιοφόρος στο blockchain;

Θέλαμε το blockchain να λύσει τα ζητήματα ασφάλειας και απορρήτου των κλασικών P2P messenger:

  • Ένα κλικ για να δημιουργήσετε έναν λογαριασμό - χωρίς τηλέφωνα ή email, χωρίς πρόσβαση σε βιβλία διευθύνσεων ή γεωγραφικές τοποθεσίες.
  • Οι συνομιλητές δεν δημιουργούν ποτέ άμεσες συνδέσεις· όλη η επικοινωνία πραγματοποιείται μέσω ενός κατανεμημένου συστήματος κόμβων. Οι διευθύνσεις IP των χρηστών δεν είναι προσβάσιμες μεταξύ τους.
  • Όλα τα μηνύματα είναι κρυπτογραφημένα End-to-End curve25519xsalsa20poly1305. Φαίνεται ότι αυτό δεν θα εκπλήξει κανέναν, αλλά ο πηγαίος κώδικας μας είναι ανοιχτός.
  • Η επίθεση MITM αποκλείεται - κάθε μήνυμα είναι μια συναλλαγή και υπογράφεται από τον Ed25519 EdDSA.
  • Το μήνυμα καταλήγει στο δικό του μπλοκ. Συνέπεια και timestamp Δεν μπορείτε να διορθώσετε τα μπλοκ, άρα και τη σειρά των μηνυμάτων.
  • Το "Δεν το είπα αυτό" δεν θα λειτουργήσει με μηνύματα στο blockchain.
  • Δεν υπάρχει κεντρική δομή που να ελέγχει την «αυθεντικότητα» ενός μηνύματος. Αυτό γίνεται από ένα κατανεμημένο σύστημα κόμβων που βασίζεται στη συναίνεση και ανήκει στους χρήστες.
  • Αδυναμία λογοκρισίας - οι λογαριασμοί δεν μπορούν να αποκλειστούν και τα μηνύματα δεν μπορούν να διαγραφούν.
  • Το Blockchain 2FA είναι μια εναλλακτική λύση στο κολασμένο 2FA μέσω SMS, κατέστρεψε πολλή υγεία.
  • Η δυνατότητα λήψης όλων των συνομιλιών σας από οποιαδήποτε συσκευή ανά πάσα στιγμή σημαίνει ότι δεν χρειάζεται καθόλου να αποθηκεύετε τις συνομιλίες τοπικά.
  • Επιβεβαίωση παράδοσης μηνύματος. Όχι στη συσκευή του χρήστη, αλλά στο δίκτυο. Ουσιαστικά, αυτό είναι επιβεβαίωση της ικανότητας του παραλήπτη να διαβάσει το μήνυμά σας. Αυτή είναι μια χρήσιμη λειτουργία για την αποστολή κρίσιμων ειδοποιήσεων.

Τα οφέλη του Blockchain περιλαμβάνουν επίσης τη στενή ενσωμάτωση με τα κρυπτονομίσματα Ethereum, Dogecoin, Lisk, Dash, Bitcoin (αυτό είναι ακόμα σε εξέλιξη) και τη δυνατότητα αποστολής κουπονιών σε συνομιλίες. Φτιάξαμε ακόμη και έναν ενσωματωμένο εναλλάκτη κρυπτογράφησης.

Και μετά - πώς λειτουργούν όλα.

Ένα μήνυμα είναι μια συναλλαγή

Όλοι είναι ήδη συνηθισμένοι στο γεγονός ότι οι συναλλαγές στο blockchain μεταφέρουν διακριτικά (κέρματα) από τον έναν χρήστη στον άλλο. Όπως το Bitcoin. Δημιουργήσαμε έναν ειδικό τύπο συναλλαγής για τη μετάδοση μηνυμάτων.

Για να στείλετε ένα μήνυμα σε ένα messenger στο blockchain, πρέπει να ακολουθήσετε διάφορα βήματα:

  1. Κρυπτογράφηση κειμένου μηνύματος
  2. Τοποθετήστε κρυπτογραφημένο κείμενο σε μια συναλλαγή
  3. Υπογράψτε τη συναλλαγή
  4. Στείλτε μια συναλλαγή σε οποιονδήποτε κόμβο δικτύου
  5. Ένα κατανεμημένο σύστημα κόμβων καθορίζει την «αυθεντικότητα» ενός μηνύματος
  6. Εάν όλα είναι εντάξει, η συναλλαγή με το μήνυμα περιλαμβάνεται στο επόμενο μπλοκ
  7. Ο παραλήπτης ανακτά τη συναλλαγή του μηνύματος και αποκρυπτογραφεί

Τα βήματα 1–3 και 7 εκτελούνται τοπικά στον πελάτη και τα βήματα 5–6 εκτελούνται στους κεντρικούς υπολογιστές.

Κρυπτογράφηση μηνυμάτων

Το μήνυμα κρυπτογραφείται με το ιδιωτικό κλειδί του αποστολέα και το δημόσιο κλειδί του παραλήπτη. Θα πάρουμε το δημόσιο κλειδί από το δίκτυο, αλλά για αυτό, ο λογαριασμός του παραλήπτη πρέπει να προετοιμαστεί, δηλαδή να έχει τουλάχιστον μία συναλλαγή. Μπορείτε να χρησιμοποιήσετε ένα αίτημα REST GET /api/accounts/getPublicKey?address={ADAMANT address}, και κατά τη φόρτωση συνομιλιών, τα δημόσια κλειδιά των συνομιλητών θα είναι ήδη διαθέσιμα.

Πώς λειτουργεί ένας αποκεντρωμένος αγγελιοφόρος στο blockchain;

Ο αγγελιοφόρος κρυπτογραφεί μηνύματα χρησιμοποιώντας τον αλγόριθμο curve25519xsalsa20poly1305 (Κουτί NaCl). Δεδομένου ότι ο λογαριασμός περιέχει κλειδιά 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 - μηδέν
  • type — τύπος μηνύματος

Τα μηνύματα χωρίζονται επίσης σε τύπους. Ουσιαστικά η παράμετρος type σου λέει πώς να καταλάβεις message. Μπορείτε να στείλετε απλώς ένα κείμενο ή μπορείτε να στείλετε ένα αντικείμενο με ενδιαφέροντα πράγματα μέσα - για παράδειγμα, αυτός είναι ο τρόπος με τον οποίο ο messenger πραγματοποιεί μεταφορές κρυπτονομισμάτων σε συνομιλίες.

Ως αποτέλεσμα, δημιουργούμε μια συναλλαγή:

{
  "transaction": {
    "type": 8,
    "amount": 0,
    "senderId": "U12499126640447739963",
    "senderPublicKey": "e9cafb1e7b403c4cf247c94f73ee4cada367fcc130cb3888219a0ba0633230b6",
    "asset": {
      "chat": {
        "message": "cb682accceef92d7cddaaddb787d1184ab5428",
        "own_message": "e7d8f90ddf7d70efe359c3e4ecfb5ed3802297b248eacbd6",
        "type": 1
      }
    },
    "recipientId": "U15677078342684640219",
    "timestamp": 63228087,
    "signature": "тут будет подпись"
  }
}

Υπογραφή συναλλαγής

Για να εξασφαλιστεί ότι όλοι είναι σίγουροι για την αυθεντικότητα του αποστολέα και του παραλήπτη, την ώρα αποστολής και το περιεχόμενο του μηνύματος, η συναλλαγή υπογράφεται. Μια ψηφιακή υπογραφή σάς επιτρέπει να επαληθεύσετε την αυθεντικότητα μιας συναλλαγής χρησιμοποιώντας ένα δημόσιο κλειδί - δεν απαιτείται ιδιωτικό κλειδί για αυτό.

Αλλά η ίδια η υπογραφή εκτελείται χρησιμοποιώντας το ιδιωτικό κλειδί:

Πώς λειτουργεί ένας αποκεντρωμένος αγγελιοφόρος στο blockchain;

Το διάγραμμα δείχνει ότι πρώτα κατακερματίζουμε τη συναλλαγή με το 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
      break

…

    default:
      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'))
}

Αποστολή συναλλαγής με μήνυμα σε κόμβο δικτύου

Εφόσον το δίκτυο είναι αποκεντρωμένο, οποιοσδήποτε από τους κόμβους με ανοιχτό API θα κάνει. Υποβολή αιτήματος POST στο τελικό σημείο api/transactions:

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

Σε απάντηση θα λάβουμε ένα αναγνωριστικό συναλλαγής του τύπου

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

Επικύρωση συναλλαγής

Ένα κατανεμημένο σύστημα κόμβων, βασισμένο στη συναίνεση, καθορίζει την «αυθεντικότητα» του μηνύματος συναλλαγής. Από ποιον και σε ποιον, πότε, αν το μήνυμα αντικαταστάθηκε με άλλο και αν η ώρα αποστολής υποδείχθηκε σωστά. Αυτό είναι ένα πολύ σημαντικό πλεονέκτημα του blockchain - δεν υπάρχει κεντρική δομή που να είναι υπεύθυνη για την επαλήθευση και η σειρά των μηνυμάτων και το περιεχόμενό τους δεν μπορούν να παραποιηθούν.

Πρώτα, ένας κόμβος ελέγχει την ακρίβεια και στη συνέχεια τον στέλνει σε άλλους - εάν η πλειοψηφία πει ότι όλα είναι εντάξει, η συναλλαγή θα συμπεριληφθεί στο επόμενο μπλοκ της αλυσίδας - αυτό είναι συναίνεση.

Πώς λειτουργεί ένας αποκεντρωμένος αγγελιοφόρος στο blockchain;

Το τμήμα του κώδικα κόμβου που είναι υπεύθυνο για τους ελέγχους μπορεί να προβληθεί στο GitHub - validator.js и επιβεβαιώστε.js. Ναι, ο κόμβος εκτελείται στο Node.js.

Συμπεριλαμβανομένης μιας συναλλαγής με ένα μήνυμα σε ένα μπλοκ

Εάν επιτευχθεί συναίνεση, η συναλλαγή με το μήνυμά μας θα συμπεριληφθεί στο επόμενο μπλοκ μαζί με άλλες έγκυρες συναλλαγές.

Τα μπλοκ έχουν μια αυστηρή ακολουθία και κάθε επόμενο μπλοκ σχηματίζεται με βάση τους κατακερματισμούς των προηγούμενων μπλοκ.

Πώς λειτουργεί ένας αποκεντρωμένος αγγελιοφόρος στο blockchain;

Το θέμα είναι ότι το μήνυμά μας περιλαμβάνεται επίσης σε αυτή τη σειρά και δεν μπορεί να «αναδιαταχθεί». Εάν πολλά μηνύματα εμπίπτουν σε ένα μπλοκ, η σειρά τους θα καθοριστεί από 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 δευτερόλεπτα - αυτή είναι η στιγμή που εμφανίζεται ένα νέο μπλοκ δικτύου - καταλήξαμε σε μια σύνδεση υποδοχής πελάτη-κόμβου και κόμβου-προς-κόμβου. Όταν ένας κόμβος λαμβάνει μια νέα συναλλαγή, ελέγχει την εγκυρότητά της και την προωθεί σε άλλους κόμβους. Η συναλλαγή είναι διαθέσιμη σε πελάτες messenger ακόμη και πριν από τη συναίνεση και τη συμπερίληψη στο μπλοκ. Με αυτόν τον τρόπο θα παραδίδουμε μηνύματα άμεσα, όπως και οι κανονικοί instant messenger.

Για να αποθηκεύσουμε το βιβλίο διευθύνσεων, δημιουργήσαμε KVS - Αποθήκευση κλειδιού-τιμής - αυτός είναι ένας άλλος τύπος συναλλαγής στον οποίο asset δεν είναι το κουτί NaCl που είναι κρυπτογραφημένο, αλλά NaCl-μυστικό κουτί. Αυτός είναι ο τρόπος με τον οποίο ο messenger αποθηκεύει άλλα δεδομένα.

Οι μεταφορές αρχείων/εικόνων και οι ομαδικές συνομιλίες απαιτούν ακόμη πολλή δουλειά. Φυσικά, στη μορφή "γκάφες και γκάφα" αυτό μπορεί να "βιδωθεί" γρήγορα, αλλά θέλουμε να διατηρήσουμε το ίδιο επίπεδο απορρήτου.

Ναι, υπάρχει ακόμα δουλειά που πρέπει να γίνει - ιδανικά, το πραγματικό απόρρητο προϋποθέτει ότι οι χρήστες δεν θα συνδεθούν σε δημόσιους κόμβους δικτύου, αλλά θα αυξήσουν τους δικούς τους. Τι ποσοστό των χρηστών πιστεύετε ότι το κάνει αυτό; Αυτό είναι σωστό, 0. Καταφέραμε να λύσουμε εν μέρει αυτό το πρόβλημα με την έκδοση Tor του messenger.

Έχουμε αποδείξει ότι ένας αγγελιοφόρος στο blockchain μπορεί να υπάρχει. Προηγουμένως, υπήρχε μόνο μία προσπάθεια το 2012 - bitmessage, το οποίο απέτυχε λόγω μεγάλου χρόνου παράδοσης μηνυμάτων, φόρτου CPU και έλλειψης εφαρμογών για κινητά.

Και ο σκεπτικισμός οφείλεται στο γεγονός ότι οι αγγελιοφόροι στο blockchain είναι μπροστά από την εποχή τους - οι άνθρωποι δεν είναι έτοιμοι να αναλάβουν την ευθύνη του λογαριασμού τους, η κατοχή προσωπικών πληροφοριών δεν είναι ακόμη τάση και η τεχνολογία δεν επιτρέπει υψηλές ταχύτητες στο blockchain. Περισσότερα τεχνολογικά ανάλογα του έργου μας θα εμφανιστούν στη συνέχεια. Θα δείτε.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο