Kako decentralizirani messenger radi na blockchainu?

Početkom 2017. počeli smo kreirati messenger na blockchainu [ime i poveznica su u profilu] raspravljajući o prednostima u odnosu na klasične P2P messengere.

nestala 2.5 godine, i uspjeli smo potvrditi naš koncept: aplikacije za slanje poruka sada su dostupne za iOS, Web PWA, Windows, GNU/Linux, Mac OS i Android.

Danas ćemo vam reći kako funkcionira blockchain messenger i kako klijentske aplikacije mogu raditi s njegovim API-jem.
Kako decentralizirani messenger radi na blockchainu?

Željeli smo da blockchain riješi pitanja sigurnosti i privatnosti klasičnih P2P glasnika:

  • Jedan klik za kreiranje računa - bez telefona ili e-pošte, bez pristupa adresarima ili geolokacijama.
  • Sugovornici nikada ne uspostavljaju izravne veze, sva se komunikacija odvija kroz distribuirani sustav čvorova. IP adrese korisnika nisu dostupne jedni drugima.
  • Sve poruke su kriptirane End-to-End curve25519xsalsa20poly1305. Čini se da to nikoga neće iznenaditi, ali naš izvorni kod je otvoren.
  • MITM napad je isključen - svaka poruka je transakcija i potpisuje je Ed25519 EdDSA.
  • Poruka završava u vlastitom bloku. Dosljednost i timestamp Ne možete popraviti blokove, a time ni redoslijed poruka.
  • "Nisam to rekao" neće raditi s porukama na blockchainu.
  • Ne postoji središnja struktura koja provjerava "autentičnost" poruke. To radi distribuirani sustav čvorova temeljen na konsenzusu, au vlasništvu je korisnika.
  • Nemogućnost cenzure - računi se ne mogu blokirati i poruke se ne mogu brisati.
  • Blockchain 2FA je alternativa paklenom 2FA putem SMS-a, uništio mnogo zdravlja.
  • Mogućnost preuzimanja svih vaših razgovora s bilo kojeg uređaja u bilo kojem trenutku znači da uopće ne morate lokalno pohranjivati ​​razgovore.
  • Potvrda isporuke poruke. Ne na uređaj korisnika, već na mrežu. U biti, ovo je potvrda sposobnosti primatelja da pročita vašu poruku. Ovo je korisna značajka za slanje kritičnih obavijesti.

Prednosti blockchaina također uključuju blisku integraciju s kriptovalutama Ethereum, Dogecoin, Lisk, Dash, Bitcoin (ova je još u tijeku) i mogućnost slanja tokena u chatovima. Čak smo napravili i ugrađeni kripto izmjenjivač.

A onda - kako to sve funkcionira.

Poruka je transakcija

Svi su već navikli na činjenicu da transakcije u blockchainu prenose tokene (coine) od jednog korisnika do drugog. Kao Bitcoin. Kreirali smo posebnu vrstu transakcije za prijenos poruka.

Da biste poslali poruku u messengeru na blockchainu, morate proći nekoliko koraka:

  1. Šifrirajte tekst poruke
  2. Stavite šifrirani tekst u transakciju
  3. Potpišite transakciju
  4. Pošaljite transakciju bilo kojem mrežnom čvoru
  5. Distribuirani sustav čvorova određuje "autentičnost" poruke
  6. Ako je sve u redu, transakcija s porukom je uključena u sljedeći blok
  7. Primatelj dohvaća transakciju poruke i dekriptira je

Koraci 1-3 i 7 izvode se lokalno na klijentu, a koraci 5-6 se izvode na hostovima.

Šifriranje poruka

Poruka je šifrirana privatnim ključem pošiljatelja i javnim ključem primatelja. Javni ključ ćemo uzeti iz mreže, ali za to mora biti inicijaliziran račun primatelja, odnosno imati barem jednu transakciju. Možete koristiti REST zahtjev GET /api/accounts/getPublicKey?address={ADAMANT address}, a prilikom učitavanja chatova već će biti dostupni javni ključevi sugovornika.

Kako decentralizirani messenger radi na blockchainu?

Messenger šifrira poruke pomoću algoritma curve25519xsalsa20poly1305 (NaCl kutija). Budući da račun sadrži Ed25519 ključeve, da bi se formirala kutija, ključevi se prvo moraju pretvoriti u Curve25519 Diffie-Hellman.

Evo primjera u JavaScriptu:

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

Formiranje transakcije porukom

Transakcija ima sljedeću opću strukturu:

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

Za transakciju poruka najvažnije je asset - trebate postaviti poruku u objekt chat sa strukturom:

  • message - spremiti šifriranu poruku
  • own_message -jednom
  • type — vrsta poruke

Poruke se također dijele na vrste. U suštini, parametar type govori vam kako razumjeti message. Možete poslati samo tekst ili možete poslati objekt sa zanimljivim stvarima unutra - na primjer, ovako glasnik vrši prijenose kriptovaluta u chatovima.

