Простые и мощные краткосрочные смарт-контракты

nzpf6ptf3oslar8b2iahzsdi6xw.jpeg

В последнее время смарт-контракты широко применяются в сети Ethereum в основном для проведения ICO и управления выпущенными токенами. Такие контракты существуют столько, сколько необходимо для обслуживания проектов, при этом месяцами обеспечивают бесперебойное взаимодействие с тысячами клиентов. Будем называть их долгосрочными смарт-контрактами.

Однако определённые задачи требуют, чтобы контракт выполнялся буквально за минуты, после чего он становится не нужен. Причём для каждого пользователя могут понадобиться особые параметры, реализовывать их в рамках большого контракта нецелесообразно. Такие контракты будем называть краткосрочными. Рассмотрим их более подробно в этой статье.

Смарт-контракт можно представить как автоматизированный кошелёк: кладёшь в него деньги, он проверяет условия и либо возвращает деньги, либо переправляет их в другой кошелек, либо рассылает их в десять таких кошельков. Но эта аналогия неполная, давайте попробуем другую.

Сравним контракт с веб-сайтом. Вы разработали сайт, выложили его в интернет, а провайдер прописал в DNS, что по адресу www.company.com находится определённый веб-сайт. Выкладку контракта в сеть Ethereum можно рассматривать схожим образом: вы разработали код, поместили его в сети (то есть послали специальную транзакцию «создай контракт») — и у контракта появился свой собственный адрес в Ethereum, в точности такой же, как адрес, который вы отдаёте кому-либо, чтобы он переслал вам эфир.

При этом у «сайта» в Ethereum есть полезные для смарт-контрактов свойства:


  • Код «сайта» нельзя изменить. Максимум можно прибить его, если заранее запрограммировать возможность «гибели» контракта при получении специального сообщения. Это не значит, что код исчезнет или смарт-контракт будет стёрт. Просто любая отправленная в него транзакция вернёт ошибку.
  • Каждый запрос к «сайту» — неоспоримая транзакция, навсегда сохраняемая в блокчейне. На ICO вы внесли один эфир и получили десять токенов — внутри контракта появилась запись о том, что у вас есть десять токенов. Ваш запрос изменил состояние контракта. Каждая такая транзакция уже неоспорима технически. Вы всегда сможете доказать, что транзакция была и её подписали именно вы (если есть нужный секретный ключ).
  • Каждый запрос от пользователя на «сайт» может содержать любое количество средств. Если мы рассматриваем контракт как сайт, то каждый запрос к «сайту» способен нести в себе эфир. Представьте себе сайт, где вы можете к каждому клику по кнопкам приложить любое количество денег, а сайт при этом понимает, сколько ему прислано, умеет отдавать сдачу и т. д.
  • Каждый запрос виден всем. Вы не можете ограничивать видимость запросов к «сайту». Вы послали запрос в смарт-контракт, а во время исполнения что-то сломалось — для Ethereum это штатная ситуация, и транзакция, вызвавшая ошибку, всё равно будет помещена в блоки, а майнеры всё равно получат свою комиссию за исполнение кода контракта (из вашего эфира, естественно).

После того как сеть «принимает» код нового контракта (все майнеры проверили код, поместили в блоках, замайнили блок, опубликовали его в сети), контракт получает собственный адрес и может принимать транзакции. Не забываем также, что каждая отправленная в блокчейн транзакция — это данные, подписанные электронной подписью со всеми её преимуществами (как минимум с подтверждением авторства). Зачем здесь блокчейн? Говоря простыми словами, дело в отсутствии shared secret, известного двум сторонам. PIN, голый хеш пароля, небезопасная web-сессия — всё это примеры того, чем может воспользоваться атакующий. В случае блокчейна единственный секрет — это ключ на клиенте, всё остальное не требует ни защищённых каналов связи, ни авторизации. Ну, добавим ещё то, что ни одна из сторон не может сделать что-то незаметно для другой: каждое изменение состояния контракта фиксируется, даже неудавшиеся или зловредные транзакции. Блокчейн фиксирует любую операцию, которая что-то изменяет внутри хранилища каждого помещённого в сети смарт-контракта. При этом операция может быть простой, а может делать и довольно сложные вещи, и любой участник сети абсолютно уверен в том, что код контракта у всех участников работает в точности так же, как и у него на машине.

