Title: Создаем RAW транзакцию Биткоин с нуля Post by: kzv on November 02, 2019, 03:58:38 PM Что-то я не встречал пошаговой инструкции на русском языке о том, как создать Биткоин транзакцию без программирования.
То есть понятно, что для создания стандартной транзакции отправки монет, можно пользоваться GUI кошельком или командой консоли типа sendtoaddress, но что если хочется создать стандартную транзакцию с нестандартным запирающим скриптом..? Попробую тут описать весь процесс. Пост получаяется длинным, поэтому буду дополнять постепенно. Используемые источники 1. https://medium.com/coinmonks/how-to-create-a-raw-bitcoin-transaction-step-by-step-239b888e87f2 2. https://en.bitcoin.it/wiki/Protocol_documentation#tx 3. https://bitcoin.org/en/developer-reference#raw-transaction-format Предисловие Сам я кодер, поэтому и тему разместил в разделе "Кодеры", но надеюсь, что инструкция будет доступна для понимания именно "не кодерам". То есть попробую описать инструкцию для обычного человека у которого кроме блокнота и клиента bitcoin-qt ничего специального на компьютере не установлено. Итак начнем. Как я уже выше упомянул, из инструментов нам понадобятся следующие приложения: 1. Блокнот 2. bitcoin-qt Это все. Продолжаем. Если вы не сын/дочь миллионера, то наверняка не захотите экспериментировать с настоящими биткоинами которые стоят денег. Поэтому запустите bitcoin-qt в тестовом режиме. Для этого ИЛИ Сделайте ярлык на рабочий стол и в свойствах ярлыка (вкладка общие) после bitcoin-qt.exe допишите через пробел параметр -testnet ИЛИ Вместо свойства ярлыка можете создать файл bitcoin.conf в директории %APPDATA%/Bitcoin В этом файле напишите текст testnet=1 Если все сделаете правильно, то кошелек bitcoin core будет запускаться в тестовой сети. В тестовой сети другие адреса и другие, бесплатные биткоины. Чтобы начать экспериментировать, вам нужно дождаться синхронизации тестового блокчейна и пополнить кошелек тестовыми биткоинами с кранов. Краны можете погуглить сами, они все время то появляются, то протухают... Когда кошелек пополнен Ну вот с этого момента и начинается самое интересное. Зайдите в меню Помощь - Окно отладки - Консоль. Введите туда самую главную команду: Code: help Code: help sendtoaddress Напомню, что наша цель создать транзакцию вручную. Чтобы посмотреть, что примерно у нас должно получиться в итоге, можете посмотреть какую-нибудь транзакцию из своего кошелька. Введите в консоли Code: listunspent Увидите список непотраченных транзакций в вашем кошельке. Возьмите из списка txid любой транзакции и введите в консоль например Code: getrawtransaction 2e4308716eaaf6d27d2b91bfe2a78fc7819176966b619b6d8dc675f5373aa3d9 На выходе получите Quote 0200000000010218f739a82336bfec14c75a55ef28260b493ea89711319ba799368380328ddbe0000000001716001408a8ba315488b40811b9886ff4396e0c94ce183efeffffff 6cc66a094304e399a9678dfbf1f88a8f2223c67d38ad0c69f5a8426d54ab479a000000006a473044022062e7ba2ec9a4a26fba867624b7786f6e1cd11363186bcf73a5a163b2f32f1d1e022 04332986870d8e8d54545313deabab49586d851f7c74c185026b731b0e172d20b012103903c1d38 0a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdffeffffff02 02450f000000000017a9148536bef594948115aa8ea99ce415767df30f8a3a87a06f2200000000001976a9140bb5f9fffd2c0f7bbb9dd4116341f3fba10d041888ac02 4730440220724686754b2c80564048cbafab84e07ab91f9e9a64c1baca8e726de70209e9cd02201f9 1ac61ce789e7032c2561dc24e26adca029d6449d82e96c252ace18d4dbba40121026f627029af8b372f3c81c5ae5cb4ce0971fb6d059e22351bc7191d9841a4156e00ac1f1800 Давайте на этом примере разберем: кто тут есть ху? Заголовок транзакции
Вход 1
Вход 2
Число выходов транзакции: 02 Выход 1 Количество сатошей в выходе 02450f0000000000 (0x00000000000f4502 = 1000706 = 0,01000706 BTC) Длина запирающего скрипта 17 (0x17 = 23 байта) Запирающий скрипт a9148536bef594948115aa8ea99ce415767df30f8a3a87 Выход 2 Количество сатошей в выходе a06f220000000000 Длина запирающего скрипта 19 Запирающий скрипт 76a9140bb5f9fffd2c0f7bbb9dd4116341f3fba10d041888ac Число SegWit данных в первом входе: 02 SegWit блок 1 Длина данных: 47 (71 байт) Данные: 30440220724686754b2c80564048cbafab84e07ab91f9e9a64c1baca8e726de70209e9cd02201f9 1ac61ce789e7032c2561dc24e26adca029d6449d82e96c252ace18d4dbba401 SegWit блок 2 Длина данных: 21 (33 байта) Данные: 026f627029af8b372f3c81c5ae5cb4ce0971fb6d059e22351bc7191d9841a4156e Число SegWit данных во втором входе: 00 Locktime: ac1f1800 (0x00181fac = 1580972) Вот собственно и вся транзакция txid = 2e4308716eaaf6d27d2b91bfe2a78fc7819176966b619b6d8dc675f5373aa3d9. Далее я попробую описать по шагам процесс создания новой транзакции, которая будет тратить какой-нибудь из выходов. Кстати говоря, декодировать RAW транзакцию конечно удобней не вручную, а в консоли: Code: decoderawtransaction 0200000000010218f739a82336bfec14c75a55ef28260b493ea89711319ba799368380328ddbe0000000001716001408a8ba315488b40811b9886ff4396e0c94ce183efeffffff6cc66a094304e399a9678dfbf1f88a8f2223c67d38ad0c69f5a8426d54ab479a000000006a473044022062e7ba2ec9a4a26fba867624b7786f6e1cd11363186bcf73a5a163b2f32f1d1e02204332986870d8e8d54545313deabab49586d851f7c74c185026b731b0e172d20b012103903c1d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdffeffffff0202450f000000000017a9148536bef594948115aa8ea99ce415767df30f8a3a87a06f2200000000001976a9140bb5f9fffd2c0f7bbb9dd4116341f3fba10d041888ac024730440220724686754b2c80564048cbafab84e07ab91f9e9a64c1baca8e726de70209e9cd02201f91ac61ce789e7032c2561dc24e26adca029d6449d82e96c252ace18d4dbba40121026f627029af8b372f3c81c5ae5cb4ce0971fb6d059e22351bc7191d9841a4156e00ac1f1800 1 Даст красивый результат: Code: { Создаем транзакцию Итак, давайте создадим RAW транзакцию в блокноте! Сначала определимся, какой выход мы будем тратить? Давайте рассмотрим по отдельности оба выхода. Выход 1. Code: { В этом выходе запирающий скрипт имеет вид Code: OP_HASH160 8536bef594948115aa8ea99ce415767df30f8a3a OP_EQUAL В переводе на русский это означает: Quote биткоины с этого выхода сможет потратить тот, кто напишет в транзакции отпирающий скрипт хэш которого равен 8536bef594948115aa8ea99ce415767df30f8a3a. Отпирающий скрипт в добавок должен вернуть значение OP_TRUE. Такие запирающие скрипты являются стандартными и называются P2SH (плачу за правильный хэша скрипта). Выход 2. Code: { тут запирающий скрипт другой Code: OP_DUP OP_HASH160 0bb5f9fffd2c0f7bbb9dd4116341f3fba10d0418 OP_EQUALVERIFY OP_CHECKSIG В переводе на человеческий скрипт означает: Quote биткоины с этого выхода сможет потратить тот, кто предоставит публичный ключ хэш которого равен 0bb5f9fffd2c0f7bbb9dd4116341f3fba10d0418 и при этом у транзакции будет правильная цифровая подпись проверяемая данным публичным ключем. Такие запирающие скрипты являются стандартными и называются P2PKH (плачу за правильный хэш публичного ключа). Для более простого объяснения давайте потратим второй выход. Первым делом, надо взять txid = 2e4308716eaaf6d27d2b91bfe2a78fc7819176966b619b6d8dc675f5373aa3d9 и перевернуть его с ног на голову! То есть ИД входящей транзакции: d9a33a37f575c68d6d9b616b96769181c78fa7e2bf912b7dd2f6aa6e7108432e На втором выходе транзакции имеется 0.022568 BTC, давайте потратим 0.02256 BTC (остальное оставим майнерам как комиссию) Надо пересчитать сумму в сатоши, потом перевести в шестнадцатиричный вид, потом перевернуть с ног на голову 0.02256 BTC = 2256000 sat = 0x0000000000226C80 = 806С220000000000 Таким образом, на текущий момент, мы имеем следующие данные для транзакции: Версия: 02000000 Флаг: не будет флага, шлем обычную транзакцию без SegWit Число входов: 01 Вход 1 ИД входящей транзакции: 2e4308716eaaf6d27d2b91bfe2a78fc7819176966b619b6d8dc675f5373aa3d9 Номер выхода у входящей транзакции: 01000000 (второй выход имеет номер 1 потому что нумерация начинается с нуля) Размер тратящего скрипта: (пока нет, см. далее) Тратящий скрипт: (пока нет, см. далее) sequence: feffffff (это для простоты пусть будет такая постоянная величина) Число выходов транзакции: 01 Выход 1 Количество сатошей в выходе: 806С220000000000 Длина запирающего скрипта: (пока нет, см. далее) Запирающий скрипт: (пока нет, см. далее) Как видим, для транзакции не зватает скриптов. Тратящий скрипт Тратящим скриптом для второго выхода служит просто публичный ключ от адреса mgasj7m5vQWp4zixtHjiEgpGxex6ZCvhX8 Получим публичный ключ командой консоли Code: getaddressinfo mgasj7m5vQWp4zixtHjiEgpGxex6ZCvhX8 Quote { "address": "mgasj7m5vQWp4zixtHjiEgpGxex6ZCvhX8", "scriptPubKey": "76a9140bb5f9fffd2c0f7bbb9dd4116341f3fba10d041888ac", "ismine": true, "iswatchonly": false, "isscript": false, "iswitness": false, "pubkey": "03903c1d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdf", "iscompressed": true, "label": "wallet", "timestamp": 1566751726, "hdkeypath": "m/0'/0'/132'", "hdseedid": "40a447c698b59ef21f858f0b7362d0fc96a7bfae", "hdmasterkeyid": "40a447c698b59ef21f858f0b7362d0fc96a7bfae", "labels": [ { "name": "wallet", "purpose": "receive" } ] } Получаем для входа нашей новой транзакции: Размер тратящего скрипта: 21 (это в шестнадцатиричном виде, а в десятичном 33 байта) Тратящий скрипт: 03903c1d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdf Осталось написать запирающий скрипт Я хочу послать биткоины на адрес 2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc. Получить скрипт для этого адреса можно командой консоли Code: validateaddress 2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc Quote { "isvalid": true, "address": "2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc", "scriptPubKey": "a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b0487", "isscript": true, "iswitness": false } Получаем для выхода нашей новой транзакции: Длина запирающего скрипта: 17 (или 23 байта) Запирающий скрипт: a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b0487 Почти конечный итог Версия: 02000000 Флаг: не будет флага, шлем обычную транзакцию без SegWit Число входов: 01 Вход 1 ИД входящей транзакции: d9a33a37f575c68d6d9b616b96769181c78fa7e2bf912b7dd2f6aa6e7108432e Номер выхода у входящей транзакции: 01000000 Размер тратящего скрипта: 21 Тратящий скрипт: 03903c1d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdf sequence: feffffff Число выходов транзакции: 01 Выход 1 Количество сатошей в выходе: 806С220000000000 Длина запирающего скрипта: 17 Запирающий скрипт: a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b0487 Locktime: 00000000 (этот параметр можно делать любым, он игнорируется если sequence=feffffff или sequence=ffffffff ) Теперь открываем блокнот и пишем все в одну строчку Quote 0200000001d9a33a37f575c68d6d9b616b96769181c78fa7e2bf912b7dd2f6aa6e7108432e010000002103903c1d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdffeffffff01806C22000000000017a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b048700000000 Наконец-то... Наша транзакция почти готова, теперь ее надо подписать. Делается это опять в консоли Code: signrawtransactionwithwallet 0200000001d9a33a37f575c68d6d9b616b96769181c78fa7e2bf912b7dd2f6aa6e7108432e010000002103903c1d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdffeffffff01806C22000000000017a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b048700000000 На выходе получим подписанную транзакцию! Quote { "hex": "0200000001d9a33a37f575c68d6d9b616b96769181c78fa7e2bf912b7dd2f6aa6e7108432e01000 0006a47304402205fd610204bc40841382e299d0d326026ae3089eb4e570afb280967cfd271f1f4 0220701479b8fce69904d8d0d269b715c558954c30dddbaed369d4545fb257b1629c012103903c1 d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdffeffffff01806c220000 00000017a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b048700000000", "complete": true } УРА! Ну вот и все. Теперь можно отправить подписанную транзакцию в сеть командой консоли Code: sendrawtransaction 0200000001d9a33a37f575c68d6d9b616b96769181c78fa7e2bf912b7dd2f6aa6e7108432e010000006a47304402205fd610204bc40841382e299d0d326026ae3089eb4e570afb280967cfd271f1f40220701479b8fce69904d8d0d269b715c558954c30dddbaed369d4545fb257b1629c012103903c1d380a2b05c96e8ad40cbc8a1b647cadef7a1deff8df6b8528a337465cdffeffffff01806c22000000000017a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b048700000000 Ответом получим txid Quote eab054c14f9fd52d103fceb33945aa682283c9148c5be7834adf0a46c579538d Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: kzv on November 02, 2019, 03:59:06 PM Еще разок
Итак, в предыдущем описании, транзакцией eab054c14f9fd52d103fceb33945aa682283c9148c5be7834adf0a46c579538d были отправлены 0.02256 BTC на адрес 2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc. Разумеется это мой адрес и поэтому я могу с него потратить опять, на другой адрес... Давайте представим гипотетическую ситуацию, что у вас на некотором адресе (2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc) лежат биткоины и вы хотите для них создать RAW транзакцию перевода на другой адрес (например на 2NE6VUq8sVhihfgKcUeRGWYqkLo29SbLRgS). Распишем алгоритм действий по шагам. 1. Ищем непотраченные входы для нашего адреса Code: listunspent 1 999999 "[\"2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc\"]" результат Quote [ { "txid": "eab054c14f9fd52d103fceb33945aa682283c9148c5be7834adf0a46c579538d", "vout": 0, "address": "2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc", "label": "", "redeemScript": "00142fcfe0ee792965d852f392c5fe53e2be71274ca8", "scriptPubKey": "a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b0487", "amount": 0.02256000, "confirmations": 205, "spendable": true, "solvable": true, "safe": true } ] 2. Получаем наш публичный ключ командой Code: getaddressinfo 2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc рузультат Quote { "address": "2MvAAds6k4M5Z6EvfyGJXfGrTP12hhT2TMc", "scriptPubKey": "a9141ff37fd7b7cb61c95ca8e98ae3349661d4a95b0487", "ismine": true, "iswatchonly": false, "isscript": true, "iswitness": false, "script": "witness_v0_keyhash", "hex": "00142fcfe0ee792965d852f392c5fe53e2be71274ca8", "pubkey": "029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6f", "embedded": { "isscript": false, "iswitness": true, "witness_version": 0, "witness_program": "2fcfe0ee792965d852f392c5fe53e2be71274ca8", "pubkey": "029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6f", "address": "tb1q9l87pmne99jas5hnjtzlu5lzhecjwn9gz3mcqt", "scriptPubKey": "00142fcfe0ee792965d852f392c5fe53e2be71274ca8" }, "label": "", "timestamp": 1566751726, "hdkeypath": "m/0'/0'/135'", "hdseedid": "40a447c698b59ef21f858f0b7362d0fc96a7bfae", "hdmasterkeyid": "40a447c698b59ef21f858f0b7362d0fc96a7bfae", "labels": [ { "name": "", "purpose": "receive" } ] } 3. Формируем запирающий скрипт для получателя Code: validateaddress 2NE6VUq8sVhihfgKcUeRGWYqkLo29SbLRgS результат Quote { "isvalid": true, "address": "2NE6VUq8sVhihfgKcUeRGWYqkLo29SbLRgS", "scriptPubKey": "a914e4b3d47cc59f2c895a4c09107c98f8acc476c96887", "isscript": true, "iswitness": false } 4. Открываем блокнот и пишем заголовок и вход нашей новой транзакции Quote 0200000001eab054c14f9fd52d103fceb33945aa682283c9148c5be7834adf0a46c579538d0000000021029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6ffeffffff Здесь сделана ошибка: предыдущая txid не перевернута с ног на голову. Надо: eab054c14f9fd52d103fceb33945aa682283c9148c5be7834adf0a46c579538d -> 8d5379c5460adf4a83e75b8c14c9832268aa4539b3ce3f102dd59f4fc154b0ea Руками такое делать неудобно, можно оставить с ошибкой, потом исправим. 5. Дописываем в блокноте выход транзакции Сначала решаем сколько будем тратить. У нас есть 0.02256000 BTC, надо потратить чуть меньше чтобы осталось на комиссию. Потратим 0.0225 BTC = 2250000 sat = 0x0000000000225510 -> 1055220000000000 01105522000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c9688700000000 Все вместе получилось Quote 0200000001eab054c14f9fd52d103fceb33945aa682283c9148c5be7834adf0a46c579538d00000 00021029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6ffeffffff 01105522000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c9688700000000 6. Теперь исправим ошибку в пункте 4. Для этого декодируем нашу транзакцию в консоли и посмотрим правильный txid Quote decoderawtransaction 0200000001eab054c14f9fd52d103fceb33945aa682283c9148c5be7834adf0a46c579538d0000000021029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6ffef fffff01105522000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c9688700000000 результат Quote { "txid": "bf75f3c31878875629d83650b978d55889aae7efa353f003a6cf03f7af8625d5", "hash": "bf75f3c31878875629d83650b978d55889aae7efa353f003a6cf03f7af8625d5", "version": 2, "size": 116, "vsize": 116, "weight": 464, "locktime": 0, "vin": [ { "txid": "8d5379c5460adf4a83e75b8c14c9832268aa4539b3ce3f102dd59f4fc154b0ea", "vout": 0, "scriptSig": { "asm": "405 OP_UNKNOWN [error]", "hex": "029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6f" }, "sequence": 4294967294 } ], "vout": [ { "value": 0.02250000, "n": 0, "scriptPubKey": { "asm": "OP_HASH160 e4b3d47cc59f2c895a4c09107c98f8acc476c968 OP_EQUAL", "hex": "a914e4b3d47cc59f2c895a4c09107c98f8acc476c96887", "reqSigs": 1, "type": "scripthash", "addresses": [ "2NE6VUq8sVhihfgKcUeRGWYqkLo29SbLRgS" ] } } ] } 7. Пишем правильную RAW транзакцию 02000000018d5379c5460adf4a83e75b8c14c9832268aa4539b3ce3f102dd59f4fc154b0ea0000000021029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6ffef fffff01105522000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c9688700000000 8. Подписываем нашу транзакцию Code: signrawtransactionwithwallet 02000000018d5379c5460adf4a83e75b8c14c9832268aa4539b3ce3f102dd59f4fc154b0ea0000000021029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6ffeffffff01105522000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c9688700000000 Готово! Quote { "hex": "020000000001018d5379c5460adf4a83e75b8c14c9832268aa4539b3ce3f102dd59f4fc154b0ea0 0000000171600142fcfe0ee792965d852f392c5fe53e2be71274ca8feffffff0110552200000000 0017a914e4b3d47cc59f2c895a4c09107c98f8acc476c968870247304402206a59b9212efdacc2e 6367c4a45b4e26b3a73a64d564c983cb82edcd2ae54564702204a5ec3e3e729009cfc855b0c23bf 2468e423344993847a37394d3810a1c7f1bb0121029501ef2a97b855ab35898a6f5901c6538bab0 8d8244cd88b213e777095a17c6f00000000", "complete": true } Эту подписанную транзакцию можно теперь распечатать на бумаге и положить в банковскую ячейку. Когда придет время, с бумажки транзакуию можно переписать в блокнот и отправить в сеть биткоина командой Code: sendrawtransaction 020000000001018d5379c5460adf4a83e75b8c14c9832268aa4539b3ce3f102dd59f4fc154b0ea00000000171600142fcfe0ee792965d852f392c5fe53e2be71274ca8feffffff01105522000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c968870247304402206a59b9212efdacc2e6367c4a45b4e26b3a73a64d564c983cb82edcd2ae54564702204a5ec3e3e729009cfc855b0c23bf2468e423344993847a37394d3810a1c7f1bb0121029501ef2a97b855ab35898a6f5901c6538bab08d8244cd88b213e777095a17c6f00000000 Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: A-Bolt on November 02, 2019, 05:12:50 PM Сразу скажу, что я так и не понял - что здесь значит один байт, который я пометил коричневым! Это количество элементов witness для второго входа. https://bitcoincore.org/en/segwit_wallet_dev/#transaction-serialization (https://bitcoincore.org/en/segwit_wallet_dev/#transaction-serialization) Quote If a txin is not associated with any witness data, its corresponding witness field is an exact 0x00, indicating that the number of witness stack items is zero. Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: kzv on November 02, 2019, 05:28:29 PM Теперь, когда мы умеем писать в блокноте RAW транзакции, предлагаю рассмотреть примеры задач в которых такое умение может пригодиться.
Задача 1 Написать и отправить транзакцию такую, чтобы потратить биткоины смог только тот, кто знает пароль: 1234567 Решение Для решения поставленной задачи, первым делом надо написать запирающий скрипт. По русски это такая программа: Code: запомнить число 1234567 На языке биткоина это такая программа Code: OP_PUSHDATA 1234567 Когда программа написана на "языке биткоина", надо перевести ее на язык "шестнадцатиричных кодов" 1. Переводим пароль в 16-ричный вид и потом переворачиваем с ног на голову 1234567 = 0x12D687 -> 87D612 2. Ищем 16-ричные представления OP_ кодов тут https://en.bitcoin.it/wiki/Script OP_PUSHDATA - это просто число, равное количеству байт в наших данных. Наши данные (наш пароль 87D612) имеют длину 3 байта, значит в нашем скрипте OP_PUSHDATA = 03 OP_EQUAL = 87 OP_VERIFY = 69 Наш запирающий скрипт в шестнадцатиричном коде выглядит так Quote 0387D6128769 Хотите прикол? Правильность написания скрипта можно проверить все в той же консоли! Code: decodescript 0387D6128769 вывод Quote { "asm": "1234567 OP_EQUAL OP_VERIFY", "type": "nonstandard", "p2sh": "2MzutsTWTTCkuYxzjBZ8uzgHNfD4UbzQrRw", "segwit": { "asm": "0 71e00f2ece42680816b5dd46ff58e6c11dd90acdb94844c92e170e0be2634a4c", "hex": "002071e00f2ece42680816b5dd46ff58e6c11dd90acdb94844c92e170e0be2634a4c", "reqSigs": 1, "type": "witness_v0_scripthash", "addresses": [ "tb1qw8sq7tkwgf5qs944m4r07k8xcywajzkdh9yyfjfwzu8qhcnrffxq7r84qv" ], "p2sh-segwit": "2N5ghhRhhPrbRgNbEw3L9V2iSYHpvTNYLtV" } } Ответ нашей задачи: Code: sendtoaddress 2MzutsTWTTCkuYxzjBZ8uzgHNfD4UbzQrRw 0.0001 Будьте бдительны! Экспериментируйте только в тестовой сети, где биткоинов не жалко! Если пошлете подобную транзакцию в реальную сеть, то ее быстро заберет какой-нибудь автоматический бот. Проблема нашего скрипта не только в том, что пароль слишком легкий, но и в том что чтобы ее потратить, придется раскрыть пароль в явном виде и тогда любой сканер блокчейна может попробовать потратить биткоины вместо вас. Попробуем усложнить задачу. Задача 2 Написать и отправить транзакцию такую, чтобы потратить биткоины смог только тот, кто знает пароль: 1234567 и одновременно имеет приватный ключ от адреса 2NDHNjQRV1vH6QaG3sCh3w8mMJ1JNJzxVVd Решение. Первым делом надо заметить, что обладание приватным ключем можно подтвердить с помощью цифровой подписи и публичного ключа. Получим публичный ключ от адреса 2NDHNjQRV1vH6QaG3sCh3w8mMJ1JNJzxVVd (разумеется здесь должен быть ваш адрес, а не мой) командой Code: getaddressinfo 2NDHNjQRV1vH6QaG3sCh3w8mMJ1JNJzxVVd публичный ключ = 02cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e2 Таким образом, наша программа по русски будет звучать так: Code: запомнить число 1234567 Эта же программа на "языке биткоина" Code: OP_PUSHDATA 1234567 Вместо OP_EQUAL OP_VERIFY можно использовать оператор OP_EQUALVERIFY Вместо OP_CHECKSIG OP_VERIFY можно использовать OP_CHECKSIGVERIFY Тогда код немножко сократится Code: OP_PUSHDATA 1234567 Переводим в шестнадцатиричный вид. К счастью, публичный ключ переворачивать с ног на голову не требуется ) OP_PUSHDATA 1234567 = 03 87D612 OP_EQUALVERIFY = 88 OP_DUP = 76 OP_PUSHDATA 02cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e2 = 21 02cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e2 OP_EQUALVERIFY = 88 OP_CHECKSIGVERIFY = AD То есть записанный в строчку, скрипт будет такой: Quote 0387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e 288AD Проверка Code: decodescript 0387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e288AD Вуаля Quote { "asm": "1234567 OP_EQUALVERIFY OP_DUP 02cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e2 OP_EQUALVERIFY OP_CHECKSIGVERIFY", "type": "nonstandard", "p2sh": "2NBjvwZHPohf4c2UBApLf1EyDC5Fd3pzo5e", "segwit": { "asm": "0 ccfbe50d65e03469f59971d8179ecae57aeaa4728638e3fbf301c2eb3a31ea16", "hex": "0020ccfbe50d65e03469f59971d8179ecae57aeaa4728638e3fbf301c2eb3a31ea16", "reqSigs": 1, "type": "witness_v0_scripthash", "addresses": [ "tb1qena72rt9uq6xnavew8vp08k2u4aw4frjscuw87lnq8pwkw33agtqpa2axv" ], "p2sh-segwit": "2N1M8RqoLGdheYDR2TZSupsLBMtxH7kWzgz" } } Ответ Quote sendtoaddress 2NBjvwZHPohf4c2UBApLf1EyDC5Fd3pzo5e 0.0001 Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: Coin-1 on November 08, 2019, 11:07:54 PM Статья о RAW-транзакциях Bitcoin добротная, оформлена в цвете, выглядит симпатично, написана доходчиво. Могу добавить, что создавать вручную и подписывать транзакции можно ещё при помощи WEB-кошелька coinb.in (https://coinb.in/), который написан на языке HTML+Javascript и работает в браузере, в том числе, в офлайне.
Первым делом, надо взять txid = 2e4308716eaaf6d27d2b91bfe2a78fc7819176966b619b6d8dc675f5373aa3d9 и перевернуть его с ног на голову! Уточню, что это 256-битное число нужно "перевернуть" побайтно, точнее изменить его порядок байтов (https://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D1%80%D1%8F%D0%B4%D0%BE%D0%BA_%D0%B1%D0%B0%D0%B9%D1%82%D0%BE%D0%B2) с "Little Endian" (от младшего к старшему) на "Big Endian" (от старшего к младшему). Например, имеется шестнадцатиричное 32-битное число 0x12345678, записанное в "Little Endian". При его переводе в "Big Endian" нужно оперировать парами символов, которые вместе составляют 1 байт. Должна получиться цифра не 0x87654321, а 0x78563412. "Little Endian" широко используется в архитектуре процессоров x86, поэтому, наверно, Сатоши Накамото применил этот порядок байтов в протоколе Bitcoin. Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: igor72 on November 11, 2019, 06:39:17 AM Задача 2 Самого интересного для себя я не увидел. Допустим, у меня есть пароль и приватный ключ от этого адреса, что мне с этим всем делать? Нанимать программиста?Написать и отправить транзакцию такую, чтобы потратить биткоины смог только тот, кто знает пароль: 1234567 и одновременно имеет приватный ключ от адреса 2NDHNjQRV1vH6QaG3sCh3w8mMJ1JNJzxVVd Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: kzv on November 11, 2019, 08:56:51 AM Задача 3
Перевести на свой адрес 2N6js52Uiz9Ry2qnJL2YNrNra9Dg9Q9vM96 монеты с адреса 2NAmabQLVgwWo7ZTun9a1tVQr6uWxXHcbgf, если известно, что тратящий скрипт должен содержать пароль 1234567 и должен быть подписан приватным ключем с адреса 2N6pgTPjjHbpTaT6YgHyZDnQewVjrzqRfsb Решение. Для решения задачи нам должен быть известен A) приватный ключ от адреса 2N6pgTPjjHbpTaT6YgHyZDnQewVjrzqRfsb. Этот приватный ключ должен быть в нашем кошельке. B) запирающий скрипт транзакции, которая перевела биткоины на адрес 2NAmabQLVgwWo7ZTun9a1tVQr6uWxXHcbgf Пусть запирающий скрипт такой же как в Задаче 2: Code: OP_PUSHDATA 1234567 Мы знаем этот скрипт и теперь чтобы потратить биткоины, нам нужно дописать в начало скрипта правильные данные. Правильные - значит такие, чтобы скрипт выдавал логическую истину. C) нужно знать txid той транзакции, которая перевела биткоины на 2NAmabQLVgwWo7ZTun9a1tVQr6uWxXHcbgf Эту информацию можно взять из любого блокэксплорера, либо получить самостоятельно командами Code: importaddress 2NAmabQLVgwWo7ZTun9a1tVQr6uWxXHcbgf Quote [ { "txid": "28ccc196d9cb7d4f62ee751f579195d5211c86c6badef89b63c974b9f23338b5", "vout": 0, "address": "2NAmabQLVgwWo7ZTun9a1tVQr6uWxXHcbgf", "label": "", "scriptPubKey": "a914c037d15a6e619a3fca62c3e25fe349e922f8318e87", "amount": 0.00100000, "confirmations": 5, "spendable": false, "solvable": false, "safe": true } ] Эти данные (txid, vout, amount) нам пригодятся в четвертом действии... Когда мы знаем все что нам нужно, можно решать задачу 1. Пишем входные данные по русски Code: поместить на вход публичный ключ 02bb64584a7b640e0a8643b71ed9b3644551a3f93697aef19f30ae436eb4c2e39b Переводим на язык скрипта биткоина Code: OP_PUSHDATA 02cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e2 2. Объединяем входные данные и запирающий скрипт вместе, чтобы получился такой отпирающий скрипт Code: OP_PUSHDATA 02cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e2 3. Записываем тратящий скрипт в шестнадцатиричном коде Code: 2102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e2 Запоминаем размер скрипта в байтах: 34+4+4+1+1+34+1+1 = 80 = 0x50 Все в строчку Quote 2102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20387D612038 7D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e288 AD И проверяем на всякий случай Code: decodescript 2102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20387D6120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e288AD 3. Формируем запирающий скрипт для получателя (в условии задачи получатель это адрес 2N6js52Uiz9Ry2qnJL2YNrNra9Dg9Q9vM96) Code: validateaddress 2N6js52Uiz9Ry2qnJL2YNrNra9Dg9Q9vM96 получаем Quote "scriptPubKey": "a914e4b3d47cc59f2c895a4c09107c98f8acc476c96887" 4. Открываем блокнот и пишем заголовок и вход нашей новой транзакции ЗаголовокЧисло_входовТранзакцияНомер_выходаТратящий_скриптfeffffff 020000000128ccc196d9cb7d4f62ee751f579195d5211c86c6badef89b63c974b9f23338b500000000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20387D612038 7D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e288 ADFEFFFFFF 5. Дописываем выход. На адрес 2N6js52Uiz9Ry2qnJL2YNrNra9Dg9Q9vM96 мы переведем 0.0008 биткоинов = 80000 sat = 0x0000000000013880 hex -> 0838010000000000 Число_выходовЧисло_сатошейЗапирающий_скрипт00000000 01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c9688700000000 6. Проверяем все вместе Code: decoderawtransaction 020000000128ccc196d9cb7d4f62ee751f579195d5211c86c6badef89b63c974b9f23338b500000000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20387D6120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e288ADFEFFFFFF01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c9688700000000 Берем правильное значение txid Quote "vin": [ Получаем правильную RAW транзакцию{ "txid": "b53833f2b974c9639bf8debac6861c21d59591571f75ee624f7dcbd996c1cc28", 0200000001b53833f2b974c9639bf8debac6861c21d59591571f75ee624f7dcbd996c1cc2800000000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20 387D6120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7e ae1f90e288ADFEFFFFFF01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc47 6c9688700000000 7. Эта транзакция не может быть отправлена в сеть, потому что ее еще надо подписать приватным ключем от адреса 2N6pgTPjjHbpTaT6YgHyZDnQewVjrzqRfsb К сожалению стандартный клиент биткоина не умеет подписывать нестандартные отпирающие скрипты и я не могу пока найти простого способа - как добавить цифровую подпись к полученной транзакции. Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: igor72 on November 11, 2019, 09:32:32 AM Получаем правильную RAW транзакцию 0200000001b53833f2b974c9639bf8debac6861c21d59591571f75ee624f7dcbd996c1cc2800000000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20 387D6120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7e ae1f90e288ADFEFFFFFF01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc47 6c9688700000000 Quote 7. Эта транзакция не может быть отправлена в сеть, потому что ее еще надо подписать приватным ключем от адреса 2N6pgTPjjHbpTaT6YgHyZDnQewVjrzqRfsb Может можно как-то openssl прикрутить для подписи, там вроде есть такая опция?К сожалению стандартный клиент биткоина не умеет подписывать нестандартные отпирающие скрипты и я не могу пока найти простого способа - как добавить цифровую подпись к полученной транзакции. Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: kzv on November 11, 2019, 11:15:45 AM Получаем правильную RAW транзакцию 0200000001b53833f2b974c9639bf8debac6861c21d59591571f75ee624f7dcbd996c1cc2800000000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20 387D6120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7e ae1f90e288ADFEFFFFFF01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc47 6c9688700000000 Это и есть она переведенная в LE. Тут такая заморочка с этими переводами: команда listunspent 0 9999999 [\"2NAmabQLVgwWo7ZTun9a1tVQr6uWxXHcbgf\"] вернет txid = 28ccc196d9cb7d4f62ee751f579195d5211c86c6badef89b63c974b9f23338b5 В RAW транзакцию это число надо перевернуть в LE. Но блин, реально, руками это переворачивание делать муторно и легко ошибиться. Поэтому я первоначальную RAW транзакцию пишу специально с ошибкой - не переворачивая txid 020000000128ccc196d9cb7d4f62ee751f579195d5211c86c6badef89b63c974b9f23338b500000000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20 387D6120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7e ae1f90e288ADFEFFFFFF01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc47 6c9688700000000 Чтобы исправить ошибку и не делать это вручную, я скармливаю неправильную транзакцию команде decoderawtransaction 020000000128ccc196d9cb7d4f62ee751f579195d5211c86c6badef89b63c974b9f23338b500000 000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20387D6 120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f9 0e288ADFEFFFFFF01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc476c968 8700000000 на выходе получаю правильный, перевернутый хэш "txid": "b53833f2b974c9639bf8debac6861c21d59591571f75ee624f7dcbd996c1cc28" И пишу теперь уже правильную RAW транзакцию с правильным перевернутым в LE хэшем 0200000001b53833f2b974c9639bf8debac6861c21d59591571f75ee624f7dcbd996c1cc2800000000502102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7eae1f90e20 387D6120387D61288762102cb460ab0b6c7b551fa7faea9e38c1a327f3d2596362db453e514dc7e ae1f90e288ADFEFFFFFF01083801000000000017a914e4b3d47cc59f2c895a4c09107c98f8acc47 6c9688700000000 Quote 7. Эта транзакция не может быть отправлена в сеть, потому что ее еще надо подписать приватным ключем от адреса 2N6pgTPjjHbpTaT6YgHyZDnQewVjrzqRfsb Может можно как-то openssl прикрутить для подписи, там вроде есть такая опция?К сожалению стандартный клиент биткоина не умеет подписывать нестандартные отпирающие скрипты и я не могу пока найти простого способа - как добавить цифровую подпись к полученной транзакции. Точно знаю, что можно с помощью openssl, но пока не могу найти времени разобраться - как именно. В англоразделе тоже не могут помочь https://bitcointalk.org/index.php?topic=5199338.0 Вообще, думаю попробовать сделать свой первый коммит в исходник биткоина, где написать функцию которая будет подписывать нестандартные скрипты. Сделал фича реквест на гитхаб - никто не откликается https://github.com/bitcoin/bitcoin/issues/17400 Title: Re: Создаем RAW транзакцию Биткоин с нуля Post by: igor72 on November 11, 2019, 11:43:21 AM В RAW транзакцию это число надо перевернуть в LE. Но блин, реально, руками это переворачивание делать муторно и легко ошибиться. Quote Точно знаю, что можно с помощью openssl, но пока не могу найти времени разобраться - как именно. В англоразделе тоже не могут помочь https://bitcointalk.org/index.php?topic=5199338.0 Вообще, думаю попробовать сделать свой первый коммит в исходник биткоина, где написать функцию которая будет подписывать нестандартные скрипты. Сделал фича реквест на гитхаб - никто не откликается https://github.com/bitcoin/bitcoin/issues/17400 |