Si funksionon një mesazher i decentralizuar në blockchain?

Në fillim të vitit 2017, ne filluam të krijonim një mesazher në blockchain [emri dhe lidhja janë në profil] duke diskutuar avantazhet ndaj mesazherëve klasikë P2P.

Kaloi 2.5 vit, dhe ne ishim në gjendje të konfirmonim konceptin tonë: aplikacionet e mesazheve janë tani të disponueshme për iOS, Web PWA, Windows, GNU/Linux, Mac OS dhe Android.

Sot do t'ju tregojmë se si funksionon mesazheri i blockchain dhe si mund të funksionojnë aplikacionet e klientëve me API-në e tij.
Si funksionon një mesazher i decentralizuar në blockchain?

Ne donim që blockchain të zgjidhte çështjet e sigurisë dhe privatësisë së lajmëtarëve klasikë P2P:

  • Një klik për të krijuar një llogari - pa telefona ose email, pa akses në librat e adresave ose vendndodhjet gjeografike.
  • Bashkëbiseduesit nuk krijojnë kurrë lidhje të drejtpërdrejta; i gjithë komunikimi zhvillohet përmes një sistemi të shpërndarë nyjesh. Adresat IP të përdoruesve nuk janë të aksesueshme për njëri-tjetrin.
  • Të gjitha mesazhet janë të koduara nga End-to-End curve25519xsalsa20poly1305. Duket se kjo nuk do të befasojë askënd, por kodi ynë burim është i hapur.
  • Sulmi MITM është i përjashtuar - çdo mesazh është një transaksion dhe është i nënshkruar nga Ed25519 EdDSA.
  • Mesazhi përfundon në bllokun e vet. Konsistenca dhe timestamp Ju nuk mund të rregulloni blloqet, dhe rrjedhimisht renditjen e mesazheve.
  • "Unë nuk e thashë këtë" nuk do të funksionojë me mesazhe në blockchain.
  • Nuk ka asnjë strukturë qendrore që kontrollon "autenticitetin" e një mesazhi. Kjo bëhet nga një sistem i shpërndarë nyjesh i bazuar në konsensus, dhe është në pronësi të përdoruesve.
  • Pamundësia e censurës - llogaritë nuk mund të bllokohen dhe mesazhet nuk mund të fshihen.
  • Blockchain 2FA është një alternativë ndaj 2FA djallëzore përmes SMS, prishi shumë shëndet.
  • Mundësia për të marrë të gjitha bisedat tuaja nga çdo pajisje në çdo kohë do të thotë që nuk keni nevojë t'i ruani fare bisedat në nivel lokal.
  • Konfirmimi i dërgimit të mesazhit. Jo në pajisjen e përdoruesit, por në rrjet. Në thelb, ky është konfirmim i aftësisë së marrësit për të lexuar mesazhin tuaj. Ky është një veçori e dobishme për dërgimin e njoftimeve kritike.

Përfitimet e Blockchain përfshijnë gjithashtu integrimin e ngushtë me kriptovalutat Ethereum, Dogecoin, Lisk, Dash, Bitcoin (kjo është ende në progres) dhe aftësinë për të dërguar argumente në biseda. Ne madje bëmë një shkëmbyes kriptosh të integruar.

Dhe pastaj - si funksionon gjithçka.

Një mesazh është një transaksion

Të gjithë janë mësuar tashmë me faktin që transaksionet në blockchain transferojnë tokenat (monedha) nga një përdorues në tjetrin. Ashtu si Bitcoin. Ne krijuam një lloj të veçantë transaksioni për transmetimin e mesazheve.

Për të dërguar një mesazh në një mesazher në blockchain, duhet të kaloni disa hapa:

  1. Enkripto tekstin e mesazhit
  2. Vendosni tekstin e shifruar në një transaksion
  3. Nënshkruani transaksionin
  4. Dërgoni një transaksion në çdo nyje rrjeti
  5. Një sistem i shpërndarë nyjesh përcakton "autenticitetin" e një mesazhi
  6. Nëse gjithçka është në rregull, transaksioni me mesazhin përfshihet në bllokun tjetër
  7. Marrësi merr transaksionin e mesazhit dhe deshifron

