Поскольку подтверждение среднего блока биткойнов занимает около 10 минут, многие криптоматы, обменники и продавцы, принимающие биткойны, сталкиваются с проблемой быстрой обработки платежей. Возникает дилемма: можно ли уже считать платеж полученным, если транзакция депозита находится в мемпуле, но еще не подтверждена? Проще говоря, насколько возможно заменить или отменить транзакцию, которая уже была добавлена в мемпул?
Что такое RBF
RBF (replace by fee) — замена неподтвержденной транзакции новой транзакцией с более высокой комиссией сети. Под заменой имеется в виду, что новая транзакция использует тот же инпут, что и исходная.
Физический смысл RBF в том, что майнеру (создателю блока) выгодней взять транзакцию с более высокой комиссией. А так как у транзакций одинаковый инпут, то вторая транзакция становится невалидной.
В 2016 год вышло обновление ноды Bitcoin Core v0.12.0.
Которое включило в себя поддержку Opt-in RBF. Это функция, которая позволяет создавать транзакции со специальным флагом, сигнализирующим о том, что транзакция может быть заменена. Технически это выглядит так: у каждой транзакции есть свой порядковый номер nSequence,
определяющий очередность взятия транзакций в блок. И, чтобы сделать транзакцию потенциально незаменимой, нужно было указать максимально возможный порядковый номер — 0xffffffff
. Например, блокчеин эксплорер mempool.space об этом указывает явно:
По правилам протокола Bitcoin, все повторные транзакции с тем же инпутом должны быть отклонены и не распространяться нодами дальше по сети. Это правило называется first seen rule. В крайнем случае, так было раньше.
В декабре 2022 году вышло обновление ноды Bitcoin Core 24.0.1.
Которое включает в себя full-RBF. Это функция, которая позволяет нодам обрабатывать повторные транзакции и считать их легитимными. По дефолту в ноде эта функция выключена, поэтому в настройках ноды для ее активации необходимо задать параметр mempoolfullrbf=1
На февраль 2023 года почти 30% всех нод Bitcoin перешли на версию 24.0.1:
Согласно исследованию, из всех нод версии 24.0.1 минимум 17% активировали full-RBF.
Пошаговая инструкция по замене транзакций
Я решил развернуть ноду Bitcoin v24.0.1 и попробовать заменить транзакцию. Ниже поэтапный план действий.
Системные требования Bitcoin Core с bitcoin.org:
- Disk space. 350 GB.
- Download. 500 MB/day (15 GB/month)
- Upload. 5 GB/day (150 GB/month)
- Memory (RAM) 1 GB.
- System. Desktop. Laptop. Some ARM chipsets >1 GHz.
- Operating system. Windows 7/8.x/10. Mac OS X.
У меня по факту заняло около 480 GB, поэтому лучше заложить дискового пространства побольше.
1. Скачиваем ноду Bitcoin Core v24.0.1
Скачиваем пропатченную ноду Bitcoin Core v24.0.1. Нода специально подключается к пирам, которые поддерживают full-RBF и способствуют распространению повторных транзакций по сети:
git clone -b full-rbf-v24.0.1 https://github.com/petertodd/bitcoin.git
Либо можно скачать оригинальную ноду и затем в настройках указать пиры, у которых стоит full-RBF. Об этом будет еще описано ниже.
2. Компилируем исходники и запускаем ноду
Вначале устанавливаем все необходимые зависимости.
Linux:
sudo apt-get install automake autotools-dev bsdmainutils build-essential ccache clang gcc git libboost-dev libboost-filesystem-dev libboost-system-dev libboost-test-dev libevent-dev libminiupnpc-dev libnatpmp-dev libqt5gui5 libqt5core5a libqt5dbus5 libsqlite3-dev libtool libzmq3-dev pkg-config python3 qttools5-dev qttools5-dev-tools qtwayland5 systemtap-sdt-dev
Mac OS:
brew install automake boost ccache git libevent libnatpmp libtool llvm miniupnpc pkg-config python qrencode qt@5 sqlite zeromq
Дальше компилируем ноду Bitcoin:
./autogen.sh
./configure
make
Если вы хотите запустить компиляцию сразу на нескольких ядрах, то для команды make
необходимо задать соответствующие параметры:
Linux
make -j "$(($(nproc) + 1))"
Mac OS
make -j "$(($(sysctl -n hw.physicalcpu) + 1))"
У меня на 8 GB RAM и 4 ядрах компиляция заняла около 15 минут.
3. Создаем конфигурационный файл bitcoin.conf
Файл необходимо создать в корневой папке ./bitcoin
.
Есть даже онлайн генератор конфигов:
Мне во время эксперимента понадобились следующие параметры:
mempoolfullrbf=1 #Включает full-RBF функционал
txindex=1 #Индексирует внешние транзакции, которые не принадлежат кошельку ноды
datadir=<dir_path> #Если необходимо сменить директорию, куда нода будет загружать все данные
Остановлюсь подробней на параметре txindex=1
. Если не включить этот параметр, то нода не сможет обращаться к входным и выходным параметрам транзакций, которые не относятся к кошельку ноды. Это может понадобиться, когда вы пытаетесь отправить повторную транзакцию с использованием того же входа, который по факту является ссылкой на выход внешней транзакции. Этот параметр потребует дополнительно порядка 20 GB дискового пространства под хранение индексированных данных.
Если у вас оригинальная нода, и вы хотите добавить пиры с включенным full-RBF, то в конфигурационный файл добавьте еще следующие параметры:
addnode=full-rbf1.btc.petertodd.net
addnode=full-rbf2.btc.petertodd.net
addnode=full-rbf3.btc.petertodd.net
addnode=full-rbf4.btc.petertodd.net
4. Запускаем ноду и создаем кошелек
Запускаем ноду при помощи команды ./bitcoind -daemon
. Если вы не хотите запускать ноду в фоновом режиме, то запускайте без параметра -daemon
Дальше нода должна начать синхронизацию, у меня весь процесс занял порядка трое суток.
Обращаться к ноде можно при помощи bitcoin-cli
. Например, посмотреть сколько уже скачено блоков можно следующей командой: ./bitcoin-cli getblockcount
.
Со всеми командами можно ознакомиться здесь:
После полной синхронизации необходимо создать кошелек. Это можно сделать разными способами (импортировать, сгенерировать), например, создать командой createwallet:
./bitcoin-cli createwallet "testwallet"
5. Подготавливаем скрипт
Для начала стоит убедиться, что вы можете обращаться к локальной Bitcoin ноде через python-bitcoinlib
:
python3 /lib/python-bitcoinlib/examples/ssl-rpc-connection.py
Дальше можно взять за основу doublespend.py
и сделать следующее:
- Отправить первую транзакцию с network fee значительно меньшим, чем у текущих транзакций в мемпуле. Так, транзакция «застрянет» в мемпуле и не попадет в блок;
- Подождать минуту, пока транзакция распространяется по сети;
- Отправить вторую транзакцию с network fee в разы превышающим текущих транзакций в мемпуле. Так, у майнера будет мотивация взять именно повторную транзакцию в блок.
Итог
Я проводил эксперимент несколько раз — результат был одинаков. Рассмотрим на примере одной из итераций:
Время создания | Network fee | |
Транзакция №1 | 00:00 | 0.0000113 BTC (0.26$) |
Транзакция №2 | 00:19 | 0.004068 BTC (±94$) |
Первую транзакцию я отправил с network fee = 0.26$. Это значительно ниже рыночных условий. Несмотря на то, что вторая транзакция имела fee в 94$, майнеры (создатели блоков) все равно ее не брали. То есть replace-by-fee не сработал.
Примечательно, как блокчеин эксплореры реагировали на повторные транзакции. В основном все отображали только первую транзакцию (например, mempool.space или blockchair). Эксплорер Blockcypher отображает повторные транзакции и предупреждает о double-spend:
Я предполагаю, что майнеры не могут взять повторную транзакцию в блок, так как этот блок будет отвергнут большинством участников сети. И пока Bitcoin Core v24.0.1 с включенным full-RBF стоит у меньшинства участников, замена транзакций работать не будет.
Читайте больше статей о криптовалютах и майнинге на сайте COOL-MINING.ORG.
Нравится ли вам читать подобные статьи о криптовалютах и майнинге, хотите ли вы поддержать меня как автора или задать вопросы? Узнавайте новости первыми, подписывайтесь на мой telegram-канал