Kuidas detsentraliseeritud messenger plokiahelas töötab?

2017. aasta alguses alustasime plokiahelas Messengeri loomist [nimi ja link on profiilis], arutledes eeliste üle klassikaliste P2P sõnumitoojate ees.

Läbitud 2.5 aastal ja saime oma kontseptsiooni kinnitada: messengeri rakendused on nüüd saadaval iOS-i, Web PWA, Windowsi, GNU/Linuxi, Mac OS-i ja Androidi jaoks.

Täna räägime teile, kuidas plokiahela messenger töötab ja kuidas saavad kliendirakendused selle API-ga töötada.
Kuidas detsentraliseeritud messenger plokiahelas töötab?

Tahtsime, et plokiahel lahendaks klassikaliste P2P sõnumitoojate turva- ja privaatsusprobleemid:

  • Konto loomiseks üks klõps – ei telefone ega e-kirju, juurdepääsu aadressiraamatutele ega geograafilistele asukohtadele.
  • Vestluspartnerid ei loo kunagi otseseid sidemeid, kogu suhtlus toimub hajutatud sõlmede süsteemi kaudu. Kasutajate IP-aadressid ei ole üksteisele juurdepääsetavad.
  • Kõik sõnumid on krüpteeritud. End-to-End curve25519xsalsa20poly1305. Tundub, et see ei üllata kedagi, kuid meie lähtekood on avatud.
  • MITM-i rünnak on välistatud – iga sõnum on tehing ja selle allkirjastab Ed25519 EdDSA.
  • Sõnum jõuab oma plokki. Järjepidevus ja timestamp Te ei saa parandada plokke ja seega ka sõnumite järjekorda.
  • "Ma ei öelnud seda" ei tööta plokiahela sõnumitega.
  • Puudub keskne struktuur, mis kontrolliks sõnumi "autentsust". Seda teeb konsensuse alusel hajutatud sõlmede süsteem ja see kuulub kasutajatele.
  • Tsensuuri võimatus – kontosid ei saa blokeerida ja sõnumeid kustutada.
  • Blockchain 2FA on alternatiiv põrgulikule 2FA-le SMS-i teel, rikkus palju tervist.
  • Võimalus hankida kõik oma vestlused mis tahes seadmest igal ajal tähendab, et te ei pea vestlusi üldse kohapeal salvestama.
  • Sõnumi kohaletoimetamise kinnitus. Mitte kasutaja seadmesse, vaid võrku. Põhimõtteliselt on see kinnitus selle kohta, et adressaat suudab teie sõnumit lugeda. See on kasulik funktsioon kriitiliste teadete saatmiseks.

Plokiahela eelised hõlmavad ka tihedat integratsiooni krüptovaluutadega Ethereum, Dogecoin, Lisk, Dash, Bitcoin (see on veel pooleli) ja võimalust vestlustes žetoone saata. Tegime isegi sisseehitatud krüptovaheti.

Ja siis – kuidas see kõik käib.

Sõnum on tehing

Kõik on juba harjunud, et plokiahelas toimuvad tehingud kannavad žetoone (münte) ühelt kasutajalt teisele. Nagu Bitcoin. Loosime sõnumite edastamiseks spetsiaalse tehingutüübi.

Plokiahelas Messengeris sõnumi saatmiseks peate läbima mitu sammu:

  1. Krüpteeri sõnumi tekst
  2. Lisage tehingusse šifreeritud tekst
  3. Allkirjastage tehing
  4. Saatke tehing mis tahes võrgusõlme
  5. Sõnumi "autentsuse" määrab hajutatud sõlmede süsteem
  6. Kui kõik on korras, lülitatakse teatega tehing järgmisesse plokki
  7. Saaja otsib sõnumitehingu ja dekrüpteerib

Sammud 1–3 ja 7 tehakse kliendil lokaalselt ning sammud 5–6 tehakse hostidel.

Sõnumite krüpteerimine

Sõnum krüpteeritakse saatja privaatvõtmega ja saaja avaliku võtmega. Võtame võrgust avaliku võtme, kuid selleks peab saaja konto olema lähtestatud, see tähendab, et sellel peab olema vähemalt üks tehing. Võite kasutada REST-päringut GET /api/accounts/getPublicKey?address={ADAMANT address}, ja vestluste laadimisel on vestluspartnerite avalikud võtmed juba saadaval.

Kuidas detsentraliseeritud messenger plokiahelas töötab?

Messenger krüpteerib sõnumid curve25519xsalsa20poly1305 algoritmi (NaCl kast). Kuna konto sisaldab Ed25519 võtmeid, tuleb kasti moodustamiseks võtmed esmalt teisendada Curve25519 Diffie-Hellmaniks.

Siin on näide JavaScriptis:

