Как выкупить базовый Tx?

Учитывая исходный, стандартный Tx ( wiki ):

01000000

01
26c07ece0bce7cda0ccd14d99e205f118cde27e83dd75da7b141fe487b5528fb
00000000
8b
48304502202b7e37831273d74c8b5b1956c23e79acd660635a8d1063d413c50b218eb6bc8a022100a10a3a7b5aaa0f07827207daf81f718f51eeac96695cf1ef9f2020f21a0de02f01410452684bce6797a0a50d028e9632be0c2a7e5031b710972c2a3285520fb29fcd4ecfb5fc2bf86a1e7578e4f8a305eeb341d1c6fc0173e5837e2d3c7b178aade078
ffffffff

02

b06c191e01000000
19
76a9143564a74f9ddb4372301c49154605573d7d1a88fe88ac

00e1f50500000000
19
76a914010966776006953d5567439e5e39f86a0d273bee88ac
00000000

и закрытый ключ:

18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

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

57 голосов | спросил ThePiachu 7 AMpSat, 07 Apr 2012 01:19:52 +040019Saturday 2012, 01:19:52

3 ответа


66

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

Краткое описание: Начнем с создания новой транзакции с помощью скриптаSig, содержащего scriptPubKey для вывода, который мы хотим выкупить. ScriptPubKey этой транзакции будет содержать скрипт, который платит за хэш открытого ключа (адрес биткойна). Мы выполняем двойной SHA256 хэш в этой транзакции с четырехбайтовым хэш-кодом типа SIGHASH_ALL, добавленным до конца. Мы подписываем этот хэш с закрытым ключом, указанным выше. Затем скриптSig этой новой транзакции заменяется сценарием, который сначала подталкивает сигнатуру DER-кодировки, а также однобайтовый код хэш-кода SIGHASH_ALL, в стек, за которым следует соответствующий открытый ключ DER-закодированного закрытого ключа.

Пошаговое описание:

Мы начинаем создавать новую сырую транзакцию, которую мы хэш и подписываем.

  1. Добавить четырехбайтное поле версии: 01000000
  2. Однобайтный varint, определяющий количество входов: 01
  3. 32-байтовый хэш транзакции, из которой мы хотим выкупить вывод: eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
  4. Четырехбайтовое поле, обозначающее выходной индекс, который мы хотим выкупить из транзакции с вышеуказанным хешем (номер выхода 2 = индекс вывода 1): 01000000
  5. Теперь идет скриптSig. Для подписания транзакции это временно заполняется скриптом scriptPubKey для вывода, который мы хотим выкупить. Сначала мы пишем однобайтный varint, который обозначает длину скриптаSig (0x19 = 25 байт): 19
  6. Затем мы записываем временный скриптSig, который, опять же, является scriptPubKey для вывода, который мы хотим выкупить: 76a914010966776006953d5567439e5e39f86a0d273bee88ac
  7. Затем мы записываем четырехбайтовое поле, обозначающее последовательность. В настоящее время всегда установлено значение 0xffffffff: ffffffff
  8. Далее идет однобайтная varint, содержащая количество выходов в нашей новой транзакции. В этом примере мы установим значение 1: 01
  9. Затем мы записываем 8-байтовое поле (целое число 64 бит), содержащее сумму, которую мы хотим выкупить из указанного вывода. Я поставлю это на общую сумму, доступную на выходе минус 0,001 BTC (0,999 BTC, или 99900000 Satoshis): 605af40500000000
  10. Затем мы начинаем писать вывод нашей транзакции. Мы начинаем с однобайтовой переменной, обозначающей длину выходного скрипта (0x19 или 25 байт): 19
  11. Тогда фактический выходной скрипт: 76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
  12. Затем мы записываем четырехбайтовое поле «время блокировки»: 00000000
  13. И наконец, мы пишем четырехбайтовый «хэш-код типа» (1 в нашем случае): 01000000

    Теперь у нас есть следующие необработанные данные транзакции:

    01000000
    01
    eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
    01000000
    19
    76a914010966776006953d5567439e5e39f86a0d273bee88ac
    ffffffff
    01
    605af40500000000
    19
    76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
    00000000
    01000000
    
  14. (этап подписания) Теперь мы удваиваем SHA256 хэш всей этой структуры, которая дает хеш 9302bda273a887cb40c13e02a50b4071a31fd3aae3ae04021b0b843dd61ad18e

  15. Затем мы создаем пару открытого /закрытого ключа из предоставленного закрытого ключа. Мы подписываем хэш от шага 14 с помощью закрытого ключа, который дает следующую DER-закодированные подписи (эта подпись будет отличаться в вашем случае): 30460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc06 К этой подписи мы добавляем один-байтовый хэш-код тип: 01. Открытый ключ: 0450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
  16. Мы строим окончательный скриптSig путем конкатенации: <Однобайтовый сценарий OPCODE, содержащий длину DER-кодированной сигнатуры плюс 1 (длина однобайтового типа хэш-кода)> | <Фактическая DER-кодированная подпись плюс однобайтовый тип хэш-кода> | <Однобайтовый сценарий OPCODE, содержащий длину открытого ключа> | <Фактический открытый ключ>
  17. Затем мы заменяем однобайтное поле длины varint с шага 5 длиной данных с шага 16. Длина составляет 140 байтов или 0x8C байтов: 8c
  18. И мы заменяем временный скриптSig с шага 6 структурой данныхпостроенный на шаге 16. Это будет выглядеть так: 4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
  19. Мы закончили с удалением четырехбайтового хеш-кода, который мы добавили на шаге 13, и в итоге получим следующий поток байтов, который является окончательной транзакцией:

    01000000
    01
    eccf7e3034189b851985d871f91384b8ee357cd47c3024736e5676eb2debb3f2
    01000000
    8c
    4930460221009e0339f72c793a89e664a8a932df073962a3f84eda0bd9e02084a6a9567f75aa022100bd9cbaca2e5ec195751efdfac164b76250b1e21302e51ca86dd7ebd7020cdc0601410450863ad64a87ae8a2fe83c1af1a8403cb53f53e486d8511dad8a04887e5b23522cd470243453a299fa9e77237716103abc11a1df38855ed6f2ee187e9c582ba6
    ffffffff
    01
    605af40500000000
    19
    76a914097072524438d003d23a2f23edb65aae1bb3e46988ac
    00000000
    

