Как пошагово вставить данные в OP_RETURN?

Я следил за https://bitcointalk.org/index.php?topic=453086.0 для создания сообщения в OP_RETURN. Я могу декодировать свою необработанную транзакцию, но на signrawtransaction она дает

ошибка: {"код":-22,"сообщение":"Ошибка декодирования TX"}

Я проверил адрес, есть ли что-то, чего не хватает в этом руководстве.

Лучшим способом было бы опубликовать полученный tx. Данные, которые вы встраиваете, меньше 40 байт?
Спасибо, Матье, ты был прав насчет размера. Но мне еще предстояло сделать больше шагов. Поэтому искал полный Step by Step.
Я получил это сообщение, когда забыл заменить исходную длину скрипта новой для моей шестнадцатеричной строки OP_RETURN.

Ответы (3)

Я написал небольшую демонстрационную программу, которая помещает фрагмент данных в сценарий OP_RETURN. Для этого требуется экземпляр биткойна, который принимает соединения RPC, хотя его можно реализовать и без него. Вы можете найти его на github здесь . Это было протестировано, но только в тестовой сети. Я собираюсь просмотреть код и объяснить, что он делает.

Начинать

[...]
logging.basicConfig()
logging.getLogger("BitcoinRPC").setLevel(logging.DEBUG)

Это делает ведение журнала более подробным. Это удобно, потому что показывает, какие вызовы RPC выполняются.

Подключиться к биткойну

rpc_user = "bitcoinrpc"
rpc_password = "87Y9A2gs25E9HDPGc9axqSqzxMR2MyTtrMkYc5KiZk2Z"

rpc = AuthServiceProxy("http://%s:%s@127.0.0.1:18332/" % (rpc_user, rpc_password))

Обратите внимание, что ваш пароль будет другим, и вы используете порт 8332 для основной сети вместо порта 18332.

Список неизрасходованных результатов

first_unspent = rpc.listunspent()[0]
txid = first_unspent['txid']
vout = first_unspent['vout']
input_amount = first_unspent['amount']
SATOSHI = Decimal("0.00000001")
change_amount = input_amount - Decimal("0.005") - SATOSHI

Часть - Decimal("0.005")заключается в том, что мы платим комиссию за транзакцию.

Создать транзакцию

# Marker address we're going to replace
# Produces a pattern that's easy to search for
mainnet = 0
if mainnet:
    dummy_address = "1111111111111111111114oLvT2"
else:
    dummy_address = "mfWxJ45yp2SFn7UciZyNpvDKrzbhyfKrY8"

Это две разные кодировки хэша Pay to Public Key, состоящего из всех нулей. Верхний — это представление основной сети, а нижний — представление тестовой сети.

# My change address
change_address = "mhZuYnuMCZLjZKeDMnY48xsR5qkjq7bAr9"

Помните, это мой адрес смены. Если вы не поменяете его, вы будете присылать мне деньги.

tx = rpc.createrawtransaction([{"txid": txid, "vout": vout}], \
                              {change_address: change_amount, \
                               dummy_address: SATOSHI})

И вот у нас есть реальная сделка. Однако он не содержит никаких наших собственных данных, поэтому нам придется это исправить.

Замените фиктивный вывод нашим собственным выводом

# Pattern to replace
# Represents length of script, then OP_DUP OP_HASH160,
# then length of hash, then 20 bytes of zeros, OP_EQUALVERIFY OP_CHECKSIG
oldScriptPubKey = "1976a914000000000000000000000000000000000000000088ac"

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

# Data to insert
data = "Melons."
if len(data) > 75:
    raise Exception("Can't contain this much data-use OP_PUSHDATA1")

newScriptPubKey = "6a" + hexlify(chr(len(data))) + hexlify(data)

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

Этот код сломается, если данные длиннее 75 байт. Если вам это нужно для большего, вы можете использовать OP_PUSHDATA1 вместо однобайтовых pushdata, которые я использую здесь.

#Append int of length to start
newScriptPubKey = hexlify(chr(len(unhexlify(newScriptPubKey)))) + newScriptPubKey

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

if oldScriptPubKey not in tx:
    raise Exception("Something broke!")

Проверка ошибок для этого очень шаткого метода.

tx = tx.replace(oldScriptPubKey, newScriptPubKey)

Наконец, замена строки заменяет новый скрипт на старый.

Подписать его

tx = rpc.signrawtransaction(tx)['hex']

Биткойн справляется с подъемом здесь.

Транслируйте его в сеть.

rpc.sendrawtransaction(tx)

Сделанный! Теперь просто подождите, пока ваша транзакция попадет в блок.

Я запустил код ( output ) и произвел транзакцию, которую вы можете увидеть в Block Explorer здесь . Если вы скопируете строку рядом с OP_RETURN и вставите ее в преобразователь Hex в ASCII , вы получите...

Melons.

Сделанный!

Другие источники

Я нашел эту веб-страницу полезной при написании этого.

Хорошо, поскольку это скрипт OP_RETURN (если я правильно понимаю), означает ли это, что tx будет удален/аннулирован сетью?
@Valmond scriptsigscriptPubKey, на самом деле. does that mean the tx will be removed / invalidated by the network?Не совсем. Он будет удален из набора неизрасходованных транзакций в некоторых клиентах, но многие клиенты все равно его помнят. (Они должны, потому что им нужно доказать новым клиентам, что это просто транзакция OP_RETURN.)
Обратите внимание, созданная вами транзакция не будет стандартной транзакцией в основной сети, потому что вывод op_return имеет значение > 0. Он ретранслируется/добывается по умолчанию в тестовой сети, поскольку тестовая сеть принимает почти все транзакции как стандартные.
@DavidA.Harding Я сделал это, потому что createrawtransaction не принял бы значение 0. Можете ли вы предоставить источник этого утверждения? Я не могу найти такую ​​проверку в Solver или IsStandard.
@NickODell о, придурок --- кажется, ты прав; Я даю людям плохие советы уже год. Извиняюсь.
У меня есть несколько вопросов. 1. Как вы вычислили фиктивные адреса с такой точностью? 2. Если я введу реальный адрес как фиктивный ( getnewaddress), а затем txhex = createrawtransaction. Затем я получаю шестнадцатеричный сценарий с помощью decodedtx = decoderawtransaction txhexи oldScriptHex = decodedtx.scriptPubKey.hex. Тогда txhex.replace(oldScriptHex, newScriptHex)и этот подход больше не декодируется (код -22; декодирование TX не удалось). Почему? newScriptHexидентичен в вашей версии и моей.
В настоящее время вы можете использовать createrawtransactionс voutподобным {"data": "your hex data"}и не иметь дело с заменой.

Вы можете упростить себе жизнь, используя одну из наших библиотек OP_RETURN:

Код также покажет вам, как именно это делается.

http://digitalcommons.augustana.edu/cscfaculty/1/

Научная статья, описывающая различные методы вставки данных для блокчейна Биткойн.