Kao rezultat, stvaramo transakciju:

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

Potpis transakcije

Kako bi svi bili sigurni u autentičnost pošiljatelja i primatelja, vrijeme slanja i sadržaj poruke, transakcija se potpisuje. Digitalni potpis omogućuje provjeru autentičnosti transakcije pomoću javnog ključa - za to nije potreban privatni ključ.

Ali sam potpis se izvodi pomoću privatnog ključa:

Kako decentralizirani messenger radi na blockchainu?

Dijagram pokazuje da transakciju prvo hashiramo pomoću SHA-256, a zatim je potpisujemo Ed25519 EdDSA i dobiti potpis signature, a ID transakcije je dio SHA-256 hash-a.

Primjer implementacije:

1 — Formirajte blok podataka, uključujući poruku

/**
 * 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 - Broj SHA-256 iz podatkovnog bloka

/**
 * 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 — Potpišite transakciju

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

Slanje transakcije s porukom mrežnom čvoru

Budući da je mreža decentralizirana, poslužit će bilo koji od čvorova s ​​otvorenim API-jem. Izrada POST zahtjeva krajnjoj točki api/transactions:

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

Kao odgovor primit ćemo ID transakcije tipa

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

Validacija transakcije

Distribuirani sustav čvorova, temeljen na konsenzusu, određuje "autentičnost" transakcijske poruke. Od koga i kome, kada, je li poruka zamijenjena drugom i je li točno naznačeno vrijeme slanja. To je vrlo važna prednost blockchaina – ne postoji središnja struktura koja je zadužena za provjeru, a slijed poruka i njihov sadržaj ne mogu se lažirati.

Prvo jedan čvor provjerava točnost, a zatim ga šalje drugima - ako većina kaže da je sve u redu, transakcija će biti uključena u sljedeći blok lanca - to je konsenzus.

Kako decentralizirani messenger radi na blockchainu?

Dio koda čvora koji je zadužen za provjere možete pogledati na GitHubu - validator.js и provjeriti.js. Da, čvor radi na Node.js.

Uključivanje transakcije s porukom u bloku

Ako se postigne konsenzus, transakcija s našom porukom bit će uključena u sljedeći blok zajedno s ostalim valjanim transakcijama.

Blokovi imaju strogi redoslijed, a svaki sljedeći blok formira se na temelju hash-ova prethodnih blokova.

Kako decentralizirani messenger radi na blockchainu?

Poanta je da je i naša poruka uključena u ovaj niz i da se ne može “preuređivati”. Ako nekoliko poruka padne u blok, njihov redoslijed će biti određen prema timestamp poruke.

Čitanje poruka

Messenger aplikacija dohvaća transakcije iz blockchaina koje su poslane primatelju. Za ovo smo napravili krajnju točku api/chatrooms.

Sve transakcije dostupne su svima - možete primati šifrirane poruke. Ali samo primatelj može dešifrirati koristeći svoj privatni ključ i javni ključ pošiljatelja:

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

I što drugo?

Budući da se poruke na ovaj način isporučuju za oko 5 sekundi - to je vrijeme kada se pojavljuje novi mrežni blok - osmislili smo vezu klijent-čvor i čvor-čvor utičnice. Kada čvor primi novu transakciju, provjerava njezinu valjanost i prosljeđuje je drugim čvorovima. Transakcija je dostupna klijentima messengera i prije nego što dođe do konsenzusa i uključivanja u blok. Na ovaj način ćemo poruke dostavljati trenutno, baš kao i obični instant messengeri.

Za pohranjivanje adresara napravili smo KVS - Key-Value Storage - ovo je još jedna vrsta transakcije u kojoj asset nije NaCl-box taj koji je šifriran, nego NaCl-tajna kutija. Ovako glasnik pohranjuje druge podatke.

Prijenosi datoteka/slika i grupni razgovori i dalje zahtijevaju puno posla. Naravno, u formatu grešaka i grešaka to se može brzo "zeznuti", ali želimo zadržati istu razinu privatnosti.

Da, ima još posla - idealno, stvarna privatnost pretpostavlja da se korisnici neće spajati na javne mrežne čvorove, već će podizati vlastite. Što mislite, koji postotak korisnika to radi? Tako je, 0. Uspjeli smo djelomično riješiti ovaj problem s Tor verzijom messengera.

Dokazali smo da glasnik na blockchainu može postojati. Prethodno je bio samo jedan pokušaj 2012. bitna poruka, koji nije uspio zbog dugog vremena isporuke poruka, opterećenja CPU-a i nedostatka mobilnih aplikacija.

A skepticizam je zbog činjenice da su glasnici na blockchainu ispred svog vremena – ljudi nisu spremni preuzeti odgovornost za svoj račun, posjedovanje osobnih podataka još nije trend, a tehnologija ne dopušta velike brzine na blockchainu. Sljedeće će se pojaviti više tehnoloških analoga našeg projekta. Vidjet ćete.

Izvor: www.habr.com

Dodajte komentar