Hvordan fungerer en desentralisert messenger på blokkjeden?

I begynnelsen av 2017 begynte vi å lage en messenger på blokkjeden [navn og lenke er i profilen] ved å diskutere fordelene fremfor klassiske P2P-budbringere.

Borte 2.5 år, og vi var i stand til å bekrefte konseptet vårt: Messenger-applikasjoner er nå tilgjengelig for iOS, Web PWA, Windows, GNU/Linux, Mac OS og Android.

I dag vil vi fortelle deg hvordan blockchain-messengeren fungerer og hvordan klientapplikasjoner kan fungere med API-en.
Hvordan fungerer en desentralisert messenger på blokkjeden?

Vi ønsket at blokkjeden skulle løse sikkerhets- og personvernproblemene til klassiske P2P-meldinger:

  • Ett klikk for å opprette en konto - ingen telefoner eller e-poster, ingen tilgang til adressebøker eller geolokasjoner.
  • Samtalepartnerne etablerer aldri direkte forbindelser; all kommunikasjon foregår gjennom et distribuert system av noder. Brukernes IP-adresser er ikke tilgjengelige for hverandre.
  • Alle meldinger er kryptert End-to-End curve25519xsalsa20poly1305. Det ser ut til at dette ikke vil overraske noen, men kildekoden vår er åpen.
  • MITM-angrep er ekskludert - hver melding er en transaksjon og er signert av Ed25519 EdDSA.
  • Meldingen havner i sin egen blokk. Konsistens og timestamp Du kan ikke fikse blokkene, og derfor rekkefølgen på meldingene.
  • "Jeg sa ikke det" vil ikke fungere med meldinger på blokkjeden.
  • Det er ingen sentral struktur som kontrollerer "ektheten" til en melding. Dette gjøres av et distribuert system av noder basert på konsensus, og det eies av brukerne.
  • Umulighet for sensur - kontoer kan ikke blokkeres og meldinger kan ikke slettes.
  • Blockchain 2FA er et alternativ til helvetes 2FA via SMS, ødela mye helse.
  • Muligheten til å få alle samtalene dine fra hvilken som helst enhet når som helst betyr at du ikke trenger å lagre samtaler lokalt i det hele tatt.
  • Bekreftelse på levering av melding. Ikke til brukerens enhet, men til nettverket. I hovedsak er dette en bekreftelse på mottakerens evne til å lese meldingen din. Dette er en nyttig funksjon for å sende kritiske varsler.

Blockchain-fordeler inkluderer også tett integrasjon med kryptovalutaene Ethereum, Dogecoin, Lisk, Dash, Bitcoin (denne pågår fortsatt) og muligheten til å sende tokens i chatter. Vi har til og med laget en innebygd kryptoveksler.

Og så - hvordan det hele fungerer.

En melding er en transaksjon

Alle er allerede vant til at transaksjoner i blokkjeden overfører tokens (mynter) fra en bruker til en annen. Som Bitcoin. Vi opprettet en spesiell type transaksjon for overføring av meldinger.

For å sende en melding i en messenger på blokkjeden, må du gå gjennom flere trinn:

  1. Krypter meldingstekst
  2. Sett chiffertekst inn i en transaksjon
  3. Signer transaksjonen
  4. Send en transaksjon til hvilken som helst nettverksnode
  5. Et distribuert system av noder bestemmer "ektheten" til en melding
  6. Hvis alt er OK, er transaksjonen med meldingen inkludert i neste blokk
  7. Mottakeren henter meldingstransaksjonen og dekrypterer

Trinn 1–3 og 7 utføres lokalt på klienten, og trinn 5–6 utføres på vertene.

Meldingskryptering

Meldingen er kryptert med avsenderens private nøkkel og mottakerens offentlige nøkkel. Vi tar den offentlige nøkkelen fra nettverket, men for dette må mottakerens konto initialiseres, det vil si ha minst én transaksjon. Du kan bruke en REST-forespørsel GET /api/accounts/getPublicKey?address={ADAMANT address}, og når du laster chatter, vil de offentlige nøklene til samtalepartnerne allerede være tilgjengelige.

Hvordan fungerer en desentralisert messenger på blokkjeden?

Messengeren krypterer meldinger ved hjelp av curve25519xsalsa20poly1305-algoritmen (NaCl-boks). Siden kontoen inneholder Ed25519-nøkler, for å danne en boks, må nøklene først konverteres til Curve25519 Diffie-Hellman.

Her er et eksempel i 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)
  }
}

Danner en transaksjon med en melding

Transaksjonen har følgende generelle struktur:

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

For en meldingstransaksjon er det viktigste asset - du må plassere en melding i objektet chat med struktur:

  • message - lagre den krypterte meldingen
  • own_message - ikke noe
  • type — meldingstype

Meldinger er også delt inn i typer. I hovedsak parameteren type forteller deg hvordan du skal forstå message. Du kan bare sende en tekstmelding, eller du kan sende et objekt med interessante ting inni - for eksempel er det slik messengeren gjør kryptovalutaoverføringer i chatter.

