Kaip blokų grandinėje veikia decentralizuotas pasiuntinys?

2017 m. pradžioje „blockchain“ [vardas ir nuoroda yra profilyje] pradėjome kurti pasiuntinį, aptardami pranašumus prieš klasikinius P2P pasiuntinius.

Dingo 2.5 metais, ir mes galėjome patvirtinti savo koncepciją: messenger programas dabar galima naudoti iOS, Web PWA, Windows, GNU/Linux, Mac OS ir Android.

Šiandien mes jums pasakysime, kaip veikia „blockchain Messenger“ ir kaip klientų programos gali veikti su jo API.
Kaip blokų grandinėje veikia decentralizuotas pasiuntinys?

Norėjome, kad blokų grandinė išspręstų klasikinių P2P pasiuntinių saugumo ir privatumo problemas:

  • Vienu paspaudimu sukurti paskyrą – jokių telefonų ar el. laiškų, jokios prieigos prie adresų knygų ar geografinių vietų.
  • Pašnekovai niekada neužmezga tiesioginių ryšių, visas bendravimas vyksta per paskirstytą mazgų sistemą. Vartotojų IP adresai vienas kitam nepasiekiami.
  • Visi pranešimai yra užšifruoti. Curve25519xsalsa20poly1305 nuo pabaigos iki galo. Atrodo, kad tai nieko nenustebins, bet mūsų šaltinio kodas yra atviras.
  • MITM ataka neįtraukta – kiekvienas pranešimas yra sandoris ir yra pasirašytas Ed25519 EdDSA.
  • Pranešimas patenka į savo bloką. Nuoseklumas ir timestamp Negalite taisyti blokų, taigi ir pranešimų tvarkos.
  • „Aš to nesakiau“ neveiks su pranešimais blokų grandinėje.
  • Nėra jokios centrinės struktūros, kuri tikrintų pranešimo „autentiškumą“. Tai atlieka paskirstyta mazgų sistema, pagrįsta bendru sutarimu, ir ji priklauso vartotojams.
  • Cenzūros neįmanoma – negalima blokuoti paskyrų ir ištrinti žinučių.
  • Blockchain 2FA yra alternatyva pragariškam 2FA SMS žinute, sugadino daug sveikatos.
  • Galimybė bet kuriuo metu gauti visus pokalbius iš bet kurio įrenginio reiškia, kad jums visai nereikia saugoti pokalbių vietoje.
  • Pranešimo pristatymo patvirtinimas. Ne į vartotojo įrenginį, o į tinklą. Iš esmės tai patvirtinimas, kad gavėjas gali perskaityti jūsų pranešimą. Tai naudinga funkcija siunčiant svarbius pranešimus.

„Blockchain“ pranašumai taip pat apima glaudžią integraciją su kriptovaliutomis „Ethereum“, „Dogecoin“, „Lisk“, „Dash“, „Bitcoin“ (šis tebevyksta) ir galimybę siųsti žetonus pokalbiuose. Netgi pagaminome įmontuotą kriptovaliutų keitiklį.

Ir tada – kaip viskas veikia.

Pranešimas yra sandoris

Visi jau yra pripratę prie to, kad blokų grandinėje operacijos perkelia žetonus (monetas) iš vieno vartotojo kitam. Kaip Bitcoin. Sukūrėme specialų operacijų tipą žinutėms perduoti.

Norėdami išsiųsti pranešimą blokų grandinėje esančiame Messenger, turite atlikti kelis veiksmus:

  1. Šifruoti pranešimo tekstą
  2. Įdėkite šifruotą tekstą į operaciją
  3. Pasirašykite sandorį
  4. Siųsti operaciją į bet kurį tinklo mazgą
  5. Paskirstyta mazgų sistema nustato pranešimo „autentiškumą“.
  6. Jei viskas gerai, operacija su pranešimu įtraukiama į kitą bloką
  7. Gavėjas nuskaito pranešimo operaciją ir iššifruoja

1–3 ir 7 žingsniai atliekami lokaliai klientui, o 5–6 žingsniai atliekami pagrindiniuose kompiuteriuose.

Laiškų šifravimas

Laiškas užšifruojamas privačiu siuntėjo raktu ir gavėjo viešuoju raktu. Mes paimsime viešąjį raktą iš tinklo, tačiau tam reikia inicijuoti gavėjo paskyrą, ty turėti bent vieną operaciją. Galite naudoti REST užklausą GET /api/accounts/getPublicKey?address={ADAMANT address}, o įkeliant pokalbius jau bus prieinami pašnekovų viešieji raktai.

Kaip blokų grandinėje veikia decentralizuotas pasiuntinys?

Messenger užšifruoja pranešimus naudodamas curve25519xsalsa20poly1305 algoritmą (NaCl dėžutė). Kadangi paskyroje yra Ed25519 raktai, norint sudaryti langelį, raktai pirmiausia turi būti konvertuoti į Curve25519 Diffie-Hellman.

Štai pavyzdys „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)
  }
}

Sandorio formavimas žinute

Sandorio bendra struktūra yra tokia:

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

Žinutės sandoriui svarbiausia yra asset - į objektą reikia įdėti pranešimą chat su struktūra:

  • message - išsaugokite užšifruotą pranešimą
  • own_message - vieną kartą
  • type - pranešimo tipas

Žinutės taip pat skirstomos į tipus. Iš esmės parametras type pasako kaip suprasti message. Galite siųsti tiesiog tekstą arba galite siųsti objektą, kuriame yra įdomių dalykų – pavyzdžiui, taip messengeris atlieka kriptovaliutų pervedimus pokalbiuose.

