Author

Topic: Эксперименты со скриптами биткоина где? (Read 341 times)

kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Уже разобрался.
Симуляторы косячно ищут хэши.
newbie
Activity: 6
Merit: 0
Там в транзакции получается скрипт
Code:
1 c47907abd2a80492ca9388b05c0e382518ff3960 1 OP_IF OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_ELSE 2 OP_EQUALVERIFY OP_ENDIF

В результате работы этого скрипта остается 0x1

Делаю скрипт
OP_IF OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_ELSE 2 OP_EQUALVERIFY OP_ENDIF

OP_IF убирает 0x1 из стека и делает OP_RIPEMD160 от пустой строки, в итоге получается 0xE63C7DC57882262676E009478111E6AE5C6C5198 и затем OP_EQUALVERIFY сравнивает его с 0x388756DC41F4EEADCB3FC5064535D1121A49D3F4

В симуляторе OP_RIPEMD160 от пустой строки срабатывает, а в реальном биткоине видимо нет.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Черт с ним со сложным скриптом. Решил с простыми поэкспериментировать. Тоже не работает нифига.

Делаю скрипт
OP_IF OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_ELSE 2 OP_EQUALVERIFY OP_ENDIF

Вот его адрес
2MwGFm13NMXNcv3yTKHWTJ3wFBfxDWhVbpS

Послал туда сатошей.

Теперь хочу забрать сатоши. Пишу транзакцию
 
Code:
02000000012d0cf45773e4730cfde672d85447c8c6761621ff708850eb2b0505247255ac5d00000000345114c47907abd2a80492ca9388b05c0e382518ff3960511c63a614388756dc41f4eeadcb3fc5064535d1121a49d3f48867528868feffffff01905f0100000000001976a914d5d4a489e479dc7ab7d64b8dbe2917575cffc79788aca2e81500

Там в транзакции получается скрипт
Code:
1 c47907abd2a80492ca9388b05c0e382518ff3960 1 OP_IF OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_ELSE 2 OP_EQUALVERIFY OP_ENDIF

И что имеем?
Тут скрипт работает без ошибок https://siminchen.github.io/bitcoinIDE/build/editor.html
А когда засылаю в сеть, пишет
Quote
16: mandatory-script-verify-flag-failed (Script failed an OP_EQUALVERIFY operation)

Спрашивается: чего я опять делаю не так?
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Что-то не проканывает.

Задумал вот такой скрипт написать
Code:
OP_IF 83e815 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_ELSE OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_RIPEMD160 5df9e3c695bb86153326a6ff4cfce8e95b3f55bf OP_EQUALVERIFY OP_ENDIF

Написал, получил адрес, послал туда тестовых битков.
адрес: 2N77oVk5k8MWE33hWS5QeS5bM2zVkgHcido
транзакция: 3a59dcef30879ebda1ffdaff4c1c46f06691d231b974acdc6f90907a973c658f

Пробую потратить такой транзакцией

Code:
02000000018f653c977a90906fdcac74b931d29166f0461c4cffdaffa1bd9e8730efdc593a000000006614c47907abd2a80492ca9388b05c0e382518ff3960514c4e630383e815b175a614388756dc41f4eeadcb3fc5064535d1121a49d3f48867a614388756dc41f4eeadcb3fc5064535d1121a49d3f488a6145df9e3c695bb86153326a6ff4cfce8e95b3f55bf8868feffffff01905f0100000000001976a91414466ff4b5a6d6e8a7e45152b47f0c5d8f9f0e4088ac83e81500

