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

saveGas_2

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

Благодаря этому твиту Джона Бакуса, я смог воплотить такое решение в своём приложении для голосования.

Скорее всего, в будущем dApp на платформе Эфириум перейдут к модели, в которой пользователи подписывают блоки данных с помощью закрытых ключей, не связанных с финансовыми счетами. Операторы приложений будут передавать транзакции в блокчейн вместе с подписями, подтверждающими согласие пользователей.
Пусть компании платят за газ, разбираются с nonce, повторно отправляют неудавшиеся транзакции и т. п.

Рабочий код приложения находится в этом репозитории, а демоверсия – на этой странице.

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

  1. криптография с открытым ключом и цифровые подписи (очень общий обзор на уровне, необходимом для понимания моего решения);
  2. подробное описание идеи и новая структура приложения;
  3. технические детали реализации (JavaScript во фронтенде и Solidity в контрактах);
  4. возможные проблемы и доработки.

Цифровые подписи

Для того чтобы понять суть этого решения, нужно иметь представление о том, как устроены цифровые подписи в криптографии. Если вы разбираетесь в криптографии с открытым ключом, можете смело переходить к следующему разделу. Здесь я постараюсь дать очень общее объяснение понятиям открытого и закрытого ключа и цифровой подписи, но настоятельно рекомендую изучить их подробнее самостоятельно. Хорошей отправной точкой могут послужить статьи в Википедии.

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

Алгоритмы в криптографических системах с открытым ключом позволяют зашифровывать, расшифровывать, подписывать и подтверждать сообщения с помощью персональной пары ключей.

Давайте рассмотрим на примере, как именно происходит подпись и подтверждение сообщения. Предположим, у Ким есть пара ключей, открытый (Pu) и закрытый (Pr):

Pu = “0x44ac12c1e3dfd8edaf83b6f65918229d5279a6f5”

Pr = “dbc226043e390cf39280e5edfd418d7ad61931c76509270867d300f110c46506”

Чтобы подписать сообщение, Ким должна выполнить некоторую функцию sign(“Vote for Alice”, Pr), которая возвращает строку из букв и цифр:

signature = 0x9127112de0033555c7f6508d963d484965a953844dfcff092712102c236467a25af57edc53b63880ea39af8ce7334f6d77a8206e805305e7c6ad919d12bfae5c1b

Эта строка является цифровой подписью сообщения «Vote for Alice» («Голосовать за Элис»), которое Ким подписала с помощью своего закрытого ключа.

Теперь любой пользователь сможет подтвердить, что Ким подписала сообщение «Vote for Alice», выполнив функцию подтверждения: verify(«Vote for Alice», signature). Эта функция возвращает значение «0x44ac12c1e3dfd8edaf83b6f65918229d5279a6f5». Заметьте, что полученное значение соответствует открытому ключу Ким (а, как мы помним, он известен всем пользователям). Это доказывает, что Ким действительно подписала сообщение. Если кто-то подменит хотя бы один символ в подписи или сообщении, алгоритм подтверждения возвратит совершенно иной открытый ключ. Вмешательство будет очевидно, поскольку этот ключ не совпадёт с Pu.

Описание решения

Если вы понимаете, как работают цифровые подписи, то решение покажется вам совершенно элементарным. Давайте рассмотрим, как его можно применить в приложении для голосования таким образом, чтобы пользователи не платили комиссии, а их голоса оставались защищёнными. На схеме ниже изображены пользователи dApp и их действия.

saveGas_2

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

Детали реализации

Теперь давайте рассмотрим конкретные вопросы реализации и работы системы в целом.

Шаг 1: подпись сообщения

Первый шаг – это подпись сообщения избирателем. Для этого используется функция eth_signTypedData. В Metamask уже предложили реализацию этой функции, благодаря чему её удобно использовать для подписи сообщений. Более подробную информацию и обсуждение этого предложения можно найти на Github. Код для подписи сообщения представлен ниже.

Важно отметить, что eth_signTypedData хеширует сообщение, и пользователь подписывает именно хешированное сообщение. За подробностями об алгоритме хеширования можете обратиться к коду функции typedSignatureHash.

Шаг 2: отправка подписанного голоса в блокчейне

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

Шаг 3: подтверждение данных о голосе в смарт-контракте

На этом этапе мы можем подтвердить подлинность данных и зафиксировать голос.

Для подтверждения подписанного сообщения я использую удобную библиотеку ECRecovery, разработанную командой Zeppelin. Функция voteForCandidate проверяет подписанное сообщение с помощью метода recover и обновляет счётчик голосов в случае успешного подтверждения.

Как я отметил ранее, функция eth_signTypedData хеширует сообщение (“Vote for Alice”) перед тем, как подписать его. Функция recover, встроенная в Solidity, не обладает знанием об алгоритме хеширования внутри функции eth_signTypedData, а значит, она не может подтвердить сообщение “Vote for Alice”. Сначала она должна вычислить хеш сообщения, и только затем подтвердить его. Вместо того чтобы вычислять хеши внутри контракта, мы заранее сгенерируем их для всех сообщений и передадим их в конструктор, что облегчит их поиск в момент подтверждения. Код для вычисления хеша находится в файле миграции:

Представленные скрипты – это всё, что вам нужно для запуска нового приложения.

Чтобы продемонстрировать, как работает такое приложение, я создал небольшую демоверсию, весь код которой выложен в моём репозитории.

Сама демоверсия находится по этому адресу.

Возможные проблемы

При разработке полноценного приложения по такой методике придётся решить некоторые дополнительные вопросы. В частности:

  1. Где будут храниться подписанные сообщения? Для этого можно использовать некий механизм очереди.
  2. Как обеспечить гарантию того, что подписанное сообщение в итоге будет записано в блокчейн?
  3. Хранить хеши всех сообщений в блокчейне нецелесообразно. Как решить задачу хранения оптимальным образом?

Примечание: судя по всему, API eth_signTypedData до сих пор находится в процессе разработки и пока реализован лишь в Metamask. Пожалуйста, имейте это в виду, если планируете использовать эту методику в основной сети или в готовом продукте.

Дополнительная информация:
Криптосистема с открытым ключом
Электронная подпись
https://github.com/danfinlay/js-eth-personal-sign-examples/
https://danfinlay.github.io/js-eth-personal-sign-examples/
https://github.com/ethereum/EIPs/pull/712

Источник



Рубрики:DApps, Ethereum, смарт-контракты, эфир

Метки: , , , ,

1 reply

  1. А не является ли это уходом от децентрализации к централизации? 🤔

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход /  Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход /  Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход /  Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход /  Изменить )

Connecting to %s