Даже в минимальном варианте (в краткосрочном виде) смарт-контракты — великолепные инструменты для учёта. Их крайне удобно использовать почти в любых проектах, так как из коробки мы получаем всю систему безопасности и отказоустойчивости. Как мы увидим далее, порой даже минимума штатных функций достаточно, чтобы реализовать множество интересных задач.

Под сроком жизни контракта в этой статье я подразумеваю не только время, но и количество обслуживаемых контрактом транзакций. Если контракт работает долго, принимает и отправляет много транзакций, он считается долгосрочным. Если принимает несколько транзакций или работает ограниченное время и затем закрывается — краткосрочным. Мы разделили контракты на два типа, получив опыт разработки нескольких контрактов и проектирования протоколов взаимодействия.

Эти контракты обладают разной экономикой газа, сложностью создания, характером отправки в сеть и помогают решать разные бизнес-задачи. Одни контракты обслуживают множество пользователей и сделок, хранят много данных. Другие применяются разово, для фиксации и исполнения единичной договорённости (продать один товар, сделать единовременный платёж, получить ранее помещённые в контракт средства и т. п.). Поэтому мы навскидку назвали контракты долгосрочными и краткосрочными. Если есть мысли, как их назвать поточнее, welcome.

Долгосрочные контракты. Обычно бизнес-модель долгосрочного контракта выглядит так: мы поместили его в сети Ethereum, и теперь тысячи пользователей ходят в контракт и что-то в нём делают. В качестве примера приведём контракт токена. Мы выкладываем в сеть контракт, хранящий балансы пользователей. К нам заходят тысячи инвесторов, и для каждого инвестора информация о количестве полученных токенов кладётся в контракт токена. Каждый раз, когда вы пересылаете токены на другой адрес, вы обращаетесь к одному и тому же контракту, расположенному по одному и тому же адресу, и инструктируете контракт изменить балансы токенов (ваш и получателя).

Контракт тащит в себе большое количество данных и живёт столько, сколько жив ваш проект. Токены внутри него могут перемещаться годами. Никаких ограничений на transfer токенов внутри контракта нет, и он будет работоспособен всегда, пока существует сеть.

Другой пример долгосрочного контракта — Equity (долевое владение). Рассмотрим пример компании, в которой вы раздаёте доли: 10% Васе, 10% Пете, а вам — 80%. Вы хотите, чтобы вся прибыль компании распределялась соответственно: если зашло 100 рублей, Васе и Пете достаётся по 10, а вам — 80. Equity делит деньги на потоке, распределяя входящие средства, и позволяет участникам неоспоримо получать свои доли, заодно фиксируя все платежи и суммы. А ведь планируется, что компания будет работать долго, поэтому вы один раз просите программиста, чтобы он выложил в сеть ваш контракт, а потом годами без дополнительных усилий спокойно пользуетесь контрактом, иногда перераспределяя доли.

Краткосрочные контракты. Они живут недолго (часы, дни) либо проводят мало операций (одиночный платёж, пару фиксаций событий). Эти контракты заточены под отдельного пользователя, отдельную сделку или отдельный товар. Например, продавая стиральные машины, можно разместить в сети по контракту на каждую машину. В такой контракт заходит пользователь, один-два раза что-то делает (переводит средства, подтверждает что-то) — и после этого взаимодействие заканчивается. От short-term контрактов нам нужен только одноразовый ввод-вывод средств с нужными проверками и фиксация события в блокчейне. В минимальном варианте любая транзакция, с эфиром или без, это сама по себе информация. Нам, в общем-то, необязательно в неё что-то вкладывать: у неё уже есть подпись отправителя и время закрытия блока.

Если контракт умеет только принимать и отдавать фиксированное количество средств (и выдавать, например, сдачу), то любая успешная транзакция становится аналогом кассового чека, а блокчейн — удобным хранилищем данных об операциях. Получилось провести транзакцию — значит, покупка совершена. Показав ETH-адрес магазина в ФНС, можно пояснить: «Вот наши чеки и наши продажи». Это вообще избавляет магазины от необходимости иметь кассовые аппараты, теперь их забота — регистрировать ключи и адреса, принадлежащие магазину, и обеспечивать их безопасность и возможность смены при взломе. Также можно без особых проблем парой строк реализовать уплату налога прямо при покупке, было бы куда платить.

