Bitcoin Forum
April 26, 2024, 11:43:12 AM *
News: Latest Bitcoin Core release: 27.0 [Torrent]
 
   Home   Help Search Login Register More  
Pages: [1]
  Print  
Author Topic: webtricks: SEED и получение адресов биткойна  (Read 385 times)
This is a self-moderated topic. If you do not want to be moderated by the person who started this topic, create a new topic. (1 post by 1+ user deleted.)
XEOP$ (OP)
Full Member
***
Offline Offline

Activity: 126
Merit: 171


In Crypto We Trust


View Profile
February 22, 2021, 04:49:21 PM
Last edit: March 07, 2021, 02:14:13 PM by XEOP$
Merited by klarki (6), Symmetrick (5), webtricks (4), xenon131 (4), diks (1), Julien_Olynpic (1), soliton (1), Captain-Cryptory (1)
 #1

 Замечу, что webtricks иногда вносит правки в свою статью, которые будут отслеживаться мною в этой теме.




Обзор работы

 Автор: webtricks



Рассматриваемая  работа под авторством  webtricks   посвящена иерархически детерминированным кошелькам биткойна.

Ее автор напоминает всем, что  первые кошельки для биткойна можно  было представлять себе как  группу приватных ключей. Отсюда следовало то , что при  генерации новых ключей пользователю приходилось сталкиваться с громоздким и  обременительным процессом, подразумевающим  под собой создание  резервной копии  каждого приватного ключа.  С появленим иерархически детерминированых (HD) кошельков этот процесс упростился.  Детерминированный кошелек подразумевает создание всех его адресов из  единой отправной точки –единой начальной фразы , которую принято обозначать как SEED фраза или просто SEED. webtricks отмечает, что на сегодняшний день детерминированные адреса создаются  в более чем 95% некастодиальных кошельков, подчеркивая при этом, что если в некоторых кошельках это не так, то такими кошельками пользоваться не стоит.

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

9 частей, на которые разбито содержание  статьи:

1. Генерация случайной последовательности или энтропии.
2. Получение контрольной суммы  и сборка выходной последовательности (выходного вектора).
3. Преобразование выходного вектора в мнемонические коды.
4. PBKDF2 или функция удлинения ключа.
5. Приватный/секретный Мастер-Ключ, Публичный/открытый  Мастер-ключ и Кодирующая Цепочка (Chain Code).
6. Путь деривации/порождения (Derivation Path) и BIP-44.
7. Порождение Дочернего Приватного Ключа (Child Private Key Derivation).
8. Создание биткойн-адресов из приватных ключей.
9. JavaScript коды.

1. Генерация случайной последовательности или энтропии.

Автор начинает эту часть с того, что первые слова, сказанные им в самом начале статьи,  о том, что отправной точкой иерархического кошелька служит начальная фраза, или как говорят мнемоника, не совсем правдивы. Чтобы получить мнемонику,  сначала нужно сгенерировать энтропию.  Защита  кошелька,  должна быть основана на чем-то непредсказуемом, поэтому используется энтропия в 128-256 бит. Самый простой способ получить энтропию - это подбросить монетку.  Нужно взять монету и подбросить ее 128 раз, а затем  записать  ноль, если она ляжет  решкой, и  единицу, если выпадет орел. После 128 подбрасываний получится случайная последовательность нулей и единиц c требуемой энтропией, которая есть не что иное, как мера “случайности”. Чтобы повысить безопасность своего кошелька, тоже самое можно проделать и  256 раз, (чем больше энтропия, тем выше безопасность). Чтобы лучше понять этот процесс, можно посмотреть на нижерасположенную  картинку:
 

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

2. Получение контрольной суммы  и сборка выходной последовательности (выходного вектора).

Автор указывает, что получив  128-битную энтропию,  нужно сгенерировать для нее контрольную сумму. Контрольная сумма - это не что иное как сигнатура, прикрепляемая к объектудля того, чтобы убедиться, что пользователь не ошибся при его копировании. Чтобы найти сигнатуру полученной  энтропии, нужно, согласно BIP-39, получить ее хэш используя функцию хеширования SHA-256. Но прежде всего необходимо преобразовать  запись для рассматриваемой энтропии, а именно  из двоичной (Base2) перейти  в шестнадцатеричную ( Base16) систему исчисления. Base2 означает, что для выражения числа используется 2 символа, «0» и «1». В свою очередь Base16 для выражения числа использует 16 символов: 0,1,2,3,4,5,6,7,8,9, a, b, c, d, e, f. е

Рисунок внизу показывает, как выполняется  такое преобразование:




Можно  увидеть, что 0001 в Base2  (в двоичной системе)   представлено   в шестнадцатеричной системе счисления символом 1, 0010 представлено символом  2 и так далее. Итак, энтропия в шестнадцатеричном формате представляется как 91d3785bf884c639f600b3e587083265. Важный момент здесь тот, что обе записи представляют одно и то же, но системы исчисления  для них отличаются.

Далее автор возвращается непосредственно к вопросу  контрольной суммы для обсуждаемой энтропии  и приводит результат ее (энтропии) хеширования функцией SHA-256:

SHA-256 hash от 0x91d3785bf884c639f600b3e587083265 = effdb98e4c4ac27c670f704c39a2e0ba8b85bc4561a7a02a6297e465ed155d30

Хэш - это 256-битное число, представленное 64-мя шестнадцатеричными символами. Другими словами, каждый шестнадцатеричный символ представляет 4 бита. Согласно BIP-39, вместо использования всего хеша, только первые (величина энтропии / 32) бита хеша используются в качестве контрольной суммы. Величина энтропии здесь равна 128, что при делении на 32 дает 4. Итак, в качестве контрольной суммы берутся  только первые четыре бита хэша. Если величина энтропии  была бы равна 256, то в качестве контрольной суммы брались бы 256/32 или первые 8 бит хеша.

Как уже говорилось, каждый шестнадцатеричный символ представлен четырьмя битами, первые четыре бита рассматриваемого хэша в шестнадцатеричном формате представлены символом «e». Поэтому  контрольная сумма хэша - это «e» в шестнадцатеричном формате и 1110 в двоичном. Добавление этой контрольной суммы в конец последовательности создаст выходную  последовательность, выраженную  в шестнадцатеричной системе, как 0x91d3785bf884c639f600b3e587083265e, или в Base2 как:

 


3. Преобразование выходного вектора в мнемонические коды.

webtricks  напоминает, что  полученный размер выходного вектора составляет 132 бита, и Следующий шаг который необходимо предпринять, это его разбиение  на отдельные  кусочки размером в 11 бит. 132 поделенное  на 11 дает 12, поэтому получается 12 фрагментов. (Если использовать 256-битную энтропию, получится  24 фрагмента.)

Вот эти 12 фрагментов:

