Как стать автором
Обновить

Адреса в Ethereum

Уровень сложностиПростой
Время на прочтение9 мин
Количество просмотров12K
Адрес аккаунта с самым большим количеством Ether
Адрес аккаунта с самым большим количеством Ether

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

Итак, приступим!

Адрес

Адрес идентифицирует аккаунт в сети Ethereum и имеет следующий вид:

0x690B9A9E9aa1C9dB991C7721a92d351Db4FaC990

Длина адреса:

160 бит | 20 байт | 40 символов (hexadecimal)

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

Кстати, адрес в примере выше, это реальный адрес из главной сети Ethereum. На момент написания статьи стостояние его аккаунта выглядело так:

Произвольный аккаунт из сети Ethereum
Произвольный аккаунт из сети Ethereum

Перейдём к описанию процесса формирования адреса.

Формирование адреса

Прежде чем приступить к рассмотрению процесса формирования адреса, необходимо уточнить, что в сети Ethereum существуют два типа аккаунта:

  • Externally-owned account (EOA)

  • Contract account

Поскольку назначение этих аккаунтов отличается, то и адреса к ним формируются по-разному. Приведу основные различия этих аккаунтов:

Externally-owned account (EOA)

Contract account

Только EOA обладает приватным ключом. EOA управляется тем, кто владеет приватным ключом.

Не обладает приватным ключом, управляется логикой кода смарт-контракта, который задеплоен по этому адресу.

Инициатором транзакций в сети Ethereum может быть только EOA.

Контракты тоже могут осуществлять транзакции, но первоначальным инициатором всё равно будет EOA.

Создание EOA абсолютно бесплатное.

Списывается пошлина в виде Gas за storage, которое используется контрактом.

Далее мы рассмотрим отдельно как формируется адрес для Externally-owned account и для Contract account.

A. Формирование адреса для EOA

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

Разберём процесс более детально.

Шаг 1. Генерация приватного ключа.

Пример приватного ключа:

0xb533ffad9736e46ef0fbe34018fb0a30572fbe477bcc3a4e641c210ecc3ed8b5

Длина приватного ключа:

256 бит | 32 байта | 64 символа (hexadecimal)

Шаг 2. Генерация публичного ключа.

На основании приватного ключа при помощи алгоритма ECDSA (Elliptic Curve Digital Signature Algorithm) генерируется публичный ключ.

Пример публичного ключа:

0x2e308d25d27f4c595c4a7543253f3c2350c9d323bcd50229c837681f08da6a33b8ab238faecd9cf2a7359e6a69089710876ab136ab017f88505c03516cc099c6

Длина публичного ключа:

512 бит | 64 байта | 128 символов (hexadecimal)

Шаг 3. Вычисляем хэш публичного ключа

Используя хэш-функцию Keccak-256 получаем хэш публичного ключа:

0x0c2be79367ba0c0b59460475b90d65a624909bc36eee6bffdecf3c5acd7774c0

Длина хэша:

256 бит | 32 байта | 64 символа (hexadecimal)

Шаг 4. Берём последние 20 байт от хэша публичного ключа

Отсчитываем последние 40 символов от хэша, который был получен на предыдущем шаге:

0xb90d65a624909bc36eee6bffdecf3c5acd7774c0

Эти 20 байт и будут нашим адресом.

После рассмотрения всех шагов, попробуем сгенерировать адрес самостоятельно.

Практический пример получения адреса для EOA

Для генерации адреса EOA я воспользуюсь библиотекой helpeth. Команда keyGenerate сгенерирует приватный и публичный ключ, а также сам адрес:

$ mkdir ecdsa-keypair
$ cd ecdsa-keypair
$ npm install helpeth
$ ./node_modules/helpeth/helpeth keyGenerate

Вывод:

Address: 0xb90d65a624909bc36eee6bffdecf3c5acd7774c0
Address (checksum): 0xb90d65a624909BC36eee6bFfdeCF3c5ACd7774C0
ICAP: XE06 LM6K C69E YWIP 77CP 2N3X 1ECC H6O2 1MO
Public key: 0x2e308d25d27f4c595c4a7543253f3c2350c9d323bcd50229c837681f08da6a33b8ab238faecd9cf2a7359e6a69089710876ab136ab017f88505c03516cc099c6
Private key: 0xb533ffad9736e46ef0fbe34018fb0a30572fbe477bcc3a4e641c210ecc3ed8b5

