В октябре 2024 года проект Bitcoin Core раскрыл информацию об отказе в обслуживании из-за слишком большого размера наборов inv-to-send, который я разработал для версий Bitcoin Core до v25.0. У меня есть несколько заметок и скриншотов с моего тогдашнего исследования, которые я хочу сохранить здесь. В начале мая 2023 года моя инфраструктура мониторинга заметила эту ошибку, затрагивающую узлы mainnet, что позволило мне точно определить, откуда возникла проблема. Благодарность за работу над исправлением выражается Anthony Towns.
2 мая 2023 года я заметил, что на одном из моих узлов мониторинга количество входящих соединений упало примерно с 1901 всего до 35 примерно за два дня. Обычно узел сохраняет свои заполненные входящие слоты до перезапуска или потери сетевого подключения.
Связавшись с другими участниками, я заметил, что узел использовал 100% ресурсов CPU. Это повлияло на узел до такой степени, что он не мог поддерживать связь со своими пирами, что привело к истечению времени ожидания входящих соединений и их разрыву. Используя perf top
в процессе узла, я увидел, что много времени CPU тратится на CTxMemPool::CompareDepthAndScore()
в потоке b-msghand
. Я записал следующую флейм-диаграмму, которая показывает, что make_heap()
, который вызывает CompareDepthAndScore()
, использовал более 45% времени CPU процесса.
В то же время была открыта несвязанная проблема 100% использования CPU со сборками Bitcoin Core в режиме отладки. Это смутило некоторых участников и пользователей, которые не запускали сборки в режиме отладки, но заметили высокое использование CPU на своих узлах. В то время как проблема с режимом отладки, вероятно, затронула только некоторых разработчиков, другая проблема с высоким использованием CPU затронула всю сеть. Это включало, например, майнинг-пулы, такие как AntPool и другие, которые сообщали о проблемах со своими майнинг-операциями из-за того, что их узлы не могли своевременно обрабатывать полученные блоки.
Наблюдение за временем пинга в сети показывает эффект этого Denial-of-Service. Поскольку обработка сообщений Bitcoin Core является однопоточной, только одно сообщение может быть создано или обработано за раз, что означает, что все остальные пиры должны ждать. Более длительное время ожидания влияет на время ответа для пинга. KIT DSN Bitcoin monitoring имеет данные о ICMP и Bitcoin protocol пингах. Сравнение их позволяет нам определить, когда у программного обеспечения узла возникают проблемы с обработкой сообщений. Данные показывают, что ICMP пинг к хосту остался незатронутым, однако медианный пинг к программному обеспечению узла Bitcoin почти удвоился примерно с 25ms до более чем 50ms между концом апреля и началом мая. Медианный Bitcoin пинг подскочил до 200ms 8 мая, в то время как ICMP пинг остался незатронутым.
Эффект также можно увидеть, посмотрев на данные о задержке распространения блоков, собранные KIT DSN Bitcoin monitoring. Примерно 8 мая 2023 года виден всплеск задержки распространения блоков. Время, которое потребовалось 50% доступных узлов, чтобы объявить блок своим узлам мониторинга, увеличилось с менее чем секунды до более чем пяти секунд. Аналогично, измерение 90% подскочило примерно с двух секунд до более чем 20 секунд.
Плохое распространение блоков также приводит к большему количеству устаревших блоков, поскольку майнинг-пулы дольше майнят на своих устаревших блоках, в то время как в сети уже существует новый блок, который они еще не видели. На основе данных из моего набора данных stale-blocks, десять устаревших блоков наблюдались в течение недели между 3 мая (начиная с устаревшего блока 788016) и 10 мая (и заканчивая блоком 789147). Это составляет около 8,84 устаревших блоков на 1000 блоков. Для сравнения, между блоками 800000 и 900000 (около двух лет) наблюдалось 73 устаревших блока. Это составляет 0,73 устаревших блоков на 1000 блоков. Это 10-кратное увеличение частоты устаревших блоков, вероятно, было вызвано значительным влиянием на распространение блоков.
Почему функция CTxMemPool::CompareDepthAndScore()
замедлила работу узла до такой степени, что ему было трудно обрабатывать P2P сообщения? В Bitcoin Core поток b-msghand
обрабатывает P2P сообщения. Например, передача вновь полученных блоков для проверки, ответы на пинги, объявление транзакций другим пирам и многое другое.
Функция CTxMemPool::CompareDepthAndScore()
используется при принятии решения о том, какие транзакции объявить следующими пиру. В Bitcoin P2P protocol транзакции объявляются через inv
(inventory) сообщения. Объявление транзакции Bitcoin Core пиру обычно содержит до 35 wtxid
записей. Чтобы отслеживать, какие транзакции объявить следующими пиру, существует набор m_tx_inventory_to_send
для каждого пира. Он содержит транзакции, которые, по мнению узла, пир еще не видел. При создании inventory сообщения для пира набор сортируется по зависимостям транзакций и feerate, чтобы приоритизировать транзакции с высоким feerate и избежать утечки порядка, в котором узел узнал о транзакциях. Для этого используется функция сравнения CTxMemPool::CompareDepthAndScore()
.
В начале мая 2023 года было broadcast огромное количество транзакций, связанных с BRC-20 tokens. Это означало, что наборы m_tx_inventory_to_send
росли быстрее, чем обычно, и больше, чем обычно. В результате сортировка наборов занимала больше времени. Вечером 7 мая (UTC) начался mint токена VMPX BRC-20, что привело к broadcast более 300 тысяч транзакций за 6 часов в дополнение к другим текущим mint токенов BRC-20. Это вызвало всплески медианного пинга и времени распространения блоков, наблюдавшиеся 8 мая.
Эффект усиливается так называемыми spy nodes, которые только слушают inv
сообщения и никогда не объявляют транзакции самостоятельно. Когда пир объявляет транзакцию узлу,
узел может удалить ее из своего набора m_tx_inventory_to_send
, поскольку она известна пиру и больше не нуждается в объявлении. Это означало, что наборы для spy nodes были еще больше и требовали еще больше времени для сортировки, поскольку они истощались медленнее. Spy nodes, например, LinkingLion и другие, являются обычными и часто имеют несколько соединений, открытых к узлу параллельно. Временами я насчитываю больше предполагаемых spy nodes, чем соединений не-spy node с моими узлами.
Огромное количество broadcast транзакций в сочетании с усилением spy nodes и неоптимальной сортировкой больших наборов m_tx_inventory_to_send
с помощью CTxMemPool::CompareDepthAndScore()
привело к тому, что узлы тратили много времени на создание новых
inventory сообщений для ретрансляции транзакций. Поскольку обработка сообщений является однопоточной, связь с другими пирами значительно замедлилась. Это достигло точки, когда блоки не обрабатывались своевременно, и некоторые соединения истекали по времени.
Исправление состоит из двух частей. Во-первых, все транзакции, которые должны быть объявлены, которые уже были добыты или по какой-либо другой причине больше не находятся в mempool, были удалены до сортировки набора m_tx_inventory_to_send
. Ранее эти транзакции удалялись только после сортировки набора. Это позволяет избежать траты времени на сортировку записей транзакций, которые никогда не будут объявлены, и уменьшает размер сортируемого набора. Во-вторых, когда наборы m_tx_inventory_to_send
велики, количество записей для извлечения из набора динамически увеличивается в зависимости от размера набора. Это означает, что когда broadcast много транзакций, узел будет объявлять больше транзакций своим пирам, пока наборы снова не станут меньше. Исправление было backported вовремя для выпуска v25.0 в конце мая 2023 года.
Хотя группа постоянных участников знала, что происходит, об этой проблеме открыто не сообщалось общественности. Проблема 100% использования CPU с режимом отладки, обсуждаемая в то же время, вызвала путаницу даже среди постоянных участников Bitcoin Core. В то время у меня было ощущение, что это можно и, возможно, следует исправить тихо и не требует большого внимания в настоящее время. Оглядываясь назад, возможно, более публичное и прозрачное отношение к проблеме тоже могло бы сработать. Большое количество broadcast BRC-20 длилось всего около недели (но это не было известно заранее), и перезапуск узла помог бы на некоторое время. Чтобы смягчить проблему, например, для майнинг-пулов, которые не могут немедленно перейти на версию с исправлением (из-за работы с пользовательскими патчами), был подготовлен ban list spy nodes, но я не знаю, был ли он когда-либо использован.
Хотя для этого события не было выделенного канала связи, использовался не указанный в списке IRC канал с P2P участниками, и заинтересованные участники были приглашены или проинформированы о событиях через прямые сообщения. Насколько мне известно, не было канала реагирования на инциденты, и я не знаю, был бы он полезен, учитывая специальный и децентрализованный характер разработки Bitcoin. Ни один участник не несет ответственности за реагирование на инциденты, но каждый может помочь.
Лично я рад, что мой мониторинг оказался полезным для этого. Хотя у меня не было настроено оповещение о потерянных соединениях в то время, и я заметил это только посмотрев на панель управления, было полезно иметь ее. Чтобы точно определить проблему, было полезно иметь несколько узлов для игры и запуска, например, perf top
. Будущий мониторинг должен включать время пинга и оповещение о потерянных соединениях.