10010001110
10011011110
00010110111
11110001000
01001100011
00011100111
11011000000
00010110011
11100101100
00111000010
00001100100
11001011110

каждый из них - это двоичное число. Наименьшее значение, которое  может быть представлено фрагментом это  00000000000, или  0 в десятичной системе, к то время как максимальное значение это  11111111111, или 2047 в десятичной системе. Таким образом, каждый фрагмент может принимать  значение от 0 до 2047. BIP-39 определяет  2048 слов, отображаемых числами от 0 до 2047. Числовые отображения этих слов представлены в таблице BIP-39 WORDS, размещенной автором на странице его персонального сайта.

Из таблицы можно увидеть, что десятичные значения наших блоков: 1166, 1246, 183, 1928, 611, 231, 1728, 179, 1836, 450, 100, 1630.Выбирая соответствующие  им слова, можно получить следующий мнемонический код:

Code:
mushroom orange black valve erase brother submit biology tortoise debate arrive slim


4.  PBKDF2 или функция удлинения/растяжения ключа

PBKDF2  (основанная на пароле функция формирования ключа./ Password-based Key Derivation Function) используется как мера защиты . Для  повышения защищенности мнемонического кода  в этой функции может использоваться «парольная фраза».  Много полезного о PBKDF2 можно почерпнуть из Википедии.

Автор  ясно дает понять, что, в процессе создания адресов  биткойна,  функция PBKDF2 применяется  для удлинения  мнемонического кода, при этом используются   2048 итераций алгоритма HMAC-SHA512. Алгоритм принимает два параметра: один из них это  мнемонический код, второй – модификатор входа хеш-функции или кратко соль (salt). Если пользователь отказывается от использования пароля , то в качестве соли выступает строка со значением  'mnemonic'. Но, если в качестве пароля пользователь решит использовать скажем слово  «webby», то  соль превратится в 'mnemonicwebby', парольная фраза подцепляется к  концу строчки 'mnemonic'.

Следовательно, функция PBKDF2 примет следующий вид:

Code:
DK = PBKDF2(PRF, Password, Salt, c, dkLen)
Или с конкретными значениями, взятыми для  PRF, Password, Salt, c and dkLen:
DK = PBKDF2(HMAC, 'mushroom orange black valve erase brother submit biology tortoise debate arrive slim', 'mnemonic', 2048, 64)

где PRF - псевдослучайная функция (здесь HMAC)
Password - это мнемонический код
Соль - это строка 'mnemonic'
c – число  итераций, 2048 для  PBKDF2
dkLen - желаемая длина ( в байтах) производимого ключа (поскольку на выходе необходимо получить SEED размером в 512-бит, параметру dkLen  предписано значение в 64 байта, 1 байт = 8 бит).

DK относится к результирующему ключу или SEED размером в  512-бит. Для использованных параметров он равен:
Code:
65729d81d461591bac7384dea43dbd44438053cd1fdc113e0769c49c5fde025ba331eed2077497634e619948437d0448e769a86c0cbbecf01b13fd53540743b3

5. Приватный/Секретный Мастер-Ключ, Публичный/Открытый  Мастер-ключ и Кодирующая Цепочка (Chain Code).

Автор еще раз напоминает,  что основной целью использования SEED  является получение иерархической древовидной структуры, в которой каждый приватный (секретный, закрытый) или публичный (открытый)  ключ порождается  своим  родителем, и который в свою очередь порождает  дочерние  элементы (ключи). На вершине иерархии находится Master Private Key. Это секретный ключ первого уровня, у которого нет родителя.

Для нахождения этого ключа и Chain Code  используется криптографическая функция HMAC-SHA512, которая в качестве одного из двух своих параметров использует 512-битный SEED, полученный в разделе 4. Вторым ее параметром служит фиксированная строка, а именно 'Bitcoin seed'.  

HMAC(SEED, 'Bitcoin seed') = Hash

Результирующий хеш,  размером  в 512-бит, принимает  вид:

Code:
a0ccf14c939faa07b896cd5fb306a37fb3f9cb041196c5364d0cca9dbd82e53a5bc9d1368631ae579f02ed8e46a56dd9dd9de8ac59e3c4e18247ff96988bdf1f

Далее  webtricks обращает внимание на то, что размер  получаемой таким образом выходной последовательности  составляет 512 бит или 128 шестнадцатеричных символов. Первые 256 бит (первые 64 шестнадцатеричныхсимвола) и служат  секретным мастер ключом  (Master Private Key), в то время как  следующие за ними  256 бит используется как кодирующая цепочка (Chain Code).

Отсюда,

Code:
Master Private Key: a0ccf14c939faa07b896cd5fb306a37fb3f9cb041196c5364d0cca9dbd82e53a
Chain Code: 5bc9d1368631ae579f02ed8e46a56dd9dd9de8ac59e3c4e18247ff96988bdf1f

Используя  криптографию на эллиптических кривых (Elliptic Curve Cryptography)  можно из секретного мастер ключа получить открытый мастер  ключ (Master Public Key).  webtricks отсылает читателя к созданной им теме по ECC, чтобы проследить как  открытый ключ получается из секретного ключа.  Тот же самый процесс справедлив  для открытого мастер ключа.

Code:
Наш Master Public Key: 03d1cc1f6bdea4d17eb7f2573d676f9ddb087f8b784c912c4466407781d8acfe38

6. Путь деривации/порождения (Derivation Path) и BIP-44.

webtricks проводит аналогию  пути деривации и обычной карты, указывающей путь, который ведет  к искомой точке через множество  дорог, а в данном случае через дочерние элементы, получаемого из секретного мастер ключа, к  адресу биткойна. Согласно BIP-44  этот путь для основной сети Bitcoin можно определить следующим образом:
Code:
Path format: m / purpose' / coin_type' / account' / change / address_index
Bitcoin Main-net format: m / 44' / 0' / 0' / 0 / address_index

Наглядное представление об этом пути можно получить из нижеследующей картинки :

......................................................................../.............

1714131792
Hero Member
*
Offline Offline

Posts: 1714131792

View Profile Personal Message (Offline)

Ignore
1714131792
Reply with quote  #2

1714131792
Report to moderator
"If you don't want people to know you're a scumbag then don't be a scumbag." -- margaritahuyan
Advertised sites are not endorsed by the Bitcoin Forum. They may be unsafe, untrustworthy, or illegal in your jurisdiction.
XEOP$ (OP)
Full Member
***
Offline Offline

Activity: 126
Merit: 171


In Crypto We Trust


View Profile
February 22, 2021, 04:51:04 PM
Last edit: February 22, 2021, 09:10:16 PM by XEOP$
Merited by Symmetrick (5)
 #2

Продолжение.



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