/**
 * 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)
  }
}

Tehingu vormistamine sõnumiga

Tehingu üldstruktuur on järgmine:

{
  "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": {}
}

Sõnumitehingu puhul on kõige olulisem asset - objektile tuleb panna teade chat struktuuriga:

  • message - salvestage krüptitud sõnum
  • own_message - mitte ükski
  • type - sõnumi tüüp

Sõnumid jagunevad ka tüüpideks. Sisuliselt parameeter type ütleb teile, kuidas aru saada message. Võid saata lihtsalt teksti või saad saata objekti, mille sees on huvitavaid asju – näiteks nii teeb messenger chatides krüptoraha ülekandeid.

Selle tulemusena loome tehingu:

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

Tehingu allkiri

Tagamaks, et kõik on kindlad saatja ja saaja autentsuses, saatmise ajas ja sõnumi sisus, allkirjastatakse tehing. Digiallkiri võimaldab kontrollida tehingu autentsust avaliku võtme abil – privaatvõtit pole selleks vaja.

Kuid allkiri ise toimub privaatvõtme abil:

Kuidas detsentraliseeritud messenger plokiahelas töötab?

Diagramm näitab, et esmalt räsime tehingu SHA-256-ga ja seejärel allkirjastame selle Ed25519 EdDSA ja saada allkiri signatureja tehingu ID on osa SHA-256 räsist.

Rakenduse näide:

1 — Andmeploki, sealhulgas sõnumi moodustamine

/**
 * 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 loendamine andmeplokist

/**
 * 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. Allkirjastage tehing

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

Tehingu saatmine sõnumiga võrgusõlme

Kuna võrk on detsentraliseeritud, sobivad kõik avatud API-ga sõlmed. POST-päringu tegemine lõpp-punktile api/transactions:

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

Vastuseks saame seda tüüpi tehingu ID

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

Tehingu kinnitamine

Jaotatud sõlmede süsteem, mis põhineb konsensusel, määrab tehinguteate autentsuse. Kellelt ja kellele, millal, kas sõnum asendati teisega ja kas saatmise aeg oli õigesti märgitud. See on plokiahela väga oluline eelis – puudub keskne struktuur, mis vastutaks kontrollimise eest ning sõnumite jada ja nende sisu ei saa võltsida.

Esiteks kontrollib üks sõlm täpsust ja seejärel saadab selle teistele - kui enamus ütleb, et kõik on korras, lülitatakse tehing ahela järgmisse plokki - see on konsensus.

Kuidas detsentraliseeritud messenger plokiahelas töötab?

Kontrollide eest vastutavat sõlme koodi osa saab vaadata GitHubis - validator.js и verify.js. Jah, sõlm töötab saidil Node.js.

Teatega tehingu lisamine plokis

Konsensuse saavutamisel kaasatakse meie sõnumiga tehing koos teiste kehtivate tehingutega järgmisesse plokki.

Plokid on range järjestusega ja iga järgnev plokk moodustatakse eelmiste plokkide räside põhjal.

Kuidas detsentraliseeritud messenger plokiahelas töötab?

Asi on selles, et meie sõnum on samuti selles jadas ja seda ei saa "ümber korraldada". Kui plokki langeb mitu sõnumit, määrab nende järjekorra timestamp sõnumeid.

Sõnumite lugemine

Messengeri rakendus hangib plokiahelast adressaadile saadetud tehingud. Selleks tegime lõpp-punkti api/chatrooms.

Kõik tehingud on kõigile kättesaadavad – saate krüpteeritud sõnumeid vastu võtta. Kuid ainult saaja saab dekrüpteerida, kasutades oma privaatvõtit ja saatja avalikku võtit:

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

Mida veel?

Kuna sõnumid edastatakse sel viisil umbes 5 sekundiga – see on aeg, mil ilmub uus võrguplokk –, leidsime kliendi-sõlme ja sõlme-sõlme pistikupesa ühenduse. Kui sõlm saab uue tehingu, kontrollib see selle kehtivust ja edastab selle teistele sõlmedele. Tehing on Messengeri klientidele kättesaadav isegi enne konsensuse saavutamist ja blokki kaasamist. Nii edastame sõnumeid koheselt nagu tavalised kiirsõnumitoojad.

Aadressiraamatu salvestamiseks tegime KVS - Key-Value Storage - see on teist tüüpi tehingud, milles asset krüptitud pole mitte NaCl-box, vaid NaCl-saladuskast. Nii salvestab messenger muid andmeid.

Failide/piltide edastamine ja grupivestlused nõuavad endiselt palju tööd. Muidugi, eksi-ja-eksi-formaadis saab selle kiiresti "kruvistada", kuid me tahame säilitada sama privaatsuse.

Jah, tööd on veel teha – ideaalis eeldab tegelik privaatsus, et kasutajad ei loo ühendust avaliku võrgu sõlmedega, vaid tõstavad oma. Kui suur protsent kasutajatest teie arvates seda teeb? See on õige, 0. Suutsime selle probleemi osaliselt lahendada Messengeri Tor-versiooniga.

Oleme tõestanud, et plokiahelas võib sõnumitooja eksisteerida. Varem oli 2012. aastal ainult üks katse - bitteade, mis ebaõnnestus pikkade sõnumite edastamisaegade, protsessori koormuse ja mobiilirakenduste puudumise tõttu.

Ja skepsis on tingitud sellest, et sõnumitoojad plokiahelas on oma ajast ees – inimesed pole valmis oma konto eest vastutust võtma, isikuandmete omamine pole veel trend ja tehnoloogia ei võimalda plokiahelal suuri kiirusi. Järgmisena ilmuvad meie projekti rohkem tehnoloogilisi analooge. Sa näed.

Allikas: www.habr.com

Lisa kommentaar