Пример кода Python:

Я создал пример скрипта Python, который делает все вышеперечисленное. Он преднамеренно максимально подробен и очень прокомментирован с минимальными функциями, чтобы походить на пошаговое руководство выше. Количество строк кода можно легко свести к одной половине, но я решил опубликовать его в этом подробном формате, поскольку я считаю, что это проще всего (т. Е. «Прыгать» назад и вперед через функции). Сценарий содержит 76 непустых строк без комментария. Скрипт зависит от bitcointools от Gavin Andresen (для сериализации и десериализации транзакций и кодирования /декодирования base58) и ecdsa_ssl.py из моей вилки из ритуального хранилища joric (для создания публичного /частного EC ключевые пары и подписание ECDSA). Самый простой способ запустить скрипт - клонировать bitcointools в папку и поместить ecdsa_ssl.py из вышеуказанного URL-адреса в той же папке вместе с этим скриптом и выполнить скрипт оттуда. Вы захотите заменить адрес в переменной SEND_TO_ADDRESS в этом скрипте с адресом, на который вы хотите отправить монеты, если только вы не чувствуете себя щедрым:).

#bitcointools
из deserialize import parse_Transaction, opcodes
из BCDataStream импортировать BCDataStream
из базы58 import bc_address_to_hash_160, b58decode, public_key_to_bc_address, hash_160_to_bc_address

import ecdsa_ssl

импортировать Crypto.Hash.SHA256 как sha256
Импорт Crypto.Random

#transaction, из которого мы хотим выкупить вывод
HEX_TRANSACTION = "010000000126c07ece0bce7cda0ccd14d99e205f118cde27e83dd75da7b141fe487b5528fb000000008b48304502202b7e37831273d74c8b5b1956c23e79acd660635a8d1063d413c50b218eb6bc8a022100a10a3a7b5aaa0f07827207daf81f718f51eeac96695cf1ef9f2020f21a0de02f01410452684bce6797a0a50d028e9632be0c2a7e5031b710972c2a3285520fb29fcd4ecfb5fc2bf86a1e7578e4f8a305eeb341d1c6fc0173e5837e2d3c7b178aade078ffffffff02b06c191e010000001976a9143564a74f9ddb4372301c49154605573d7d1a88fe88ac00e1f505000000001976a914010966776006953d5567439e5e39f86a0d273bee88ac00000000"
#output для выкупа. должен существовать в HEX_TRANSACTION
OUTPUT_INDEX = 1
#address мы хотим отправить выкупленные монеты.
# РАЗМЕЩАЙТЕ СВОИМ СОБСТВЕННЫМ АДРЕСОМ, если вы не чувствуете щедрую
SEND_TO_ADDRESS = "1L4xtXCdJNiYnyqE6UsB8KSJvqEuXjz6aK"
#fee мы хотим заплатить (в BTC)
TX_FEE = 0,001
#constant, который определяет количество Satoshis за BTC
МОНЕТЫ = 100000000
#constant используется для определения, какая часть транзакции хеширована.
SIGHASH_ALL = 1
#private key, чьи хэши-ключи общего доступа к хешу, содержащиеся в файле scriptPubKey выходного номера * OUTPUT_INDEX * в транзакции, описанной в HEX_TRANSACTION
Private_key = 0x18E14A7B6A307F426A94F8114701E7C8E774E7F9A47E2C2035DB29A206321725

