Как создать сервер чата на Solidity
Для начала нам нужно определиться с архитектурой приложения. Основная проблема, которую нужно решить — это имитировать обычное серверное приложение для чата со смарт-контрактами.
В простом приложении чата у нас есть 2 пользователя, отправляющих тексты друг другу в одном экземпляре чата. Этот чат может быть таблицей базы данных, содержащей в каждой строке сообщение с from
, to
и message
.
Мы же создадим смарт-контракт, который заменит эту базу данных переменными контракта.
Создание контракта
Первое, что мы должны определить, это контракт, который будет работать как чат-сервер. Для этого мы можем начать создавать простой контракт, в котором определим структуру сообщения.
pragma solidity >=0.7.0 <0.9.0;
contract Chat {
struct Message {
uint id;
address from;
string text;
}
}
Получение сообщения
Чат-приложения обычно работают с сокетами для того, чтобы "прослушивать" серверное приложение на случай отправки новых сообщений пользователю.
В Solidity способ сообщить фронтенду о том, что что-то произошло, - это события, поэтому именно их мы будем использовать для получения сообщения.
pragma solidity >=0.7.0 <0.9.0;
contract Chat {
struct Message {
uint id;
address from;
string message;
}
// tells the frontend that a new message has been sent.
event sendMessageEvent(uint indexed _id, address indexed _from, string _message);
Отправка сообщений
Для запуска действий внутри смарт-контракта мы можем поступать, как обычно в других языках программирования, вызывая функции, поэтому в данном случае мы вызовем функцию внутри смарт-контракта, которая создаст новое сообщение и запустит sendMessageEvent
, чтобы сообщить получателю, что новое сообщение было отправлено.
pragma solidity >=0.7.0 <0.9.0;
contract Chat {
// ...
// We can use an autoincremental id for each new message.
uint lastMessageId;
function sendMessage(string _text) public {
// increments id and sends message.
lastMessageId++;
sendMessageEvent(lastMessageId, msg.sender, _text);
}
}
Создание диалога
Очень важной особенностью чатов является возможность не только отправлять и получать сообщения как пользователь, но и составлять список всех старых сообщений, которые мы отправляли друг другу во время разговора.
Для этого в языке программирования Solidity есть удобный тип под названием mapping
. С помощью этого типа данных мы можем хранить массивы данных, сопоставленных по индексам.
pragma solidity >=0.7.0 <0.9.0;
contract Chat {
// the list of old messages in the chat
mapping(uint => Message) public messagesList;
function listMessages() public constant returns (uint[]){
// if the chat is empty
if(lastMessageId == 0) {
return new uint[](0);
}
// give me the ids.
uint[] memory ids = new uint[](lastMessageId);
// loads all the message ids on 'ids' list.
for (uint i = 1; i <= lastMessageId; i++) {
// if the sender is different than me.
if(messages[i].sender != msg.sender) {
ids[numOfMessages] = messagesList[i].id;
}
}
return ids;
}
}
Проблема теперь в том, что messageList
всегда пуст, для загрузки этого списка нам нужно изменить функцию sendMessage
, чтобы загружать новое сообщение в мэппинг каждый раз, когда отправляется одно сообщение.
pragma solidity >=0.7.0 <0.9.0;
contract Chat {
function sendMessage(string _text) public {
lastMessageId++;
// loads the message into the list.
messageList[messageIdCounter] = Message(
lastMessageId,
msg.sender,
_text
);
//Trigger event
newMessageEvent(lastMessageId, msg.sender, _text);
}
}
Развертывание контракта
Существует множество платформ, на которых вы можете очень просто протестировать свои собственные контракты на фронтенд-приложениях. Наиболее популярной из них является Remix, вы можете написать контракт, развернуть его в тестовой цепочке и докручивать его там.