Отмечу сразу, что данная статья не о том как писать код на Solidity, а как существующую классическую архитектуру вашего приложения можно перевести на рельсы blockchain и думать в ключе децентрализации.
Пару лет назад я работал над одним интересным веб приложением сервиса p2p доставки посылок. По определенным причинам разработку пришлось заморозить на этапе прототипа, так что я просто выложил исходный код на GitHub и забыл про него.
В последнее время по роду деятельности мне довелось поработать с несколькими проектами связанными с криптовалютой и blockchain-технологиями. Познакомившись ближе с Ethereum и его идеологией децентрализованных приложений (ĐApp) я просто заболел этой идеей: никакой цензуры, никто не может прикрыть ваш бизнес, никто не может конфисковать ваши средства, невозможно просто взять и выключить сервер на котором работает ваше приложение. В определенный момент я пришёл к выводу, что именно в такой среде мой проект может иметь шансы на жизнь.
Итак, взглянем на фронт работ.
Фронт работ
Изначальная идея проекта была в том, что люди которые часто путешествуют, могли бы перевозить что-нибудь в своих чемоданах или автомобилях. Этакий Uber для доставки. Пользователи делились на путешественников (осликов) и клиентов. Если вы собираетесь ехать, допустим, из Москвы в Минск на эти выходные, вы можете добавить в сервисе новую поездку (трип) с описанием того, что вы можете перевезти и когда. Клиенты, подписанные на данное направление, получали уведомление о новом трипе и могли добавлять запросы на доставку, например лекарства какие-нибудь или iPad для мамы. Стороны договаривались об оплате (самописный escrow на PayPal) и условиях доставки. После исполнения заказа стороны могли оставлять рейтинги и комментарии друг о друге. В профиле каждого пользователя имелась небольшая статистика.
Со временем произошло переосмысление некоторых пунктов в алгоритме работы сервиса. Стало ясно, что будет правильно если бы сначало клиенты добавляли свои заказы на перевозку, а ослики уже выбирали бы кому оказывать услугу. Далее вы увидите, как такая концепция обеспечивает гибкость и масштабируемость.
По сути нам надо переписать весь back-end на смарт контракты и задеплоиться на блокчейне. Приложение будет абсолютно открытым, неизменяемым (immutable), децентрализованным и, следовательно, свободным от регуляции даже самим разработчиком. Таким образом, мы получим децентрализованную платформу на базе которой любой человек сможет реализовать своё front-end, мобильное или серверное приложение.
Протокол работы подобного сервиса в краткой форме будет выглядеть следующим образом:
- Клиент добавляет Заказ на перевозку из точки A в точку Б
- Ослик может добавить Предложение на оказание услуги
- Клиент может добавить Ответ на Предложение
- Ослик выставляет Счёт на оплату Заказа с указанием суммы Предоплаты и Депозита
- Клиент оплачивает Счёт и может установить Ключ на снятие Депозита
- Ослик выполняет заказ и получает Ключ на снятие Депозита (QR-код например)
- Ослик в любой момент может возместить средства Клиенту
- Ослик и Клиент обмениваются рейтингами
Приступим
Данный протокол будет реализован смарт контрактом на Solidity, который назовём Osliki. Для начала заведём переменные для хранения заказов, предложений, счетов и статистики:
Order[] public orders; // заказы
Offer[] public offers; // предложения (тут же будут храниться и ответы от клиентов)
Invoice[] public invoices; // счета
mapping (address => Stat) internal stats; // маппинг адреса пользователя к его статистике
Описание всех структур:
struct Order {
address customer; // ethereum-адрес клиента
string from; // гео координаты в формате "lat,lon" или ethereum-адрес "0x..." (например адрес принадлежащий магазину, ресторану, ферме или частному лицу) или просто пустое значение (например если важно что привезут, а не откуда)
string to; // тоже самое что и предыдущее
string params; // параметры посылки в формате "вес(кг),длина(м),ширина(м),высота(м)"
uint expires; // дата истечения срока действия, Unix-время в секундах
string message; // просто текст в свободной форме
uint[] offerIds; // массив id предложений
address carrier; // ethereum-адрес выбранного ослика для исполнения заказа
uint invoiceId; // прикрепленный оплаченный счёт
EnumOrderStatus status; // статус заказа
uint createdAt; // дата создания
uint updatedAt; // дата изменения
}
struct Offer {
address carrier; // ethereum-адрес ослика
uint orderId; // id заказа
string message; // текст предложения в свободной форме
string respond; // текст ответа от клиента в свободной форме.
uint createdAt;
uint updatedAt;
}
struct Invoice {
address sender; // ethereum-адрес ослика
uint orderId; // id заказа
uint prepayment; // сумма для предоплаты (может быть 0)
uint deposit; // сумма для депозита (может быть 0)
uint expires; // срок годности инвойса
EnumCurrency currency; // валюта в которой выставлен счёт
bytes32 depositHash; // хеш Ethereum-SHA-3 (Keccak-256) депозитного ключа (устанавливается клиентом в момент оплаты счета)
EnumInvoiceStatus status; // статус счёта
uint createdAt;
uint updatedAt;
}
struct Stat {
uint[] orders; // массив id заказов в которых участвовал юзер в качестве клиента или ослика
uint rateSum; // сумма оценок
uint rateCount; // количество оценок, средняя оценка averageRate = rateSum / rateCount
mapping (uint => Review) reviews; // маппинг id заказа к отзыву и оценке этого заказа
}
struct Review {
uint8 rate; // оценка между 1 и 5
string text; // текст отзыва
uint createdAt;
}
Статусы:
enum EnumOrderStatus { New, Process, Fulfilled }
enum EnumInvoiceStatus { New, Settled, Closed, Refund }
enum EnumCurrency { ETH, OSLIK } // оплата счетов в эфире (комиссия 1%) или в своих внутренних токенах OSLIK (без комиссии)
Далее не буду приводить тела функций, иначе займет слишком много места. Ниже будет ссылка на исходный код на GitHub.
Клиент добавляет Заказ:
function addOrder(
string from, // "" || "lat,lon" || ethereum-адрес "0x..."
string to, // "" || "lat,lon" || ethereum-адрес "0x..."
string params, // "weight(kg),length(m),width(m),height(m)"
uint expires,
string message
) public;
Ослик добавляет Предложение:
function addOffer(
uint orderId,
string message
) public;
Клиент отправляет Ответ:
function respond(
uint offerId,
string message
) public; // только один ответ на предложение
Ослик отправляет Счёт:
function addInvoice(
uint orderId,
uint prepayment,
uint deposit,
EnumCurrency currency, // ETH || OSLIKI
uint expires
) public;
Клиент оплачивает Счёт:
function pay(
uint invoiceId,
bytes32 depositHash // клиент отправляет хеш keccak256 ключа для доступа к депозиту, может быть пустым если нет депозита и все средства оплачены вперед
) public payable; // после оплаты, счёт прикрепляется к заказу и отправитель счёта считается выбранным в качестве исполнителя заказа
Ослик выполняет Заказ. К примеру при доставке Ослик сканирует QR-код у Клиента, в котором вшит Ключ от Депозита:
function fulfill(
uint orderId,
string depositKey // ключ который хешируется и сравнивается с depositHash, может быть пустым если депозита не было, в этом случае просто поменяется статус заказа на ‘Выполнен’
) public;
Ослик возвращает средства Клиенту если тот не доволен или что-то пошло не так в процессе:
function refund(
uint invoiceId
) public payable;
Стороны могут добавлять отзывы друг о друге с оценкой впечатления от Заказа:
function addReview(
uint orderId,
uint8 rate,
string text
) public; // тут же обновляем статистику
Плюс еще пачка функций для доступа к данным. В итоге у нас вышло 2 контракта:
- Контракт Osliki, который реализует наш протокол и является ядром платформы для построения любого бизнеса связанного или зависимого от доставки.
- Токен внутренней валюты OSLIK, который может использоваться для оплаты без комиссии. Токены можно распродать в рамках crowdfunding-а и привлечь средства на развитие и продвижение платформы и сервисов построенных на этой платформе.
Примеры
По сути, контракт Osliki является открытой базой данных заказов на доставку, которую может использовать любое частное лицо, организация или какой-нибудь дрон по типу Amazon Prime Air.
Представьте себе предпринимателя с парком грузовых дронов, базирующихся на крыше какого-нибудь здания в вашем городе. Дроны мониторят базу данных в блокчейне и если заказ соответствует каким-то определенным параметрам (например приемлемый радиус действия и габариты груза), автоматически отправляют предложения и счета, а потом летят исполнять заказы.
Представьте себе Мистера X с Крэйглиста, который выращивает у себя на даче особо душистый сорт канабиса (конечно же в странах где это легально). Он кидает вам ссылку, по которой вы можете добавить заказ в Osliki с указанием ethereum-адреса Мистера X напрямую, чтобы другие ослики не спамили предложениями. Далее счёт, оплата и вот посылка у вас уже на руках. И даже в случае блокировки аккаунта Мистера X на Крэйглисте, поклонники его садоводческого таланта будут всегда помнить куда слать заказы.
Можно представить себе маркетплейс, где фермеры продают свои биологические овощи без ГМО “только что с грядки”, свежее молоко и мясо. Доставка может осуществляться, например, водителями, курсирующими из пригорода в город. Фермеры таким образом получат неограниченный доступ к розничным клиентам в обход супермаркетов.
Ну и изначальная идея с путешественниками, перевозящими посылки между городами и странами, также остается актуальной.
Планы
Как часть платформы, сюда хорошо вписался бы такой же децентрализованный маркетплейс (Osliki Marketplace) или доска объявлений (Osliki Classifieds). Или, возможно, использовать уже готовые решения.
С использованием методов BigData и AI можно более глубоко анализировать поведение и статистику каждого пользователя и выдавать результаты о его благонадежности. Например можно выявлять пользователей, которые накрутили себе рейтинг.
На данный момент пока стоит задача реализовать front-end приложение osliki-js (как один из вариантов реализации) на каком-нибудь GitHub Pages, чтобы можно было привычным способом в браузере работать с контрактами. Плюс набор виджетов для встраивания на сайты.
Если тема вам показалась интересной, присоединяйтесь к разработке.
Ссылки
Ссылки на исходные коды на GitHub:
Контракты на данный момент задеплоины в тестовой сети Ethereum Ropsten. Тестовый эфир, чтобы поиграться, можно получить тут.
Адреса контрактов:
- Osliki 0xa5AF61cE7deEb042e7Ad6A2118b183D3733E1D99
- ERC20-совместимый токен OSLIK 0x7ee36e1ddac343649edaa622f9bb067ed74545b2