- PVSM.RU - https://www.pvsm.ru -
Всем привет! В этой статье мы разберемся что такое Replace-By-Fee транзакции и как их использовать. Бонус! Пара поучительных историй в конце.
Итак, представим ситуацию когда A посылает монеты B. Он создает и подписывает транзакцию, и выпускает ее в сеть для подтверждения. К сожалению, транзакция долго не подтверждается, у транзакции слишком маленькая комиссия (или комиссия нормальная в обычных обстоятельствах, но в данный момент очередь неподтвержденных транзакций слишком большая и майнеры предпочитают майнить транзакции с бОльшими комиссиями). Что делать пользователю A? Варианты: a) ждать подтверждения (ждать можно долго) б) ждать отмены транзакции (когда большинство нод решат наконец выкинуть из mempool транзакцию) и освобожденными монетами можно будет снова распоряжаться (то есть создавать транзакции и пулы не будут считать их даблспендами и отвергать) в) Заменить транзакцию с повышением комиссии (RBF)
Итак, RBF — замена существующей транзакции новой транзакцией с повышением комиссии. Замена означает что в новой транзакции будут использованы те же входы что и в старой транзакции, и это не будет считаться даблспендом (читай, мошенничеством). В итоге только одна транзакция будет добавлена в блокчейн (с большой вероятностью это будет транзакция с повышенной комиссией).
RBF описан в BIP-0125 github.com/bitcoin/bips/blob/master/bip-0125.mediawiki [1]
Какая транзакция может быть заменена? Это хороший вопрос. Замена транзакций была предусмотрена еще создателем системы Сатоши Накамото, но выключена в определенный момент. Затем она была доработана до 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 [2] с помощью которой можно вручную легко создавать транзакции с нужными нам входами и выходами. Работает полностью в браузере не общаясь с сервером. Живая версия доступна по адресу coinb.in [3]
Создаем транзакцию через 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 — если исходный адрес многоразовый и на нем много транзакций, то нам придется во входах вручную прописать только те инпуты, которые входят в транзакцию которую мы хотим заменить (удалить лишние, оставить только нужные). Входов этих могут быть сотни, и в некоторых случаях это может быть достаточно кропотливая работа.
Подписываем:

Транслируем:

Вот так это теперь выглядит на bitcoin.info. У новой транзакции есть все шансы быть включенной в блок раньше своей предыдущей версии.

Ждем. Есть подтверждение:

Мы только что заменили транзакцию с помощью RBF, причем монеты отправили совсем не на тот адрес который хотели изначально.
Опять о безопасности.
Не всегда nSequence=MAX гарантирует что транзакция финальная и заменить ее нельзя. На самом деле галку “Make this a RBF transaction” можно было не ставить, но:
Т.о. мы приходим к мысли что любые транзакции без подтверждения хотя бы в 2х блоках являются ненадежными. Почему в 2х а не 1м? Короткий ответ — orphaned blocks.
Ссылки и материалы:
Бонус!
История 1.
Как-то один из проектов использующих мой биткоин платежный шлюз взломали, и поставили на вывод 2 BTC. В той версии шлюза была захардкоджена комиссия 0.0001 BTC, что было достаточно мало на тот момент. Времени оказалось достаточно чтобы во всем разобраться и заменить транзакцию на возврат средств, повысив комиссию до 0.1 BTC. Злоумышленники наверное расстроились. Мораль — используйте RBF во благо!
История 2.
Я на bitmedia.io [5] пополнял счет биткоином. Счет там пополняется засчитывая входящие транзакции на специальный top-up адрес пользователя, и прибавляя сумму транзакции к общему балансу пользователя. Засчитываются только транзакции с 6 подтверждениями.
Баланс я хотел пополнить поскорее, поэтому когда первая транзакция зависла, я несколько раз заменял транзакцию с помощью RBF. Когда транзакция подтвердилась — баланс мне приплюсовался в столько раз больше, сколько RBF замен я сделал. Я сразу написал в поддержку, чтобы баг исправили. В благодарность лишние 0.3 BTC мне подарили как bug bounty. Мораль — проверьте свои системы как они поведут себя с RBF!
Автор: Igor Korsakov
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/platezhny-e-sistemy/253525
Ссылки в тексте:
[1] github.com/bitcoin/bips/blob/master/bip-0125.mediawiki: https://github.com/bitcoin/bips/blob/master/bip-0125.mediawiki
[2] github.com/OutCast3k/coinbin: https://github.com/OutCast3k/coinbin/
[3] coinb.in: https://coinb.in
[4] en.bitcoin.it/wiki/Transaction_broadcasting: https://en.bitcoin.it/wiki/Transaction_broadcasting
[5] bitmedia.io: https://bitmedia.io
[6] Источник: https://habrahabr.ru/post/327218/?utm_source=habrahabr&utm_medium=rss&utm_campaign=best
Нажмите здесь для печати.