Dėl to mes sukuriame operaciją:

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

Sandorio parašas

Siekiant užtikrinti, kad visi būtų įsitikinę siuntėjo ir gavėjo tikrumu, išsiuntimo laiku ir pranešimo turiniu, sandoris pasirašomas. Skaitmeninis parašas leidžia patikrinti operacijos autentiškumą naudojant viešąjį raktą – privataus rakto tam nereikia.

Tačiau pats parašas atliekamas naudojant privatųjį raktą:

Kaip blokų grandinėje veikia decentralizuotas pasiuntinys?

Diagrama rodo, kad pirmiausia sumaišome sandorį su SHA-256 ir tada pasirašome Ed25519 EdDSA ir gauti parašą signature, o operacijos ID yra SHA-256 maišos dalis.

Diegimo pavyzdys:

1 — Suformuokite duomenų bloką, įskaitant pranešimą

/**
 * 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 – suskaičiuokite SHA-256 iš duomenų bloko

/**
 * 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 — Pasirašykite sandorį

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

Operacijos su pranešimu siuntimas į tinklo mazgą

Kadangi tinklas yra decentralizuotas, tiks bet kuris mazgas su atvira API. POST užklausos pateikimas galutiniam taškui api/transactions:

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

Atsakydami gausime tokio tipo operacijos ID

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

Sandorio patvirtinimas

Paskirstyta mazgų sistema, pagrįsta sutarimu, nustato operacijos pranešimo „autentiškumą“. Iš ko ir kam, kada, ar žinutė buvo pakeista kita, ar teisingai nurodytas išsiuntimo laikas. Tai labai svarbus blokų grandinės privalumas – nėra centrinės struktūros, atsakingos už patikrinimą, o pranešimų seka ir jų turinys negali būti suklastoti.

Pirma, vienas mazgas patikrina tikslumą, o tada siunčia jį kitiems – jei dauguma sako, kad viskas tvarkoje, sandoris bus įtrauktas į kitą grandinės bloką – tai yra sutarimas.

Kaip blokų grandinėje veikia decentralizuotas pasiuntinys?

Mazgo kodo dalį, kuri yra atsakinga už patikrinimus, galite peržiūrėti „GitHub“ - validator.js и verify.js. Taip, mazgas veikia Node.js.

Operacijos su pranešimu įtraukimas į bloką

Jei bus pasiektas sutarimas, operacija su mūsų pranešimu bus įtraukta į kitą bloką kartu su kitomis galiojančiomis operacijomis.

Blokai turi griežtą seką, o kiekvienas paskesnis blokas formuojamas remiantis ankstesnių blokų maišais.

Kaip blokų grandinėje veikia decentralizuotas pasiuntinys?

Esmė ta, kad mūsų žinutė taip pat įtraukta į šią seką ir negali būti „pertvarkyta“. Jei keli pranešimai patenka į bloką, jų eiliškumą lems timestamp žinutes.

Pranešimų skaitymas

„Messenger“ programa nuskaito operacijas iš „blockchain“, kurios siunčiamos gavėjui. Tam mes nustatėme galutinį tašką api/chatrooms.

Visos operacijos yra prieinamos visiems – galite gauti šifruotus pranešimus. Tačiau tik gavėjas gali iššifruoti naudodamas savo privatų raktą ir siuntėjo viešąjį raktą:

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

Ir kas dar?

Kadangi pranešimai tokiu būdu pristatomi maždaug per 5 sekundes – tai yra laikas, kai atsiranda naujas tinklo blokas – mes sugalvojome kliento-mazgo ir mazgo-mazgo lizdo ryšį. Kai mazgas gauna naują operaciją, jis patikrina jos galiojimą ir persiunčia kitiems mazgams. Operacija yra prieinama „Messenger“ klientams dar prieš pasiekiant sutarimą ir įtraukiant į bloką. Tokiu būdu pranešimus pristatysime akimirksniu, kaip ir įprasti momentiniai pasiuntiniai.

Norėdami išsaugoti adresų knygą, sukūrėme KVS - Key-Value Storage - tai dar vienas operacijos tipas, kuriame asset užšifruota ne NaCl dėžutė, o NaCl slaptoji dėžutė. Taip pasiuntinys saugo kitus duomenis.

Failų / vaizdų perkėlimas ir grupiniai pokalbiai vis dar reikalauja daug darbo. Žinoma, klaidos ir klaidos formatu tai gali būti greitai „susukta“, tačiau norime išlaikyti tą patį privatumo lygį.

Taip, dar reikia padirbėti – idealiu atveju tikrasis privatumas numato, kad vartotojai neprisijungs prie viešųjų tinklo mazgų, o kels savuosius. Kaip manote, koks procentas vartotojų tai daro? Teisingai, 0. Mes sugebėjome iš dalies išspręsti šią problemą naudodami „Tor“ „Messenger“ versiją.

Mes įrodėme, kad blokų grandinėje gali egzistuoti pasiuntinys. Anksčiau buvo tik vienas bandymas 2012 m. bitinis pranešimas, kuris nepavyko dėl ilgo pranešimų pristatymo laiko, procesoriaus apkrovos ir mobiliųjų programų trūkumo.

O skepticizmas kyla dėl to, kad „blockchain“ pasiuntiniai lenkia savo laiką – žmonės nėra pasirengę prisiimti atsakomybės už savo paskyrą, asmeninės informacijos turėjimas dar nėra tendencija, o technologijos neleidžia didelio greičio „blockchain“. Daugiau technologinių mūsų projekto analogų pasirodys vėliau. Pamatysite.

Šaltinis: www.habr.com

Добавить комментарий