Глоссарий / Транзакции и комиссии
SIGHASH_ALL
- Что такое SIGHASH_ALL?
- SIGHASH_ALL является флагом, который заставляет подпись охватывать каждый вход и каждый выход транзакции, поэтому после подписания в платеже нельзя изменить ничего.
Это флаг, который использует ваш кошелек, и именно благодаря ему одобрение транзакции на экране аппаратного кошелька что-то значит. Его значение равно 1, и его можно увидеть как последний байт почти каждой подписи в обозревателе блоков. В Taproot значение по умолчанию переименовали в SIGHASH_DEFAULT и перестали записывать этот байт вообще, что экономит один байт на подпись.
Как это работает
При SIGHASH_ALL хеш охватывает версию транзакции, каждый расходуемый outpoint, каждый номер последовательности, сумму каждого выхода, скрипт каждого выхода и locktime.
Измените один сатоши в одном выходе, и все подписи транзакции перестанут действовать. Добавьте вход, удалите выход, поменяйте их порядок, сдвиньте locktime: результат тот же. Именно это имеют в виду, когда говорят, что подписанная транзакция биткоина окончательна по содержанию еще до того, как она отправлена в сеть. Единственное решение, которое остается кому-либо, таково: отправить ее или нет.
Одно намеренно исключено: скрипты разблокировки других входов. Если бы хеш охватывал и их, то в транзакции с двумя подписантами каждую подпись пришлось бы делать после другой, что образует замкнутый круг и невозможно. Именно их исключение позволяет независимым сторонам подписывать одну и ту же транзакцию в любом порядке.
Кодирование зависит от типа входа. Во входе формата legacy или segwit версии 0 байт 01 добавляется к подписи в кодировке DER, и отсюда берется привычный общий размер 71 или 72 байта. При расходовании по ключевому пути Taproot BIP341 определяет значение ноль как SIGHASH_DEFAULT, что означает тот же охват без записи байта вообще, поэтому подпись составляет ровно 64 байта. Явная запись 01 во входе Taproot по-прежнему допустима и дает тот же хеш, просто она стоит лишнего байта впустую.
Где это встречается
SIGHASH_ALL используется почти везде: Bitcoin Core, Electrum, Sparrow, каждый аппаратный кошелек и каждый механизм вывода средств на биржах по умолчанию подписывают так, и ни один из них не предлагает настройку, чтобы это изменить.
Практическое следствие ощущается, когда платеж застревает. Поскольку комиссия представляет собой остаток между входами и выходами, а все выходы подписаны, нельзя срезать несколько тысяч сатоши с выхода, чтобы повысить комиссию. Транзакцию приходится собирать заново и подписывать с начала, а это может сделать только кошелек, хранящий ключи. Поэтому replace-by-fee является функцией отправляющего кошелька, а не чем-то, что биржа или обозреватель блоков могут выполнить за вас, и поэтому child-pays-for-parent существует как запасной выход, когда отправитель недоступен.
Это же превращает экран аппаратного кошелька в договор, а не в подсказку. Устройство показывает адрес получателя, сумму и комиссию, и поскольку создаваемая им подпись охватывает все три, никакое программное обеспечение ниже по цепочке не может изменить ни одно из них, не отбросив эту подпись. Одобрите то, что показывает экран, и больше ничего произойти не может.
SIGHASH_ALL vs SIGHASH_NONE
SIGHASH_ALL охватывает полный список выходов; SIGHASH_NONE не охватывает ни одного из них. Разрыв между ними представляет собой разрыв между платежом и голым разрешением переместить монету куда-то без указания места. Два флага отстоят друг от друга на одно значение, поэтому библиотека подписи, принимающая флаг как целочисленный параметр, находится в одной опечатке от того, чтобы превратить вывод средств клиента в монету, которую сможет перенаправить любой, кто следит за пулом памяти. Рабочий код подписи должен жестко задавать значение, а не принимать его как входной параметр.