Готово. Мы получили как ключи, так и сам адрес. Единственное, вызов команды keyGenerate не отображает хэш публичного ключа, так как хэш является промежуточным шагом для получения адреса. Но для наглядности я покажу как мы можем вычислить хэш самостоятельно командой keccak256 на вход которой мы подадим публичный ключ:

$ ./node_modules/helpeth/helpeth keccak256 0x2e308d25d27f4c595c4a7543253f3c2350c9d323bcd50229c837681f08da6a33b8ab238faecd9cf2a7359e6a69089710876ab136ab017f88505c03516cc099c6

Вывод:

Input data: 0x2e308d25d27f4c595c4a7543253f3c2350c9d323bcd50229c837681f08da6a33b8ab238faecd9cf2a7359e6a69089710876ab136ab017f88505c03516cc099c6
Data hash: 0c2be79367ba0c0b59460475b90d65a624909bc36eee6bffdecf3c5acd7774c0

Видим, что последние 40 символов хэша действительно отражают адрес нашего аккаунта:

b90d65a624909bc36eee6bffdecf3c5acd7774c0

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

Отлично, мы познакомились с получением адреса для Externally-owned account, и теперь пришло время взглянуть на получение адреса для Contract account.

B. Формирование адреса для Contract account

Как было отмечено выше, у Contract account нет приватного ключа, поэтому его адрес формируется из адреса EOA-аккаунта, с которого он был создан, и поля nonce этого аккаунта. Про поле nonce я рассказывал в одной из своих предыдущих статей.

Если изобразить функцию создания адреса на псевдокоде, то она будет выглядеть следующим образом:

contract_address =  keccak_256(rlp_encode(creator_address, nonce))

Разберём детально по шагам, что же здесь происходит:

  1. Берется адреc EOA аккаунта, который отправил транзакцию на создание контракта

  2. Берётся поле nonce из транзакции на создание контракта

  3. Адрес отправителя и nonce сериализуются при помощи RLP-кодека

  4. Вычисляется Keccak-256 хэш от сериализованного значения

  5. Последние 160 бит хэша и будут адресом нашего контракта

Таким образом, адрес одного и того же контракта, повторно созданного с одного и того же аккаунта будет всегда различаться, так как с каждой новой транзакцией поле nonce увеличивается на 1 и меняет хэш адреса-отправителя.

Одно важное уточнение. У созданного аккаунта смарт-контракта тоже есть поле nonce, но в отличие от EOA аккаунта, оно инициализируется единицей, тогда как у EOA поле nonce инициализируется нулём. Изначально у обеих типов аккаунтов поле nonce инициализировалось нулём, но затем были внесены правки.

Помимо адресов, созданных участниками блокчейна, существуют ещё и служебные адреса, которые принадлежат самому блокчейну. Давайте рассмотрим парочку таких адресов.

Служебные адреса

Нулевой адрес 0x0000000000000000000000000000000000000000

Выше я упомянул, что контракт создаётся при помощи транзакции. У любой транзакции должен быть отправитель и получатель, и если с отправителем всё более менее понятно, то не совсем понятен адрес получателя при создании контракта. Если вдуматься, то на момент отправки такой транзакции блокчейн ещё ничего не знает о новом контракте, а соответсвенно в сети нет его адреса. На какой же адрес отправляется эта транзакция? Ответ прост - мы либо вообще не указываем в транзакции поле отправителя, либо явно указываем to: null

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

Из ранней документации Solidity:

If the target account is the zero-account (the account with the address 0), the transaction creates a new contract.

После версии v0.5.0 Solidity:

If the target account is not set (the transaction does not have a recipient or the recipient is set to null), the transaction creates a new contract.

В официальной спецификации Ethereum, так называемой Yellow Paper, просто говорится про RLP empty byte sequence:

The address hash Tt is slightly different: it is either a 20-byte address hash or, in the case of being a contract creation transaction (and thus formally equal to ∅), it is the RLP empty byte sequence and thus the member of B0

Примечательно, что Gavin Wood, который собственно и написал Yellow Paper, в своей книге Mastering Ethereum указал адрес создания контракта как 0x0:

