Описание CHECKSIG из вики

Я использовал Биткойн Вики, чтобы узнать больше о CHECKSIG и графике, который там показан .

Этот вопрос будет довольно длинным, но мне уже очень помогло бы, если бы вы просто прочитали мое описание и просто дали мне знать, прав я или нет.

Мой вопрос касается того, как рассчитать хэш (из сериализованной транзакции), который затем подписывается.

Как я понимаю, сначала мы создаем новую необработанную транзакцию с вводом и выводом с помощью pubKeyScripts.

Затем, чтобы создать подпись, мы копируем pubKeyScript из транзакции, которую мы тратим, в SigScript на входе нашей новой транзакции. Затем мы сериализуем новую транзакцию, добавляя к строке хэш-тип (SIGHASH_ALL/SIGHASH_NONE и т. д.), а затем создаем подпись для этой строки.

Затем мы очищаем SigScript, помещаем открытый ключ и подпись в SigScript и получаем подписанную транзакцию. Когда сеть Биткойн подтвердит транзакцию, она создаст сериализованную строку транзакции аналогичным образом и проверит, что подпись, доставленная в SigScript новой транзакции, относится к рассчитанной строке (и из правильного закрытого ключа).

Прежде всего, это правильно? И если да, то почему pubKeyScript транзакции, которую мы тратим, используется при вычислении сериализованной строки новой транзакции, которую мы хотим подписать?

Новая транзакция уже ссылается на идентификатор расходующей транзакции во входных данных новой. Поэтому я не понимаю, почему полезно также временно копировать pubKeyScript расходной транзакции в SigScript новой транзакции, чтобы сделать подпись?

Более того, я пытался просмотреть исходный код Биткойна, чтобы узнать, что происходит. Я не программист, особенно на C++. Но я узнал, что подпись проверяется здесь: https://github.com/bitcoin/bitcoin/blob/48efbdbe986355bd2478f0fdd366b20952fbf30a/src/script/interpreter.cpp#L847

Ссылка на «scriptCode» в строке 838, которая используется в качестве аргумента для checker.CheckSig(), по-видимому, содержит только pubKeyScript из расходуемой транзакции (без ввода, вывода и т. д. из новой транзакции?).

Мне не удалось выяснить, где именно в исходном коде определена функция checker.CheckSig().

Но вычисляет ли checker.CheckSig сериализованную транзакцию, как я описал выше, добавляя scriptCode в SigScript и сериализуя его? Если да, то знаете ли вы, где в исходном коде это происходит?

Я надеюсь, что вы можете дать мне некоторые материалы для этого. Спасибо!

Небольшое замечание: обычно пишутся scriptSig и scriptPubKey, а не наоборот.
Кроме того, недавно мы обсуждали это в Bitcoin Lounge (на самом деле, вызванном вашим другим вопросом) chat.stackexchange.com/transcript/message/24348979#24348979 . место, где подпись удаляется перед подписанием.
Приятно отметить в Bitcoin Lounge сериализацию в коде! github.com/bitcoin/bitcoin/blob/… Таким образом, явно используется scriptPubKey из транзакции, на которую ссылается новая (расходующая) транзакция, и, вероятно, подпись работает, как показано на рисунке. Мне просто интересно, почему этот скрипт используется в сериализации?

Ответы (1)

Цель подписи — «захватить» некоторые данные о транзакции — ее выходы и так далее. Подписанты контролируют некоторые из них, используя флаги SIGHASH, но некоторые из них контролируются сетью.

Помните, что любой, кто проверяет ваши подписи, должен знать, что вы утверждаете в своей подписи, и как воспроизвести тот же хэш для ECDSA.

При создании подписи и ее проверке SignatureHash() вызывается для каждого ввода, потому что каждый ввод имеет свои собственные подписи.

Для каждого ввода транзакция повторно сериализуется с помощью CTransactionSignatureSerializer , который вносит в транзакцию изменения, зависящие от ввода. Он обнуляет ввод/вывод с учетом некоторых флагов SIGHASH, а также требует включения scriptPubKey предыдущей транзакции.

Кажется, вы спрашивали отчасти об этом. Причина захвата этого с помощью хэша заключается в том, что подпись нельзя использовать в других транзакциях. Подпись действительна только для данного input и всех требований его scriptPubKey.

Следовательно, вы обнаружите, что службы, которые проверяют подписи, должны иметь scriptPubKey подписываемой транзакции.

Редактировать: На самом деле, более конкретно, то, что включается в качестве сценария при подписании, - это любой сценарий, начинающийся с последнего OP_CODESEPARATOR или с начала выходного сценария. Поэтому, если сценарий содержит OP_CODESEPARATOR, подписывается только часть выходного сценария.

Один момент, когда вы выглядите сбитым с толку, заключается в следующем:

Затем мы очищаем SigScript и помещаем открытый ключ и подпись в SigScript и получаем подписанную транзакцию.

Это неверно - сериализованная форма забывается после создания хэша. Подписи помещаются в другой экземпляр транзакции.

Код checker.CheckSig() — обратите внимание, что он вызывает SignatureHash()для создания хэша, а затем VerifySignature()вызывает pubkey.Verify(sighash, vchSig), который является фактическим вызовом ECDSA.

Итак, большая часть того, что вы ищете для проверки, находится в нижней части интерпретатора.cpp, но также проверьте интерпретатор.h для интерфейсов.

Спасибо за ответ. Я до сих пор не понимаю, почему для подписи включен scriptPubKey предыдущей транзакции. Входные данные уже содержат хеш-транзакцию и индекс предыдущего расходуемого вывода. В чем тогда преимущество включения scriptPubKey из этого предыдущего вывода транзакции в подпись? Подпись может использоваться только для транзакций, содержащих именно тот конкретный хэш транзакции, на который есть ссылка во входных данных. И scriptPubKey обычно одинаков для всех выходов на один и тот же биткойн-адрес (dup hash160 "bitcoin-address" equalverify checksig)
Привет, я обновил свой ответ (см. редактирование). Преимущества неясны, но данные, включенные в эту часть хешированной транзакции, иногда могут быть подмножеством scriptPubKey, если используется OP_CODESEPARATOR. Есть разные отчеты о том, полезна ли эта функция, она редко используется. Дайте мне знать, если это ответит на ваши вопросы!