У одноразовых контрактов есть преимущества и недостатки. То, что контрактов много и они мелкие, сильно повышает стоимость помещения в сети Ethereum, ведь это довольно дорогая операция. Но зато контракты отвечают за относительно небольшие суммы, их существенно легче заменить, попросту перейдя на новую версию. Рисков, связанных с безопасностью, намного меньше в силу простоты кода и малого количества данных. Простота кода также означает дешевизну для пользователей. Есть ещё несколько моментов, но о них позже.

В качестве примеров опишем, скажем, три типа простых краткосрочных контрактов:


  • invoice-paid;
  • commit-reveal;
  • one-time multisig.

Первый — это аналог выставленного счёта на оплату, второй позволяет «пообещать отдать деньги» и «позволить их забрать, доказав выполнение задания», третий — решить спор, «чьи деньги», с помощью арбитра. Поехали.

jlgfpx7gw67pt4mciyuqufv1qgu.pngФактически перед нами ценник. Представьте, что мы продаём арбузы. Мы выпускаем ценник прямо под конкретный плод с конкретным весом, наклеиваем на арбуз и пишем: «Стоит 1 ETH». После этого нам нужно, чтобы ценник либо принял 1 ETH, либо «сгнил» через сутки: возможно, на следующий день мы захотим продать арбуз за 2 эфира.

В краткосрочном варианте мы публикуем короткий контракт под конкретный товар на ограниченный срок. Из внутренних данных в контракте находится только цена и время, когда контракт станет невалидным.

Когда кто-то хочет купить товар, он присылает эфир в наш одноразовый контракт, а тот, в свою очередь, пересылает эфир на ETH-адрес магазина, отсылает сдачу (если нужно) и закрывается. В блокчейне сохранилась транзакция, а анализируя транзакции из контрактов, созданных магазином, легко получить всю историю продаж. В момент покупки контракт в том числе может платить налог или комиссии тому, кто привёл в него клиента.

Ещё одна довольно близкая аналогия такого контракта-ценника — выставленный счёт. Поэтому мы и назвали контракт invoice-paid: это два рабочих состояния, в которых он может находиться. В режиме invoice он ждёт оплату, в режиме paid принял оплату и ничего не делает. Третье состояние, когда истекло время жизни (TTL — time-to-live), стандартно для всех краткосрочных контрактов. Истекшее TTL заставляет контракт самоуничтожиться или попросту игнорировать любую пришедшую транзакцию.

Можно поместить полезные данные в invoice-paid, например выставить такой invoice конкретному адресу прямо в момент, когда покупатель решил приобрести товар. В этом случае контракт принимает эфир строго с заданного адреса. В контракт можно добавить хеш любых данных: фотографии товара, id«а объявления на Avito, архива с пакетом документов и т. п.

as1iqdejr3iq3qwoeaynv8tmbc8.pngЭта схема в блокчейне используется для того, чтобы пообещать что-то сделать и потом доказать, что дело сделано.

При отправке транзакции в сеть любая нода, передающая транзакцию по p2p-сети, может проанализировать содержимое транзакции до того, как она будет включена в блок. Такая нода может создать собственную транзакцию с большей комиссией, которую block-producer«ы с большей вероятностью включат в блок и которая встанет в списке транзакций выше, чем исходная. Давайте вспомним игру «камень, ножницы, бумага». Играть в неё, напрямую отправляя транзакции, в данном случае нельзя. Понятно, что я не отправлю «ножницы» в контракт: мой оппонент может подсмотреть публичную транзакцию и создать свою, с «камнем».

Решение задачи — в схеме, когда доказывающая сторона (в нашем примере их две, я и мой оппонент) «обещает» в будущем показать некоторое значение, пока секретное (наши «ножницы» или «бумагу»). Для этого мы отправляем в контракт сначала хеш от слова «ножницы» (я), затем хеш от слова «бумага» (мой оппонент). Только затем я могу открыто опубликовать «ножницы», а оппонент — «бумагу». Обмануть тут уже не получится: я открою «ножницы», только когда увижу хеш решения оппонента, то же самое касается его. Смысл этапа размещения хешей хорошо передаёт слово commit, а этапа раскрытия значений — reveal, отсюда и название.