Hapat 1-3 dhe 7 kryhen në nivel lokal te klienti dhe hapat 5-6 kryhen te hostet.

Kriptimi i mesazheve

Mesazhi është i koduar me çelësin privat të dërguesit dhe çelësin publik të marrësit. Ne do të marrim çelësin publik nga rrjeti, por për këtë, llogaria e marrësit duhet të inicializohet, domethënë të ketë të paktën një transaksion. Ju mund të përdorni një kërkesë REST GET /api/accounts/getPublicKey?address={ADAMANT address}, dhe gjatë ngarkimit të bisedave, çelësat publikë të bashkëbiseduesve do të jenë tashmë të disponueshëm.

Si funksionon një mesazher i decentralizuar në blockchain?

Lajmëtari kodon mesazhet duke përdorur algoritmin curve25519xsalsa20poly1305 (Kuti NaCl). Meqenëse llogaria përmban çelësat Ed25519, për të formuar një kuti, çelësat fillimisht duhet të konvertohen në Curve25519 Diffie-Hellman.

Këtu është një shembull në 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)
  }
}

Formimi i një transaksioni me një mesazh

Transaksioni ka strukturën e përgjithshme të mëposhtme:

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

Për një transaksion mesazhi, gjëja më e rëndësishme është asset - duhet të vendosni një mesazh në objekt chat me strukturë:

  • message - ruani mesazhin e koduar
  • own_message - jo herë
  • type - lloji i mesazhit

Mesazhet ndahen gjithashtu në lloje. Në thelb, parametri type ju tregon si ta kuptoni message. Ju mund të dërgoni vetëm një tekst, ose mund të dërgoni një objekt me gjëra interesante brenda - për shembull, kjo është mënyra se si mesazheri bën transferime kriptomonedhash në biseda.

Si rezultat, ne krijojmë një transaksion:

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

Nënshkrimi i transaksionit

Për të siguruar që të gjithë të kenë besim në origjinalitetin e dërguesit dhe marrësit, kohën e dërgimit dhe përmbajtjen e mesazhit, transaksioni nënshkruhet. Një nënshkrim dixhital ju lejon të verifikoni vërtetësinë e një transaksioni duke përdorur një çelës publik - një çelës privat nuk është i nevojshëm për këtë.

Por vetë nënshkrimi kryhet duke përdorur çelësin privat:

Si funksionon një mesazher i decentralizuar në blockchain?

Diagrami tregon se ne fillimisht e hasim transaksionin me SHA-256 dhe më pas e nënshkruajmë atë Ed25519 EdDSA dhe merrni një nënshkrim signature, dhe ID-ja e transaksionit është pjesë e hash-it SHA-256.

Shembull i zbatimit:

1 — Formoni një bllok të dhënash, duke përfshirë një mesazh

