Blog ⟩ Крипто-кошельки и seed-фраза

Крипто-кошельки и seed-фраза

Если вы хоть раз пользовались крипто-кошельком, то скорее всего помните, что для "регистрации" вас просили запомнить 12 (или 24) случайных слов, потеряв которые вы не сможете восстановить доступ к своему кошельку. Эти слова называются seed-фразой.

Если же вы пользовались кошельком, который поддерживает несколько валют (например, Bitcoin, Ethereum и какой-нибудь Dogecoin), то вы могли задуматься, как же так получается, что seed-фраза одна, а кошельков несколько, причем адреса у них все разные. Давайте, расскажу как это работает.

 

Зачем нужна seed-фраза?

Тут стоит напомнить, что блокчейн - это децентрализованная штука, что означает, что нет какой-то одной базы данных (читай - сайта), которая может сказать, что пользователь с именем Х существует. Ну и думаю понятно, что нет такой организации, которая говорит Васе Пупкину, что его биткоин адрес теперь 123.

Данные проблемы решаются генератором случайных чисел: создается длинное число, состоящее из ±77 случайных цифр (256 бит или 64 hex цифры, если кому-то интересны подробности). Это число называется приватным ключом. По сути, это ваш пароль от кошелька, с помощью которого вы отправляете деньги (токены) другим людям. А если вы теряете его, то теряете и доступ к своим крипто-миллиардам.

Из приватного ключа можно получить публичный ключ (этот ключ вы говорите другим людям, чтобы получать от них деньги, типа как номер карты), причем, только один. А вот из публичного ключа получить приватный нельзя.

Забавный момент, вы можете создать собственный приватный ключ, не используя компьютер: берете листок бумаги, карандаш и начинаете писать 0x, а затем 64 случайных символа из следующего набора: цифры от 0 до 9, буквы латинского алфавита от a до f. Вуаля, ваш приватный ключ готов.

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

Как я уже писал в самом начале, seed-фраза - это 12 (или 24) случайных слова, которые можно, например, выучить наизусть и нигде не записывать. Тогда знать эти слова будете только вы. Если совсем упростить, то seed-фраза определенным образом преобразуется в приватный ключ, причем так, что из одной и той же seed-фразы всегда будет получаться один и тот же приватный ключ.

У самых внимательных сейчас должен был возникнуть вопрос.

Если из seed-фразы мы можем получить только один приватный ключ, то как же так происходит, что используя одну фразу, почему мы видим разные адреса у разных монет в кошельке?

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

Представим, что мы не знаем про упомянутый стандарт, но нам нужно иметь несколько адресов, но с одной seed-фразой. Как это можно сделать? Ну, например, добавить номер кошелька (первый, второй и т.д.) из уже полученную seed-фразу превращать в приватный ключ. Т.е. алгоритм будет примерно таким:

seed_phrase = "shove question submit peasant idle patch model bracket friend blanket chef pepper"
private_key_1 = get_private_key_from("shove question submit peasant idle patch model bracket friend blanket chef pepper 1")
private_key_2 = get_private_key_from("shove question submit peasant idle patch model bracket friend blanket chef pepper 2")
...
private_key_100 = get_private_key_from("shove question submit peasant idle patch model bracket friend blanket chef pepper 100")

Супер, одну проблему мы решили, но как нам понять, для какого блокчейна какой ключ используется (например, какой из ключей от нашего Bitcoin-адреса, а какой от Dogecoin?). Давайте тогда еще записывать еще и название блокчейна рядом с номером:

seed_phrase = "shove question submit peasant idle patch model bracket friend blanket chef pepper"
# сначала создаем 100 биткоин-адресов
private_key_bitcoin_1 = get_private_key_from("shove question submit peasant idle patch model bracket friend blanket chef pepper bitcoin 1")
private_key_bitcoin_2 = get_private_key_from("shove question submit peasant idle patch model bracket friend blanket chef pepper bitcoin 2")
...
private_key_bitcoin_100 = get_private_key_from("shove question submit peasant idle patch model bracket friend blanket chef pepper bitcoin 100")

# а теперь создаем 1 догекоин-адрес
private_key_dogecoin_1 = get_private_key_from("shove question submit peasant idle patch model bracket friend blanket chef pepper dogecoin 1")

Отлично, с этой проблемой мы тоже справились. Но какие минусы есть у такого решения? Разработчики одного приложения могут писать название блокчейнов в нижнем регистре, другого в верхнем и тогда, если первое приложение перестанет работать, то при попытке использовать нашу seed-фразу во втором, мы получим другой набор кошельков (т.к. регистр имеет значение). Значит, все разработчики должны договориться и следовать какому-то стандарту.

 

Так что там в стандарте, в итоге?

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

Строка, которая добавляется к seed-фразе, выглядит следующим образом:

m / 44' / coin_type' / account' / change / address_index

где

m - так и остается m, типа разделитель

44 - это номер стандарта (BIP44), если появится новый стандарт, то будет что-то другое

coin_type - это номер криптовалюты. Например, биткоин - 0, Ethereum - 60 и т.д. полный список тут

account - это просто уникальный номер аккаунта. Обычно используется 0, но если вдруг хотите, можно из одной сид-фразы сделать аж 10 аккаунтов

change - либо 0, либо 1. Чаще всего 1 используется в Bitcoin-блокчейне, а для остальных валют 0. Могу отдельно написать пост о том, как работает биткоин, если будет интересно.

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

 

И что же получается

Допустим, мы разработали приложение, которое поддерживает Bitcoin, Ethereum, Polygon и Dogecoin. Получая от пользователя всего одну seed-фразу, мы можем создать из нее минимум 4 приватных: по одному на каждую поддерживаемую валюту. Причем, если этот пользователь решит уйти к нашим конкурентам и введет свою seed-фразу у них, то конкуренты смогут создать ровно те же приватные ключи. А значит, пользователь не потеряет доступ к своим деньгам.

А если вы разработчик, вы понимаете, насколько красивым получается решение: если вам нужно создавать много кошельков (например, для магазина) и хранить их в базе данных, то достаточно один раз сохранить seed-фразу, а потом, используя уже имеющуюся у вас информацию (порядковый номер кошелька), получать адреса. Т.е. в базе данных не будет избыточной информации. Красиво же?