Обязательно надо отметить, что перед вычислением хеша строка («ножницы» или «бумага») обязательно дополняется случайным числом. От криптостойкости числа зависит возможность «угадывать» выбор оппонента по опубликованному хешу, поэтому к его генерации надо подходить со всей серьёзностью. Это число — временный секрет, оно опубликовывается вместе со строкой на этапе reveal, чтобы участники могли проверить корректность хеша.

Давайте рассмотрим вариант контракта commit-reveal для оплаты курьерской доставки. Магазин посылает курьера вместе с товаром к клиенту и хочет, чтобы курьер получил оплату, только если доставит товар за три часа и вручит его лично клиенту. Для этого магазин генерирует секретное слово, создаёт контракт, помещает в него хеш слова и время (три часа), после которого магазин сможет просто вернуть свои средства, отправив транзакцию refund в контракт. Магазин выдаёт курьеру (в его мобильное приложение или браузер) адрес контракта, и курьер видит, что за три часа сможет забрать оплату, если узнает секретное слово. Само слово магазин отправляет по SMS клиенту. Логику контракта описывает фраза «Если курьер пришлёт в течение трёх часов слово — прообраз хеша, использованного при создании контракта, я высылаю средства курьеру».

При конфликте с покупателем магазин может сам разблокировать средства для курьера, попросту отправив тому секретное слово.

Разумеется, для реального использования контракты содержат ещё и модификации — дополнительные данные и проверки, комиссии, пороги входа, динамическое изменение стоимости доставки и т. п. Но основная схема commit-reveal — базовая для построения удобных протоколов взаимодействия бизнес-агентов.

54p5uojxwymfrdqnz1ijpxiwuyy.pngЭтот контракт — адрес, с которого можно вывести средства, предоставив N из M подписей.

Предположим, муж и жена решили держать часть семейных сбережений в криптовалюте. Хочется, чтобы контракт принимал и хранил средства, но не давал выводить их из контракта, пока не получит как минимум два из трёх подтверждений — от мужа, жены или банка. Если у жены украдут телефон, муж сможет пойти в банк и, получив подпись банка, вывести деньги. В обычном режиме средства выводятся из контракта, когда и муж и жена предоставили свои подписи. Это крайне удобно, ибо взлом одного из участников не грозит несанкционированным выводом средств.

Вообще multisig может оперировать произвольными N и M, но его вариант «два из трёх» покрывает огромное количество бизнес-задач, где требуется третья сторона. Когда покупаешь квартиру и банк отдаёт ключи от ячейки, только когда получает документы на недвижимость, — это и есть multisig 2/3. Деньги кладутся на multisig-адрес, где участвуют продавец квартиры, покупатель и банк. Банк, как только видит договор о смене собственника, ставит свою электронную подпись. Такой контракт также включает в себя ограничения по времени и, конечно же, комиссию для банка. Для единичных задач, оперирующих большими суммами, разумно использовать именно одноразовые контракты. Уязвимость в огромном универсальном контракте, который будет управлять всеми аккредитивами, может привести к куда более серьёзным последствиям, нежели успешная атака на одноразовый контракт.