Contract creation transactions are sent to a special destination address called the zero address; the to field in a contract registration transaction contains the address 0x0.

После обсуждений со @splix я решил самостоятельно вручную создать тело транзакции с контрактом в поле data и отправить её сначала на адрес со всеми нулями, а затем на адрес null. Так вот контракт создался именно когда в поле to был null, или поля to вообще не было. В случае с нулями, транзакция просто уйдёт на нулевой адрес, а контракт не будет создан. Если в поле value при создании контракта ещё и были Ether, то они просто пополнят баланс нулевого адреса.

Вот так выглядит нулевой адрес:

Нулевой адрес
Нулевой адрес

Если присмотреться, то можно обнаружить, что на нулевом адресе лежит довольно крупная сумма Ether. На момент написания статьи, сумма была эквивалентна 21 миллионам долларов США. Откуда на нулевом адреса эти средства? Как ни странно, это навсегда замороженные средства, которые были намеренно или по ошибке отправлены на нулевой адрес. Ошибиться мог как человек, забывший указать поле адреса-получателя, и оно было интерпретировано как нулевой адрес, так и программист, который неверно инициализировал адрес получателя. Выше мы и сами видели один из примеров ошибочной отправки Ether на нулевой адрес при попытке создания смарт-контракта. Почитать подробнее про причину появления средств на нулевом адресе можно здесь и здесь. Так же нулевой адрес можно использовать и для намеренного уничтожения Ether, и таких адресов достаточно много.

Адреса для уничтожения Ether

Если вы немерено решили уничтожить свои средства, то для этого в сети Ethereum существуют специальные Burn адреса. Судя по их капитализации, на данный момент основным адресом для этих целей выступает dEaD адрес.

Burn адрес. Адрес для уничтожения Ether.
Burn адрес. Адрес для уничтожения Ether.

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

Кстати, у нулевого и dEaD аккаунтов, а так же у других Burn адресов, нет приватного ключа, поэтому никто не имеет к ним доступ. Если же вам всё же удастся подобрать приватный ключ к ним, то технически вам ничто не мешает распоряжаться средствами на этих аккаунтах.

В заключение я бы хотел коснуться хэш-функции Keccak-256 в Ethereum.

Keccak-256 vs SHA-3

Стандарт SHA-3 реализован по алгоритму хэширования Keccak-256. Но между реализацией алгоритма Keccak-256 в стандарте SHA-3 и в сети Ethereum есть различия.

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

Чтобы точно определить какая перед вами реализация, есть эталонные хэши от пустой строки:

Ethereum Keccak256("") =
  c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470

SHA3("") =
  a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a

Почему команда Ethereum решила написать свою реализацию алгоритма Keccak-256? Для этого обратимся к истории создания этих двух реализаций.

Алгоритм хэширования Keccak-256 стал победителем среди алгоритмов претендующих на стандарт семейства Secure Hash Algorithm (SHA), и был стандартизован как FIPS-202 SHA-3 standard в 2015 году. Уже после завершения процесса стандартизации, и перед самым опубликованием стандарта, National Institute of Standards and Technology (NIST) решил скорректировать некоторые параметры алгоритма, предположительно для улучшения его эффективности. 

Всё это произошло в то же самое время, когда Эдвард Сноуден обнародовал документы, которые пролили свет на тот факт, что National Security Agency (NSA) могло повлиять на NIST, чтобы намеренно ослабить стандарт генератора случайных чисел Dual_EC_DRBG, фактически поместив бэкдор в стандартный генератор случайных чисел.

После огласки, выход реализации стандарта SHA-3 пришлось отложить. Ethereum в этот момент был ещё на стадии разработки. Чтобы не останавливать разработку, а так же иметь более прозрачную реализацию алгоритма, команда Ethereum решила самостоятельно реализовать оригинальный алгоритм Keccak-256.

Более подробно про SHA3 и Keccak-256 можно почитать здесь.

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

Предыдущие части

UPD:

В статье была допущена ошибка при описании адреса создания контракта. Статья была обновлена. Спасибо пользователю @splix за то что обратил на это внимание.

Теги:
Хабы:
Всего голосов 6: ↑6 и ↓0+6
Комментарии8

Публикации

Работа

Ближайшие события