Далее автор объясняет разницу между усиленными и стандартными потомками. Когда говорится о  «первом усиленном потомке», на самом деле подразумевается  (231 + 1) -й потомок. Для облегчения понимания 231 заменяют символом штриха ('). Итак, 231 + 1 или 2147483649-й дочерний элемент родителя является первым усиленным (или 0 ' в штрих обозначениях) дочерним элементом.

Для дальнейшего обсуждения пути деривации автор отсылает к теме одного из пользователей (blue Snow)  форума : https://bitcointalk.org/index.php?topic=5243350

7. Порождение Дочернего Приватного Ключа (Child Private Key Derivation)

Продолжая, автор переходит к рассмотрению получения дочернего ключа, для чего используется алгоритм хеширования HMAC-SHA512. Как уже говорилось криптографическая функция  HMAC-SHA512 требует для себя 2 параметра – один из них это сообщение (Message) и  второй – это секретный ключ. В данном случае роль  сообщения берут на себя  или секретный или открытый мастер ключи, сцепленные (конкатенированные)  с дочерним индексом (Index), а роль  секретного ключа принимает на себя кодирующая  цепочка (Chain Code). Отсюда,

Hash = HMAC(master key + index, chain code)

Отмечается, что в случае получения усиленного потомка  в качестве сообщения используется секретный мастер ключ, в то время как при для получения стандартного (normal) потомка  используется открытый мастер ключ.

Весь  процесс выглядит следующим образом :

УРОВЕНЬ 1: Получение 45-го усиленного потомка мастер ключа ( а поскольку он усилен используется  закрытый мастер ключ)
Code:
Master Private Key = a0ccf14c939faa07b896cd5fb306a37fb3f9cb041196c5364d0cca9dbd82e53a (взято из раздела 5)
Index = 8000002c (= 2147483692 т.e. 2^31+44 в шестнадцатиричной системе)
Chain Code = 5bc9d1368631ae579f02ed8e46a56dd9dd9de8ac59e3c4e18247ff96988bdf1f (взято из раздела 5)

Итак,
Message = 00a0ccf14c939faa07b896cd5fb306a37fb3f9cb041196c5364d0cca9dbd82e53a8000002c
(00, т.е. 8 пустых бит в начале  добавлено из-за требования получения  сообщения –Message- размером в 296 бит. Поскольку размер секретного мастер ключа составляет 256 бит, а индекса –Index-  32 бита, потребовалось  дополнительные 8 бит)

Key = 5bc9d1368631ae579f02ed8e46a56dd9dd9de8ac59e3c4e18247ff96988bdf1f

HMAC(message, key) = 7fc9ce32a6aeffbeaf5057f266f0d6ed6383ed84f21c96d53c0c1e3838a87e2481d4b120fcd3a11837e5d035fc508bb8b31c47285fdd7506d8d264144b4d8df7

webtricks  обращает внимание на то, размер выходной последовательности HMAC(message, key) составляет  512 бит из которых левые 256 бит используются  для создания  секретного ключа 45-го усиленного потомка (дочернего элемента) , а правые 256 бит представят кодовую цепочку (Сode Chain) этого потомка. Итак, для создания секретного ключа 45-го усиленного потомка (Private Key of 45th Hardened Child of Master Private Key) левые  256 бит, рассматриваемые в 16-ричном представлении прибавляются к    родительскому (в данном случае мастер) секретному  ключу (Parent Private Key ), а именно производится операция сложения по модулю 'n',  определяемому  стандартом SECG Vol 2

Code:
Left 256 bits = 7fc9ce32a6aeffbeaf5057f266f0d6ed6383ed84f21c96d53c0c1e3838a87e24
Parent Private Key =  a0ccf14c939faa07b896cd5fb306a37fb3f9cb041196c5364d0cca9dbd82e53a
N = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 (это и есть n)

Private Key of 45th Hardened Child of Master Private Key = ( Left 256 bits + Parent Private Key ) % N
Private Key of 45th Hardened Child of Master Private Key = 2096bf7f3a4ea9c667e7255219f77a6e5ccedba2546abbcfc9468a4925f5221d

Chain Code of 45th Hardened Child of Master Private Key = 81d4b120fcd3a11837e5d035fc508bb8b31c47285fdd7506d8d264144b4d8df7 (right 256 bits)

УРОВЕНЬ 2: получение первого усиленного потомка от потомка уровня 1 (поскольку он усилен, используется закрытый ключ)

Code:
Private Key = 2096bf7f3a4ea9c667e7255219f77a6e5ccedba2546abbcfc9468a4925f5221d
Index = 80000000 (value of 2147483648 i.e. 2^31 in hexadecimal)
Chain Code = 81d4b120fcd3a11837e5d035fc508bb8b31c47285fdd7506d8d264144b4d8df7

So,
Message = 002096bf7f3a4ea9c667e7255219f77a6e5ccedba2546abbcfc9468a4925f5221d80000000
Key = 81d4b120fcd3a11837e5d035fc508bb8b31c47285fdd7506d8d264144b4d8df7

HMAC(message, key) = dee4c4cb625b27f231194cf3befea6e67a73122f77a748b987fded5333ca63f7d665636fd64693411687f8d4deeb8382d14deb3d9937e72635e77af48c4da4e6

Left 256 bits = dee4c4cb625b27f231194cf3befea6e67a73122f77a748b987fded5333ca63f7
Parent Private Key =  2096bf7f3a4ea9c667e7255219f77a6e5ccedba2546abbcfc9468a4925f5221d
N = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 (n as defined)

Private Key of first hardened child of Level 1 Child = ( Left 256 bits + Parent Private Key ) % N
Private Key of first hardened child of Level 1 Child = ff7b844a9ca9d1b899007245d8f62154d741edd1cc1204895144779c59bf8614

Chain Code of first hardened child of Level 1 Child: d665636fd64693411687f8d4deeb8382d14deb3d9937e72635e77af48c4da4e6

УРОВЕНЬ 3: получение первого усиленного потомка от потомка уровня 2 (поскольку он усилен, используется закрытый ключ)

Code:

Private Key = ff7b844a9ca9d1b899007245d8f62154d741edd1cc1204895144779c59bf8614
Index = 80000000 (value of 2147483648 i.e. 2^31 in hexadecimal)
Chain Code = d665636fd64693411687f8d4deeb8382d14deb3d9937e72635e77af48c4da4e6

So,
Message = 00ff7b844a9ca9d1b899007245d8f62154d741edd1cc1204895144779c59bf861480000000
Key = d665636fd64693411687f8d4deeb8382d14deb3d9937e72635e77af48c4da4e6

HMAC(message, key) = 2839a8f276409794544cdc9f4d2748a3ea3ca988b64f82e72414d67dedaf751bfb106a1896e38ddc80b3d3b4fdaba9b003d1e6caa08c6cbbdc5d63fa6836b613

