Kako funkcionira decentralizirani glasnik na blockchainu?

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

Prošlo 2.5 godine, i uspjeli smo potvrditi naš koncept: aplikacije za messenger su sada 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 funkcionira decentralizirani glasnik na blockchainu?

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

  • Jedan klik za kreiranje naloga - bez telefona ili e-pošte, bez pristupa adresarima ili geolokacijama.
  • Sagovornici nikada ne uspostavljaju direktne veze, sva komunikacija se odvija kroz distribuirani sistem čvorova. IP adrese korisnika nisu dostupne jedni drugima.
  • Sve poruke su šifrirane End-to-End curve25519xsalsa20poly1305. Čini se da ovo nikoga neće iznenaditi, ali naš izvorni kod je otvoren.
  • MITM napad je isključen - svaka poruka je transakcija i potpisana je od strane Ed25519 EdDSA.
  • Poruka završava u svom bloku. Dosljednost i timestamp Ne možete popraviti blokove, a samim tim i redoslijed poruka.
  • “Nisam to rekao” neće raditi s porukama na blockchainu.
  • Ne postoji centralna struktura koja provjerava “autentičnost” poruke. To radi distribuirani sistem čvorova zasnovan na konsenzusu, a u vlasništvu je korisnika.
  • Nemogućnost cenzure - nalozi 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 koje vrijeme je mogućnost da se razgovori uopće ne pohranjuju lokalno.
  • Potvrda isporuke poruke. Ne na uređaj korisnika, već na mrežu. U suštini, ovo je potvrda sposobnosti primaoca da pročita vašu poruku. Ovo je korisna funkcija za slanje kritičnih obavještenja.

Prednosti Blockchain-a također uključuju blisku integraciju sa kriptovalutama Ethereum, Dogecoin, Lisk, Dash, Bitcoin (ova je još uvijek u toku) 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 (kovanice) s jednog korisnika na drugog. Kao Bitcoin. Napravili smo posebnu vrstu transakcije za prijenos poruka.

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

  1. Šifrirajte tekst poruke
  2. Stavite šifrirani tekst u transakciju
  3. Potpišite transakciju
  4. Pošaljite transakciju na bilo koji mrežni čvor
  5. Distribuirani sistem čvorova određuje “autentičnost” poruke
  6. Ako je sve u redu, transakcija s porukom se uključuje u sljedeći blok
  7. Primalac preuzima transakciju poruke i dešifruje

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

Šifrovanje poruke

Poruka je šifrovana privatnim ključem pošiljaoca i javnim ključem primaoca. Uzet ćemo javni ključ 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 ćaskanja javni ključevi sagovornika će već biti dostupni.

Kako funkcionira decentralizirani glasnik na blockchainu?

Messenger šifrira poruke koristeći curve25519xsalsa20poly1305 algoritam (NaCl Box). Pošto račun sadrži ključeve Ed25519, da bi se formirala kutija, ključevi se prvo moraju konvertovati u Curve25519 Diffie-Hellman.

Evo primjera u JavaScript-u:

/**
 * 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 sa porukom

Transakcija ima sledeću opštu 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 poruke najvažnije je asset - potrebno je postaviti poruku u objekat chat sa strukturom:

  • message - sačuvati šifrovanu poruku
  • own_message -jednom
  • type — tip poruke

Poruke se također dijele na vrste. U suštini, parametar type govori vam kako da razumete message. Možete poslati samo tekstualnu poruku ili možete poslati objekt sa zanimljivim stvarima unutra - na primjer, ovako messenger vrši prijenos kriptovaluta u razgovorima.

Kao rezultat, kreiramo 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šiljaoca i primaoca, vrijeme slanja i sadržaj poruke, transakcija se potpisuje. Digitalni potpis vam omogućava da provjerite autentičnost transakcije pomoću javnog ključa - privatni ključ za to nije potreban.

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

Kako funkcionira decentralizirani glasnik na blockchainu?

Dijagram pokazuje da prvo heširamo transakciju sa SHA-256, a zatim je potpisujemo Ed25519 EdDSA i dobijete 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 - Brojite SHA-256 iz bloka podataka

/**
 * 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 na mrežni čvor

Budući da je mreža decentralizirana, bilo koji od čvorova s ​​otvorenim API-jem će odgovarati. Izrada POST zahtjeva do krajnje točke api/transactions:

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

Kao odgovor ćemo dobiti ID transakcije tog tipa

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

Validacija transakcije

Distribuirani sistem čvorova, zasnovan na konsenzusu, određuje “autentičnost” poruke o transakciji. Od koga i kome, kada, da li je poruka zamenjena drugom i da li je tačno naznačeno vreme slanja. Ovo je vrlo važna prednost blockchaina - ne postoji centralna struktura koja je odgovorna za verifikaciju, a slijed poruka i njihov sadržaj se ne mogu lažirati.

Prvo, jedan čvor provjerava tač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 funkcionira decentralizirani glasnik na blockchainu?

Dio koda čvora koji je odgovoran za provjere može se pogledati na GitHubu - validator.js и verify.js. Da, čvor radi na Node.js.

Uključujući transakciju s porukom u bloku

Ako se postigne konsenzus, transakcija sa našom porukom biće uključena u sledeći blok zajedno sa ostalim važećim transakcijama.

Blokovi imaju strogi redoslijed, a svaki sljedeći blok se formira na osnovu hashova prethodnih blokova.

Kako funkcionira decentralizirani glasnik na blockchainu?

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

Čitanje poruka

Messenger aplikacija preuzima transakcije iz blockchaina koje se šalju primaocu. Za ovo smo napravili krajnju tačku api/chatrooms.

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

**
 * 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 šta još?

Pošto se poruke isporučuju na ovaj način za oko 5 sekundi – ovo je vrijeme kada se pojavljuje novi mrežni blok – došli smo do veze klijent-čvor i čvor-čvor socket konekcije. Kada čvor primi novu transakciju, provjerava njenu valjanost i prosljeđuje je drugim čvorovima. Transakcija je dostupna messenger klijentima čak i prije nego što dođe do konsenzusa i uključivanja u blok. Na ovaj način ćemo isporučiti poruke trenutno, baš kao i obični instant messengeri.

Za čuvanje adresara napravili smo KVS - Key-Value Storage - ovo je još jedna vrsta transakcije u kojoj asset nije NaCl-box taj koji je šifrovan, već NaCl-tajna kutija. Ovako messenger pohranjuje ostale podatke.

Prijenos 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 da zadržimo isti nivo privatnosti.

Da, ima još posla - u idealnom slučaju, prava privatnost pretpostavlja da se korisnici neće povezivati ​​na javne mrežne čvorove, već će podići svoje. Šta mislite, koliki procenat korisnika to radi? Tako je, 0. Uspjeli smo djelomično riješiti ovaj problem sa Tor verzijom messengera.

Dokazali smo da glasnik na blockchainu može postojati. Ranije je bio samo jedan pokušaj 2012. bitmessage, 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 ličnih podataka još nije trend, a tehnologija ne dozvoljava velike brzine na blockchainu. Sljedeće će se pojaviti više tehnoloških analoga našeg projekta. Videćete.

izvor: www.habr.com

Dodajte komentar