def dsha256 (данные):
   return sha256.new (sha256.new (data) .digest ()). digest ()

tx_data = HEX_TRANSACTION.decode ( 'hex_codec')
tx_hash = dsha256 (tx_data)

#here мы используем bitcointools для анализа транзакции. это дает легкий доступ к различным областям транзакции, из которых мы хотим выкупить выход
stream = BCDataStream ()
stream.write (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 ['version'] = 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']) #hash транзакции, из которой мы хотим выкупить вывод
# 'prevout_n'
tx_fields ['output_index'] = OUTPUT_INDEX
sign_tx.write_uint32 (tx_fields ['output_index']) # какой вывод транзакции с tx id 'prevout_hash' мы хотим выкупить?

## next - это часть ввода транзакции. здесь мы помещаем скрипт * output *, который мы хотим выкупить
tx_fields ['scriptSigHash'] = tx_info ['txOut'] [OUTPUT_INDEX] ['scriptPubKey']
# первый напишите размер
sign_tx.write_compact_size (LEN (tx_fields [ 'scriptSigHash']))
# then данные
sign_tx.write (tx_fields [ 'scriptSigHash'])

#'последовательность'
tx_fields ['sequence'] = 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] ['значение'] - (TX_FEE * COIN)
sign_tx.write_int64 (tx_fields [ 'значение'])
## вот где наш скриптPubKey идет (скрипт, который платит за адрес)
# Мне нужен следующий скрипт:
# "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 (opcodes.OP_DUP) + chr (opcodes.OP_HASH160) + \
   chr (20) + address_hash + chr (opcodes.OP_EQUALVERIFY) + chr (opcodes.OP_CHECKSIG)
# сначала напишите длину этого куска данных
tx_fields ['scriptPubKey'] = scriptPubKey
sign_tx.write_compact_size (LEN (tx_fields [ 'scriptPubKey']))
# then данные
sign_tx.write (tx_fields [ 'scriptPubKey'])

#write locktime (0)
tx_fields ['locktime'] = 0
sign_tx.write_uint32 (tx_fields [ 'Locktime'])
# и тип хеш-кода (1)
tx_fields ['hash_type'] = SIGHASH_ALL
sign_tx.write_int32 (tx_fields [ 'hash_type'])

# then мы получаем хеш транзакции без подписи (хеш, который мы подписываем, используя наш закрытый ключ)
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 ()
# then мы создаем подпись над хэшем транзакции без подписи
sig_data = k.sign (hash_scriptless)
#a один байтовый «хеш-тип» добавляется к концу подписи (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]):
   bytes = b58decode (SEND_TO_ADDRESS, 25)
   raise RuntimeError: «Предоставленный закрытый ключ не может использоваться для выкупа индекса вывода% d \ nВы необходимо предоставить закрытый ключ для адреса% s%
                           (OUTPUT_INDEX, hash_160_to_bc_address (tx_info ['txOut'] [OUTPUT_INDEX] ['scriptPubKey'] [3: -2], байты [0]))

## теперь мы начинаем создавать окончательную транзакцию. это дубликат транзакции без подписи,
## с скриптом, заполненным скриптом, который подталкивает подпись и однобайтовый тип хеш-кода и открытый ключ сверху, в стек

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 (LEN (scriptSig))
# then данные
final_tx.write (scriptSig)

##, а затем мы просто записываем одни и те же данные после скриптаSig, который находится в транзакции без подписи,
# оставляя четырехбайтовый хэш-код (поскольку он закодирован в одном байте после подписиданные)

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 [ 'Locktime'])

# отпечатывает окончательную транзакцию в шестнадцатеричном формате (может использоваться как аргумент для sendrawtransaction bitcoind)
print final_tx.input.encode ('hex')
ответил runeks 31 +04002012-10-31T22:20:26+04:00312012bEurope/MoscowWed, 31 Oct 2012 22:20:26 +0400 2012, 22:20:26
5

Этот смысл , частично основанный на ответе Рунекса, показывает, как перенести 0.01 биткойнов в Рубин. Он извлекает информацию из предыдущей транзакции из Blockchain.info, поэтому вам просто нужно передать ей свой секретный ключ и адрес (последний является избыточным, но полезным для демонстрации). Я добавил много комментариев, чтобы объяснить предпринятые шаги.

ответил Sjors Provoost 14 Maypm13 2013, 12:45:48
1

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

Я написал библиотеку PHP для обработки необработанных транзакций (среди других функций биткойнов). Вот как вы могли бы выкупить обычную транзакцию: https://github.com/Bit-Wasp/bitcoin-lib-php Он нуждается в обновлении для поддержки подписей P2SH, но он будет эффективно использовать обычную транзакцию.

ответил karimkorun 14 AMpMon, 14 Apr 2014 00:15:38 +040015Monday 2014, 00:15:38

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132