/**
 * 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 - Numëroni SHA-256 nga blloku i të dhënave

/**
 * 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 — Nënshkruani transaksionin

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

Dërgimi i një transaksioni me një mesazh në një nyje rrjeti

Meqenëse rrjeti është i decentralizuar, çdo nyje me një API të hapur do të funksionojë. Bërja e një kërkese POST në pikën përfundimtare api/transactions:

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

Si përgjigje ne do të marrim një ID transaksioni të llojit

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

Vleresimi i transaksionit

Një sistem i shpërndarë nyjesh, bazuar në konsensus, përcakton "autenticitetin" e mesazhit të transaksionit. Nga kush dhe kujt, kur, nëse mesazhi është zëvendësuar nga një tjetër dhe nëse koha e dërgimit është treguar saktë. Ky është një avantazh shumë i rëndësishëm i blockchain - nuk ka asnjë strukturë qendrore që është përgjegjëse për verifikimin, dhe sekuenca e mesazheve dhe përmbajtja e tyre nuk mund të falsifikohen.

Së pari, një nyje kontrollon saktësinë dhe më pas ua dërgon të tjerëve - nëse shumica thotë se gjithçka është në rregull, transaksioni do të përfshihet në bllokun tjetër të zinxhirit - ky është konsensus.

Si funksionon një mesazher i decentralizuar në blockchain?

Pjesa e kodit të nyjës që është përgjegjëse për kontrollet mund të shihet në GitHub - vërtetues.js и verifikoj.js. Po, nyja funksionon në Node.js.

Përfshirja e një transaksioni me një mesazh në një bllok

Nëse arrihet konsensusi, transaksioni me mesazhin tonë do të përfshihet në bllokun tjetër së bashku me transaksionet e tjera të vlefshme.

Blloqet kanë një sekuencë strikte dhe çdo bllok pasues formohet në bazë të hasheve të blloqeve të mëparshme.

Si funksionon një mesazher i decentralizuar në blockchain?

Çështja është se mesazhi ynë është gjithashtu i përfshirë në këtë sekuencë dhe nuk mund të "riorganizohet". Nëse disa mesazhe bien në një bllok, rendi i tyre do të përcaktohet nga timestamp mesazheve.

Leximi i mesazheve

Aplikacioni i mesazherit rimerr transaksionet nga blockchain që i dërgohen marrësit. Për këtë kemi bërë një pikë përfundimtare api/chatrooms.

Të gjitha transaksionet janë të disponueshme për të gjithë - ju mund të merrni mesazhe të koduara. Por vetëm marrësi mund të deshifrojë duke përdorur çelësin e tij privat dhe çelësin publik të dërguesit:

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

Dhe cfare tjeter?

Meqenëse mesazhet shpërndahen në këtë mënyrë për rreth 5 sekonda - kjo është koha kur shfaqet një bllok i ri rrjeti - ne dolëm me një lidhje klient-to-node dhe nyje-to-node fole. Kur një nyje merr një transaksion të ri, ai kontrollon vlefshmërinë e tij dhe ia kalon atë nyjeve të tjera. Transaksioni është i disponueshëm për klientët e mesazherëve edhe përpara se të ndodhë konsensusi dhe përfshirja në bllok. Në këtë mënyrë ne do të dërgojmë mesazhe në çast, ashtu si lajmëtarët e zakonshëm të çastit.

Për të ruajtur librin e adresave, ne bëmë KVS - Ruajtja e vlerës së çelësit - ky është një lloj tjetër transaksioni në të cilin asset nuk është kutia NaCl ajo që është e koduar, por NaCl-kuti sekrete. Kjo është mënyra se si mesazheri ruan të dhëna të tjera.

Transferimet e skedarëve/imazheve dhe bisedat në grup kërkojnë ende shumë punë. Natyrisht, në formatin "gafa dhe gabime" kjo mund të "ngatërrohet" shpejt, por ne duam të ruajmë të njëjtin nivel privatësie.

Po, ka ende punë për të bërë - në mënyrë ideale, privatësia reale supozon që përdoruesit nuk do të lidhen me nyjet e rrjetit publik, por do të rrisin të tyren. Sa përqind e përdoruesve mendoni se e bën këtë? Kjo është e drejtë, 0. Ne ishim në gjendje ta zgjidhnim pjesërisht këtë çështje me versionin Tor të mesazherit.

Ne kemi vërtetuar se një lajmëtar në blockchain mund të ekzistojë. Më parë, kishte vetëm një përpjekje në 2012 - bitmesazh, i cili dështoi për shkak të kohërave të gjata të dërgimit të mesazheve, ngarkesës së CPU-së dhe mungesës së aplikacioneve celulare.

Dhe skepticizmi është për faktin se lajmëtarët në blockchain janë përpara kohës së tyre - njerëzit nuk janë të gatshëm të marrin përgjegjësinë për llogarinë e tyre, zotërimi i informacionit personal nuk është ende një trend, dhe teknologjia nuk lejon shpejtësi të larta në blockchain. Më shumë analoge teknologjike të projektit tonë do të shfaqen në vazhdim. Ju do të shihni.

Burimi: www.habr.com

Shto një koment