Som et resultat oppretter vi en transaksjon:

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

Transaksjonssignatur

For å sikre at alle er trygge på autentisiteten til avsender og mottaker, tidspunktet for sending og innholdet i meldingen, signeres transaksjonen. En digital signatur lar deg verifisere ektheten til en transaksjon ved hjelp av en offentlig nøkkel - en privat nøkkel er ikke nødvendig for dette.

Men selve signaturen utføres ved å bruke den private nøkkelen:

Hvordan fungerer en desentralisert messenger på blokkjeden?

Diagrammet viser at vi først hash transaksjonen med SHA-256 og deretter signerer den Ed25519 EdDSA og få en signatur signature, og transaksjons-ID-en er en del av SHA-256-hashen.

Implementeringseksempel:

1 — Form en datablokk, inkludert en melding

/**
 * 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 - Tell SHA-256 fra datablokken

/**
 * 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 — Signer transaksjonen

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

Sende en transaksjon med en melding til en nettverksnode

Siden nettverket er desentralisert, vil enhver av nodene med en åpen API gjøre det. Foreta en POST-forespørsel til endepunktet api/transactions:

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

Som svar vil vi motta en transaksjons-ID av typen

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

Transaksjonsvalidering

Et distribuert system av noder, basert på konsensus, bestemmer "ektheten" til transaksjonsmeldingen. Fra hvem og til hvem, når, om meldingen ble erstattet med en annen, og om tidspunktet for sending ble angitt riktig. Dette er en veldig viktig fordel med blokkjeden - det er ingen sentral struktur som er ansvarlig for verifisering, og sekvensen av meldinger og innholdet deres kan ikke forfalskes.

Først sjekker en node nøyaktigheten, og sender den deretter til andre - hvis flertallet sier at alt er i orden, vil transaksjonen bli inkludert i neste blokk i kjeden - dette er konsensus.

Hvordan fungerer en desentralisert messenger på blokkjeden?

Den delen av nodekoden som er ansvarlig for kontroller kan sees på GitHub - validator.js и verify.js. Jepp, noden kjører på Node.js.

Inkludert en transaksjon med en melding i en blokk

Hvis konsensus oppnås, vil transaksjonen med meldingen vår inkluderes i neste blokk sammen med andre gyldige transaksjoner.

Blokker har en streng sekvens, og hver påfølgende blokk dannes basert på hashen fra tidligere blokker.

Hvordan fungerer en desentralisert messenger på blokkjeden?

Poenget er at budskapet vårt også er inkludert i denne sekvensen og ikke kan "omorganiseres". Hvis flere meldinger faller inn i en blokk, vil rekkefølgen deres bestemmes av timestamp meldinger.

Leser meldinger

Messenger-applikasjonen henter transaksjoner fra blokkjeden som sendes til mottakeren. For dette laget vi et endepunkt api/chatrooms.

Alle transaksjoner er tilgjengelige for alle – du kan motta krypterte meldinger. Men bare mottakeren kan dekryptere ved hjelp av sin private nøkkel og avsenderens offentlige nøkkel:

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

Hva annet?

Siden meldinger leveres på denne måten på ca. 5 sekunder - dette er tiden en ny nettverksblokk dukker opp - kom vi opp med en klient-til-node og node-til-node-socket-forbindelse. Når en node mottar en ny transaksjon, sjekker den gyldigheten og videresender den til andre noder. Transaksjonen er tilgjengelig for messenger-klienter selv før konsensus inntreffer og inkludering i blokken. På denne måten vil vi levere meldinger umiddelbart, akkurat som vanlige direktemeldinger.

For å lagre adresseboken laget vi KVS - Key-Value Storage - dette er en annen type transaksjon der asset det er ikke NaCl-boks som er kryptert, men NaCl-sekretboks. Dette er hvordan messenger lagrer andre data.

Overføring av filer/bilder og gruppechatter krever fortsatt mye arbeid. Selvfølgelig, i blunder-and-blunder-formatet kan dette "skrues opp" raskt, men vi ønsker å opprettholde samme nivå av personvern.

Ja, det er fortsatt arbeid å gjøre - ideelt sett forutsetter ekte personvern at brukere ikke vil koble seg til offentlige nettverksnoder, men vil øke sine egne. Hvor mange prosent av brukerne tror du gjør dette? Det stemmer, 0. Vi var i stand til å delvis løse dette problemet med Tor-versjonen av messenger.

Vi har bevist at en messenger på blokkjeden kan eksistere. Tidligere var det bare ett forsøk i 2012 - bitmelding, som mislyktes på grunn av lange meldingsleveringstider, CPU-belastning og mangel på mobilapplikasjoner.

Og skepsis skyldes det faktum at budbringere på blokkjeden er forut for sin tid – folk er ikke klare til å ta ansvar for kontoen sin, å eie personlig informasjon er ennå ikke en trend, og teknologien tillater ikke høye hastigheter på blokkjeden. Flere teknologiske analoger av prosjektet vårt vil dukke opp neste gang. Du vil se.

Kilde: www.habr.com

Legg til en kommentar