ОБЛОМ ((

Декодирую, вижу
Quote
"scriptSig": {
   "asm": "c47907abd2a80492ca9388b05c0e382518ff3960 1 630383e815b175a614388756dc41f4eeadcb3fc5064535d1121a49d3f48867a614388756dc41f4e eadcb3fc5064535d1121a49d3f488a6145df9e3c695bb86153326a6ff4cfce8e95b3f55bf8868",
   "hex": "14c47907abd2a80492ca9388b05c0e382518ff3960514c4e630383e815b175a614388756dc41f4e eadcb3fc5064535d1121a49d3f48867a614388756dc41f4eeadcb3fc5064535d1121a49d3f488a6 145df9e3c695bb86153326a6ff4cfce8e95b3f55bf8868"
},

В asm то, что я хочу видеть, в hex вижу пару лишних байтов

Code:
c47907abd2a80492ca9388b05c0e382518ff3960 OP_1 OP_PUSHDATA1 OP_PUSHDATA4 OP_IF 83e815 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_ELSE OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_RIPEMD160 5df9e3c695bb86153326a6ff4cfce8e95b3f55bf OP_EQUALVERIFY OP_ENDIF

Лишние тут OP_PUSHDATA1 OP_PUSHDATA4
Понятно откуда они взялись: это пушится мой скрипт.
Непонятно - какого хрена они учитываются как данные, когда скрипт исполняетсяHuh

Получается в моем начальном скрипте надо делать как-то так:

Code:
OP_DROP OP_DROP OP_IF 83e815 OP_CHECKLOCKTIMEVERIFY OP_DROP OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_ELSE OP_RIPEMD160 388756dc41f4eeadcb3fc5064535d1121a49d3f4 OP_EQUALVERIFY OP_RIPEMD160 5df9e3c695bb86153326a6ff4cfce8e95b3f55bf OP_EQUALVERIFY OP_ENDIF

?
sr. member
Activity: 770
Merit: 305
Ну молодцы, сами разобрались?
Отлично, мне работы меньше.
Я просто думал, что не только вам объяснить, но и другим полезно было бы понять,
почему ушли от p2pk к p2pkh, потом к p2sh (это было радикальное изменение,
уже без Сатоши, пришлось софт-форком улучшать то, до чего Сатоши не допер)

И поняв это, достаточно легко уже понять сегвит - тоже софт-форк, который исправляет
другие неучтенные проблемы.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Но вот это все целиком тоже валидный скрипт:
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Как узнать: где кончаются подписи и начинается скрипт?

Вы неправильно написали:
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Правильно так:
vin.scriptSig = PUSH правильная_подпись1 PUSH правильная_подпись2 ... PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Понятно, в чём разница? PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ - это уже не скрипт, а данные (сериализованный скрипт), над которыми должен выполнить действия другой скрипт - scriptPubKey.

Как-то так:
PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ OP_HASH160 PUSH хэш_скрипта OP_EQUAL

Сериализованный скрипт стоит последним в scriptSig, тем он и выделяется среди впереди стоящих подписей.

В случае успешного выполнения PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ OP_HASH160 PUSH хэш_скрипта OP_EQUAL
дальше уже выполняется PUSH sig1 PUSH sig2 ... ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Обратите внимание: ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ стоит уже без PUSH. Это уже не данные, а скрипт - последовательность опкодов к исполнению.


Спасибо огромное, я четвертый час это вот все нагуглить никак не могу. Теперь спать спокойно могу пойти  Smiley
legendary
Activity: 2317
Merit: 2318
Но вот это все целиком тоже валидный скрипт:
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Как узнать: где кончаются подписи и начинается скрипт?

Вы неправильно написали:
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Правильно так:
vin.scriptSig = PUSH правильная_подпись1 PUSH правильная_подпись2 ... PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Понятно, в чём разница? PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ - это уже не скрипт, а данные (сериализованный скрипт), над которыми должен выполнить действия другой скрипт - scriptPubKey.

Как-то так:
PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ OP_HASH160 PUSH хэш_скрипта OP_EQUAL

Сериализованный скрипт стоит последним в scriptSig, тем он и выделяется среди впереди стоящих подписей.

В случае успешного выполнения PUSH ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ OP_HASH160 PUSH хэш_скрипта OP_EQUAL
дальше уже выполняется PUSH sig1 PUSH sig2 ... ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Обратите внимание: ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ стоит уже без PUSH. Это уже не данные, а скрипт - последовательность опкодов к исполнению.




kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Pay-To-Script Hash

кто хочет потратить, в своей транзакции на входе пишет
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ
vin.txid = ид транзакции которую надо потратить.

На выходе транзакции которую собираются тратить стоит
vout.scriptPubKey = OP_HASH160 хэш_скрипта OP_EQUAL

В этом месте у меня уже нет полного понимания, как оно на самом деле работает.

А работает оно в два этапа.
Сначала выполняется вот этот скрипт: <ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ> OP_HASH160 <хэш_скрипта> OP_EQUAL
Тем самым, проверяется соответствие скрипта и его хеша, подтверждая право владельца скрипта на его выполнение.

А потом выполняется сам скрипт, тратящий выход: ... <ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ>.

Но вот это все целиком тоже валидный скрипт:
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ

Как узнать: где кончаются подписи и начинается скрипт?
legendary
Activity: 2317
Merit: 2318
Pay-To-Script Hash

кто хочет потратить, в своей транзакции на входе пишет
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ
vin.txid = ид транзакции которую надо потратить.

На выходе транзакции которую собираются тратить стоит
vout.scriptPubKey = OP_HASH160 хэш_скрипта OP_EQUAL

В этом месте у меня уже нет полного понимания, как оно на самом деле работает.

А работает оно в два этапа.
Сначала выполняется вот этот скрипт: <ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ> OP_HASH160 <хэш_скрипта> OP_EQUAL
Тем самым, проверяется соответствие скрипта и его хеша, подтверждая право владельца скрипта на его выполнение.

А потом выполняется сам скрипт, тратящий выход: ... <ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ>.
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Ну можно было короче:

В первых версиях биткоина было так (pay-to-pubkey transaction):

кто хочет потратить, в своей транзакции на входе писал
vin.scriptSig = PUSH правильная_подпись
vin.txid = ид транзакции которую надо потратить.

На выходе транзакции которую собираются тратить стояло
vout.scriptPubKey = PUSH публичный_ключ CHECKSIG

Итого полный скрипт получался
Code:
PUSH правильная_подпись PUSH публичный_ключ CHECKSIG

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

В следующих версиях скрипты слегка усложнили.
(pay-to-pubkey-hash)

кто хочет потратить, в своей транзакции на входе пишет
vin.scriptSig = PUSH правильная_подпись PUSH публичный_ключ
vin.txid = ид транзакции которую надо потратить.

На выходе транзакции которую собираются тратить стоит
vout.scriptPubKey = DUPLICATE HASH160 хэш_публичного_ключа EQUALVERIFY CHECKSIG

Итого полный скрипт получается
Code:
PUSH правильная_подпись PUSH публичный_ключ DUPLICATE HASH160 хэш_публичного_ключа EQUALVERIFY CHECKSIG

Потом появилась еще одна разновидность
Pay-To-Script Hash

кто хочет потратить, в своей транзакции на входе пишет
vin.scriptSig = PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ
vin.txid = ид транзакции которую надо потратить.

На выходе транзакции которую собираются тратить стоит
vout.scriptPubKey = OP_HASH160 хэш_скрипта OP_EQUAL

Итого полный скрипт получается.
Code:
PUSH правильная_подпись1 [PUSH правильная_подпись2] [правильная_подпись3] [...] ЛЮБОЙ_ВАЛИДНЫЙ_СКРИПТ OP_HASH160 хэш(vin.scriptSig) OP_EQUAL


В этом месте у меня уже нет полного понимания, как оно на самом деле работает. Походу дела как-то так:
1. Сначала исполняется скрипт vin.scriptSig
2. Потом берется хэш от vin.scriptSig (или берется хэш только от скрипта, а подписи не хэшируются ?)
3. Полученный хэш засовывается в стек и дальше начинает исполняться OP_HASH160 хэш_скрипта OP_EQUAL
4. Если на выходе стека получится не ноль, значит предыдущая транзакция считается потраченной.

Наконец самое последнее веяние:
SegWit транзакции.
Тут я как раз хотел у знатоков поинтересоваться - как они работают.
sr. member
Activity: 770
Merit: 305
Давай с самого начала начнем.

Можно начать с самой первой транзакции
https://www.blockchain.com/btc/tx/4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b

Скрипт выхода (scriptPub) этой транзакции из двух операндов состоит
PUSH ( 04678afdb0fe5... ) CHECKSIG

Чтобы потратить эту транзакцию надо придумать такой входной скрипт (scriptSig)
чтобы последовательное выполнение scriptSig + scriptPub над одним и тем же стеком
давало бы в конце ненулевое значение на вершине стека. Обращаю внимание: сперва выполняется
scriptSig, а потом scriptPub!

Тут надо понимать как работает операция CHECKSIG - она что-то крутит-вертит с транзакцией
(той в которой мы будем тратить) и еще берет scriptPub из первой транзакции - получает некоторый
дайджест. Потом надо сигнатуру, публичный ключ и дайджест скормить функции проверки правильности
подписи и в стек записать нолик или единичку в зависимости от того прошло или не прокатило.

Значит самый простой вариант scriptSig - это просто PUSH ( тутправильнаяподпись )

Можно изъебнуться. Например NOP NOP NOP PUSH ( чтонибудь ) DROP PUSH ( тутправильнаяподпись )
тоже будет приводить к тому же результату. Это достаточно давно заметили и решили
что клиент должен делать дополнительные проверки, например в scriptSig нельзя(?)
использовать ничего кроме PUSH-операций.

Потом Сатоши Накамото почесал репу, и решил - надо по-другому делать.
Он таки башковитый был, решил что предложенный вариант несколько кривоват.
Вернее даже не кривоват, наоборот, он слишком прямой. Надо бы изъебнуться так,
что владелец средств не показывает никому свой публичный ключ до того, как он
тратит бабки.

И стали все использовать другой вариант scriptPub который называется p2pkh

Об этом в следующей главе. Мне лениво много печатать, а если я не начну с самого начала,
то вы мысль не уловите - нахера было от p2pk переходить к p2pkh, потом к p2sh, а потом к сегвитам

kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Прошу консультации: кто может объяснить работу со скриптами биткоина?

Вот мое понимание.

Обычно желающий потратить биткоины, в своей тратящей транзакции в vin.scriptSig пишет некий скрипт, а в vin.txid пишет транзакцию куда этот скрипт подставить.

Правило такое: если vin.scriptSig тратящей транзакции (currentTX) поставить перед (prevTX) vout.scriptPubKey из той транзакции которую тратят, то получившийся скрипт (currentTX.vin.scriptSig prevTX.vout.scriptPubKey) должен после выполнения, в стеке давать не ноль. Если в стеке не ноль, то транзакция prevTX считается израсходованной...

Вроде с этим все понятно.
Однако с некоторых пор, в сети биткоина появился новый тип транзакций, связанный с SegWit. В SegWit транзакциях помимо vin.scriptSig присутствует поле vin.txinwitness

Насколько я понял, в этом новом поле тоже пишется скрипт, и он тоже подставляется вперед prevTX.vout.scriptPubKey и тоже должен получиться не ноль. Но вот есть загвоздка в моем понимании:
1. vin.txinwitness это массив.
2. Каждый элемент массива что-то не очень похож на скрипт. Вот пример http://chainquery.com/bitcoin-api/getrawtransaction/e6511db40a71ca407965e011ad76a7711acc5cfccf51586aa71a78dc535e6a4b/1

3. Если это поле все-таки скрипты, тогда еще вопрос: если в одной транзакции есть и непустое vin.scriptSig и vin.txinwitness, то кто подставляется первым?
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange

Спасибо.
По первой ссылке ничего правда не понятно, но вторая - вещь!
kzv
legendary
Activity: 1722
Merit: 1285
OpenTrade - Open Source Cryptocurrency Exchange
Привет. Где-то давно попадался сайт, где можно было потренироваться в написании биткоин скриптов. Вводишь команды, смотришь стек и что возвращается в результате. Сейчас что-то сходу не могу рабочую версию нагуглить. Никто не в курсе случайно где?
Jump to: