Что такое TXID транзакции, для чего они нужны, возможные проблемы
Проведение операций в сети блокчейн – довольно сложный процесс, на первый взгляд.
Большую роль в нем играет TXID транзакции или идентификатор.
В статье подробно описано проведение сделок в сети, присвоение txid, а также решение вопросов с отсутствием идентификатора в блокчейне.
Содержание:
Отправка криптовалюты с кошелька на кошелек
Сама процедура начала транзакции выглядит довольно просто.
Необходимо открыть кошелек где хранятся биткоины, ввести сумму и адрес получателя.
Также от отправителя потребуется секретный ключ и адрес отправителя. В результате, переводу будет присвоен хэш, который и является идентификаторов транзакции или TXID.
Для чего он нужен и как его использовать впоследствии? В первую очередь, идентификатор сделок необходимо для отслеживания состояния операции и того, попала ли она вообще в блокчейн.
В отличие от операций с наличными фиатными деньгами или банковских переводов, транзакции с сети блокчейн не такие однозначные.
В первую очередь, необходимо понимать, что биткоинов физически нет.
Они не существуют даже на жестком диске или холодном кошельке, который использует держатель для хранения криптовалюты.
Если говорить о том, что на определенном адресе хранится какая-то сумма биткоинов, речь идет о записи. В какой-то мере это сравнимо с хранением средств в банке. Делается соответствующая запись о том, что клиент внес определенную сумму. Он может распоряжаться ей через пластиковую карту, осуществляя транзакции везде, где есть терминалы.Тоже самое относится и к криптовалюте. Даже в большей степени. Потому что в банк клиент приносит наличные, когда открывает счет.
А в мире криптовалют физического выражения денежного эквивалента не существует. Более того, нет и файла, в котором бы хранились биткоины.
Все это заменяется балансами и записями транзакций, которые проводятся между разными участниками. Причем информация о всех криптопереводах хранится в самом блокчейне.
Операции между пользователями по переводу выглядят следующим образом:
- Адрес отправителя криптовалюты (Input).
- Количество единиц.
- Адрес получателя криптовалюты (Output).
Помимо этого, каждая транза сопровождается созданием хэша (txid), который служит для получения информации о транзакции.
Итак, для того, чтобы отправить определенную сумму другому пользователю, вводим адрес получателя и закрытый ключ. Txid записывается сразу, в виде маркера транзакции, как только он поступает в сеть. Генерируется случайно и представляет собой последовательность из букв и цифр размером в 64 символа. Приватный ключ также представляет собой последовательность из букв и цифр, но в отличие от TXiD, его не следует раскрывать, так как это даст возможность получить доступ к вашему кошельку.Вся информация (кроме приватного ключа) отправляет в сеть, где она проверяется майнерами. Они же проводят размещение транзакции в одном из последующих блоков и только после этого криптосделку можно считать подтвержденной.
перейти к содержанию ↑Время подтверждения транзакции
Все операции в сети блокчейн проводятся не в автоматическом режиме. Подтверждение осуществляется майнерами.
Соответственно, для подтверждения сделок потребуется определенное время. В среднем, каждый новый блок появляется раз в 10 минут.
Это минимальное время, за которое может пройти операция в сети биткоина.
Сегодня существует большое количество других криптовалют, у которых транзакции проходят намного быстрее за счет большего объема блока, меньшего размера транзакции и более быстрого алгоритма хеширования.
На время подтверждения биткоин-транзакции могут влиять следующие факторы – загруженность сети и комиссии. Причем здесь все изменяется прямопропорционально. С ростом количества транзакций, увеличивается и размер комиссии. Происходит это потому, что при увеличении загруженности, пользователи начинают предлагать большую сумму в качестве комиссии майнерам за попадание в следующий блок. Соответственно, для того, чтобы сделка прошла быстрее, пользователю рекомендуется выплатить большую комиссию.
перейти к содержанию ↑Проверка криптотранзакции в блокчейне
После отправки криптотранзакции, иногда возникает необходимость ее проверки в блокчейне.
Связано это может быть с задержкой платежа или непоступлением средств адресату.
Отследить перевод можно на сайтах blockchain.info и Chain.so. Здесь и понадобится txid или хэш операции.
Его нужно вести в поисковую строку одного из перечисленных сайтов.
Здесь можно получить информацию о самом криптопереводе, а также количестве подтверждений.
Для того, чтобы средства были зачислены на счет получателя, требуется 6 подтверждений. Сделано это с целью избежать повторных расходований средств. Естественно, все это увеличивает время проведения операций.
перейти к содержанию ↑Как найти идентификатор транзакции
TXID очень важен для всех описанных в статье шагов. С его помощью можно находить и отслеживать криптосделки, узнавать об их подробностях.
Но далеко не все начинающие пользователи знают, как узнать идентификатор. Предлагаем небольшую инструкцию.
Для этого необходимо зайти в blockchain.info и в поисковике ввести адрес отправления (то есть Input).
Затем открывается окно со всеми транзакциями, которые относятся к этому адресу, начиная от самых последних.
Для того, чтобы найти подходящий перевод, рекомендуется воспользоваться встроенным поиском браузера, точно указав сумму перевода.
Идентификатор будет отображаться над данными о транзакции в сером поле. Щелкнув по ссылке TXID, пользователь может увидеть всю информацию о сделке.
перейти к содержанию ↑TXID — Транзакции с многократным выводом
В некоторых случаях, под одним и тем же идентификатором может идти несколько транзакций.
Они также называются криптотранзакциями с многократным выводом.
Подобное происходит чаще всего при отправке средств из пула или на кошельках, поддерживающих лайтнайт нетворк.
перейти к содержанию ↑Что делать, если биткоин-транзакция не имеет подтверждений
Не все операции проходят без проблем. В некоторых случаях они могут «зависнуть» и не подтверждаться
В этом случае, рекомендуется выполнить следующие действия:
- Подождать. Иногда сделка может проводиться в течение нескольких дней. Если после этого ничего не происходит, необходимо предпринимать определенные шаги.
- Иногда переводы могут задерживаться из-за низкой комиссии. В этом случае, рекомендуется подождать, пока на транзу обратят внимание майнеры или изменить размер комиссии.
- Если заявка на перевод создавалась без комиссии, она может вообще не пройти.
- В случае, если биткоин-транзакция задерживается по причине перегрузки сети, отправитель может попытаться провести так называемый double-spending. Иногда это помогает операции начать хоть какое-то движение.
- Использование ускорителей.
Отмена неподтвержденных транзакций
С ростом нагрузки на сеть, неподтвержденные транзакции стали нормальным явлением.
Поэтому если пользователь сталкивается с подобной ситуацией, он может попытаться произвести отмену.
В первую очередь, важно определиться с тем, действительно ли заявка была «забракована» или количество транзакций настолько велико, что она оказалась в длинной очереди.
Во втором случае стоит подождать.
Однако если требуется перевести деньги как можно быстрее, можно попробовать воспользоваться отменой.
Для этого необходимо выполнить следующие шаги:
- Перейти в кошелек и убедиться в том, что средства еще не списаны с баланса.
- В случае, если транзакция все еще остается неподтвержденной, необходимо перейти в отладочную панель кошелька и выбрать «Консоль».
- В случае с зашифрованным счетом, необходимо получить к нему доступ.
- На адрес создается неформальный ключ dumpprivkey и адрес получателя. Далее, нужно скопировать строчку.
- Далее — выйти из кабинета пользователя и переименовать кошелек или переместить его в другое место.
- Перезапуск клиента, создающего новое хранилище. После этого, зайти в консоль и набрать importprivkey и добавить ключи, созданные ранее.
- Как только операция завершиться, в кошельке появятся старые данные. Это и будет показателем успеха всех манипуляций.
При этом, сведения о неподтвержденном денежном переводе исчезнут, а средства вернутся на счет. Далее, пользователь может повторно отправлять криптотранзакцию уже с правильной комиссией.
Что такое TXID транзакции, для чего они нужны, проблемы по включению сделки в блокчейн
5 (100%) 1 голос(ов)bitgid.com
Транзакции Hash ID (TXID) — что это и как найти ID транзакции
Вы только что сделали платеж, и получатель запрашивает «хэш транзакции» (“transaction hash”)? Транзакция могла быть в любой криптовалюте: Биткойне, Ethereum (Эфире), Litecoin или Dash; часто продавец попросит у Вас хэш или идентификатор транзакции в качестве доказательства оплаты.
Существуют и другие сценарии, в которых сторонняя служба кошельков или торговая платформа потребует от Вас отправки идентификатора хэша транзакции, чтобы устранить любые проблемы, которые у Вас есть.
Так как же найти эту (Tx Hash) Хэш-ТХ / TxID? Но прежде всего, безопасно ли делиться этой информацией и что такое идентификатор транзакции?
Что такое хэш транзакции или ID (Tx Hash / TxID)?
Tx Hash – это хэш транзакции, он также известен как идентификатор транзакции (TxID). Он состоит из буквенно-цифровых символов и представляет собой идентификационный номер, указанный для транзакции Биткойнов. Каждая транзакция, которая проводится в блокчейне Bitcoin, имеет этот уникальный идентификатор. Точно так же транзакции на блокчейне Ethereum будут иметь уникальный Tx Hash, и каждая транзакция на блокчейне Litecoin также будет иметь TxID.
Например, Биткоин ID транзакции выглядит так:
a1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d
Хэш транзакции Ethereum выглядит так:
0xb4bc263278d3f77a652a8d73a6bfd8ec0ba1a63923bbb4f38147fb8a943da26d
Этот уникальный идентификатор представляет собой длинную строку букв и цифр, которая используется для идентификации конкретной транзакции в блокчейне.
Использование TxHash и TxID
Все транзакции на блокчейне находятся в открытом доступе. После того, как вы сделаете платеж или переведете монеты на другой адрес, транзакция будет транслироваться, а также будет сгенерирован хэш-идентификатор Tx. С помощью ID статус транзакции можно легко отслеживать.
Тот, у кого есть TxID узнает все детали перевода, такие как: адрес отправителя, адрес получателя, общая сумма отправки, дата перевода, высота блока, комиссия и количество подтверждений.
Хэш транзакции и идентификаторы не содержат личной информации, поэтому делиться ею можно совершенно безопасно.
Чем эти хэш транзакции и идентификаторы полезны? Как мы уже говорили, это полезно во многих отношениях: например, вы можете поделиться этой информацией с поставщиком услуг, чтобы позволить им узнать о состоянии перевода. Вы можете поделиться своим идентификатором транзакции с продавцом, чтобы подтвердить, что транзакция прошла успешно, и есть много других сценариев, в которых Вам может потребоваться эта функция.
Как найти hash ID транзакцию?
Если вы новичок в Bitcoin, то Вы можете задаться вопросом, как найти ID транзакции битко
bytwork.com
TXID – Что это такое и как его узнать
Криптовалюты — популярное средство для совершения платежей, но они не существуют физически, поэтому криптовалютные транзакции имеют особую систему подтверждения оплаты. Они, точнее запись о них, и есть криптовалюта. Поговорим о маркере криптовалютных транзакций, который именуют как TXID.
Что такое TXID транзакции?
TXID — зашифрованный идентификатор транзакции в системе блокчейн. После отправки платежа данный номер присваивается каждой операции и является общедоступной информацией, которая не передает личные данные об отправителе.
Номер TXID выглядит как код из определенной последовательности буквы и цифр. Всего номер состоит из 64 символов, которые являются уникальными для каждой транзакции и хранят информацию о ней в блокчейн цепочке.
Передача TXID третьим лицам является абсолютно безопасной, поскольку данный номер — это зашифрованная информация о сумме транзакции, отправителе и получателе, по ней невозможно восстановить приватный номер криптовалютного кошелька отправителя.
Где используется TXID?
TXID присваивается каждому криптовалютному переводу, но, как правило, отправитель не обращает внимание на данный идентификатор пока с переводом не возникают проблемы.
Например, в ситуациях, когда платеж долго не приходит получателю, возникает необходимость отследить состояние транзакции и сделать это можно только по номеру TXID. ID транзакции может быть также запрошен в качестве подтверждения отправки перевода получателем, поскольку является основным доказательством совершения платежа.
Не все операции проходят без замедлений. Транзакции в сети блокчейн осуществляются не в автоматическом режиме. Подтверждают их майнеры. Как правило, на одну операцию требуется не менее 10 минут. Но порой может понадобиться и несколько дней.
Причины, по которым задерживаются сделки:
- Причиной зависания транзакций могут быть низкие комиссии. В таком случае нужно либо ждать пока какой-то майнер обратит внимание на транзакцию, так как именно он подтверждает ее, или увеличить размер комиссии. Если заявка создавалась без комиссии, то она вообще может не пройти.
- Транзакция может задерживаться из-за перегрузки сети. В этом случае отправитель может попытаться провести double-spending и дать операции движение.
Зная TXID транзакции в любой момент можно узнать всю информацию о ней. Если операция длительное время не подтверждается, то ее можно попытаться отменить. Но стоит это делать только в том случае, если транзакция забракована, а не просто попала в длительную очередь (это узнать позволит TXID). Но если нет времени ждать, когда очередь дойдет до вашей транзакции, то можно ее отменить, чтобы создать новую с новой комиссией.
Как узнать TXID?
Несмотря на важность данного номера, далеко не все пользователи знают, как найти его. Идентификатор транзакций является публичной информацией для большинства криптовалют, доступ к которой может получить любой пользователь. TXID можно отследить в криптовалютном кошельке, с которого была совершена транзакция.
Как отследить TXID в Blockchain
Работа с сервисами сводиться к следующим шагам:
- Требуется перейти на сервис, позволяющий отыскать идентификатор транзакции для той криптовалюты, в которой был отправлен платеж.
- В строке “Поиск” необходимо ввести информацию о произведенной транзакции. Публичный ключ получателя платежа — наиболее подходящая информация для начала поиска.
- После поиска на странице будет отображены все транзакции, которые связаны с публичным ключом получателя.
- В списке может отображаться огромное количество транзакций, поэтому следующим шагом нужно отыскать точную сумму совершенной транзакции.
- TXID отображается над данными транзакции, щелкнув по нему мышью, можно узнать детальную информацию о платеже. При необходимости именно данный номер необходимо скопировать.
Для того, чтобы легко найти нужный идентификатор транзакции, не просматривая длинный список всех операций по заданным параметрам поиска можно воспользоваться встроенным в браузер поиском по странице, который сразу укажет на нужную сумму. Для macOS и Windows поиск открывается при нажатии сочетания клавиш Ctrl + F.
Иногда в один идентификатор TXID может входить несколько транзакций. Данное явление называется транзакции с многократным выводом. Зачастую такое случается при выводе криптовалюты с пула или отправки платежа через криптовалютные кошельки поддерживающие лайтнайт нетворк, где обменная служба преобразования монет создает несколько транзакций, входящих в один номер TXID. В таком случае система будет находить только первый платеж по идентификатору. Для того, чтобы избежать такой ситуации, транзакции из пулов следует изначально отправлять на личный кошелек и с него уже проводить оплату.
Выводы
TXID — единственный идентификатор, который подтверждает криптовалютную транзакцию и позволяет отслеживать ее пересылку на любом этапе. Иногда случается, что платежи в блокчейне зависают и только по данному номеру можно отследить состояние операции. Самый простой способ узнать идентификатор транзакции в системе блокчейн — просмотреть информацию о платеже в кошельке, с которого он был совершен. В случае, если к кошельку отправителя нет доступа, то можно воспользоваться информационными сервисами, которые предоставляют публичный доступ к цепочке блокчейн и позволяют отыскать идентификатор любой транзакции по номеру кошелька получателя.
dwips.com
Неподтвержденная транзакция, или Возвращаем криптовалюту из небытия / Habr
Волею судеб довелось мне иметь дело с криптовалютами. Не то что бы плотно работаю с ними, но иногда то отправлю монетки, то получу. Скажем так, понемногу прощупываю новую сферу изнутри.Начну с того, что пост местами пронизан догадками относительно происходящего. Где-то я могу ошибаться. Объективные поправки и дополнения только приветствуются.
Немного о терминологии.
- Кошельком буду называть файл wallet.dat.
- Комиссия — Transaction Fee. Называть эту штуку комиссией, я считаю, не совсем правильно, но это наиболее привычный и не режущий слух вариант, потому пусть будет комиссией.
- Размер транзакции — размер блока данных, в котором содержится вся информация о транзакции.
Ко всей этой криптовалютной кухне я изначально подошел как типичный юзер — не особо вникая в систему. Установил, запустил, работает — и ладно. Иногда при попытке послать куда-то средства клиент выдавал сообщение вроде «Размер транзакции слишком велик, нельзя просто взять и послать ее. Но вы можете добавить комиссию в размере N, и тогда все будет хорошо» — я соглашался с добавлением комиссии, и все действительно было хорошо.
Казалось бы, если меня просят дополнительно заплатить, когда это нужно, значит я и буду платить, когда попросят (если комиссия будет устраивать). Это и было главной ошибкой.
Делаю очередной перевод на сумму значительно крупнее, чем обычно. Средства со счета уходят, предложения заплатить комиссию не было и… Ничего. До получателя средства не доходят, статус транзакции «0/Не подтверждено». И такую картину я наблюдал больше недели, попутно перегугливая и перечитывая интернеты в поисках информации по решению подобной проблемы. Причем искал и для конкретной криптовалюты, и в целом для всех — проблем куча, решения нет.
А, собственно, что же это за комиссия? Идея в том, что транзакции могут проходить без комиссии, но только в случае соблюдения некоторых условий:
- Размер транзакции должен быть не больше определенной величины.
- Переводимая сумма должна быть больше некоего порога.
- Транзакция должна обладать достаточным приоритетом.
Если первые два пункта более-менее понятны (конкретные величины приводить не стал, полагаю, они могут варьироваться от форка к форку), то в третьем вся загвоздка. Грубо говоря, транзакции при создании попадают в очередь, отсортированные по приоритету. При генерации очередного блока в него включаются транзакции с комиссией (которая идет в награду тому, кто нашел блок), а также транзакции без комиссии с наиболее высоким приоритетом.
Сам приоритет прямо зависит от объема переводимых средств и от числа их подтверждений (чем дольше именно эти деньги лежали у вас на счету, тем больше должен быть приоритет, по сути) и обратно зависит от размера транзакции. Таким образом, при неудачном стечении обстоятельств транзакция без комиссии может оказаться в зависшем неподтвержденном состоянии, с которым я и столкнулся.
Поиски решения показали, что это сравнительно частая для криптовалют проблема. К сожалению, все советы сводились к нижеперечисленному и часто не помогали:
- Подождите сутки-двое, вашу транзакцию включат в очередной блок.
- Подождите сутки-двое, программе-клиенту надоест ждать, и она сама отменит транзакцию.
- Загрузите заново всю цепочку блоков.
- Запустите клиент с каким-нибудь волшебным ключом (-rescan / -reindex / -salvagewallet).
Ждал я больше недели. Ни в какой блок транзакцию не включили. Даже после повторных отправок через sendrawtransaction. Блокчейн говорил, что о той транзакции ничего не знает, и на счету лежат те самые средства, никуда они не ушли. И только клиент стоял на своем: «Я транзакцию отправил, дальше как хочешь. Уже потраченными деньгами распоряжаться не позволю».
Итак, в чем же суть проблемы? Транзакция не попала в блок и уже не попадет. В кошельке хранится информация о том, что транзакция в общем-то была, поэтому средства, которые должны были с ней отправиться, недоступны для использования. Возможно, спустя еще какое-то время транзакция будет отменена, на этот счет у меня несколько предположений:
- Зависит от валюты, где-то быстро отменяется, где-то нужно ждать долго.
- Баг конкретного клиента.
- Информация об отмене неверна.
В любом случае, неделя — это достаточно большой срок. Если за это время не прошло само, то есть основания полагать, что и не пройдет.
Как нормальный человек, после того, как все уже сломалось, я наконец-то пошел читать мануалы. Попытка повторить транзакцию с включением в нее комиссии потерпела фиаско. Зато бесконечные прыжки по ссылкам между форумами (в выдаче гугла ничего стоящего не нашлось) довели меня до некоего поста, где и нашлась дельная подсказка. К сожалению, теперь найти этот пост не удается, даже не знаю, что был за форум. Полагаю, некий околобиткойновый.
Решение буду описывать на примере клиента, наиболее распространенного для форков, известного как Satoshi Client. Насколько понимаю, оно применимо и к прочим клиентам, но, возможно, со своими нюансами.
Несмотря на простоту и очевидность решения, к нему мало кто приходит, судя по многостраничным веткам форумов. Средства на счете блокируются кошельком, и все, что требуется, это получить доступ к счету вне текущего кошелька.
Итак, если транзакция зависла и не имеет подтверждений:
- Проявите терпение. Не поднимайте сразу панику. Подождите пару дней, вдруг и правда само пройдет.
- Убедитесь, что транзакция зависла. Зайдите в Block Explorer (обычно гуглится по запросу «blockchain %названиекриптовалюты%») и проверьте, что про зависшую транзакцию там ничего не знают, а на счете деньги на самом деле есть.
- Перейдите в отладочную консоль (Помощь — Окно отладки — Консоль)
- Если кошелек зашифрован (он же зашифрован?), то для начала необходимо получить доступ, используя команду walletpassphrase <passphrase>.
- Теперь нужно получить приватный ключ от нужного счета. dumpprivkey <address>. Вместо <address> нужно подставить публичный номер счета, на котором лежат заблокированные средства. В ответ получите приватный ключ данного счета. Его нужно куда-нибудь скопировать, он еще понадобится. Если средства для транзакции брались с нескольких счетов, то и импортировать нужно их все. И да, никогда не храните приватные ключи в доступном для кого-либо месте. Знание ключа дает полный доступ к соответствующему ему счету.
Обратите также внимание на то, что на каждую команду в отладочной консоли приходит ответ. Он может быть пустым, но он есть всегда. Позже будет понятно, к чему я это. - Закройте клиент и удалите кошелек. Расположение кошелька (wallet.dat) зависит от конкретного клиента и ОС. Естественно, совсем удалять его не стоит, лучше переименовать или переместить в надежное место.
- Запустите клиент заново. Создастся новый кошелек. В него необходимо импортировать полученный ранее ключ (ключи). Идем в отладочную консоль и пишем importprivkey <privkey>. Импорт может производиться достаточно долго. Позвольте ему завершиться — дождитесь получения ответа на команду.
- В новом кошельке должен появиться счет с реальным его состоянием. Для надежности можно перезапустить клиент с ключом -rescan, но, полагаю, это уже лишнее. Ранее заблокированные средства снова доступны для отправки, шлите их заново, на этот раз не забудьте включить комиссию. (есть важные дополнения по этому пункту в upd3)
- Если на старом кошельке остались прочие используемые и важные счета, можно снова вернуться к нему.
Таким нехитрым образом удалось вернуть криптомонетки. Хотя я уже и начал думать, что они потеряны совсем. В общем, не надейтесь на чудо, перед использованием нового клиента убедитесь, что комиссия всегда включена. Рекомендуемый размер комиссии, думаю, лучше искать на ресурсах, посвященных используемой криптовалюте.
Upd: Необходимость описанной операции зависит от используемого клиента (а так как у многих криптовалют по одному клиенту, то и от используемые валюты). Похоже, в некоторых случаях информация о транзакции записывается не в файл кошелька, а только в локальную цепочку блоков. При таком раскладе может помочь удаление имеющейся цепочки или запуск клиента с ключами.
Upd2: Значительно упростить описанный процесс переноса счетов в новый кошелек может ключ -salwagewallet, уже упомянутый ранее. При запуске клиента с этим ключом создается новый wallet.dat, в который импортируются все счета из старого, а история транзакций к нему берется из цепочки блоков (за описание спасибо grich). К сожалению, запуск с данным ключом реализован не во всех клиентах.
Upd3: Если после импорта перечислить не всю сумму, имеющуюся на счету, то часть средств (сдача от используемых выходов) будет перечислена на другой счет нового кошелька. Учитывайте это, если планируете вернуться к старому кошельку:
- Импортировав счет в новый кошелек, переведите все средства с него на другой свой счет, вернитесь к старому кошельку и после этого уже распоряжайтесь возвращенными средствами.
- После совершения транзакции с нового кошелька определите, на какой счет упала сдача, и импортируйте этот счет в старый кошелек.
habr.com
Транзакции, часть 1 / Habr
Привет, Habr! Представляю вашему вниманию перевод статьи «Building Blockchain in Go. Part 4: Transactions 1».Содержание
- Blockchain на Go. Часть 1: Прототип
- Blockchain на Go. Часть 2: Proof-of-Work
- Blockchain на Go. Часть 3: Постоянная память и интерфейс командной строки
- Blockchain на Go. Часть 4: Транзакции, часть 1
- Blockchain на Go. Часть 5: Адреса
- Blockchain на Go. Часть 6: Транзакции, часть 2
- Blockchain на Go. Часть 7: Сеть
Вступление
Транзакции являются сердцем Биткоина, и единственная цель цепи блоков — это хранить транзакции безопасным и надежным способом, чтобы никто не смог модифицировать их после создания. В этой статье мы начинаем работу над реализацией механизма транзакций. Но поскольку это довольно большая тема, я разбил ее на две части: в этой части мы реализуем общий механизм, а во второй части мы детально разберем весь остальной функционал.
В данной статье мы будем практически полностью редактировать весь наш предыдущий код, поэтому не имеет смысла описывать каждое изменение, все изменения вы можете посмотреть тут.
Ложки нет
Если Вы когда-то разрабатывали веб-приложение, то для реализации платежей, вероятно, создавали в базе данных две эти таблицы:
учетные записи
и транзакции
. Учетная запись хранила информацию о пользователе, включая его персональную информацию и баланс, а транзакция хранит информацию о переводе денег с одной учетной записи на другую. В Биткоине платежи осуществляются совершенно по-другому:- Нет аккаунтов.
- Нет балансов.
- Нет адресов.
- Нет монет.
- Нет отправителей и получателей.
Поскольку блокчейн является публичной и открытой базой данных, мы не хотим хранить конфиденциальную информацию о владельцах кошельков. Монеты не хранятся на счетах. Транзакции не переводят деньги с одного адреса на другой. Нет полей и атрибутов, которые содержат баланс счета. Есть только транзакции. Но что внутри?
Биткоин транзакция
Транзакция представляет собой комбинацию входов и выходов:
type Transaction struct {
ID []byte
Vin []TXInput
Vout []TXOutput
}
Входы новой транзакции ссылаются на выходы предыдущей транзакции (есть исключение, о котором мы поговорим ниже). Выходы — место, где хранятся монеты. Следующая диаграмма демонстрирует взаимосвязь транзакций:
Заметьте:
- Есть выходы, которые не связаны с входами.
- В одной транзакции входы могут ссылаться на выходы нескольких транзакций.
- Вход всегда должен ссылаться на выход.
В этой статье мы будем использовать слова «деньги», «монеты», «потратить», «отправить», «учетная запись» и т.д. Но в Биткоине таких концепций нет. Транзакции — это просто заблокированное скриптом значение, которое может разблокировать лишь тот, кто его заблокировал.
Выходы транзакций
Начнем с выходов:
type TXOutput struct {
Value int
ScriptPubKey string
}
Фактически, это выходы, которые хранят «монеты» (обратите внимание на поле
Value
выше). Средства блокируются особым пазлом, которая хранится в ScriptPubKey
. Внутри Bitcoin использует скриптовый язык Script
, который используется для определения логики блокировки и разблокировки выходов. Язык довольно примитивен (это делается намеренно, чтобы избежать возможных взломов), но мы не будем обсуждать его подробно. Вы можете прочитать подробнее о нем здесь.В Биткоине поле значений хранит количество сатоши, а не количество BTC. 1 сатоши = 0.00000001 BTC. Таким образом, это самая маленькая единица валюты в Биткойне (как, к примеру, цент).Поскольку у нас нет адресов, пока что мы будем избегать всей связанной с сценариями логики. Для начала
ScriptPubKey
будет хранить произвольную строку (пользовательский адрес кошелька).Кстати, наличие такого языка сценариев означает, что Биткоин можно использовать как платформу смарт-контрактов.Одна важная вещь которую нужно знать о выходах, это то, что они неделимы, а это означает, что вы не можете ссылаться на часть своего значения. Когда выход ссылается на новую транзакцию, он расходуется полностью. И если его значение больше, чем требуется, генерируется разница и новое значение отправляется обратно отправителю. Это похоже на реальную ситуацию в мире, когда вы платите, скажем, $5 долларов за то, что стоит $1, и получаете сдачу $4.
Входы транзакций
Рассмотрим вход:
type TXInput struct {
Txid []byte
Vout int
ScriptSig string
}
Как упоминалось ранее, вход ссылается на предыдущий выход:
Txid
хранит идентификатор такой транзакции, а Vout
хранит индекс выхода данной транзакции. ScriptSig
— это скрипт, который предоставляет данные, которые будут в дальнейшем использоваться в скрипте ScriptPubKey
. Если данные верны, выход можно разблокировать, а его значение можно использовать для генерации новых выходов; если же это не так, вход не может ссылаться на выход. Этот механизм гарантирует, что пользователи не могут тратить монеты, принадлежащие другим людям.Опять же, поскольку у нас все еще нет адресов, ScriptSig
сохранит только произвольный пользовательский адрес кошелька. Мы создадим открытый ключ и проверку подписи в следующей статье.
Подведем итог. Выходы — это место, в котором хранятся «монеты». Каждый выход имеет сценарий разблокировки, который определяет логику разблокировки выхода. Каждая новая транзакция должна иметь как минимум один вход и выход. Вход ссылается на результат предыдущей транзакции и предоставляет данные (поле ScriptSig
), которые используются в сценарии разблокировки выхода, чтобы разблокировать его и использовать его значение для создания новых выходов.
Но что пришло первым: входы или выходы?
Яйцо
В Биткоине, яйцо появилось до цыпленка. Логика inputs-referencing-outputs представляет собой классическую ситуацию «курица или яйцо»: входы производят выходы, а выходы позволяют создавать входы. И в Биткоине выходы всегда появляются перед входами.
Когда майнер начинает майнить блок, он добавляет к нему coinbase транзакцию. Транзакция coinbase — это особый тип транзакции, который не требует ранее существующих выходов. Он создает выходы (т. е. «Монеты») из ниоткуда. Яйцо без курицы. Это награда, которую майнеры получают за добычу новых блоков.
Как вы знаете, в начале цепи есть блок генезиса. Именно этот блок генерирует самый первый выход в цепочке блоков. И никаких предыдущих выходов не требуется, поскольку нет предыдущих транзакций и нет никаких выходов.
Давайте создадим coinbase транзакцию:
func NewCoinbaseTX(to, data string) *Transaction {
if data == "" {
data = fmt.Sprintf("Reward to '%s'", to)
}
txin := TXInput{[]byte{}, -1, data}
txout := TXOutput{subsidy, to}
tx := Transaction{nil, []TXInput{txin}, []TXOutput{txout}}
tx.SetID()
return &tx
}
В coinbase транзакции имеется только один вход. В нашей реализации
Txid
пуст, а Vout
равен -1. Кроме того, транзакция coinbase не хранит скрипт в ScriptSig
. Вместо этого там хранятся произвольные данные.В биткойне самая первая coinbase транзакция содержит следующее сообщение: “The Times 03/Jan/2009 Chancellor on brink of second bailout for banks”. Вы сами можете на это посмотреть.
subsidy
— это сумма вознаграждения. В Биткоине это число не хранится нигде и рассчитывается только на основе общего количества блоков: количество блоков делится на 210000. Майнинг блока генезиса приносит 50 BTC, и каждые 210000 блоков награда уменьшается вдвое. В нашей реализации мы будем хранить вознаграждение как константу (по крайней мере на данный момент).Сохранение транзакций в цепи
С этого момента каждый блок должен хранить как минимум одну транзакцию, и должно стать невозможным майнить блоки без транзакции. Теперь, удалим поле date из Block и вместо этого теперь будем хранить транзакции.
type Block struct {
Timestamp int64
Transactions []*Transaction
PrevBlockHash []byte
Hash []byte
Nonce int
}
NewBlock
и NewGenesisBlock
также должны быть соответствующим образом измененыfunc NewBlock(transactions []*Transaction, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), transactions, prevBlockHash, []byte{}, 0}
...
}
func NewGenesisBlock(coinbase *Transaction) *Block {
return NewBlock([]*Transaction{coinbase}, []byte{})
}
Теперь создадим функцию
CreateBlockchain
func CreateBlockchain(address string) *Blockchain {
...
err = db.Update(func(tx *bolt.Tx) error {
cbtx := NewCoinbaseTX(address, genesisCoinbaseData)
genesis := NewGenesisBlock(cbtx)
b, err := tx.CreateBucket([]byte(blocksBucket))
err = b.Put(genesis.Hash, genesis.Serialize())
...
})
...
}
Теперь функция принимает адрес, который получит вознаграждение за добычу блока генезиса.
Proof-of-Work
Алгоритм Proof-of-Work должен рассматривать транзакции, хранящиеся в блоке, чтобы гарантировать согласованность и надежность цепи как хранилища транзакции. Итак, теперь мы должны изменить метод
ProofOfWork.prepareData
:func (pow *ProofOfWork) prepareData(nonce int) []byte {
data := bytes.Join(
[][]byte{
pow.block.PrevBlockHash,
pow.block.HashTransactions(), // This line was changed
IntToHex(pow.block.Timestamp),
IntToHex(int64(targetBits)),
IntToHex(int64(nonce)),
},
[]byte{},
)
return data
}
Вместо
pow.block.Data
теперь мы добавим pow.block.HashTransactions ()
:func (b *Block) HashTransactions() []byte {
var txHashes [][]byte
var txHash [32]byte
for _, tx := range b.Transactions {
txHashes = append(txHashes, tx.ID)
}
txHash = sha256.Sum256(bytes.Join(txHashes, []byte{}))
return txHash[:]
}
Повторюсь, мы используем хеширование как механизм обеспечения уникального представления данных. Мы хотим, чтобы все транзакции в блоке были однозначно идентифицированы с помощью одного хеша. Чтобы достичь этого, мы получаем хеши каждой транзакции, объединяем их и получаем хеш объединенных комбинаций.
Биткоин использует более сложную технику: он представляет все транзакции, содержащиеся в блоке как дерево хешей, и использует корневой хеш дерева в системе Proof-of-Work. Такой подход позволяет быстро проверить, содержит ли блок определенную транзакцию, имеющую только корневой хеш и без загрузки всех транзакций.Проверим правильность работы:
$ blockchain_go createblockchain -address Ivan
00000093450837f8b52b78c25f8163bb6137caf43ff4d9a01d1b731fa8ddcc8a
Done!
Отлично! Мы получили свою первую награду. Но как нам проверить баланс?
Непотраченные выходы
Нам нужно найти все непотраченные выходы (UTXO). Это означает, что эти выходы не ссылались ни на какие входы. На приведенной выше диаграмме это:
- tx0, output 1;
- tx1, output 0;
- tx3, output 0;
- tx4, output 0.
Конечно, когда мы проверяем баланс, нам не нужны они все, нужны только те, которые могут быть разблокированы ключом, которым мы владеем (в настоящее время у нас нет реализованных ключей и вместо них будут использоваться пользовательские адреса). Для начала, давайте определим методы блокировки-разблокировки на входах и выходах:
func (in *TXInput) CanUnlockOutputWith(unlockingData string) bool {
return in.ScriptSig == unlockingData
}
func (out *TXOutput) CanBeUnlockedWith(unlockingData string) bool {
return out.ScriptPubKey == unlockingData
}
Здесь мы просто сравниваем поля из
ScriptSig
с unlockingData
. В следующей статье мы их улучшим, после того, как реализуем адреса, основанные на закрытых ключах.Следующий шаг — поиск транзакций с непотраченными выходами — это уже более сложно:
func (bc *Blockchain) FindUnspentTransactions(address string) []Transaction {
var unspentTXs []Transaction
spentTXOs := make(map[string][]int)
bci := bc.Iterator()
for {
block := bci.Next()
for _, tx := range block.Transactions {
txID := hex.EncodeToString(tx.ID)
Outputs:
for outIdx, out := range tx.Vout {
// Was the output spent?
if spentTXOs[txID] != nil {
for _, spentOut := range spentTXOs[txID] {
if spentOut == outIdx {
continue Outputs
}
}
}
if out.CanBeUnlockedWith(address) {
unspentTXs = append(unspentTXs, *tx)
}
}
if tx.IsCoinbase() == false {
for _, in := range tx.Vin {
if in.CanUnlockOutputWith(address) {
inTxID := hex.EncodeToString(in.Txid)
spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
}
}
}
}
if len(block.PrevBlockHash) == 0 {
break
}
}
return unspentTXs
}
Поскольку транзакции хранятся в блоках, мы должны проверять каждый блок в цепочке. Начнем с выходов:
if out.CanBeUnlockedWith(address) {
unspentTXs = append(unspentTXs, tx)
}
Если выход был заблокирован по тому же адресу, мы ищем непотраченные выходы, которые мы хотим. Но перед тем, как принять его, нам нужно проверить, был ли на выходе уже указан вход:
if spentTXOs[txID] != nil {
for _, spentOut := range spentTXOs[txID] {
if spentOut == outIdx {
continue Outputs
}
}
}
Мы пропускаем те, на которые уже ссылаются входы (их значения были перенесены на другие выходы, поэтому мы не можем их подсчитать). После проверки выходов мы собираем все входы, которые могут разблокировать выходы, заблокированные с предоставленным адресом (это не относится к coinbase транзакциям, поскольку они не разблокируют выходы):
if tx.IsCoinbase() == false {
for _, in := range tx.Vin {
if in.CanUnlockOutputWith(address) {
inTxID := hex.EncodeToString(in.Txid)
spentTXOs[inTxID] = append(spentTXOs[inTxID], in.Vout)
}
}
}
Функция возвращает список транзакций, содержащих непотраченные выходы. Для расчета баланса нам нужна еще одна функция, которая берет транзакции и возвращает только выходы:
func (bc *Blockchain) FindUTXO(address string) []TXOutput {
var UTXOs []TXOutput
unspentTransactions := bc.FindUnspentTransactions(address)
for _, tx := range unspentTransactions {
for _, out := range tx.Vout {
if out.CanBeUnlockedWith(address) {
UTXOs = append(UTXOs, out)
}
}
}
return UTXOs
}
Готово! Теперь реализуем команду
getbalance
func (cli *CLI) getBalance(address string) {
bc := NewBlockchain(address)
defer bc.db.Close()
balance := 0
UTXOs := bc.FindUTXO(address)
for _, out := range UTXOs {
balance += out.Value
}
fmt.Printf("Balance of '%s': %d\n", address, balance)
}
Баланс счета — это сумма значений всех непотраченных выходов, заблокированных адресом учетной записи.
Давайте проверим наш баланс после добычи блока генезиса:
$ blockchain_go getbalance -address Ivan
Balance of 'Ivan': 10
Это наши первые монеты!
Отправка монет
Теперь мы хотим отправить несколько монеты кому-то другому. Для этого нам нужно создать новую транзакцию, поместить ее в блок и обработать его. До сих пор мы реализовали только транзакцию coinbase (которая является специальным типом транзакций), теперь нам нужна общая транзакция:
func NewUTXOTransaction(from, to string, amount int, bc *Blockchain) *Transaction {
var inputs []TXInput
var outputs []TXOutput
acc, validOutputs := bc.FindSpendableOutputs(from, amount)
if acc < amount {
log.Panic("ERROR: Not enough funds")
}
// Build a list of inputs
for txid, outs := range validOutputs {
txID, err := hex.DecodeString(txid)
for _, out := range outs {
input := TXInput{txID, out, from}
inputs = append(inputs, input)
}
}
// Build a list of outputs
outputs = append(outputs, TXOutput{amount, to})
if acc > amount {
outputs = append(outputs, TXOutput{acc - amount, from}) // a change
}
tx := Transaction{nil, inputs, outputs}
tx.SetID()
return &tx
}
Прежде чем создавать новые выходы, нам для начала нужно найти все непотраченные выходы и убедиться, что в них есть достаточное количество монет. Это метод
FindSpendableOutputs
. После этого для каждого найденного выхода создается вход, ссылающийся на него. Затем мы создаем два выхода:- Тот, который заблокирован с адресом получателя. Это фактическая передача монет на другой адрес.
- Тот, который заблокирован с адресом отправителя. Это разница. Он создается только тогда, когда непотраченные выходы имеют большее значение, чем требуется для новой транзакции. Помните: выходы неделимы.
Метод
FindSpendableOutputs
основан на методе FindUnspentTransactions
, который мы определили ранее:func (bc *Blockchain) FindSpendableOutputs(address string, amount int) (int, map[string][]int) {
unspentOutputs := make(map[string][]int)
unspentTXs := bc.FindUnspentTransactions(address)
accumulated := 0
Work:
for _, tx := range unspentTXs {
txID := hex.EncodeToString(tx.ID)
for outIdx, out := range tx.Vout {
if out.CanBeUnlockedWith(address) && accumulated < amount {
accumulated += out.Value
unspentOutputs[txID] = append(unspentOutputs[txID], outIdx)
if accumulated >= amount {
break Work
}
}
}
}
return accumulated, unspentOutputs
}
Метод выполняет обход по всем неизрасходованным транзакциям и накапливает их значения. Когда накопленное значение больше или равно сумме, которую мы хотим передать, обход останавливается и возвращает накопленные значения и выходные индексы, сгруппированные по идентификаторам транзакций. Нам не надо брать больше, чем мы собираемся потратить.
Теперь мы можем изменить метод Blockchain.MineBlock
:
func (bc *Blockchain) MineBlock(transactions []*Transaction) {
...
newBlock := NewBlock(transactions, lastHash)
...
}
Наконец, создадим команду
send
:func (cli *CLI) send(from, to string, amount int) {
bc := NewBlockchain(from)
defer bc.db.Close()
tx := NewUTXOTransaction(from, to, amount, bc)
bc.MineBlock([]*Transaction{tx})
fmt.Println("Success!")
}
Отправка монет означает создание транзакции и добавление ее в цепь блоков посредством майнинга блока. Но Биткоин не делает это сразу (как и мы). Вместо этого он помещает все новые транзакции в пул памяти (или mempool), и когда майнер готов к добыче блока, он берет все транзакции из mempool и создает блок-кандидат. Транзакции становятся подтвержденными только тогда, когда блок, содержащий их, добывается и добавляется к цепи блоков.
Давайте проверим, как работает отправка монет:
$ blockchain_go send -from Ivan -to Pedro -amount 6
00000001b56d60f86f72ab2a59fadb197d767b97d4873732be505e0a65cc1e37
Success!
$ blockchain_go getbalance -address Ivan
Balance of 'Ivan': 4
$ blockchain_go getbalance -address Pedro
Balance of 'Pedro': 6
Отлично! Теперь давайте создадим больше транзакций и убедимся, что отправка с нескольких выходов работает правильно:
$ blockchain_go send -from Pedro -to Helen -amount 2
00000099938725eb2c7730844b3cd40209d46bce2c2af9d87c2b7611fe9d5bdf
Success!
$ blockchain_go send -from Ivan -to Helen -amount 2
000000a2edf94334b1d94f98d22d7e4c973261660397dc7340464f7959a7a9aa
Success!
Теперь монеты Хелен заблокированы на двух выходах: один выход от Педро и один от Ивана. Отправим кому-нибудь еще:
$ blockchain_go send -from Helen -to Rachel -amount 3
000000c58136cffa669e767b8f881d16e2ede3974d71df43058baaf8c069f1a0
Success!
$ blockchain_go getbalance -address Ivan
Balance of 'Ivan': 2
$ blockchain_go getbalance -address Pedro
Balance of 'Pedro': 4
$ blockchain_go getbalance -address Helen
Balance of 'Helen': 1
$ blockchain_go getbalance -address Rachel
Balance of 'Rachel': 3
Выглядит хорошо! Теперь давайте протестируем исключения:
$ blockchain_go send -from Pedro -to Ivan -amount 5
panic: ERROR: Not enough funds
$ blockchain_go getbalance -address Pedro
Balance of 'Pedro': 4
$ blockchain_go getbalance -address Ivan
Balance of 'Ivan': 2
Заключение
Уф! Это было нелегко, но теперь у нас есть транзакции! Хотя, некоторые ключевые особенности Биткоин-подобной криптовалюты отсутствуют:
- Адреса. Пока у нас нет адресов на основе приватного ключа.
- Награды. Майнить блоки абсолютно невыгодно!
- UTXO. Получение баланса требует сканирования всей цепочки блоков, что может занять очень много времени, когда есть очень много блоков. Кроме того, это займет очень много времени, если мы захотим подтвердить последующие транзакции. UTXO предназначен для решения этих проблем и для быстрой работы с транзакциями.
- Mempool. Здесь хранятся транзакции, прежде чем они будут упакованы в блок. В нашей текущей реализации блок содержит только одну транзакцию, и это очень неэффективно.
Ссылки
- Full source codes
- Transaction
- Merkle tree
- Coinbase
habr.com
Разбираемся с Replace-By-Fee транзакциями Bitcoin / Habr
Всем привет! В этой статье мы разберемся что такое Replace-By-Fee транзакции и как их использовать. Бонус! Пара поучительных историй в конце.Итак, представим ситуацию когда A посылает монеты B. Он создает и подписывает транзакцию, и выпускает ее в сеть для подтверждения. К сожалению, транзакция долго не подтверждается, у транзакции слишком маленькая комиссия (или комиссия нормальная в обычных обстоятельствах, но в данный момент очередь неподтвержденных транзакций слишком большая и майнеры предпочитают майнить транзакции с бОльшими комиссиями). Что делать пользователю A? Варианты: a) ждать подтверждения (ждать можно долго) б) ждать отмены транзакции (когда большинство нод решат наконец выкинуть из mempool транзакцию) и освобожденными монетами можно будет снова распоряжаться (то есть создавать транзакции и пулы не будут считать их даблспендами и отвергать) в) Заменить транзакцию с повышением комиссии (RBF)
Итак, RBF — замена существующей транзакции новой транзакцией с повышением комиссии. Замена означает что в новой транзакции будут использованы те же входы что и в старой транзакции, и это не будет считаться даблспендом (читай, мошенничеством). В итоге только одна транзакция будет добавлена в блокчейн (с большой вероятностью это будет транзакция с повышенной комиссией).
RBF описан в BIP-0125
Какая транзакция может быть заменена? Это хороший вопрос. Замена транзакций была предусмотрена еще создателем системы Сатоши Накамото, но выключена в определенный момент. Затем она была доработана до RBF и включена в код Bitcoin Core 0.12 и старше. Для замены в данных транзакции предусмотрено специальное int поле nSequence, которое сигнализирует о том, какая транзакция старше какой. Чтоб транзакция была потенциально заменяемой, значение nSequence должно быть меньше (0xffffffff — 1).
По умолчанию, большинство современных кошельков nSequence сразу ставят максимальным, и возможность заменять транзакции надо включать в настройках. Вот так это выглядит в кошельке Electrum:
Пример декодированой транзакции:
...
"inputs": [
{
"addresses": [
"...."
],
"output_index": 0,
"output_value": 1010000,
"prev_hash": "...",
"script": "...",
"script_type": "pay-to-pubkey-hash",
"sequence": 0
},
...
Sequence указывается для каждого входа транзакции.
В том же кошельке Electrum после включения опции RBF станет возможным увеличивать комиссию за транзакцию прямо в GUI, но сейчас мы с вами будем собирать RBF транзакцию руками. Зачем? Во-первых — разобраться, во-вторых — для большей гибкости, например, в этом случае в RBF транзакции мы сможем указать совершенно другие целевые адреса для перевода, чем периодически пытаются воспользоваться злоумышленники.
Выглядит это так: A хочет заплатить B. A создает, подписывает и транслирует транзакцию на перевод монет B, но умышленно ставит пониженную или нулевую комиссию. Если B недостаточно осторожен он засчитывает неподтвержденную транзакцию (и отдает товар А, например). После этого A заменяет транзакцию и перенаправляет выход транзакции на С, где С подконтролен А.
Как B может этого избежать? Если все же B предпочитает засчитывать неподтвержденные транзакции (для скорости), не стоит засчитывать пластичные транзакции (где nSequence позволяет транзакции заменять). И вообще, с подозрением относиться к таким транзакциям. Если вы когда-то засчитывали пластичные транзакции то неплохо бы их перепроверить на факт наличия фрода и применить санкции к плательщику, если тот мошенничает. Ну и естественно, идеально было бы дожидаться не менее 2х подтверждений.
Пример.
Будем пользоваться замечательной опенсорсной тулзой github.com/OutCast3k/coinbin с помощью которой можно вручную легко создавать транзакции с нужными нам входами и выходами. Работает полностью в браузере не общаясь с сервером. Живая версия доступна по адресу coinb.in
Создаем транзакцию через New → Transaction. Берем приватный ключ в формате WIF и загружаем доступные (непотраченные) выходы для этого ключа. Во вкладке Outputs указываем целевой адрес, указываем amount. В Advanced Options не забываем поставить галку на “Make this a RBF transaction” — в этом случае nSequence транзакции будет меньше максимального значения. Жмем Submit.
Транзакция создана. Обратите внимание на Transaction Fee — это разница между доступным количеством монет суммарно со всех входов МИНУС сумма всех выходов (в нашем случае 1 выход). Разница и будет комиссией которую майнер заберет себе. В этом примере она специально нулевая.
Жмем Sign, копипастим туда полученный hex транзакции, все тот же приватный ключ, и подписываем транзакцию:
Транслируем транзакцию в сеть (можно использовать любой ендпоинт):
Так транзакция выглядит:
Теперь давайте воспользуемся RBF. Создаем новую транзакцию, входы берем с того же WIF. Но выходы укажем совершенно иные! Один из выходов укажем 1CWHWkTWaq1K5hevimJia3cyinQsrgXUvg — донейт адрес создателя coinb.in
Amount укажем такие чтоб комиссия за транзакцию была достаточной для быстрого подтверждения.
Обратите внимание на вкладку Inputs — если исходный адрес многоразовый и на нем много транзакций, то нам придется во входах вручную прописать только те инпуты, которые входят в транзакцию которую мы хотим заменить (удалить лишние, оставить только нужные). Входов этих могут быть сотни, и в некоторых случаях это может быть достаточно кропотливая работа.
Подписываем:
Транслируем:
Вот так это теперь выглядит на blockchain.info. У новой транзакции есть все шансы быть включенной в блок раньше своей предыдущей версии.
Ждем. Есть подтверждение:
Мы только что заменили транзакцию с помощью RBF, причем монеты отправили совсем не на тот адрес который хотели изначально.
Опять о безопасности
Не всегда nSequence=MAX гарантирует что транзакция финальная и заменить ее нельзя. На самом деле галку “Make this a RBF transaction” можно было не ставить, но:
- При замене транзакции не все ноды согласятся ретранслировать новую версию, ссылаясь на mempool-conflict
- Некоторые ноды примут транзакцию, и таки ретранслируют ее в сеть
- Информационные ресурсы типа blockchain.info будут прямым текстом писать DOUBLE SPEND
- Не все пулы будут майнить эту транзакцию, справедливо считая её даблспендом. Некоторые пулы будут ее майнить, но это может занять больше времени (по сравнению со временем если вся сеть будет её майнить)
- Если новая транзакция будет таки включена в блок — это абсолютно валидно и не противоречит правилам сети. Любые претензии потенциально обманутых нигде не принимаются
Т.о. мы приходим к мысли что любые транзакции без подтверждения хотя бы в 2х блоках являются ненадежными. Почему в 2х а не 1м? Короткий ответ — orphaned blocks.
Ссылки и материалы:
Бонус!
История 1.
Как-то один из проектов использующих мой биткоин платежный шлюз взломали, и поставили на вывод 2 BTC. В той версии шлюза была захардкоджена комиссия 0.0001 BTC, что было достаточно мало на тот момент. Времени оказалось достаточно чтобы во всем разобраться и заменить транзакцию на возврат средств, повысив комиссию до 0.1 BTC. Злоумышленники наверное расстроились. Мораль — используйте RBF во благо!
История 2.
Я на bitmedia.io пополнял счет биткоином. Счет там пополняется засчитывая входящие транзакции на специальный top-up адрес пользователя, и прибавляя сумму транзакции к общему балансу пользователя. Засчитываются только транзакции с 6 подтверждениями.
Баланс я хотел пополнить поскорее, поэтому когда первая транзакция зависла, я несколько раз заменял транзакцию с помощью RBF. Когда транзакция подтвердилась — баланс мне приплюсовался в столько раз больше, сколько RBF замен я сделал. Я сразу написал в поддержку, чтобы баг исправили. В благодарность лишние 0.3 BTC мне подарили как bug bounty. Мораль — проверьте свои системы как они поведут себя с RBF!
habr.com