Left 256 bits = 2839a8f276409794544cdc9f4d2748a3ea3ca988b64f82e72414d67dedaf751b
Parent Private Key =  ff7b844a9ca9d1b899007245d8f62154d741edd1cc1204895144779c59bf8614
N = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 (n as defined)

Private Key of first hardened child of Level 2 Child = ( Left 256 bits + Parent Private Key ) % N
Private Key of first hardened child of Level 2 Child = 27b52d3d12ea694ced4d4ee5261d69fa06cfba73d318e734b586ef8d7738b9ee

Chain Code of first hardened child of Level 2 Child: fb106a1896e38ddc80b3d3b4fdaba9b003d1e6caa08c6cbbdc5d63fa6836b613


УРОВЕНЬ 4: получение первого стандартного потомка от потомка уровня 3 (поскольку он стандартный, используется открытый ключ)

Code:
Private Key = 27b52d3d12ea694ced4d4ee5261d69fa06cfba73d318e734b586ef8d7738b9ee
Public Key = 03fc371a6939557697a438cca5c81fc899d611d41f605d1b6d1a8096fd5e3e0343 (using ECC)
Index = 00000000 (value of 0 in hexadecimal)
Chain Code = fb106a1896e38ddc80b3d3b4fdaba9b003d1e6caa08c6cbbdc5d63fa6836b613

