Как мы делали первую сделку-аккредитив на блокчейн в Альфа-Банке
Думаю многие слышали или читали про блокчейн — вокруг технологии много хайпа и как обычно до нас это всё дошло с некоторым запозданием. Но всё таки дошло и теперь многие хотят, чтобы в их продуктах был блокчейн. Возможно мощный маркетинг приведёт к «зиме» в ещё одной технологии, а возможно мы все окажемся в одном большом блокчейне. Давайте всё таки разбираться с технологией и сделаем это на примере продукта Аккредитивы, который мы создали.
Так выглядит наш интерфейс:
Технически мы сделали:
- Фронт-приложение для клиентов
- Фронт-приложение для сотрудника банка
- API для взаимодействия с внутрибанковскими сервисами, сохранения документов в БД и взаимодействия с Ethereum
- Контракты для создания аккредитивов и осуществления операций над ними
- Инфраструктурные приседания по минимуму
Всё это на js: react, redux, node. Контракты на solidity. Приложения в Docker-контейнерах, а клиент для сети Ethereum — Parity взяли отсюда. Кстати, подняли мы его на отдельной машине, так как он достаточно активно потребляет ресурсы, но как оказалось впоследствии — с этим мы добавили себе ещё кучу проблем. В целом создание приложений и возня с инфраструктурой заняли гораздо больше времени, чем работа над контрактами. Но про фронты и инфраструктуру статей достаточно, поэтому поговорим о контрактах.
Несмотря на хайп, самым частым вопросом остаётся:, а зачем вам блокчейн и смарт-контракты? Многие видят потенциал в технологии, и ещё больше людей настроены скептически, нам же просто интересно. Но есть проблема — найти бизнес-кейс для того, чтобы применить блокчейн и не притягивать за уши. Поэтому мы пошли простым путём и вдохновились идеей Barclays, которые первыми в мире сделали Аккредитивы на блокчейне вместе с компанией Wave. К тому же продукт, который мы сделали нужен был и без блокчейна, а тут ещё получилось и заняться некоторым исследованием.
Как же можно применять эту технологию? Хорошо, когда вы можете использовать одну из доступных криптовалют, чтобы производить денежный оборот, но, как известно, у нас законодательно запрещена работа с криптовалютами, даже если это не будет фактически валютой, а, например, color coin, поэтому никакого эквивалента реальным деньгам мы не переносили внутрь сети. Следующая идея — использовать блокчейн как «неизменную» базу данных (про неизменность и просто вспомнить), состояние которой валидируют и сохраняют участники сети, тогда можно создать зависимость ваших бизнес-операций от некоторого состояния внутри блокчейна и использовать его как ещё один регулятор, как бы иронично это ни звучало. Плюсом добавления в вашу систему такого «переключателя» является открытость и доступность его состояния, любой, кто может скачать и запустить клиент сети, с которой работает ваше приложение, может узнать, в каком состоянии находятся сейчас «переключатели» при условии того, что этот человек знает, как их найти с помощью необходимых идентификаторов. Соответственно, чем больше логики перейдет на блокчейн, тем более открытым будет процесс для клиентов, и тем ближе будет ваше приложение к концепции DAPP. Теперь очередь подходит к тому, чтобы выбрать, какую же сеть использовать, а их достаточно много, можно почитать анализ полугодовой давности о некоторых популярных из них. Говоря о популярных — многие слышали про биткойн, но не все знают о том, что в нём есть скриптовый язык, и некоторые концепции вполне реализуются с помощью него. Тем не менее, появляются новые криптовалюты, сосредоточенные на том чтобы решить проблемы биткоина, и одна из таких — Ethereum. Решение использовать Ethereum было принято не из-за наличия смарт-контрактов, а из-за совокупности улучшений, описанных в White-Paper.
Но перед тем, как погрузиться в написание нужной нам логики на стороне Ethereum, посмотрим на бизнес-процесс. Во время сделки был открыт и исполнен так называемый безотзывный покрытый аккредитив. За этими непонятными терминами скрывается достаточно простая процедура, в которой участвуют 3 стороны: покупатель, продавец и банк:
Покупатель услуг заполняет анкету на открытие аккредитива, в которой указывает реквизиты Продавца и условия операции: сумму, сроки и требуемые документы.
Банк принимает эту заявку и, если все проверки успешно пройдены, открывает аккредитив. Тут же снимает с Покупателя указанную им сумму. Теперь очередь Продавца. Он должен отправить банку те документы, которые от него ждет Покупатель. Эти документы являются доказательством того, что он исполнил свои услуги. Например, счёт-фактуру, которая удостоверяет фактическую отгрузку товаров. Банк проверяет эти документы. Если все хорошо, то аккредитив исполняется, деньги переводятся на счет Продавцу. В итоге все довольны, в том числе банк, который берет свою комиссию за все эти манипуляции.
В нашем случае был не самый сложный аккредитив — все операции проводились в пределах одного банка, так как и покупатель, и продавец были нашими клиентами. На данном этапе частичное исполнение аккредитива реализовывать не нужно было, поэтому сам процесс получился достаточно простым.
Что же такое смарт-контракты Ethereum? Это просто программы, исполняемые EVM (Ethereum Virtual Machine). Контракты могут писаться на нескольких языках, например, на solidity. Все поддерживаемые языки тьюринг-полные, но это не значит, что вы можете всё сделать на EVM. Исполнение вашего кода должно контролироваться, т.к. его будут исполнять другие участники сети, поэтому за исполнение вы будете платить деньги — wei (деноминация ether). Чтобы код, написанный в вашем контракте, начал исполняться, вам нужно создать транзакцию. Эти транзакции передаются майнерам — тем участникам сети, которые занимаются её поддержкой. Они добавляют новые блоки с валидными транзакциями в блокчейн (объяснение намеренно упрощено, подробнее можно почитать тут и тут). Если в качестве адресата транзакции указан смарт-контракт, то майнер исполняет код функции, которая вызывается в транзакции, это стоит вычислительных ресурсов майнера, их использование вы должны оплатить. Создание смарт-контракта это тоже транзакция, но с пустым адресатом, сохранение контракта в следующее состояние сети потребует ресурсов, которые тоже нужно оплатить. Чтобы сделать процесс оплаты более открытым (превратить его в маркетплэйс), введена концепция внутрисетевого ресурса — газа (вам надо будет много газа, так что вы, видимо, протосс). Газ не является деноминацией эфира, но у любых вычислительных операций и у сохранения данных есть ценник в единицах газа. В транзакции, которую вы хотите сделать, указывается число startgas — сколько газа будет стоить её исполнить, а стоимость одной единицы газа (gasprice) вы назначаете сами, поэтому, указывая более высокий gasprice, вы повышаете шанс включения вашей транзакции в блок, хотя стоит сказать, что большинство майнеров не отбирают транзакции, значит, можно оставлять дефолтное значение. Посчитать стоимость вашего контракта можно в web-редакторе.
Кажется, чтобы вообще что-то cделать, нужно платить, но если у вас нету друга, который вам подарил эфир на новый год, то можно настроить клиент на приватный чейн, там можно вводить читкоды на миллионы эфиров и тестировать свои творения. Вообще так лучше всегда делать.
У Parity есть возможность запуска с приватным чейном. Хотя есть способ проще — воспользоваться симулятором. Также есть фрэймворки, упрощающие написание смарт-контрактов и приложений, взаимодействующих с ними, например, truffle или embark. Особых предпочтений у меня нет, но на этом проекте я больше работал с embark. Хотя truffle позволяет компилировать контракты — создавать js-обвязку над вызывом функций контрактов, ориентирован на TDD и по дефолту создает проект метакоин (как сделать свою валюту внутри Ethereum), поэтому, чтобы начать разбираться, я бы порекомендовал его.
Вроде бы всё просто, бери фрэймворк и в продакшн, но вы же понимаете, что цена ошибки в контракте может стоить очень дорого, например, первый хардфорк Ethereum, когда проблема была в контрактах написанных для DAO, об этой проблеме говорили на этапе crowdsale DAO, но в итоге всё это вылилось в хардфорк и создание Ethereum Classic (очень обще — взлом DAO убрали костылём, но много людей было за вариант: накосячили — их вина, платформа не должна спасать тех, кто накосячил, если платформа выполняет свои функции правильно), советую почитать подробнее. Поэтому прежде чем переводить логику ваших приложений внутрь децентрализованной сети, имеет смысл почитать гайд.
Зависимость от блокчейна мы добавили после второго и четвертого пункта в бизнес-процессе. После подтверждения аккредитива сотрудником банка в первом контракте создаётся объект аккредитива.
struct LetterOfCredit {
bool init;
bytes32 hash;
}
Эти структуры надо где-то хранить, желательно в чем-то типа key-value store, в solidity есть:
mapping (bytes32 => LetterOfCredit) lcs;
Теперь по 32-байтному ключу мы можем получить доступ к объекту аккредитива или создать его. В нашем случае ключом будет md5 от трёх полей — ИНН покупателя + ИНН продавца + Название и номер договора (да, это одно поле). Насколько вы помните, md5 — 128 битная хэш функция, значит, digest — 16 байт, если представить в виде символов — используем шестнадцатеричное исчисление, соответственно, один символ кодирует 2^4 — полбайта (называется ниббл), 16 байт превращаются в 32 ниббла, в 32 символа, при передаче этих символов в виде строчки из стороннего клиента логично предположить, что кодироваться они будут одним байтом, значит, 32 байта (пуф 16 байт стали 32). Т.к. мы сохраняем работу с нашими контрактами из клиентов с web-view, то приходится мириться с тем, что они там будут вводить именно строчку, поэтому md5. С точки зрения защиты данных md5 разумеется не стоит использовать, повысить устойчивость к атакам можно добавив дополнительный слой хэширования перед md5 и использовать например SHA-3.
Так выглядят функции первого контракта, если подключиться к нему через клиент-сети Parity (это функции, не меняющие состояние блокчейна — соответственно они бесплатные)
Что делают эти функции, из названия должно быть понятно. Второе поле в функции checkData — хэш от даты создания аккредитива + дата закрытия + сумма.
Дальше нам нужно реализовать закрытие аккредитива и можно всё сделать внутри этого же контракта, но т.к. в дальнейшем есть планы по развитию и на следующих этапах будет целесообразно использовать некоторое множество контрактов — мы решили делать закрытие в другом контракте, поэтому нам нужно как-то передать объект, для этого есть opcode call, например:
nextContract.call(bytes4(sha3("create(bytes32,bytes32)")), id, lcs[id].hash);
Из этой строчки становится понятно, что мы просто вызываем функцию create с определёнными параметрами у nextContract. Теперь, чтобы возможность вызывать функцию create осталась только у первого контракта, нужно сделать ограничение доступа к определённым функциям, это можно решить, добавив модификатор
modifier restricted() {
if (msg.sender == owner) _;
}
или отдельный контракт, от которого можно наследоваться:
contract Owned {
function Owned() { owner = msg.sender; }
address owner;
modifier restricted {
if (msg.sender != owner)
throw;
_;
}
}
где address owner будет инициализирован при создании контракта вашим адресом.
Теперь второй контракт можно создавать из первого контракта (используя просто new NextContract ()), пометив нужные функции второго контракта модификатором restricted, потому что в таком случае msg.sender будет первый контракт, а не ваш аккаунт, который оплачивает транзакцию, его адрес будет в tx.origin (пример контрактов). Так происходит потому, что вызов одного контракта другим происходит с помощью сообщений, при этом всё выполнение оплачивается из изначального газа в транзакции.
Также вы можете создать функцию, позволяющую изменять адреса следующих контрактов в цепи, тем самым изменяя логику вашего бизнес процесса.
Когда аккредитив попадает во второй контракт, в его структуру добавляется поле статус, и по умолчанию он будет открытый.
После того как продавец предоставит соответствующие документы, которые проверит сотрудник банка, во втором контракте будет вызвана функция закрытия аккредитива, доступ к которой ограничен другим модификатором, в данном случае это не первый контракт, а только аккаунт (адрес), у которого есть права для закрытия аккредитива. Если на вход функции закрытия переданы существующий во втором контракте id и полученный с помощью mapping аккредитив содержит поле hash, идентичное переданному на вход в функцию, то произойдет закрытие аккредитива, т.е. значение поля «статус» в структуре аккредитива поменяется.
Посмотрим на это через Parity, подсоединенному ко второму контракту:
Здесь помимо функций видны результаты их работы — под полем retVal
Все эти операции осуществляются тремя транзакциями:
- txCreate — создание аккредитива
- txForSend — посылка его во второй контракт для закрытия
- txClose — закрытие аккредитива
Как это выглядит для пользователя:
(транзакции со скриншота на приватном чейне)
Посмотреть паблик транзакции можно, на специальном поисковике (etherscan). Наши контракты на etherscan: первый и второй.
Как вы понимаете, сейчас в смарт-контрактах реализовано просто сохранение хэшей от некоторых полей аккредитива, его статус и проверка информации при закрытии. Да, у нас было больше идей того, что можно перенести на сторону блокчейна, и, возможно, в дальнейшем их получится реализовать. В данном проекте мы большую часть времени потратили на создание приложений: фронт-энд и апи. Хотя есть мнение, что так происходит во многих проектах, связанных с блокчейном.
Если вы добрались до запуска вашего проекта с блокчейном и будете поднимать свой клиент для сети, то вы можете столкнуться с проблемами из-за неточного времени, например, вы не сможете зайти в веб-интерфейс Parity. Поэтому советую вам сразу настроить синхронизацию времени по NTP на сервере, где у вас будет запущен Parity, своё время можно проверить с помощью часиков, если у вас не точное, то тоже синхронизируйтесь.
Если у вас микросервисная архитектура и приложение, требующее соединения с клиентом сети, находится не на том же сервере, на котором клиент, то на запросы вашего приложения будут возвращаться ошибки. Потому что по умолчанию Parity (клиент) слушает только localhost. Мы решили эту проблему, поставив перед ним Nginx, чтобы правильно спроксировать запросы и дополнительно обойти CORS:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Origin http://127.0.0.1:8080;
}
это должно вам сэкономить немного нервов и времени (иные способы победить эту проблему с помощью параметров запуска у нас не заработали).
И всё таки зачем вам может понадобиться блокчейн? В идеале если вам нужно создать систему обмена информацией между участниками, независимую от уровня доверия между ними, то это то что вам нужно. Такая задача становится выполнимой потому что у каждого клиента сети есть связка приватный/публичный ключ, следовательно реализуется цифровая подпись, например: транзакция подписывается приватным ключом, аккаунта, который её создал. Как я понимаю, КЭП (квалифицированная электронная цифровая подпись), который имеет юридическую ценность, это обычная цифровая подпись, в брелке приватный ключ, а в реестре, куда вы принесли свой паспорт — публичный, подписывают приватным ключом хэш от ваших документов. В Ethereum можно просто создать транзакцию с хэшом в поле «дата», которая будет подписана вашим приватным ключом следовательно можно будет проверить, что транзакция была создана вами. Получается, у вас есть механизм авторизации, такой же, как в КЭП, и всё? Не совсем, когда все участники сделки авторизованы, проблемой может стать исполнение алгоритма самой сделки, гарантом обычно выступает учреждение, через которое вы собираетесь осуществить сделку, вот тут проведение сделок через платформу для смарт-контрактов начинает выигрывать. Потому что логика, прописанная в смарт-контрактах, не меняется после того, как они были созданы и находится в открытом доступе, а исполнение его гарантируют все участники сети, которые майнят блоки. Как это применить? Например, вы можете просто перенести некоторую часть ваших процессов внутрь сети, если между участниками этих процессов нету доверия. Также в такой системе можно создавать децентрализованные сообщества с общим капиталом, где предложения по использованию ресурсов будут решаться кворумом участников. Сейчас создание продуктов, использующих блокчейн, ещё развивается как направление, поэтому будьте готовы к тому, что best-practices будут меняться.
Итак, easy level использования блокчейн — вы хотите делать записи об операциях в вашем бизнес-процессе во внешнем ресурсе, чтобы потом можно было показать транзакции об этих операциях и обратиться к смарт-контрактам, которые вернут формальное подтверждение произведённым операциям (мы на easy).
Medium level — вы авторизуете всех участников сделки и сохраняете информацию о созданных ими документах (вы, наверное, уже догадались, что речь идёт о хэшах) в блокчейн, с помощью смарт-контрактов. Так как вы делаете это через транзакцию от имени клиентов, транзакция подписана приватным ключом, проверяется через паблик-ключ — вы получаете подписанный электронный документ (аналог КЭП), соответственно, все дальнейшие операции с этими документами будут также подписаны и вы можете создать аналог электронного документооборота в рамках сети, разумеется, пересылать сами документы внутри блокчейна будет достаточно дорогостоящей операцией, поэтому целесообразно использовать хэши, с помощью которых следует проверять электронный документ переданный другим способом.
Формальные критерии проведения сделок лучше реализовать в отдельных контрактах, в данном случае это уже можно сделать, т.к. все элементы сделки есть внутри блокчейна. Вы получаете платформу для создания и проведения сделок, где все операции подписаны и схемы проведения сделок неизменны. Конечно, не всё так идеально, в этой концепции есть много вопросов, требующих ответа, и многие из них не технические, а юридические.
Fun level — есть достаточно много статей, рассматривающих потенциал технологии и то, что можно сделать с помощью неё. Сейчас есть определенный интерес к технологии, поэтому стартапы в сфере, связанной с блокчейн, могут сыскать необходимые инвестиции.
В заключение:
Основной проблемой применения блокчейна внутри проектов остается юридическая сторона решения. Возможно, с поддержкой Германа Грефа в 2017 году что-то изменится, но в данный момент использование технологии сильно ограничено. В подобных проектах юристы необходимы с самого начала. Сейчас еще нет четких правил регуляции криптовалюты в РФ, поэтому наличие такого человека с момента закладывания основ и брейншторма воздушных замков сильно уменьшает риск потенциальных проблем. Блокчейн-часть — это бэкэнд вашего проекта, поэтому можете начинать писать ваше децентрализованное приложение с фронта, вы там всё равно проведёте большую часть времени.
У нас получилось создать простое решение, сохраняющее результаты своей деятельности в блокчейне, это далеко от того, как можно в принципе использовать технологию, но мы только в самом начале.
Сейчас есть много гипотез и идей, как можно использовать блокчейн. Что из этого удастся реализовать в соответствии с законодательством РФ? На этот вопрос можно будет ответить только после принятия большего количества законов, регулирующих использование блокчейна.
Что ещё почитать:
Writing more robust smart contracts
Understanding oracles
Ещё про Ethereum:
Yellow paper
Ethereum trie