Hur fungerar en decentraliserad budbärare på blockkedjan?

I början av 2017 började vi skapa en budbärare på blockkedjan [namn och länk finns i profilen] genom att diskutera fördelarna jämfört med klassiska P2P-budbärare.

Borta 2.5 år, och vi kunde bekräfta vårt koncept: Messenger-applikationer är nu tillgängliga för iOS, Web PWA, Windows, GNU/Linux, Mac OS och Android.

Idag kommer vi att berätta hur blockchain messenger fungerar och hur klientapplikationer kan fungera med dess API.
Hur fungerar en decentraliserad budbärare på blockkedjan?

Vi ville att blockkedjan skulle lösa säkerhets- och integritetsproblemen för klassiska P2P-budbärare:

  • Ett klick för att skapa ett konto - inga telefoner eller e-postmeddelanden, ingen tillgång till adressböcker eller geografiska platser.
  • Samtalspartnerna upprättar aldrig direkta förbindelser, all kommunikation sker genom ett distribuerat system av noder. Användarnas IP-adresser är inte tillgängliga för varandra.
  • Alla meddelanden är krypterade End-to-End curve25519xsalsa20poly1305. Det verkar som att detta inte kommer att överraska någon, men vår källkod är öppen.
  • MITM-attack är utesluten - varje meddelande är en transaktion och är signerat av Ed25519 EdDSA.
  • Meddelandet hamnar i ett eget block. Konsekvens och timestamp Du kan inte fixa blocken, och därför ordningen på meddelandena.
  • "Jag sa inte det" kommer inte att fungera med meddelanden på blockchain.
  • Det finns ingen central struktur som kontrollerar ett meddelandes "äkthet". Detta görs av ett distribuerat system av noder baserat på konsensus, och det ägs av användarna.
  • Omöjlighet till censur - konton kan inte blockeras och meddelanden kan inte raderas.
  • Blockchain 2FA är ett alternativ till helvetes 2FA via SMS, förstört mycket hälsa.
  • Möjligheten att få alla dina konversationer från vilken enhet som helst när som helst innebär att du inte behöver lagra konversationer lokalt alls.
  • Bekräftelse på leverans av meddelande. Inte till användarens enhet, utan till nätverket. I huvudsak är detta en bekräftelse på mottagarens förmåga att läsa ditt meddelande. Detta är en användbar funktion för att skicka viktiga meddelanden.

Blockchain-fördelarna inkluderar även nära integration med kryptovalutorna Ethereum, Dogecoin, Lisk, Dash, Bitcoin (denna pågår fortfarande) och möjligheten att skicka tokens i chattar. Vi gjorde till och med en inbyggd kryptoväxlare.

Och sedan - hur det hela fungerar.

Ett meddelande är en transaktion

Alla är redan vana vid att transaktioner i blockkedjan överför tokens (mynt) från en användare till en annan. Som Bitcoin. Vi skapade en speciell typ av transaktion för att överföra meddelanden.

För att skicka ett meddelande i en messenger på blockchain måste du gå igenom flera steg:

  1. Kryptera meddelandetext
  2. Lägg chiffertext i en transaktion
  3. Signera transaktionen
  4. Skicka en transaktion till valfri nätverksnod
  5. Ett distribuerat system av noder bestämmer "äktheten" för ett meddelande
  6. Om allt är OK ingår transaktionen med meddelandet i nästa block
  7. Mottagaren hämtar meddelandetransaktionen och dekrypterar

Steg 1–3 och 7 utförs lokalt på klienten och steg 5–6 utförs på värdarna.

Meddelandekryptering

Meddelandet krypteras med avsändarens privata nyckel och mottagarens publika nyckel. Vi tar den publika nyckeln från nätverket, men för detta måste mottagarens konto initieras, det vill säga ha minst en transaktion. Du kan använda en REST-begäran GET /api/accounts/getPublicKey?address={ADAMANT address}, och när chattar laddas kommer samtalspartnernas publika nycklar redan att vara tillgängliga.

Hur fungerar en decentraliserad budbärare på blockkedjan?

Messengern krypterar meddelanden med curve25519xsalsa20poly1305-algoritmen (NaCl Box). Eftersom kontot innehåller Ed25519-nycklar, för att bilda en box, måste nycklarna först konverteras till Curve25519 Diffie-Hellman.

Här är ett exempel 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)
  }
}

Forma en transaktion med ett meddelande

Transaktionen har följande allmänna 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": {}
}

För en meddelandetransaktion är det viktigaste asset - du måste placera ett meddelande i objektet chat med struktur:

  • message - spara det krypterade meddelandet
  • own_message - nonce
  • type — meddelandetyp

Meddelanden är också indelade i typer. I huvudsak parametern type berättar hur du förstår message. Du kan bara skicka ett sms, eller så kan du skicka ett objekt med intressanta saker inuti – till exempel är det så här budbäraren gör kryptovalutaöverföringar i chattar.