So,
Message = 03fc371a6939557697a438cca5c81fc899d611d41f605d1b6d1a8096fd5e3e034300000000
(Since we are using Public Key which is already of 264 bits, we needn't concat additional bits in the starting)

Key = fb106a1896e38ddc80b3d3b4fdaba9b003d1e6caa08c6cbbdc5d63fa6836b613

HMAC(message, key) = bd63f3fe2daf72bd61d983477a8330e377ecc1fa664bee4a90da90003de9ef8c29a2907541b35ab602c72d52c330184a2e7908060b98acca9b17ebfaea0135a8

Left 256 bits = bd63f3fe2daf72bd61d983477a8330e377ecc1fa664bee4a90da90003de9ef8c
Parent Private Key =  27b52d3d12ea694ced4d4ee5261d69fa06cfba73d318e734b586ef8d7738b9ee
N = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 (n as defined)

Private Key of first normal child of Level 3 Child = ( Left 256 bits + Parent Private Key ) % N
Private Key of first normal child of Level 3 Child = e519213b4099dc0a4f26d22ca0a09add7ebc7c6e3964d57f46617f8db522a97a

Chain Code of first hardened child of Level 3 Child: 29a2907541b35ab602c72d52c330184a2e7908060b98acca9b17ebfaea0135a8
УРОВЕНЬ 5: получение первых трех стандартных потомков от потомка уровня 4 (поскольку они стандартные, используется открытый ключ)

Code:
Private Key = e519213b4099dc0a4f26d22ca0a09add7ebc7c6e3964d57f46617f8db522a97a
Public Key = 0321bd38eb2f97c56762b82f22e9677d6aa205a73664b93aaf8ed087bd9fc26420 (using ECC)
Index = 00000000, 00000001 and 00000002
Chain Code = 29a2907541b35ab602c72d52c330184a2e7908060b98acca9b17ebfaea0135a8

So,
Message1 = 0321bd38eb2f97c56762b82f22e9677d6aa205a73664b93aaf8ed087bd9fc2642000000000
Message2 = 0321bd38eb2f97c56762b82f22e9677d6aa205a73664b93aaf8ed087bd9fc2642000000001
Message3 = 0321bd38eb2f97c56762b82f22e9677d6aa205a73664b93aaf8ed087bd9fc2642000000002

Key = 29a2907541b35ab602c72d52c330184a2e7908060b98acca9b17ebfaea0135a8

HMAC(message1, key) = a8764acda4ebc575ff750e113353a805186febf32372deb4fab9ed180a7b4db3a3e1295ec9c664d73d77841b263d019306d914e431fdc84973cf53abaa0883cb
HMAC(message2, key) = fb58f1f53183d06aed97ba85ad30fc89d4500bb3c5d47880cc96c368f044618743a1580a9757af12b8597450ff8a5b37e9a51660b0a30e672b736464f4cdb7d0
HMAC(message3, key) = b30db2ea8ad0e61c43acf2052ecc0d3c174cf5a57655ba038ba8894f3bc2f0d8c140e5f51589c16e3d3502b08fc005e8a9acfa5a56dda2e08b520b3179c1f163

Left 256 bits of HMAC1 = a8764acda4ebc575ff750e113353a805186febf32372deb4fab9ed180a7b4db3
Left 256 bits of HMAC2 = fb58f1f53183d06aed97ba85ad30fc89d4500bb3c5d47880cc96c368f0446187
Left 256 bits of HMAC3 = b30db2ea8ad0e61c43acf2052ecc0d3c174cf5a57655ba038ba8894f3bc2f0d8

Parent Private Key =  e519213b4099dc0a4f26d22ca0a09add7ebc7c6e3964d57f46617f8db522a97a
N = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 (n as defined)

Private Key of first normal child of Level 4 Child = 8d8f6c08e585a1804e9be03dd3f442e3dc7d8b7aad8f13f881490e18ef67b5ec (to be used for deriving first bitcoin address)
Private Key of second normal child of Level 4 Child = e0721330721dac753cbe8cb24dd19768985dab3b4ff0adc45325e469d530c9c0 (to be used for deriving second bitcoin address)
Private Key of third normal child of Level 4 Child = 9826d425cb6ac22692d3c431cf6ca81adb5a952d0071ef471237aa5020af5911 (to be used for deriving third bitcoin address)


8.  Создание биткойн-адресов из приватных ключей

Переходя к рассмотрению процесса получения биткойн-адресов, webtricks обращается к   3-м секретным ключам (с индексами  00000000, 00000001 и 00000002) уровня 5. Он говорит, что если нужно получить больше ключей, то все что для этого необходимо это  продолжать увеличивать индекс на 1. В частности , 00000003 это индекс для следующего секретного  ключа, ну  и так далее. Также он напоминает, что индекс берется в   шестнадцатеричном формате, поэтому после 00000009 следующим индексом будет 0000000a, а не 00000010.

Тема webtricks , в которой он  подробно объясняет, как генерируются из закрытого ключа Legacy  адреса (адреса, начинающиеся  с «1») можно найти по следующей ссылке How Bitcoin Addresses are generated? Understand the Math behind Bitcoin Если следовать рассуждениям  указанной темы, то можно  сгенерировать первые три (в иерархии) Legacy адреса биткойна, используя закрытые ключи из раздела 7.

Этими адресами будут :

Quote
1MbJqqvN8ZPYsUch45HdRAxKbH6bJeGfZi
1GMpMNYwhb7Wvu8q1Zy52MtZUGWvLgCXak
12fv5eg3kzBgZQy7ue2yYmC9xXohmKWGR3

В обсуждаемой же работе webtricks фокусируется  на адресах  P2SH. P2SH сильно отличается от адреса P2PKH (Legacy), который  создается  из хэша открытого ключа. В отличие от такого сценария, для   P2SH адреса сначала создается скрипт, а затем вычисляется его хэш. Транзакция делается в направлении скрипта, который может быть буквально любым и  при этом отправителю транзакции не обязательно знать, что означает сам сценарий (скрипт). Биткойн имеет свой собственный язык сценариев( SCRIPT),  который  определяет множество операций, таких как OP_ADD, OP_EQUAL и другие. Итак, можно  без всякого преувеличения создать любой  сценарий, например такой, «что нужно добавить к 3 чтобы получить  5», получить хэш этого скрипта и использовать этот хеш в качестве  адреса P2SH. Перечисления, полученные на этот адрес P2SH, можно потратить путем предоставления исходного скрипта ( т.е. «что нужно добавить к 3 чтобы получить  5») и скрипта, который предоставляет его решение, т.е. «2» вместе с хешем.

P2SH можно использовать для создания гораздо более сложных сценариев, но одним из наиболее распространенных типов P2SH является P2WPKH-in-P2SH,  который  означает использование P2PKH внутри скрипта P2SH. Этому типу соответствуют  адреса, начинающееся  с цифры «3».

Далее автор переходит непосредственно к созданию таких адресов:

Схема для формата P2WPKH-in-P2SH определена в BIP-49.  Как уже говорилось для создания иерархически детерминированных Legacy адресов используется  45-й усиленный потомок  секретного мастер ключа ( Master Private Key). Однако для P2WPKH-in-P2SH используется  50-й усиленный потомок, поэтому путь деривации превращается в следующий:

Code:
m / 49' / 0' / 0' / 0 / address_index

За исключением этого, все остальное подобно тому , как обсуждалось разделах 6 и 7, поэтому webtrics  повторяет процедуру с секретным мастер ключом, то есть a0ccf14c939faa07b896cd5fb306a37fb3f9cb041196c5364d0cca9dbd82e53a, описанную им в разделе  7 и получает  три секретных ключа, которые служат ему  в качестве первых 3-х стандартных потомков в пути деривации  m / 0 '/ 0' / 0 (т.е. потомков уровня 5):

Code:
Private Key of first normal child of Level 4 Child = 26e1061459e7961eeac018efa765339d785bd30de91f8fade64c639b275d74c4 (to be used for deriving first bitcoin address)
Private Key of second normal child of Level 4 Child = 501a42ccd834bf61c211f5277bcbebe4120eea952efc91fd71125f61a1e7eec4 (to be used for deriving second bitcoin address)
Private Key of third normal child of Level 4 Child = 13ae95a5d643b9ebe355d103679ad4bcf3863efef78873f4e4f20a57cf044a51 (to be used for deriving third bitcoin address)

Далее, используя ECC, он получает следующие открытые ключи (напоминания при этом , что для того, чтобы узнать  как из секретного  ключа получается открытый ключ можно обратиться к созданной им для этого cпециальной теме:

Code:
Public Key of first normal child of Level 4 Child = 021549dd72d89cbc844bb74ab6247239cf60d184cbfb0cfc4d024150a4985412fe (to be used for deriving first bitcoin address)
Public Key of second normal child of Level 4 Child = 02e589abcbdbcf7b9746d1e2f5d97e5d2836c82b5910c5716f094801c0178ecfc2 (to be used for deriving second bitcoin address)
Public Key of third normal child of Level 4 Child = 020711fb2e08e67c13bcfb2cca60ff5ac3b7c6fb9e902722127ef776e5d2db6046 (to be used for deriving third bitcoin address)

Прежде чем перейти к объяснению происходящего webtrics предлагает ознакомиться с нижеследующим рисунком, который схематично описывает весь процесс преобразования открытого ключа в биткойн-адрес:






Шаг №1:  с помощью функции SHA-256 происходит хеширование  открытого ключа:

Code:
SHA-256(public key) = Hash

SHA-256(021549dd72d89cbc844bb74ab6247239cf60d184cbfb0cfc4d024150a4985412fe) = 189a3015638daa02871973bf840b434aad92cb71775b65680acd266b81e85e3f

Шаг №2:  полученный хеш хешируется функцей  ripemd160

Code:
RIPEMD160(hash) = Hash160

RIPEMD160(189a3015638daa02871973bf840b434aad92cb71775b65680acd266b81e85e3f) = 2bf545ff88c159408f5ba759f99e78566763fe1a

Шаг №3:  к началу Hash160   прицепляется 0х0014

Code:
serialization = 0x0014 + Hash160

serialization = 00142bf545ff88c159408f5ba759f99e78566763fe1a

Примечание: 0x00 представляет OP_0, а 0x14 - это размер данных (в шестнадцатеричном формате) , помещаемых в стек. Отсюда, скрипт P2WPKH-P2SH это  OP_0 PushData <hash>.

Шаги №4 и №5: Вычисляются хеши скрипта:
Code:
SHA-256(script) = Hash

SHA-256(00142bf545ff88c159408f5ba759f99e78566763fe1a) = c2d24e021347966656ed4b0312f9b3a49498c257294bd75e9bc84ba8353deb9a

Then,
RIPEMD160(hash) = Hash160

RIPEMD160(c2d24e021347966656ed4b0312f9b3a49498c257294bd75e9bc84ba8353deb9a) = 2d7193893e4143fc11bb69c7f004452198bdf6cd
 

Шаг №6: к началу Hash160  прицепляется  байт кодировки в виде 0x05 :

Code:
serialization = 0x05 + Hash160

serialization = 052d7193893e4143fc11bb69c7f004452198bdf6cd

 Шаг №7: Вычисление контрольной суммы через двойное хеширование:
Code:
checksum = first four bytes of SHA-256(SHA-256(hash))

SHA256(SHA256(052d7193893e4143fc11bb69c7f004452198bdf6cd)) = dcd3b30cd36dcef8265fbe414e435fc7841ced941f93ef86afd86e344c4a700e
First four bytes = dcd3b30c
Шаг №8: контрольная сумма цепляется  к концу  хэша и результат перекодируется в Base58:
Code:
final serialization = 052d7193893e4143fc11bb69c7f004452198bdf6cddcd3b30c

Base58(052d7193893e4143fc11bb69c7f004452198bdf6cddcd3b30c) = 35qJPbZX23wt3uuB9nz4pxhoouUfG28zxB
First four bytes = dcd3b30c

Отсюда 35qJPbZX23wt3uuB9nz4pxhoouUfG28zxB - это первый адрес в иерархии биткойн адресов.











XEOP$ (OP)
Full Member
***
Offline Offline

Activity: 126
Merit: 171


In Crypto We Trust


View Profile
February 22, 2021, 04:51:16 PM
Last edit: February 22, 2021, 05:46:47 PM by XEOP$
Merited by Symmetrick (5)
 #3

Продолжение.



ПРИМЕЧАНИЕ  к этой части, сделанное webtrics. Для предотвращения раздувания темы в эту часть не включены расширенные  ключи (Extended Keys). Они  являются неотъемлемой частью кошельков HD (HD - иерархически детерминированных) и помогают в иерархическом импорте адресов без необходимости использования мнемонического кода. Таким ключам будет посвящена отдельная тема.

9. JavaScript коды

Действующий код: https://webtricks.website/seed

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

При написании кодов использована библиотека CryptoJS для алгоритмов SHA-256, RIPEMD-160 и HMAC-SHA512.




Файлы кодов.

File 1: mnemonic.js ( https://github.com/web-tricks/seed-guide/blob/main/mnemonic.js)

Code:
//Generating random 128 bits. 128 - 256 bits can be used but for this tutorial we are strictly generating 128 bits entropy
const getRandomBytes = () => {
    const array = new Uint32Array(4); //creating Uint32 array having length = 4
    const randomBytes = crypto.getRandomValues(array); //Filling array with random 32-bits integers

    let binaryString = '';
    let hexString = '';
    randomBytes.forEach(byte => {
        let binChunk = byte.toString(2);
        binChunk = binChunk.length === 32 ? binChunk : '0'.repeat(32 - binChunk.length)+binChunk;
        let hexChunk = parseInt(binChunk,2).toString(16);

        binaryString += binChunk;
        hexString += hexChunk;
    })

    return [binaryString,hexString];
}

//Generating SHA-256 hash of random bytes for checksum
let [randomBits,randomBitsHex] = getRandomBytes();
const byteHash = CryptoJS.SHA256(CryptoJS.enc.Hex.parse(randomBitsHex)).toString(); //SHA-256 Hash of random bytes
let checksum = parseInt(byteHash[0],16).toString(2); //Taking (entropy-length / 32) bits of SHA256 hash which in our case is 4 bits
checksum = checksum.length === 4 ? checksum : '0'.repeat(4 - checksum.length)+checksum; //Adding '0' bits if hex is smaller than '8' or '0b1000' in binary

//Adding checksum at the end of random bytes
randomBits += checksum;

//Splitting random bytes into segments of 11-bits length and storing in an array
const segmentArray = [];
let i = 0;
while (i < randomBits.length) {
    segmentArray.push(randomBits.substring(i,i+11));
    i += 11;
}

//Converting every 11-bits segment into decimal equivalent
const decimalArray = segmentArray.map(segment => parseInt(segment,2));

//Picking 'word' at position equivalent to decimal array from 'mnemonic words list' (words.js)
const mnemonicArray = decimalArray.map(decimal => wordsArray[decimal]);

//getMnemonic Function - This function will be called in front-end when user will create new seed
const getMnemonic = () => {
    return mnemonicArray.join(' '); //Converting mnemonic array into mnemonic string
}


File 2: p2pkh.js (https://github.com/web-tricks/seed-guide/blob/main/p2pkh.js)
Code:
//This function will generate Legacy bitcoin address (public key hash) using public key
const generateLegacyAddress = (publicKey) => {
    const keyHex = CryptoJS.enc.Hex.parse(publicKey);
    const ripedHashedKey = CryptoJS.RIPEMD160(CryptoJS.SHA256(keyHex)).toString();
    const mainRipeKeyString = '00'+ripedHashedKey;
    const mainRipeKey = CryptoJS.enc.Hex.parse(mainRipeKeyString);
    const doubleHashedKey = CryptoJS.SHA256(CryptoJS.SHA256(mainRipeKey)).toString();
    const checkSum = doubleHashedKey.substr(0, 8);
    const binaryAddress = mainRipeKeyString+checkSum;
    const arrayBinary = binaryAddress.match(/.{1,2}/g); //Converting serialization into array of every 2nd character
    const binaryUint = new Uint8Array(arrayBinary.map(hex => parseInt(hex,16))); //Converting hex array into uint8array to be used as input in base58 function
    
    return to_b58(binaryUint,bs58Chars);
}

File 3: p2sh.js  (https://github.com/web-tricks/seed-guide/blob/main/p2sh.js)
Code:
//This function will generate P2SH bitcoin address (P2WPKH-in-P2SH) using public key
const generateP2SHAddress = (publicKey) => {
    const keyHex = CryptoJS.enc.Hex.parse(publicKey);
    const ripeHash = CryptoJS.RIPEMD160(CryptoJS.SHA256(keyHex)).toString();

    const script = '0014'+ripeHash;
    const scriptHex = CryptoJS.enc.Hex.parse(script);
    const scriptRipeHash = '05'+CryptoJS.RIPEMD160(CryptoJS.SHA256(scriptHex)).toString();

    const doubleHashedKey = CryptoJS.SHA256(CryptoJS.SHA256(CryptoJS.enc.Hex.parse(scriptRipeHash))).toString();
    const checkSum = doubleHashedKey.substr(0, 8);

    const binaryAddress = scriptRipeHash+checkSum;

    const arrayBinary = binaryAddress.match(/.{1,2}/g); //Converting serialization into array of every 2nd character
    const binaryUint = new Uint8Array(arrayBinary.map(hex => parseInt(hex,16))); //Converting hex array into uint8array to be used as input in base58 function
    
    return to_b58(binaryUint,bs58Chars);
}


File 4: ecc.js  (https://github.com/web-tricks/seed-guide/blob/main/ecc.js)
Code:
//This file contains code for generating public key from private key using Elliptic Curve Cryptography
const Pcurve = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F');

const Gx = BigInt('55066263022277343669578718895168534326250603453777594175500187360389116729240');
const Gy = BigInt('32670510020758816978083085130507043184471273380659243275938904335757337482424');

const G = [Gx, Gy];

const generatePublicKey = privateKey => {
    const ECCPoints = ECMultiply(G, privateKey);

    const checkKey = key => key.length < 64 ? '0'.repeat(64 - key.length)+key : key;

    const publicKeyX = checkKey(ECCPoints[0].toString(16));

    if (ECCPoints[1]%BigInt(2)===BigInt(1)) {
    return '03'+publicKeyX;
    } else {
    return '02'+publicKeyX;
    }
}

//mod inverse function
function modInverse(a, n) {

    a = (a % n + n) % n

    const dArray = [];
    let b = n;

    while(b) {
    [a, b] = [b, a % b];
    dArray.push({a, b});
    }

    if (a !== BigInt(1)) {
    return null;
    }

    let x = BigInt(1);
    let y = BigInt(0);

    for(let i = dArray.length - 2; i >= 0; --i) {
    [x, y] = [y,  x - y * BigInt(dArray[i].a / dArray[i].b)];
    }

    return (y % n + n) % n;
}

//mod of function
function modOf(a,b) {
    const r = ((a % b) + b)% b;
    return r;
}

//ECAdd - Elliptic Curve Addition Function
function ECAdd(a,b) {
    const lamAdd = modOf((b[1] - a[1]) * BigInt(modInverse(b[0] - a[0], Pcurve)), Pcurve);
    const x = modOf((lamAdd*lamAdd - a[0] - b[0]), Pcurve);
    const y = modOf((lamAdd*(a[0] - x) - a[1]), Pcurve);
    return [x, y];
}

//ECDouble - Elliptic Curve Point Doubling
function ECDouble(a) {
    const lamda = modOf(((BigInt(3)*a[0]*a[0])*(modInverse(BigInt(2)*a[1], Pcurve))), Pcurve);
    const x = modOf((lamda*lamda - BigInt(2)*a[0]), Pcurve);
    const y = modOf((lamda*(a[0] - x) - a[1]), Pcurve);
    return [x, y];
};

//ECMultiply - Ellptic Curve Multiplication
function ECMultiply(genPoint, pvtKey) {
    const scalarBinary = BigInt('0x'+pvtKey).toString(2);
    let GP = genPoint;

    for (let i=1; i < scalarBinary.length; i++) {
        GP = ECDouble(GP)
        if (scalarBinary[i] === '1') {
            GP = ECAdd(GP, genPoint);
        }
    }
    return GP;
}


File 5: bs58.js (https://github.com/web-tricks/seed-guide/blob/main/bs58.js)
Code:
//This javascript code for base58 encoding is taken from https://gist.github.com/diafygi/90a3e80ca1c2793220e5/
var to_b58 = function(B,A){var d=[],s="",i,j,c,n;for(i in B){j=0,c=B[i];s+=c||s.length^i?"":1;while(j in d||c){n=d[j];n=n?n*256+c:c;c=n/58|0;d[j]=n%58;j++}}while(j--)s+=A[d[j]];return s};
var bs58Chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';n*58+c:c;c=n>>8;d[j]=n%256;j++}}while(j--)b.push(d[j]);return new Uint8Array(b)};

File 6: address.js  (Link: https://github.com/web-tricks/seed-guide/blob/main/address.js)
Code:
//This function will take mnemonic code as the input and produce addresses
const detailsFromMnemonic = mnemonic => {

    // 1. Creating 'seed' from mnemonic code
    const salt = 'mnemonic'; //constant string 'mnemonic' used for salt in PBKDF2 function (custom passphrase could be used as well)
    const seed =  CryptoJS.PBKDF2(mnemonic, salt, {
        hasher:CryptoJS.algo.SHA512,
        keySize: 512 / 32,
        iterations:2048
    }).toString(); //using 2048 rounds of PBKDF2 key-stretching function

    // 2. Creating 'Master Private Key' and 'Chain Code' from 'seed'
    const hmacHash =  CryptoJS.HmacSHA512(CryptoJS.enc.Hex.parse(seed),'Bitcoin seed').toString();
    const masterPrivateKey = hmacHash.substr(0,64); //Left 256 bits of HMAC-512 hash
    const chainCode = hmacHash.substr(64,64); //Right 256 bits of HMAC-512 hash
    let masterPublicKey = generatePublicKey(masterPrivateKey);

    // 3. Generating childs using BIP-44 Derivation Path for LEGACY (Derivation Path - m/44'/0'/0'/0); - Hardened Child - 0x8000002c
        let {addresses: legacyAddresses, privateKeys: legacyPrivateKeys} = returnChild(masterPrivateKey,masterPublicKey,chainCode,'8000002c');
        //  Converting Public Key in the Array to Legacy Bitcoin Addresses
        legacyAddresses = legacyAddresses.map(publicKey => generateLegacyAddress(publicKey));

    // 4. Generating childs using BIP-49 Derivation Path for P2SH (Derivation Path - m/49'/0'/0'/0); - Hardened Child - 0x80000031
        let {addresses: p2shAddresses, privateKeys: p2shPrivateKeys} = returnChild(masterPrivateKey,masterPublicKey,chainCode,'80000031');
       //  Converting Public Key in the Array to P2SH Bitcoin Addresses
       p2shAddresses = p2shAddresses.map(publicKey => generateP2SHAddress(publicKey));
  
    // 5. Generating childs using BIP-84 Derivation Path for Bech32 (Derivation Path - m/84'/0'/0'/0); - Hardened Child - 0x80000054
        let {addresses: bech32Addresses, privateKeys: bech32PrivateKeys} = returnChild(masterPrivateKey,masterPublicKey,chainCode,'80000054');
       //  Converting Public Key in the Array to P2SH Bitcoin Addresses
       bech32Addresses = bech32Addresses.map(publicKey => generateBech32Address(publicKey));
      
    return {
        masterPrivateKey,
        masterPublicKey,
        legacyPrivateKeys,
        legacyAddresses,
        p2shAddresses,
        p2shPrivateKeys,
        bech32Addresses,
        bech32PrivateKeys
    }
}

//Function for Deriving Children based on Hardened Child
function returnChild(masterPrivateKey,masterPublicKey,chainCode,hardenedChild) {
    // First Level: m/H':
    const [firstChildPrivate,firstChildPublic,firstChildChain] = generatingChild(masterPrivateKey,masterPublicKey,chainCode,hardenedChild,'private');
    // Second Level: m/H'/0':
    const [secondChildPrivate,secondChildPublic,secondChildChain] = generatingChild(firstChildPrivate,firstChildPublic,firstChildChain,'80000000','private');
    // Third Level: m/H'/0'/0':
    const [thirdChildPrivate,thirdChildPublic,thirdChildChain] = generatingChild(secondChildPrivate,secondChildPublic,secondChildChain,'80000000','private');
    // Fourth Level: m/H'/0'/0'/0 - For main receiving addresses:
    const [fourthChildPrivate,fourthChildPublic,fourthChildChain] = generatingChild(thirdChildPrivate,thirdChildPublic,thirdChildChain,'00000000','public');

    // Fifth Level: This level will be used for addresses
    //  We will generate 10 addresses from m/H'/0'/0'/0/0 to m/H'/0'/0'/0/9 branch to be used as receiving addresses
    let addresses = [];
    const privateKeys = [];
    for (let i=0;i<10;i++) {
        const childSet = generatingChild(fourthChildPrivate,fourthChildPublic,fourthChildChain,'0000000'+i,'public');
        addresses.push(childSet[1]); //Pushing Public Key in the Array
        privateKeys.push(childSet[0]); //Pushing Private Key in the Array
    }

    return {
        addresses,
        privateKeys
    }
}

//Function to generate child private key, child public key and child chain code
function generatingChild(parentPrivateKey, parentPublicKey, parentChainCode,index,type) {
    let parentPrivate = parentPrivateKey.length === 64 ? parentPrivateKey : '0'.repeat(64-parentPrivateKey.length)+parentPrivateKey;
    const keyToUse = type === 'private' ? '00'+parentPrivate : parentPublicKey; //Use private key if hardened-index else public key
    const hmacHash = CryptoJS.HmacSHA512(CryptoJS.enc.Hex.parse(keyToUse+index),CryptoJS.enc.Hex.parse(parentChainCode)).toString();
    const [leftBits,childChainCode] = separateKeyChain(hmacHash);
    const N = '0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'; //As defined in secp256k1 ecc
    let childPrivateKey = (BigInt('0x'+parentPrivate) + BigInt('0x'+leftBits)) % BigInt(N);
    childPrivateKey = childPrivateKey.toString(16); //Converting from decimal to hex
    const childPublicKey = generatePublicKey(childPrivateKey); //Using ECC function taken from 'ecc.js' file

    return [childPrivateKey,childPublicKey,childChainCode];
}

//Function to be used in generatingChild function to separate hash into private key and chain code
function separateKeyChain(hmacHash) {
    const privateKeyPart = hmacHash.substr(0,64);
    const chainCodePart = hmacHash.substr(64,64);
    return [privateKeyPart,chainCodePart];
}


File 7: words.js
Файл можно загрузить отсюда: https://github.com/web-tricks/seed-guide/blob/main/words.js

File 8: index.html  ( https://github.com/web-tricks/seed-guide/blob/main/index.html)
Code:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
    <title>Bitcoin Addresses from Mnemonic Code</title>
    <style>
        body {
            font-family:Georgia, 'Times New Roman', Times, serif;
            text-align: center;
        }
        .seed {
            width: 300px;
        }
        #display {
            font-family: 'Times New Roman', Times, serif;
        }
    </style>
</head>
<body>
    <h1>Deriving Bitcoin Address from Seed Phrase (Mnemonic Seed)</h1>
    <p><button id="generate_random">Generate Random Seed</button></p>
    <h2>OR</h2>
    Enter your Seed:
    <form id="import">
        <input class="seed"><input type="submit" value="generate addresses">
    </form>
    <div id="display"></div>
    <script src="bs58.js"></script>
    <script src="ecc.js"></script>
    <script src="p2pkh.js"></script>
    <script src="p2sh.js"></script>
    <script src="bech32.js"></script>
    <script src="words.js"></script>
    <script src="mnemonic.js"></script>
    <script src="address.js"></script>

    <script>
        let mnemonic;

        document.getElementById('generate_random').addEventListener('click', () => {
            mnemonic = getMnemonic();
            prepareDisplay();
        });

        document.getElementById('import').addEventListener('submit', e => {
            e.preventDefault();
            mnemonic = document.querySelector('.seed').value;
            prepareDisplay();
        });

        function prepareDisplay() {
            const {masterPrivateKey,masterPublicKey,legacyPrivateKeys,
            legacyAddresses,p2shAddresses,p2shPrivateKeys,bech32Addresses,
            bech32PrivateKeys} = detailsFromMnemonic(mnemonic);

            let html = `
                <p>Mnemonic Code (Seed Phrase): ${mnemonic}<br></p>
                <p>Master Private Key: ${masterPrivateKey}<br></p>
                <p>Master Public Key: ${masterPublicKey}<br></p>
            `;

            html += `<p>Legacy Addresses (first 10)<br>`;
            legacyAddresses.forEach((address,index) => {
                html += `${index+1}. ${address} - Private Key: ${legacyPrivateKeys[index]}<br>`;
            });

            html += `</p>`;

            html += `<p>P2SH Addresses (first 10)<br>`;
            p2shAddresses.forEach((address,index) => {
                html += `${index+1}. ${address} - Private Key: ${p2shPrivateKeys[index]}<br>`;
            });

            html += `</p>`;

            html += `<p>Bech32 Addresses (first 10)<br>`;
            bech32Addresses.forEach((address,index) => {
                html += `${index+1}. ${address} - Private Key: ${bech32PrivateKeys[index]}<br>`;
            });

            html += `</p>`;

            document.getElementById('display').innerHTML = html;
        }
    </script>
</body>
</html>


File 9: bech32.js  (https://github.com/web-tricks/seed-guide/blob/main/bech32.js)

Code:
//This function will generate P2SH bitcoin address (P2WPKH-in-P2SH) using public key
const generateBech32Address = (publicKey) => {
    const bech32schema = 'qpzry9x8gf2tvdw0s3jn54khce6mua7l';

    const keyHex = CryptoJS.enc.Hex.parse(publicKey);
    const ripeHash = CryptoJS.RIPEMD160(CryptoJS.SHA256(keyHex)).toString();

    let binString = BigInt('0x'+ripeHash).toString(2);
    binString = binString.length === 160 ? binString : '0'.repeat(160-binString.length)+binString;

    const decArray = binString.match(/.{1,5}/g).map(binary => parseInt(binary,2));
    decArray.unshift(0);

    const checkSum = createChecksum(decArray);

    const hexString = decArray.map(decimal => ('00'+decimal.toString(16)).substr(-2)).join('');

    let address = '';

    (hexString+checkSum).match(/.{1,2}/g).forEach(hexVal => {
        address += bech32schema[parseInt(hexVal,16)];
    });
  
    return 'bc1'+address;
}

//Checksum generation using BCH Codes
function createChecksum(decArr) {
    const GEN = [0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3];
    let chk = 1

    let decArray = [3,3,0,2,3].concat(decArr)
    decArray = decArray.concat([0,0,0,0,0,0]);
  
    decArray.forEach(dec => {
        let b = chk >> 25;
        chk = (chk & 0x1ffffff) << 5 ^ dec;

        for (let i=0;i<5;i++) {
            chk ^= ((b >> i) & 1) ? GEN[i] : 0;
        }
    });

    const polymod = chk ^ 1;

    const returnVal = [];

    for (let v=0;v<6;v++) {
        returnVal.push((polymod >> 5 * (5 - v)) & 31)
    }

    return returnVal.map(val => ('00'+val.toString(16)).substr(-2)).join('');
}

Чтобы запустить код необходимо сохранить все файлы в одной папке и открыть в браузере файл index.js  

XEOP$ (OP)
Full Member
***
Offline Offline

Activity: 126
Merit: 171


In Crypto We Trust


View Profile
February 22, 2021, 05:20:36 PM
 #4

reserved

Pages: [1]
  Print  
 
Jump to:  

Powered by MySQL Powered by PHP Powered by SMF 1.1.19 | SMF © 2006-2009, Simple Machines Valid XHTML 1.0! Valid CSS!