Учитывая необработанный стандартный Tx ( вики ):
01000000
01
26c07ece0bce7cda0ccd14d99e205f118cde27e83dd75da7b141fe487b5528fb
00000000
8b
48304502202b7e37831273d74c8b5b1956c23e79acd660635a8d1063d413c50b218eb6bc8a022100a10a3a7b5aaa0f07827207daf81f718f51eeac96695cf1ef9f2020f21a0de02f01410452684bce6797a0a50d028e9632be0c2a7e5031b710972c2a3285520fb29fcd4ecfb5fc2bf86a1e7578e4f8a305eeb341d1c6fc0173e5837e2d3c7b178aade078
ffffffff
02
b06c191e01000000
19
76a9143564a74f9ddb4372301c49154605573d7d1a88fe88ac
00e1f50500000000
19
76a914010966776006953d5567439e5e39f86a0d273bee88ac
00000000
и закрытый ключ:
18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725
Как построить новую транзакцию, чтобы выкупить монету из второго выхода? Я долго пытался понять это, используя диаграмму etotheipi , но, похоже, не могу понять, как должна быть создана правильная транзакция. Пошаговое руководство приветствуется.
В этом ответе я рассмотрю шаги, необходимые для погашения второго выхода транзакции, перечисленного выше. Ответ будет ограничен выкупом вывода определенного типа, присутствующего в этой транзакции (вывод, который требует предоставления новой транзакции, подписанной закрытым ключом, чей хэш открытого ключа соответствует хешу в сценарии рассматриваемого вывода), как этот ответ уже довольно длинный, даже без учета других типов вывода.
Краткое резюме: мы начинаем с создания новой транзакции со scriptSig, содержащим scriptPubKey вывода, который мы хотим выкупить. scriptPubKey этой транзакции будет содержать скрипт, который платит по хэшу открытого ключа (биткойн-адреса). Мы выполняем двойной хэш SHA256 для этой транзакции с четырехбайтовым хэш-кодом типа SIGHASH_ALL, добавленным в конец. Мы подписываем этот хеш закрытым ключом, указанным выше. Затем scriptSig этой новой транзакции заменяется сценарием, который сначала помещает в стек подпись в кодировке DER, а также однобайтовый хэш-код типа SIGHASH_ALL, а затем соответствующий открытый ключ закрытого ключа в кодировке DER.
Пошаговое описание:
Мы начинаем создавать новую необработанную транзакцию, которую мы хешируем и подписываем.
01000000
01
eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
01000000
19
76a914010966776006953d5567439e5e39f86a0d273bee88ac
ffffffff
01
605af40500000000
19
76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
00000000
И, наконец, пишем четырехбайтный «тип хеш-кода» (1 в нашем случае):01000000
Теперь у нас есть следующие необработанные данные о транзакциях:
01000000
01
eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
01000000
19
76a914010966776006953d5567439e5e39f86a0d273bee88ac
ffffffff
01
605af40500000000
19
76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
00000000
01000000
(этап подписания) Теперь мы дважды хешируем всю эту структуру SHA256, что дает хэш9302bda273a887cb40c13e02a50b4071a31fd3aae3ae04021b0b843dd61ad18e
30460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc06
К этой подписи мы добавляем однобайтный хеш-код типа: 01
. Открытый ключ:0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
Мы создаем окончательный scriptSig путем объединения:
Затем мы заменяем однобайтовое поле длины varint из шага 5 на длину данных из шага 16. Длина составляет 140 байтов или 0x8C байтов:8c
4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
Мы завершаем удалением четырехбайтового типа хэш-кода, который мы добавили на шаге 13, и получаем следующий поток байтов, который является финальной транзакцией:
01000000
01
eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
01000000
8c
4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
ffffffff
01
605af40500000000
19
76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
00000000
Пример кода Python:
Я создал пример скрипта Python, который делает все вышеперечисленное. Оно намеренно сделано как можно более подробным и подробно прокомментировано, с минимальным количеством функций, чтобы оно напоминало пошаговое руководство выше. Количество строк кода можно легко сократить до половины, но я предпочитаю публиковать его в таком подробном формате, поскольку считаю, что его проще всего следовать (т. е. без «прыжков» вперед и назад по функциям). Скрипт содержит 76 непустых строк без комментариев. Сценарий зависит от bitcointools (для сериализации и десериализации транзакций и кодирования/декодирования base58) и ecdsa_ssl.py.из моего форка репозитория joric's brutus (для создания пар открытых/закрытых ключей EC и подписи ECDSA). Самый простой способ запустить скрипт — клонировать bitcointools в папку и поместить ecdsa_ssl.py из указанного выше URL-адреса в ту же папку вместе с этим скриптом и запустить скрипт оттуда. Вы захотите заменить адрес в SEND_TO_ADDRESS
переменной в этом скрипте адресом, на который вы хотите отправить монеты, если вы не чувствуете себя щедрым :).
#биткоинтулс from deserialize import parse_Transaction, коды операций из BCDataStream импортировать BCDataStream из base58 импортировать bc_address_to_hash_160, b58decode, public_key_to_bc_address, hash_160_to_bc_address импортировать ecdsa_ssl импортировать Crypto.Hash.SHA256 как sha256 импортировать Crypto.Random #транзакция, с которой мы хотим выкупить выход HEX_TRANSACTION="010000000126c07ece0bce7cda0ccd14d99e205f118cde27e83dd75da7b141fe487b5528fb000000008b48304502202b7e37831273d74c8b5b1956c23e79acd660635a8d1063d413c50b218eb6bc8a022100a10a3a7b5aaa0f07827207daf81f718f51eeac96695cf1ef9f2020f21a0de02f01410452684bce6797a0a50d028e9632be0c2a7e5031b710972c2a3285520fb29fcd4ecfb5fc2bf86a1e7578e4f8a305eeb341d1c6fc0173e5837e2d3c7b178aade078ffffffff02b06c191e010000001976a9143564a74f9ddb4372301c49154605573d7d1a88fe88ac00e1f505000000001976a914010966776006953d5567439e5e39f86a0d273bee88ac00000000" #Вывод для погашения. должен существовать в HEX_TRANSACTION ВЫХОД_ИНДЕКС=1 #адрес, на который мы хотим отправить погашенные монеты. #ЗАМЕНИТЕ СВОИМ АДРЕСОМ, если только вы не чувствуете себя щедрым SEND_TO_ADDRESS="1L4xtXCdJNiYnyqE6UsB8KSJvqEuXjz6aK" #комиссия, которую мы хотим заплатить (в BTC) TX_FEE=0,001 #константа, определяющая количество сатоши на BTC МОНЕТА=100000000 #constant используется для определения того, какая часть транзакции хешируется. SIGHASH_ALL=1 #private key, чей открытый ключ хэшируется на хэш, содержащийся в scriptPubKey выходного номера *OUTPUT_INDEX* в транзакции, описанной в HEX_TRANSACTION PRIVATE_KEY=0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725 защита dsha256 (данные): вернуть sha256.new (sha256.new (данные). дайджест ()). дайджест () tx_data=HEX_TRANSACTION.decode('hex_codec') tx_hash = dsha256 (tx_data) #здесь мы используем биткойнтулс для разбора транзакции. это дает легкий доступ к различным полям транзакции, из которых мы хотим выкупить вывод поток = BCDataStream() поток.запись (tx_data) tx_info = parse_Transaction (поток) если len(tx_info['txOut']) < (OUTPUT_INDEX+1): поднять RuntimeError, «в транзакции, которую вы пытаетесь использовать, есть только %d выходных данных. вы хотите использовать выходной индекс %d» % (len(tx_info['txOut']), OUTPUT_INDEX) #этот словарь используется для хранения значений различных полей транзакций # это полезно, потому что нам нужно построить одну транзакцию для хеширования и подписи # и еще одна, которая будет последней транзакцией tx_fields = {} ##здесь мы начинаем создавать транзакцию, которую мы хешируем и подписываем sign_tx = BCDataStream() ##сначала пишем номер версии, который равен 1 tx_fields['версия'] = 1 sign_tx.write_int32 (tx_fields ['версия']) ##тогда мы записываем количество входов транзакции, то есть один tx_fields['num_txin'] = 1 sign_tx.write_compact_size (tx_fields ['num_txin']) ##затем мы записываем фактические данные транзакции #'prevout_hash' tx_fields['prevout_hash'] = tx_hash sign_tx.write(tx_fields['prevout_hash']) #хеш транзакции, из которой мы хотим выкупить вывод #'prevout_n' tx_fields['output_index'] = ВЫХОД_ИНДЕКС sign_tx.write_uint32(tx_fields['output_index']) # какой вывод транзакции с идентификатором tx 'prevout_hash' мы хотим погасить? ##далее идет часть ввода транзакции. здесь мы размещаем скрипт *выхода*, который мы хотим выкупить tx_fields['scriptSigHash'] = tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey'] #сначала напишите размер sign_tx.write_compact_size (len (tx_fields ['scriptSigHash'])) #тогда данные sign_tx.write(tx_fields['scriptSigHash']) #'последовательность' tx_fields['последовательность'] = 0xffffffff sign_tx.write_uint32 (tx_fields ['последовательность']) ##затем пишем количество выходов транзакции. мы просто будем использовать один выход в этом примере tx_fields['num_txout'] = 1 sign_tx.write_compact_size (tx_fields ['num_txout']) ##затем мы записываем фактические выходные данные транзакции #мы выкупим все из исходного вывода за вычетом TX_FEE tx_fields['value'] = tx_info['txOut'][OUTPUT_INDEX]['value']-(TX_FEE*COIN) sign_tx.write_int64 (tx_fields ['значение']) ##здесь находится наш scriptPubKey (скрипт, который выплачивает по адресу) # нам нужен следующий скрипт: #"OP_DUP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG" address_hash = bc_address_to_hash_160(SEND_TO_ADDRESS) #chr(20) — длина address_hash (20 байт или 160 бит). scriptPubKey = chr(коды операций.OP_DUP) + chr(коды операций.OP_HASH160) + \ chr(20) + address_hash + chr(коды операций.OP_EQUALVERIFY) + chr(коды операций.OP_CHECKSIG) #сначала напишите длину этого куска данных tx_fields['scriptPubKey'] = скриптPubKey sign_tx.write_compact_size (len (tx_fields ['scriptPubKey'])) #тогда данные sign_tx.write(tx_fields['scriptPubKey']) #записать время блокировки (0) tx_fields['время блокировки'] = 0 sign_tx.write_uint32 (tx_fields ['время блокировки']) # и тип хэш-кода (1) tx_fields['hash_type'] = SIGHASH_ALL sign_tx.write_int32 (tx_fields ['hash_type']) #затем получаем хэш транзакции без подписи (хэш, который мы подписываем с помощью нашего закрытого ключа) hash_scriptless = dsha256 (sign_tx.input) ##теперь мы начнем с материала ECDSA. ## мы создаем закрытый ключ из предоставленных данных закрытого ключа и подписываем им hash_scriptless ## мы также проверяем, что соответствующий публичный ключ закрытого ключа действительно может погасить указанный вывод k = ecdsa_ssl.KEY() k.generate(('%064x' % PRIVATE_KEY).decode('hex')) #здесь мы извлекаем данные открытого ключа, сгенерированные из предоставленного закрытого ключа pubkey_data = k.get_pubkey() #затем мы создаем подпись поверх хэша транзакции без подписи sig_data=k.sign(hash_scriptless) # к концу подписи добавляется однобайтовый «хеш-тип» (https://en.bitcoin.it/wiki/OP_CHECKSIG) sig_data = sig_data + chr(SIGHASH_ALL) #давайте проверим, что предоставленный приватный ключ действительно может выкупить рассматриваемый вывод if (bc_address_to_hash_160(public_key_to_bc_address(pubkey_data)) != tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey'][3:-2]): байты = b58decode(SEND_TO_ADDRESS, 25) поднять RuntimeError, "Предоставленный закрытый ключ не может быть использован для выкупа выходного индекса %d\nВам необходимо предоставить закрытый ключ для адреса %s" % \ (OUTPUT_INDEX, hash_160_to_bc_address(tx_info['txOut'][OUTPUT_INDEX]['scriptPubKey'][3:-2], bytes[0])) ##теперь мы начинаем создавать финальную транзакцию. это дубликат транзакции без подписи, ## со scriptSig, заполненным скриптом, который помещает подпись, однобайтовый хеш-код и открытый ключ сверху в стек final_tx = BCDataStream() final_tx.write_int32 (tx_fields ['версия']) final_tx.write_compact_size (tx_fields ['num_txin']) final_tx.write(tx_fields['prevout_hash']) final_tx.write_uint32 (tx_fields ['output_index']) ##теперь нам нужно написать сам скриптSig. ## состоит из закодированных по DER значений r и s из подписи, однобайтового типа хэш-кода и открытого ключа в несжатом формате ## нам также нужно добавить длину этих двух фрагментов данных (закодированных как один байт ##, содержащий длину), перед каждой частью данных. эта длина является опкодом скрипта, который сообщает ## Интерпретатор биткойн-скриптов для помещения в стек следующих x байтов scriptSig = chr (len (sig_data)) + sig_data + chr (len (pubkey_data)) + pubkey_data #сначала напишите длину этих данных final_tx.write_compact_size (длина (scriptSig)) #тогда данные final_tx.write(scriptSig) ##а затем просто пишем те же данные после scriptSig, что и в безподписной транзакции, # исключая тип четырехбайтового хеш-кода (поскольку он закодирован в одном байте, следующем за данными подписи) final_tx.write_uint32 (tx_fields ['последовательность']) final_tx.write_compact_size (tx_fields ['num_txout']) final_tx.write_int64 (tx_fields ['значение']) final_tx.write_compact_size (len (tx_fields ['scriptPubKey'])) final_tx.write(tx_fields['scriptPubKey']) final_tx.write_uint32 (tx_fields ['время блокировки']) #выводит окончательную транзакцию в шестнадцатеричном формате (может использоваться как аргумент для sendrawtransaction биткойна) распечатать final_tx.input.encode('hex')
k.generate(('%064x' % PRIVATE_KEY).decode('hex'))
делает? Должен ли я сгенерировать что-то из закрытого ключа и подписать с помощью ЭТОГО, или я просто подпишусь с помощью закрытого ключа?Этот gist , частично основанный на ответе Runeks, показывает, как перевести 0,01 биткойна в Ruby. Он извлекает информацию из предыдущей транзакции с Blockchain.info, поэтому вам просто нужно передать ему свой закрытый ключ и адрес (последний избыточен, но полезен для демонстрации). Я добавил много комментариев, чтобы объяснить необходимые шаги.
Как только вы узнаете идентификатор транзакции, vout, scriptPubKey этой транзакции и закрытый ключ в кодировке WIF, соответствующий открытому ключу, хешированному для получения адреса, вы можете создать необработанную транзакцию и подписать ее, не находясь в сети.
Я написал библиотеку PHP для работы с необработанными транзакциями (помимо других функций биткойнов). Вот как можно погасить обычную транзакцию: https://github.com/Bit-Wasp/bitcoin-lib-php Требуется обновление для поддержки подписей P2SH, но обычную транзакцию он погашает без проблем.
пользователь7295
Мюльхаузен
f2b3eb2deb76566e7324307cd47c35eeb88413f971d88519859b1834307ecfec
, который, по словам blockexplorer.com, не существуетВолшебник Оззи