Отлично, буду ждать.
Краткий ликбез P2SH timelock.
Допустим, нам надо заблокировать монеты. Простейший скрипт для блокировки будет выглядеть так:
<таймстамп или номер блока> OP_CHECKLOCKTIMEVERIFY OP_DROP <публичный ключ> OP_CHECKSIG
Пусть будет блок 606800 и адрес 129h9Nj3iKZPMG3qMmHR3fcsNN4qGqUxAD.
Дампим приватный ключ и декодируем с помощью bitaddress.org, или иного инструмента, получается следующее:
770A37063E3E4D0BD76E24F0585C4A56566278BE897C1581AA6DA99DC0F27FD7
Ему соответствует публичный ключ:
021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549
606800 в сериализованном виде будет 504209. Подставляем это значение и публичный ключ, собираем скрипт по таблице опкодов:
03504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549acЧто эквивалентно этому:
606800 OP_CHECKLOCKTIMEVERIFY OP_DROP 021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549 OP_CHECKSIGБайты, выделенные
этим цветом - префиксы, означающие длину последующего вектора в 16-ричном формате.
Это и будет redeem скрипт. Считаем p2sh адрес для него:
$ btc decodescript 03504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F8254 9ac
{
"asm": "606800 OP_CHECKLOCKTIMEVERIFY OP_DROP 021f9ab9e432320627f162b5d9627e02cef8ee392ad4a986573b1c882b55f82549 OP_CHECKSIG",
"type": "nonstandard",
"p2sh": "39LkFZvuM3My1r1bdQKyaLRrKL9nr66Ge9",
"segwit": {
"asm": "0 f51002c06d11cf6bcac51594bacb588c11b92ea5741ff6af0621307eda9911bf",
"hex": "0020f51002c06d11cf6bcac51594bacb588c11b92ea5741ff6af0621307eda9911bf",
"reqSigs": 1,
"type": "witness_v0_scripthash",
"addresses": [
"bc1q75gq9srdz88khjk9zk2t4j6c3sgmjt49ws0ldtcxyyc8ak5ezxlsj4a784"
],
"p2sh-segwit": "346mg8ETWSqtQXgtteKhPjRVtY8T7FmUPU"
}
}
$
Отправляю на него монетки:
$ btc sendtoaddress 39LkFZvuM3My1r1bdQKyaLRrKL9nr66Ge9 0.001
acebe7707eebf6ac66271f0c9f8343d90f8362fa18ba95622e4e26723a0ccc0b
$
https://blockstream.info/tx/acebe7707eebf6ac66271f0c9f8343d90f8362fa18ba95622e4e26723a0ccc0bТеперь надо потратить. Посмотрим, что оно нам нагенерировало в транзакции.
$ btc getrawtransaction acebe7707eebf6ac66271f0c9f8343d90f8362fa18ba95622e4e26723a0ccc0b
02000000000101105284e1fa54095164f472edf1ea9af961bd4e01d441272ffefb8190edc7e4ea000000001716001485db6ba29e11f75a1f96ea4a69898d1c939f2f3ffeffffff0271a70a000000000017a9140cf4c70fb2750ff640f799ec206534e2835bbe3487a08601000000000017a91453eb9acee2a540e7ef1678f4b46ddaf571940a6887024730440220263aa894cbace0f042943681ef2acf0b7171793f5becbb76309bbe9b847ac43a02206d3deadeec3f46e625c189a77a4f15bbf07793305d5a99f5fda08f86f5e9c2630121037ca5e2097fb8d8b35c3633a2b58e89a45d3ff46ea8b6f0783dcdd681444aaad980420900
$ btc decoderawtransaction 02000000000101105284e1fa54095164f472edf1ea9af961bd4e01d441272ffefb8190edc7e4ea000000001716001485db6ba29e11f75a1f96ea4a69898d1c939f2f3ffeffffff0271a70a000000000017a9140cf4c70fb2750ff640f799ec206534e2835bbe3487a08601000000000017a91453eb9acee2a540e7ef1678f4b46ddaf571940a6887024730440220263aa894cbace0f042943681ef2acf0b7171793f5becbb76309bbe9b847ac43a02206d3deadeec3f46e625c189a77a4f15bbf07793305d5a99f5fda08f86f5e9c2630121037ca5e2097fb8d8b35c3633a2b58e89a45d3ff46ea8b6f0783dcdd681444aaad980420900
{
"txid": "acebe7707eebf6ac66271f0c9f8343d90f8362fa18ba95622e4e26723a0ccc0b",
"hash": "7340f97dfc3e9fd2af227f9e7f5bcedf4a9e8db81a2905684390e224b8920395",
"version": 2,
"size": 247,
"vsize": 166,
"weight": 661,
"locktime": 606848,
"vin": [
{
"txid": "eae4c7ed9081fbfe2f2741d4014ebd61f99aeaf1ed72f464510954fae1845210",
"vout": 0,
"scriptSig": {
"asm": "001485db6ba29e11f75a1f96ea4a69898d1c939f2f3f",
"hex": "16001485db6ba29e11f75a1f96ea4a69898d1c939f2f3f"
},
"txinwitness": [
"30440220263aa894cbace0f042943681ef2acf0b7171793f5becbb76309bbe9b847ac43a02206d3deadeec3f46e625c189a77a4f15bbf07793305d5a99f5fda08f86f5e9c26301",
"037ca5e2097fb8d8b35c3633a2b58e89a45d3ff46ea8b6f0783dcdd681444aaad9"
],
"sequence": 4294967294
}
],
"vout": [
{
"value": 0.00698225,
"n": 0,
"scriptPubKey": {
"asm": "OP_HASH160 0cf4c70fb2750ff640f799ec206534e2835bbe34 OP_EQUAL",
"hex": "a9140cf4c70fb2750ff640f799ec206534e2835bbe3487",
"reqSigs": 1,
"type": "scripthash",
"addresses": [
"32sXGHo3LCMiADZuqLR7Bui3SppFc7eiDq"
]
}
},
{
"value": 0.00100000,
"n": 1,
"scriptPubKey": {
"asm": "OP_HASH160 53eb9acee2a540e7ef1678f4b46ddaf571940a68 OP_EQUAL",
"hex": "a91453eb9acee2a540e7ef1678f4b46ddaf571940a6887",
"reqSigs": 1,
"type": "scripthash",
"addresses": [
"39LkFZvuM3My1r1bdQKyaLRrKL9nr66Ge9"
]
}
}
]
}
$
Итого, у нас есть транзакция acebe7707eebf6ac66271f0c9f8343d90f8362fa18ba95622e4e26723a0ccc0b, в которой нас интересует выход 1.
Создаем шаблон транзакции траты:
$ btc createrawtransaction '[{"txid": "acebe7707eebf6ac66271f0c9f8343d90f8362fa18ba95622e4e26723a0ccc0b","vout": 1}]' '[{"129h9Nj3iKZPMG3qMmHR3fcsNN4qGqUxAD":0.0009}]' 606800
02000000010bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac0100000000feffffff01905f0100000000001976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac50420900
В этой транзакции есть всё нужное, но поле scriptSig не заполнено. Подписывание транзакции делается в 4 этапа:
1. Создается версия транзакции со scriptSig, включающим только redeemScript и не содержащим подписи.
2. Считается её двойной sha256 хэш.
3. Производится подписывание хэша с помощью приватного ключа, который мы ранее получили.
4. Аналогично первому шагу, только собирается полный scriptSig, включающий подпись.
Для выполнения 1 и 4 шага разбираем транзакцию на составные части:
02000000 <- Версия схемы
01 <- Количество входов
0bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac <- txid входа
01000000 <- номер входа
00 <- Префикс пустого вектора, именно вместо него нужно вставить scriptSig c префиксом
feffffff <- nSequence
01 <- Количество выходов
905f010000000000 <- Сумма выхода
1976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac <- Запирающий скрипт выхода
50420900 <- lockTime
Выполняем первый шаг, подставляя redeemScript:
02000000 <- Версия схемы
01 <- Количество входов
0bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac <- txid входа
01000000 <- номер входа
2903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549ac <- префикс + redeemScript
feffffff <- nSequence
01 <- Количество выходов
905f010000000000 <- Сумма выхода
1976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac <- Запирающий скрипт выхода
50420900 <- lockTime
Собираем транзакцию, объединяя строки в том же порядке, в каком разбирали:
02000000010bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac010000002903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549acfeffffff01905f0100000000001976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac50420900
Добавляем флаг SIGHASH_ALL в Little Endian:
02000000010bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac010000002903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549acfeffffff01905f0100000000001976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac5042090001000000
Считаем sha256(sha256(tx + flag)):
$ echo 02000000010bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac010000002903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549acfeffffff01905f0100000000001976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac5042090001000000 | xxd -r -p | openssl dgst -sha256 -binary | openssl dgst -sha256 -hex
(stdin)= aca8b6ef49f50b328365ad0d17e3b9affcd91765000ad36513ed37f165b42933
$
Хэш данных для подписи aca8b6ef49f50b328365ad0d17e3b9affcd91765000ad36513ed37f165b42933.
Выполняем подписывание:
$ python
Python 2.7.12 (default, Oct 8 2019, 14:14:10)
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import hashlib, binascii, ecdsa
>>> txhash = bytearray.fromhex('aca8b6ef49f50b328365ad0d17e3b9affcd91765000ad36513ed37f165b42933')
>>> privkey = bytearray.fromhex('770A37063E3E4D0BD76E24F0585C4A56566278BE897C1581AA6DA99DC0F27FD7')
>>> signingkey = ecdsa.SigningKey.from_string(privkey, curve=ecdsa.SECP256k1)
>>> sigbytes = signingkey.sign_digest(txhash, sigencode=ecdsa.util.sigencode_der_canonize)
>>> binascii.hexlify(sigbytes)
'304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059'
>>> exit()
Наша подпись
304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059
К ней нужно добавить флаг SIGHASH_ALL, и тогда она будет выглядеть так:
304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d2805901
Получившийся вектор нужно объединить с redeem скриптом, после чего получится полный скрипт, который можно будет добавить в транзакцию.
Сначала считаем префикс байты:
47 304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d2805901
29 03504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549ac
Теперь объединяем и проверяем:
47304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059012903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549ac
$ btc decodescript 47304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059012903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549ac
{
"asm": "304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d2805901 03504209b17521021f9ab9e432320627f162b5d9627e02cef8ee392ad4a986573b1c882b55f82549ac",
"type": "nonstandard",
"p2sh": "3CkKRxxvGxWUa9zk3DnbBS7fPgBJYWhuDt",
"segwit": {
"asm": "0 34277eb829c1589343f8ccd261ea531b9392c52849c5537168a5766fdb3d6e7e",
"hex": "002034277eb829c1589343f8ccd261ea531b9392c52849c5537168a5766fdb3d6e7e",
"reqSigs": 1,
"type": "witness_v0_scripthash",
"addresses": [
"bc1qxsnhawpfc9vfxslcenfxr6jnrwfe93fgf8z4xutg54mxlkeadelq772sq5"
],
"p2sh-segwit": "3Lc5zXYZRsvW25crtbUjjGZHQ31icN2v4Q"
}
}
$
Судя по содержимому поля asm, скрипт соответствует нашим желаниям. Считаем префикс байт для скрипта в целом, получается 0x72. Объединяем со скриптом:
7247304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059012903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549ac
Всё. Теперь у нас есть scriptSig для нашей волшебной транзакции, тратящей заблокированный выход.
Как? Очень просто, берем разобранную ранее на части неподписанную транзакцию и подставляем скрипт:
02000000 <- Версия схемы
01 <- Количество входов
0bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac <- txid входа
01000000 <- номер входа
7247304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059012903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549ac <- префикс + scriptSig
feffffff <- nSequence
01 <- Количество выходов
905f010000000000 <- Сумма выхода
1976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac <- Запирающий скрипт выхода
50420900 <- lockTime
Объединяем строки:
02000000010bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac010000007247304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059012903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549acfeffffff01905f0100000000001976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac50420900
Все, у нас теперь есть подписанная транзакция
Отправляем в сеть:
$ btc sendrawtransaction 02000000010bcc0c3a72264e2e6295ba18fa62830fd943839f0c1f2766acf6eb7e70e7ebac010000007247304402205fcd53ba8c4d58d46c110d3ce545c6eef37dfb309625d94ddfa5a45a601ee6a202203b18ee2a35a644b809cdd06ef851a8f795f70ed2aa0878de08ed121f84d28059012903504209b17521021F9AB9E432320627F162B5D9627E02CEF8EE392AD4A986573B1C882B55F82549acfeffffff01905f0100000000001976a9140c9cf6de1a55f192f2e8fc33279c9bfa92678d4988ac50420900
4878dc56a7d29d342c8653582ba9a123036a76a2ea022a8cec8cd8f3427d88e0
Результат:
https://blockstream.info/tx/4878dc56a7d29d342c8653582ba9a123036a76a2ea022a8cec8cd8f3427d88e0При желании, этот процесс не проблема автоматизировать скриптом из 5-10 строчек. Однако же, это вовсе не сложно сделать руками.
Кроме прочего, вместо консоли питона для подписывания можно использовать утилиту openssl.
P.S. Современный биткойн не принимает неканоничные подписи, которые иногда генерируются openssl и обвязками вокруг него. Самое простое решение - это проверить, встречаются ли в подписи байты 022100 и, если да, то повторять операцию sign_digest до тех пор, пока их в подписи не будет. Иногда, бывает, надо повторить подписывание хэша 2-3 раза, прежде чем получится хорошая подпись.
P.P.S. Если у тратящей транзакции несколько входов, то подписывание каждого из них делается независимо. То есть, для каждого входа подписывается отдельно формируемая болванка, redeemScript или scriptpubKey подставляются только на место для scriptSig подписываемого входа, остальные оставляются пустыми. После получения подписей для всех входов точно так же формируются скрипты и далее добавляются в болванку.