Som ett resultat skapar vi en transaktion:

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

Transaktionssignatur

För att säkerställa att alla är säkra på äktheten hos avsändaren och mottagaren, tidpunkten för sändningen och innehållet i meddelandet, signeras transaktionen. En digital signatur låter dig verifiera äktheten av en transaktion med hjälp av en offentlig nyckel - en privat nyckel behövs inte för detta.

Men själva signaturen utförs med den privata nyckeln:

Hur fungerar en decentraliserad budbärare på blockkedjan?

Diagrammet visar att vi först hash transaktionen med SHA-256 och sedan signerar den Ed25519 EdDSA och få en signatur signature, och transaktions-ID:t är en del av SHA-256-hash.

Exempel på implementering:

1 — Bilda ett datablock, inklusive ett meddelande

/**
 * 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 - Räkna SHA-256 från datablocket

/**
 * 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 — Signera transaktionen

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

Skickar en transaktion med ett meddelande till en nätverksnod

Eftersom nätverket är decentraliserat kommer alla noder med ett öppet API att fungera. Gör en POST-begäran till slutpunkten api/transactions:

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

Som svar kommer vi att få ett transaktions-ID av typen

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

Transaktionsvalidering

Ett distribuerat system av noder, baserat på konsensus, bestämmer transaktionsmeddelandets "äkthet". Från vem och till vem, när, om meddelandet ersattes med ett annat, och om tidpunkten för sändningen angavs korrekt. Detta är en mycket viktig fördel med blockkedjan - det finns ingen central struktur som är ansvarig för verifiering, och sekvensen av meddelanden och deras innehåll kan inte fejkas.

Först kontrollerar en nod noggrannheten och skickar den sedan till andra - om majoriteten säger att allt är i sin ordning kommer transaktionen att inkluderas i nästa block i kedjan - detta är konsensus.

Hur fungerar en decentraliserad budbärare på blockkedjan?

Den del av nodkoden som är ansvarig för kontroller kan ses på GitHub - validator.js и verify.js. Japp, noden körs på Node.js.

Inklusive en transaktion med ett meddelande i ett block

Om konsensus uppnås kommer transaktionen med vårt meddelande att inkluderas i nästa block tillsammans med andra giltiga transaktioner.

Block har en strikt sekvens, och varje efterföljande block bildas baserat på hasharna från tidigare block.

Hur fungerar en decentraliserad budbärare på blockkedjan?

Poängen är att vårt budskap också ingår i denna sekvens och inte kan "ordnas om". Om flera meddelanden hamnar i ett block kommer deras ordning att avgöras av timestamp meddelanden.

Läser meddelanden

Messenger-applikationen hämtar transaktioner från blockkedjan som skickas till mottagaren. För detta gjorde vi en slutpunkt api/chatrooms.

Alla transaktioner är tillgängliga för alla - du kan ta emot krypterade meddelanden. Men bara mottagaren kan dekryptera med sin privata nyckel och avsändarens offentliga nyckel:

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

Vad annars?

Eftersom meddelanden levereras på detta sätt på cirka 5 sekunder - det här är den tid ett nytt nätverksblock dyker upp - kom vi fram till en klient-till-nod och nod-till-nod-socket-anslutning. När en nod tar emot en ny transaktion kontrollerar den dess giltighet och vidarebefordrar den till andra noder. Transaktionen är tillgänglig för messenger-klienter redan innan konsensus inträffar och inkludering i blocket. På så sätt kommer vi att leverera meddelanden direkt, precis som vanliga snabbmeddelanden.

För att lagra adressboken gjorde vi KVS - Key-Value Storage - detta är en annan typ av transaktion där asset det är inte NaCl-box som är krypterad, utan NaCl-sekretbox. Så här lagrar budbäraren annan data.

Fil-/bildöverföringar och gruppchattar kräver fortfarande mycket arbete. Naturligtvis, i blunder-and-blunder-formatet kan detta "skruvas upp" snabbt, men vi vill behålla samma nivå av integritet.

Ja, det finns fortfarande arbete att göra - idealiskt sett förutsätter verklig integritet att användare inte kommer att ansluta till publika nätverksnoder, utan kommer att höja sina egna. Hur stor andel av användarna tror du gör detta? Det stämmer, 0. Vi kunde delvis lösa det här problemet med Tor-versionen av messenger.

Vi har bevisat att en budbärare på blockkedjan kan existera. Tidigare gjordes det bara ett försök under 2012 - bitmeddelande, som misslyckades på grund av långa meddelandeleveranstider, CPU-belastning och brist på mobilapplikationer.

Och skepsis beror på att budbärare på blockkedjan är före sin tid – folk är inte redo att ta ansvar för sitt konto, att äga personlig information är ännu inte en trend och tekniken tillåter inte höga hastigheter på blockkedjan. Fler tekniska analoger till vårt projekt kommer att dyka upp härnäst. Du kommer se.

Källa: will.com

Lägg en kommentar