How does a decentralized messenger on the blockchain work?
At the beginning of 2017, we started creating a messenger on the blockchain [the name and link is in the profile] by discussing the advantages over classic P2P messengers.
Passed 2.5 years, and we managed to confirm our concept: instant messenger applications for iOS, Web PWA, Windows, GNU/Linux, Mac OS and Android are now available.
Today we will tell you how the blockchain messenger works and how client applications work with its API.
We wanted the blockchain to solve the security and privacy issues of classic P2P messengers:
One click to create an account - no phones and emails, no access to address books and geolocations.
The interlocutors never establish direct connections, all communication goes through a distributed system of nodes. User IP addresses are inaccessible to each other.
All messages are encrypted with End-to-End curve25519xsalsa20poly1305. It seems that this will not surprise anyone, but we have a source code open.
MITM attack is ruled out - each message is a transaction and is signed by Ed25519 EdDSA.
The message goes into its own block. Sequence and timestamp Blocks cannot be corrected, and hence the order of messages.
“I didn’t say that” won’t work with messages on the blockchain.
There is no central structure that makes checks for the "authenticity" of the message. This is done by a distributed system of nodes based on consensus, and it is owned by users.
Impossibility of censorship - accounts cannot be blocked, and messages cannot be deleted.
The ability to get all your dialogs from any device at any time is the ability to not store dialogs locally at all.
Message delivery confirmation. Not to the user's device, but to the network. In fact, this is a confirmation of the recipient's ability to read your message. This is a useful feature for sending critical notifications.
Of the benefits of the blockchain, there is also close integration with the cryptocurrencies Ethereum, Dogecoin, Lisk, Dash, Bitcoin (this one is still in progress) and the ability to send tokens in chats. We even made a built-in crypto-exchanger.
And then - how it all works.
Message is a transaction
Everyone is already used to the fact that blockchain transactions transfer tokens (coins) from one user to another. Like bitcoin. We have created a special type of transaction for message passing.
To send a message in a messenger on the blockchain, you need to go through several steps:
Encrypt message text
Put ciphertext into transaction
Sign transaction
Send a transaction to any network node
A distributed system of nodes determines the “credibility” of a message
If everything is OK, the transaction with the message is included in the next block
The recipient retrieves the transaction with the message and decrypts
Steps 1-3 and 7 are performed locally on the client, and steps 5-6 are performed on the hosts.
Message encryption
The message is encrypted with the sender's private key and the recipient's public key. We will take the public key from the network, but for this the recipient's account must be initialized, that is, have at least one transaction. You can use REST request GET /api/accounts/getPublicKey?address={ADAMANT address}, and when loading chats, the public keys of the interlocutors will already be available.
The messenger encrypts messages using the algorithm curve25519xsalsa20poly1305 (NaCl Box). Since the account contains Ed25519 keys, to form the box, the keys must first be converted to Curve25519 Diffie-Hellman.
For a message transaction, the most important thing is asset - it needs to place a message in the object chat with structure:
message - save the encrypted message
own_message nonce
type - message type
Messages are also divided into types. Essentially, the parameter type tells how to understand message. You can send just a text, or you can send an object with interesting things inside - for example, this is how the messenger makes cryptocurrency transfers in chats.
In order for everyone to be sure of the authenticity of the sender and recipient, the time of sending and the content of the message, the transaction is signed. A digital signature allows you to verify the authenticity of a transaction using a public key - a private key is not needed for this.
But the signature itself is just performed by a private key:
It can be seen from the diagram that the transaction is first hashed by SHA-256, and then signed Ed25519 EdDSA and get a signature signature, and the transaction ID is part of the SHA-256 hash.
Implementation example:
1 - We form a data block, including a message
/**
* 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)
}
A distributed system of nodes based on consensus determines the “authenticity” of a transaction-message. From whom and to whom, when, whether the message was replaced by another, and whether the time of sending is correct. This is a very important advantage of the blockchain - there is no central structure that is responsible for verification, and the sequence of messages and their content cannot be faked.
First, one node checks the authenticity, and then sends it to others - if the majority say that everything is in order, the transaction will be included in the next block of the chain - this is the consensus.
The part of the node code that is responsible for the checks can be viewed on GitHub - validator.js и verify.js. Yep, node runs on Node.js.
Include a transaction with a message in a block
If consensus is reached, the transaction with our message will go into the next block along with other valid transactions.
Blocks have a strict sequence, and each subsequent block is formed based on the hashes of previous blocks.
The bottom line is that our message is also included in this sequence and cannot be “rearranged”. If there are multiple messages in a block, their order will be determined by timestamp messages.
Reading messages
The messenger application retrieves transactions from the blockchain that are sent to the addressee. To do this, we made an endpoint api/chatrooms.
All transactions are available to everyone - you can receive encrypted messages. But only the recipient can decrypt with his private key and the public key of the sender:
Since messages are delivered in this way for about 5 seconds - this is the time a new block of the network appears - we came up with a socket connection client-to-node and node-to-node. When a node receives a new transaction, it checks its validity and passes it on to other nodes. The transaction is available to messenger clients even before consensus and inclusion in the block. So we will deliver messages instantly, like the usual messengers.
To store the address book, we made KVS - Key-Value Storage - this is another type of transaction in which asset encrypted not with NaCl-box, but NaCl-secretbox. So the messenger stores other data.
File/image transfer and group chats still require a lot of work. Of course, in a blunder format, this can be “fastened” quickly, but we want to maintain the same level of privacy.
Yes, there is still work to be done - ideally, real privacy assumes that users will not connect to public network nodes, but will raise their own. What percentage of users do you think do this? That's right, 0. Partially, we managed to solve this issue with the Tor version of the messenger.
We proved that a blockchain messenger can exist. Previously, there was only one attempt in 2012 - bitmessage, which failed due to the high message delivery time, processor load and lack of mobile applications.
And skepticism is due to the fact that blockchain messengers are ahead of their time - people are not ready to take responsibility for their account, the possession of personal information is not yet in trend, and technology does not allow high speeds on the blockchain. More technological analogues of our project will appear next. Here you will see.