Вообще multisig — это как автомат Калашникова, с помощью краткосрочных multisig 2/2 и 2/3 легко реализуются сделки с escrow, сделки, требующие коллективного решения, а дальнейшее добавление функционала в multisig и динамическое изменение N и M — это уже переход к долевому голосованию и управлению (но это тема для отдельной статьи).


  1. Простота и, как следствие, безопасность. В Solidity приходится думать буквально над каждым шагом, любая операция стоит газа, каждый лишний вызов внешнего контракта — это потенциальная уязвимость, именно здесь кроется большое количество проблем с безопасностью. Краткосрочный контракт содержит мало кода и не требует взаимодействия с другими контрактами, поэтому более безопасен, чем его старший долгосрочный брат.


  2. Чем меньше кода, тем дешевле. Дешевле публиковать, разрабатывать и писать под него тесты. У простого контракта более простой и унифицированный интерфейс, который можно спокойно выставлять наружу в любом конструкторе. Это позволяет другим разработчикам работать с вашими смарт-контрактами, легко приспосабливая их под свои нужды.


  3. Краткосрочные смарт-контракты легко переносить на другие блокчейны. Если вы делаете простой краткосрочный смарт-контракт, то, скорее всего, сможете реализовать его безо всяких проблем на графене, EOS и других платформах. Простая логика — больше вероятность, что это уже реализовано в конкурирующих движках, а значит, разработка дешевле и контракт надёжней.


  4. Очень удобно фиксить. Дыра в токене — серьёзная проблема. Придётся заменять токен и перетаскивать баланс — это стоит огромное количество газа. В случае краткосрочных версий мы не контракты деплоим, а фиксим их версию, а потом просто ждём, пока плохие контракты «сгниют» и будут заменены новыми версиями.


  5. Правильно уничтожая контракты, мы будем чистить local storage на клиентах, оптимизируя используемое место. Тут пока ещё тонкий момент, удаляет ли selfdestruct контракта из storage db физически на клиенте, но, по идее, должен. Это позволит не раздувать сильно объём блокчейна.


  6. TTL. Если мы ищем по блокчейну контракты-ценники и знаем, что они живут не больше суток, то поисковик по Ethereum можно настроить на работу лишь с блокчейном за последние сутки. Это делает такие поисковики удобными и быстрыми, так как данных немного, а работающие с краткосрочными контрактами сайты очень быстрые и функциональные. В противном случае шерстить сотни гигабайт блокчейнов, делая tracking тысяч аккаунтов — непростая задача для программистов.

Не бывает всё очень хорошо. Первая проблема — газ. На каждый деплой смарт-контракта придётся тратить газ. Сервис, которые «пуляет» десятки тысяч контрактов, требует серьёзного исследования и балансировки. В Ethereum сегодня можно послать транзакцию при цене газа 5 Gwei, и она прилетит за минуту. А иногда надо ставить 50 Gwei за газ и ждать час. Те, кто используют смарт-контракты, не очень рады росту курса эфира, ибо это означает удорожание отправки контрактов в сеть. Выкладка контракта в сеть — процедура дорогая, поэтому краткосрочные контракты — удовольствие недешёвое.

Мы рассматриваем не только Ethereum и на своей платформе планируем использовать сразу несколько сетей, работающих со смарт-контрактами. Проблема стоимости исполнения — это их общая проблема. Рано или поздно она решится — либо конкуренция за стоимость исполнения одной строки контракта сильно понизит стоимость. Кроме того, скорее всего, различаться по стоимости будут даже одинаковые типы операций, так что я не исключаю появление стратегий типа «выполняем контракты с криптопреобразованиями в одном блокчейне, а со строковыми операциями — в другом».

Вторая проблема — мёртвые, но не добитые selfdestruct«ом контракты. Мы «пульнули» смарт-контракт, заплатили за него, а он умер в блокчейне, ничего не сделав. Это нехорошо для state-database, и мы размышляем, что бы такого придумать. Возможно, скрипты-«дворники» за небольшую комиссию будут вызывать selfdestruct у контрактов с просроченным TTL. :)

Третья проблема — сложная инфраструктура для отправки контрактов в сеть. Управление таким количеством смарт-контрактов, их деплой, мониторинг требует серьёзной надстройки вокруг Ethereum и остальных блокчейнов. Это и поисковик по блокчейну, и автоматический деплоер, и очереди для управления push«ами для мобильных клиентов, а также управление фондами с большими суммами эфира, адресами и ключами. Всё это весьма серьёзная история с точки зрения и безопасности, и архитектуры.


Итак, мы рассмотрели некоторые особенности смарт-контрактов, описали несколько кейcов и немного порассуждали о проблемах и решениях. Строительство платформы, которая сможет поддержать запуск большого количества различных типов контрактов, не жертвуя при этом безопасностью и децентрализацией, уже идёт — это наш проект Smartz.io. Разработчикам и небольшим командам платформа дает возможность заработка при размещении своих DApp-ов, а простым пользователям она предоставляет удобный способ в несколько кликов запустить и начать использовать один из множества полезных DApp-ов для персонального использования. Будем знакомы. :)

© Habrahabr.ru