Title: bittranshelperawk Post by: yurm on January 29, 2014, 08:14:01 PM Создал тут скрипт (https://github.com/yurm-develop/bittransacthelperawk) для упрощения переводов с/на мультисигнатурные адреса и для модификации nlocktime. Пока неполнофункциональный (планируются также вариации на тему sighash).
Просьба за парсинг транзакций сильно ногами не бить :) Он пока реализован через вызов bitcoind decoderawtransaction и парсинг JSON-ответа грубо-велосипедным способом, в дальнейшем планируется самостоятельный разбор hex-encoded транзакции. Замена sequence number осуществляется простым текстовым поиском-заменой (а-ля sed 's/00ffffffff/00<new nseq>/'), поэтому теоретически возможны ложные срабатывания (с вероятностью в районе 1/2^40, около одной триллионной). Запилю самостоятельный разбор транзакции — исключу и эту вероятность. Скрипт ничего не отправляет в сеть — только формирует и подписывает транзакцию. Перед её опубликованием (bitcoind sendrawtransaction) категорически рекомендуется её проверить (bitcoind decoderawtransaction). Описания использования и use cases на английском пока нет (задача на ближайшее будущее), поэтому выложу здесь — см. следующие посты. Инсталляция: Linux (*nix?) only. Просто скачайте скрипты *.awk, *.sh и установите их, например, в /usr/local/bin Зависимости: bitcoind, awk, bc (+ sh, sed, date, имеющиеся практически в каждом Linux-дистрибутиве). Использование: Quote bittransacthelperawk.awk filename filename — файл с описанием генерируемой транзакции, формат файла см. ниже. Для упрощения формирования списка входов можно воспользоваться командойQuote bitcoind-unspent.sh >> filename Правила безопасности: 1. Не оставляйте скрипты в домашнем каталоге после скачивания, перенесите их в /usr/local/bin (или, скажем, /opt/bin) и поменяйте владельца на root:root. В противном случае, если, допустим, в браузере обнаружится уязвимость, позволяющая злоумышленнику получить доступ к файловой системе (пусть и с правами простого пользователя, не root), то он сможет пропатчить скрипт, добавив туда перевод биткоинов с вашего кошелька на свой адрес. 2. Заведите отдельный пользовательский аккаунт только для работы кошелька (вместе с этим скриптом). Не запускайте браузер (и вообще любые другие программы) из этого аккаунта. Т.е., для торговли на бирже используйте другой аккаунт. Желательно тоже отдельный. Следование этому правилу не отменяет необходимости предыдущего. 3. При подписи транзакции в скрипте используется (почти) полная форма команды signrawtransaction, т.е. приватные ключи передаются как параметры командной строки при вызове bitcoind. И, соответственно, при определённом везении могут быть перехвачены, например, командой «ps aux» (запущенной из-под любого пользователя — параметры вызова, как правило, в Linux не скрываются; впрочем, нужно точно подгадать момент — bitcoind signrawtransaction выполняется быстро). Обойти эту уязвимость можно выделением отдельной виртуальной системы только для кошелька со скриптом, однако см. следующее правило. Надо будет попробовать предложить разработчикам официального клиента патч, позволяющий указывать адреса или публичные ключи вместо приватных. 4. Хотя при правильной настройке отдельная виртуальная система для кошелька будет безопаснее, чем просто отдельный пользовательский аккаунт, при неправильном подходе эта система будет более уязвима (вплоть до полной иллюзорности безопасности). Виртуальная система должна быть именно отдельной (т.е., ваша повседневная активность должна проходить в другой гостевой виртуальной системе, но не на самом хосте!), а хост-система должна иметь лишь самый минимум ПО (желательно ограничиться максимум роутингом, особые умельцы могут и его перенести в виртуалку с пробросом туда сетевого интерфейса, а хосту оставить только функциональность свитча). Также нужно тщательно изучить вопрос качества генерируемого рандома — например, haveged (генератор энтропии на базе нестабильности работы процессора) в некоторых виртуальных окружениях выдаёт предсказуемые данные (https://security.stackexchange.com/questions/34523/is-it-appropriate-to-use-haveged-as-a-source-of-entropy-on-virtual-machines), что может привести к тому, что ваши случайные приватные ключи будут не столь случайными. Впрочем, для решения проблемы из предыдущего правила, думаю, будет достаточно Linux Containers (LXC); скорее всего, эмуляции rdtsc там нет и качество haveged-энтропии в виртуалке не ухудшится. to be continued... Title: Re: bittranshelperawk Post by: yurm on January 29, 2014, 08:18:30 PM Формат файла описания транзакции.
Формат строк для входов транзакции: Quote i <txid> <vout> [- [(-|<amount>) [<source detailed address>]]] txid/vout (обязательные поля): «координаты» «монеты» — id транзакции и номер выхода в ней для соответствующего txout, который мы собираемся потратить);- (необязательное поле): зарезервировано для sighash; amount (необязательное поле): точное количество биткоинов в данном ещё не потраченном txout (можно опустить, указав «-»); detailed address (необязательное поле): адрес, на котором лежит данный ещё не потраченный txout (формат <detailed address> см. далее). Формат строк для выходов: Quote o (-|<value>) [<destination detailed address>] value (обязательное поле): сумма, переводимая на <detailed address>; если указан «-», то на <detailed address> будет переведена сдача (если минусов несколько, сдача будет поделена поровну);detailed address: адрес для перевода; отсутствующий адрес обозначает комиссию (т.е., комиссию в данном скрипте нужно указывать явно, по умолчанию она нулевая). Формат строк для уточнения адресов: Quote a <detailed address> Формат строк для nlocktime: Quote l <sequence number> <locktime> sequence number: номер варианта (присваивается каждому входу), обычно 0 — замена транзакций на данный момент сетью не поддерживается, заблокированные по времени транзакции просто не принимаются до наступления времени разблокировки;locktime: время разблокирования транзакции в формате, воспринимаемом программой date (указывается без кавычек). Формат строк для (частично) подписанных транзакций: Quote t <hex-encoded raw transaction> Используется при передаче между отдельными подписантами (например, адрес входа является мультисигнатурным 2-из-2, один ключ лежит на одном компьютере, второй на другом).Формат строк для действий: Quote checkonly Только проверка транзакции без её подписания (проверяемая транзакция в этом случае указывается в строке «t <hex-encoded raw transaction>»).Формат строк комментариев: Quote [#;-] Произвольный текст Вывод скрипта (на stdout): Quote (false|<txid>) false — в случае, если транзакция подписана не полностью; <txid> — для полностью подписанной транзакции.<hex-encoded raw transaction> Возможна также выдача предупреждений на stderr. Формат поля <detailed address>: Quote (- <address> [<pubkey>]|<m> <a1>...<an>) Первый вариант — для простых адресов (pay-to-pubkey-hash) или для мультисигнатурных в явном виде, второй — для мультисигнатурных m-of-n (pay-to-script-hash). На данный момент n не может быть больше 3 (ограничение биткоин-сети).Для создания мультисигнатурного адреса (bitcoind createmultisig) требуются публичные ключи (в справке официального клиента сказано, что можно указывать адреса или публичные ключи, на самом деле при указании адреса клиент извлекает соответствующий публичный ключ из кошелька, если он там был — иначе генерирует ошибку). Поэтому адрес, например, 2-из-3 стоит расписывать так: Quote ... 2 <a1> <a2> <a3> При описании мультисигнатурного адреса можно вместо адресов участников указывать их публичные ключи (а также смешивать ключи с адресами). Впрочем, на данный момент получение адреса из публичного ключа в скрипте не реализовано, поэтому каждый публичный ключ всё равно потребуется уточнять (как в 3 нижних строках последнего примера).a - <a1> <pub1> a - <a2> <pub2> a - <a3> <pub3> Предупреждение: не записывайте простые адреса в форме «1 <address>», данная запись будет воспринята как мультисигнатурный адрес 1-из-1. Предупреждение: порядок следования адресов участников во множественном адресе имеет значение, например 2-of-{A,B} ≠ 2-of-{B,A}. Уточнение адресов необходимо лишь для входов транзакции, для выходов оно необязательно. Т.е., разрешена такая запись транзакции: Quote i <txid0> <vout0> - <amount0> - 3<xxxxx> Обратите внимание, что участников мультисигнатурного адреса 3<yyyyy> мы не раскрываем (хотя, разумеется, это не запрещено).o - - 3<yyyyy> a 2 1<xxxxx1> 1<xxxxx2> 1<xxxxx3> a - 1<xxxxx1> <pubkey_xxxxx1> a - 1<xxxxx2> <pubkey_xxxxx2> a - 1<xxxxx3> <pubkey_xxxxx3> Множественные адреса можно раскрывать непосредственно в строке входа/выхода транзакции (см. описание формата строк). Т.е., в вышеописанном примере первую и третью строки можно объединить в одну: Quote i <txid0> <vout0> - <amount0> 2 1<xxxxx1> 1<xxxxx2> 1<xxxxx3> На момент генерации транзакции те транзакции, выходы которых мы собираемся тратить, не обязаны быть уже опубликованными (генерируемая транзакция становится «орфанной»). В этом случае требуется указание точной суммы <amount> и исходного адреса <source detailed address> для каждого «орфанного» входа, в противном случае их можно опустить (соответствующая транзакция будет извлечена с помощью bitcoind getrawtransaction). Для формирования списка непотраченных выходов («монет») можно воспользоваться командой Quote bitcoind-unspent.sh >> filename после чего в filename удалить ненужные строки, соответствующие монетам, которые мы не собираемся тратить. Затем в каждой оставшейся строке убрать 2 последних поля (confirmations и account), например, командойQuote cat filename | sed 's/\t[[:digit:]]\+\t"[^"]*"$//' > new-filename Внимание: непотраченные монеты на мультисигнатурных адресах в вашем клиенте, скорее всего, отображаться не будут (официальный клиент их в выводе listunspent на данный момент вроде не перечисляет, даже если вы явно добавили такой адрес командой addmultisigaddress). За этими монетами нужно следить самостоятельно.to be continued... Title: Re: bittranshelperawk Post by: yurm on January 29, 2014, 08:26:00 PM Use cases.
Навеяно этой статьёй (https://en.bitcoin.it/wiki/Contracts). В данном посте мультисигнатурные адреса буду обозначать так: {A1,...,An}m или просто {A1,...,An} если n == m. 1. Наследство/резервный вывод. Создаём и подписываем транзакцию Code: TxR[0]: A1,...,An → R; TxR[0].nlocktime устанавливаем, допустим, на год вперёд Code: TxT[1]: A1,...,An → A Code: TxR[1]: (id(TxT[1]),0)A → R; TxR[1].nlocktime = currtime + delay Code: TxT[i]: (id(TxT[i-1]),0)A → A Получатель не сможет задействовать резервную транзакцию до времени разблокирования; нам остаётся лишь следить за тем, чтобы не пропускать моменты TxR[ i ].nlocktime. 2. Хранение биткоинов на разных адресах (принадлежащих разным кошелькам) одновременно. Code: Tx1: S1,...,Sn → {A1,A2} При необходимости можно выпустить резервную транзакцию к Tx1 Code: TxR: (id(Tx1),0){A1,A2} → F; TxR.nlocktime = currtime + delay ВНИМАНИЕ: в связи со свежевсплывшей (https://bitcointalk.org/index.php?topic=461503.0) уязвимостью (https://en.bitcoin.it/wiki/Transaction_Malleability) bitcoin-протокола не гарантируется стопроцентная безопасность для покупателя последующих схем (с транзакцией возврата). 3. Покупка/обмен по предварительной договорённости без ожидания подтверждений и без доступа в интернет. As1,...,Asn (start), Ap (participant), Af (final) — адреса покупателя, в частном случае могут быть одинаковыми; Bp, Bf — то же самое для продавца. TxR — reserve transaction, TxP — payment transaction, Tx1 — транзакция пополнения баланса. Покупатель создаёт и подписывает транзакцию Code: Tx1: As1 & ... & Asn → {Ap,Bp} Code: TxR: (id(Tx1),0){Ap,Bp} → Af; TxR.nlocktime = currtime + delay Подписывает своим ключом транзакцию TxR (она подписывается лишь наполовину, нужна ещё подпись продавца). Передаёт TxR_part продавцу. Продавец проверяет TxR_part, доподписывает её и возвращает полностью подписанную TxR покупателю. Покупатель проверяет TxR и в случае её корректности публикует Tx1. На данный момент ситуация такова, что покупатель свои средства не теряет — даже если продавец исчезнет, покупатель сможет получить свои деньги обратно после TxR.nlocktime. Одновременно с подписью TxR_part продавец формирует транзакцию оплаты Code: TxP: (id(Tx1),0){Ap,Bp} → Bf Double spend с адреса {Ap,Bp} (точнее, с (id(Tx1),0)) невозможен, т.к. для него требуется подпись ключами и продавца, и покупателя. Поэтому, а также из-за того, что TxR временно заблокирована, публикование TxP можно отложить, а ожидание подтверждений вообще отбросить. Теоретически возможен лишь double spend с адресов As1...Asn вместо транзакции Tx1, но предполагается, что от момента предварительной договорённости (включения Tx1 в блокчейн) до момента покупки прошло достаточно много подтверждений. 4. Модификация предыдущей схемы для множественных покупок/обмена по частям без отправки промежуточных транзакций в блокчейн. {Ap,Bp} теперь рассматриваем как некий счёт покупателя в магазине (у продавца, в обменнике). Точно так же генерируем, подписываем, обмениваемся и публикуем Tx1 и TxR, как в предыдущем варианте (только, возможно, стоит отодвинуть TxR.nlocktime на более дальний срок, особенно если планируется длительное сотрудничество). Но теперь продавец запрашивает трату лишь части средств покупателя транзакцией Code: TxP[1]: (id(Tx1),0){Ap,Bp} → (payment[1])Bf & (change[1])Af; TxP[1].nlocktime = TxR.nlocktime - delta[1] Продавец отправляет TxP[1]_part, покупатель получает её, проверяет, доподписывает и отправляет TxP[1] продавцу. Для новых покупок/траншей: Code: TxP[i]: (id(Tx1),0){Ap,Bp} → (cumulated_payment[i])Bf & (change[i])Af; TxP[i].nlocktime = TxP[i-1].nlocktime - delta[i] На данном этапе средства обоих участников «виртуальны» (их ещё нельзя потратить ни продавцу, ни покупателю). Однако, если сейчас сотрудничество прекратится, то продавец опубликует TxP[last] в момент времени TxP[last].nlocktime, и каждый участник получит свою долю в единоличное распоряжение. Единственный момент — продавцу следует публиковать транзакцию TxP[last] сразу после наступления TxP[last].nlocktime, иначе через некоторое время у покупателя появится возможность опубликовать предыдущую транзакцию TxP[last-1] или даже TxP[last-k], которые отменят оплату k последних покупок. В случае, если нужно снять средства, то очередную транзакцию оплаты TxP[ i ] выпускаем без nlocktime и публикуем её в блокчейне. Для пополнения баланса можно просто перевести сумму пополнения на {Ap,Bp} Code: Tx2: An+1,...,An+m → {Ap,Bp} Code: TxR2: (id(Tx2),0){Ap,Bp} → Af; TxR2.nlocktime = TxR.nlocktime Code: TxP[i]: (id(Tx1),0){Ap,Bp} & (id(Tx2),0){Ap,Bp} → (cumulated_payment[i])Bf & (change[i])Af; 5. Модификация предыдущей схемы для случая, когда возможно движение средств в обе стороны. - средства вносит один участник («покупатель»): Действия те же, что и в предыдущей схеме, только подписывает TxP[ i ] первым тот, кто в данный момент собирается принимать биткоины. Ну и следить за приближением currtime к TxP[ i ].nlocktime теперь должны оба. - средства вносят оба равноправных участника: То же, что и в предыдущем варианте. Единственный нюанс — теперь возникает новая транзакция Tx2, переводящая средства от участника B на общий адрес. Для этой транзакции участником A (который на данной стадии играет роль продавца) подписывается транзакция возврата TxRB, TxRB.nlocktime = TxRA.nlocktime. Ну и теперь все TxP тратят средства с выходов как Tx1, так и Tx2 (аналогично варианту с пополнением баланса): Code: TxP[i]: (id(Tx1),0){Ap,Bp} & (id(Tx2),0){Ap,Bp} → (shareB[i])Bf & (shareA[i])Af; Для гарантированного получения своих средств назад обоим участникам необходимо хранить лишь последнюю TxP, а также все TxR, выпущенные после последней TxP (после очередной TxP, включающей соответствующие входы, все TxR становятся неактуальными). Предупреждение: для каждого совместного счёта следует использовать отдельный адрес-участник. Если используется один адрес (Bp), возможна такая ситуация: - участник A говорит, что Ap принадлежит ему, генерирует и подписывает TxA, просит магазин подписать Code: TxRA_part: (id(TxA),0){Ap,Bp} → Af; TxRA.nlocktime = currtime + delay - участник E говорит, что Ep == Ap принадлежит ему, якобы генерирует и якобы подписывает TxE == TxA, просит магазин подписать Code: TxRE_part: (id(TxA),0){Ap,Bp} → Ef; TxRE.nlocktime произвольное Если же адреса BAp и BEp будут разными, то для кражи злоумышленнику придётся подбирать Eup такой, чтобы {Eup,BEp} == {Ap,BAp} (т.е. чтобы хэши их redeemScript'ов совпадали), что на данный момент практически нереально. 6. Модификация п.4 для множественных покупок/обмена с участием централизованного посредника. C — адреса посредника. Каждая из сторон (покупатели и продавцы) независимо друг от друга и в разное время заключают соглашения с посредником, перечисляют средства на совместные с ним адреса и генерируют транзакции возврата. Покупатель совершает покупку, продавец формирует транзакцию оплаты (перевод части средств с совместного с посредником счёта) Code: TxPB[i]: (id(Tx1B),0){Bp,CBp} → (cumulated_paymentB[i])Bf & (changeB[i])CBf; Code: TxPA[j]: (id(Tx1A),0){Ap,CAp} → (cumulated_paymentA[j])CAf & (changeA[j])Af; Покупатель проверяет TxPA_part[j] (в т.ч. её соответствие тому, что получил продавец в документе), доподписывает её и возвращает TxPA[j] посреднику. Посредник, проверив TxPA[j], доподписывает TxPB[ i ] и в свою очередь передаёт последнюю продавцу. Риски, вносимые централизованным посредником: - блокировка средств. Возможна лишь временная, т.к. у всех подписавших договор есть транзакции возврата. - приём оплаты от покупателя (TxPA) и отсутствие передачи связанной TxPB продавцу. В этом случае покупатель передаёт полностью подписанную транзакцию TxPA продавцу, а тот выкладывает в общем доступе претензию (где указаны TxPA и подписанный документ с TxPB_part и TxPA_part) с требованием выпустить TxPB. В этой ситуации посредник либо уступает требованию, либо теряет репутацию, и клиенты переходят к другим посредникам. Если же сумма оплаты столь велика, что её потеря будет чувствительна для покупателя, то можно либо переводить её частями, либо перечислить напрямую от покупателя продавцу без посредников (необходимость ожидания подтверждений — меньшее зло в данном случае). Пока всё. Title: Re: bittranshelperawk Post by: apxu on January 30, 2014, 08:46:58 AM Quote Надо будет попробовать предложить разработчикам официального клиента патч, позволяющий указывать адреса или публичные ключи вместо приватных. Публичным ключом транзакцию не подписать. Для подписи нужен именно приватный ключTitle: Re: bittranshelperawk Post by: yurm on January 30, 2014, 12:11:13 PM Публичным ключом транзакцию не подписать. Для подписи нужен именно приватный ключ Само собой. Я имею в виду, чтобы клиент сам извлекал приватный ключ, соответствующий этому публичному ключу или адресу, если этот пубкей/адрес в кошельке есть. Примерно как createmultisig поступает:Для создания мультисигнатурного адреса (bitcoind createmultisig) требуются публичные ключи (в справке официального клиента сказано, что можно указывать адреса или публичные ключи, на самом деле при указании адреса клиент извлекает соответствующий публичный ключ из кошелька, если он там был — иначе генерирует ошибку). Title: Re: bittranshelperawk Post by: rPman on February 02, 2014, 09:33:57 AM Неужели это тот самый ожидаемый инструментарий для работы с multisig транзакциями?... потестирую на досуге.
Впрочем, для решения проблемы из предыдущего правила, думаю, будет достаточно Linux Containers (LXC); скорее всего, эмуляции rdtsc там нет и качество haveged-энтропии в виртуалке не ухудшится. Осторожно, надежность контейнеров LXC не на много выше обычного chroot, т.е. обладающий root доступом в одной гостевой системе спокойно вылезает из песочницы в хост систему и соответственно в остальные гостевые.Title: Re: bittranshelperawk Post by: yurm on February 02, 2014, 10:59:08 AM Неужели это тот самый ожидаемый инструментарий для работы с multisig транзакциями?... потестирую на досуге. Я старался :) Правда, есть ещё что исправлять — sighash пока никак не учитывается, ну и checkonly работает только с разблокированным кошельком (пароль нужно ввести), хотя по смыслу этого требоваться не должно. Надо себя заставить :)Осторожно, надежность контейнеров LXC не на много выше обычного chroot, т.е. обладающий root доступом в одной гостевой системе спокойно вылезает из песочницы в хост систему и соответственно в остальные гостевые. Да, я в курсе, но root-доступ кошельку и не нужен. Впрочем, лично я LXC не использую — это так, умозаключения.UPDATE. checkonly исправил, пароль больше не нужен. Title: Re: bittranshelperawk Post by: amaclin on February 02, 2014, 01:00:07 PM Quote Скрипт ничего не отправляет в сеть — только формирует и подписывает транзакцию. Слушай, а можно тебя вопросами помучать? А то что-то ни на английском, ни тем более на русском не найду. Даже спросить не у кого. Я вот долго бился с подписыванием транзакций самостоятельно. В конце концов набрел на вот это описание http://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx Всё получилось, хотя пришлось еще разобраться с вариантом когда используется сокращенный формат публичного ключа. (вроде разобрался) Остался вопрос - как надо модифицировать алгоритм, чтобы подписывать несколько инпутов? Да, на всякий случай поясню - мне надо подписать обычную транзакцию с несколькими инпутами, а не мультисиг. Title: Re: bittranshelperawk Post by: yurm on February 02, 2014, 02:05:23 PM Остался вопрос - как надо модифицировать алгоритм, чтобы подписывать несколько инпутов? Вообще я не реализовывал самостоятельное подписывание транзакций, а лишь делегировал это команде «bitcoind signrawtransaction», но, судя по описанию (http://bitcoin.stackexchange.com/questions/3374/how-to-redeem-a-basic-tx), нужно просто повторить шаги 3-7 по количеству инпутов, а затем подписывать каждый инпут, повторяя шаги 15-18 (хэш из шага 14 не изменяется). Правда, что такое «hash code type» из шагов 13 и 19 (имеет ли он какое-нибудь отношение к sighash, и если имеет, то почему он один на всю транзакцию), я пока не понял. Завтра попробую код официального клиента почитать.Да, на всякий случай могу порекомендовать почитать это (https://en.bitcoin.it/wiki/OP_CHECKSIG), особенно разделы «Procedure for Hashtype SIGHASH_*», вдруг что прояснится. Title: Re: bittranshelperawk Post by: amaclin on February 02, 2014, 06:11:45 PM Что-то не получается.
Вот тут https://bitcointalk.org/index.php?topic=422021.0 написано, что надо формировать "simplified version" для каждого инпута, то есть хеши таки будут разными. В принципе логично, что инпуты подписываются независимо друг от друга и от порядка следования. Тогда какую цифру ставить в пункте 9? Допустим, на каждом инпуте по 1000 сатоши, а всего 10 инпутов. Ставим 10к или 1к? В общем, буду благодарен за любую подсказку или совет. Title: Re: bittranshelperawk Post by: yurm on February 02, 2014, 08:45:59 PM A hash of the simplified transaction consisted of a SINGLE INPUT and ALL outputs is created, this is then signed by the private key for that input and stored in the "sig & pubkey" portion of that tx input. У меня есть подозрения, что это ни разу не так. Т.е. это вполне адекватный алгоритм для SIGHASH_ANYONECANPAY, но не для дефолтного SIGHASH_ALL, при котором, насколько я понял, должны подписываться не только все выходы, но и все входы.Я доберусь-таки завтра до кода :) Тогда какую цифру ставить в пункте 9? Допустим, на каждом инпуте по 1000 сатоши, а всего 10 инпутов. Ставим 10к или 1к? Пункт 9 — это про выходы, во входах amount нигде не указывается. Так что 10к, если он у нас один. Впрочем, тут стоит иметь в виду, что если это вся транзакция, то для её принятия сетью потребуется указать комиссию, минимально допустимое значение которой в нашем случае будет как раз 10к :) В общем, стоит добавить инпут покрупнее.Title: Re: bittranshelperawk Post by: amaclin on February 03, 2014, 04:14:39 AM В общем, я вроде победил эту проблему. Надо еще тестировать и причесать код, но в целом получилось вот что:
Если инпутов N - то надо сериализовать данные из транзакции, подставляя на место всех scriptSig (пункты 5,6) либо scriptPubKey, либо "пустышку". Очевидно, данные для хеширования получатся разные. Я все время был "около правильного кода", просто в цикле перепутал i и j :) Title: Re: bittranshelperawk Post by: yurm on February 03, 2014, 03:01:06 PM Если инпутов N - то надо сериализовать данные из транзакции, подставляя на место всех scriptSig (пункты 5,6) либо scriptPubKey, либо "пустышку". Очевидно, данные для хеширования получатся разные. Да, действительно. Расследование показало, что для каждого инпута создаётся отдельная «упрощённая» транзакцияscript.cpp: Code: uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType) Далее, тоже script.cpp: Code: class CTransactionSignatureSerializer { Теперь входы (файл тот же): Code: class CTransactionSignatureSerializer { P.S. В процессе расследования наткнулся на замечательное: Code: fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)), Title: Re: bittranshelperawk Post by: yurm on February 04, 2014, 12:14:58 AM Добавил ещё один use case (https://bitcointalk.org/index.php?topic=439210.msg4825322#msg4825322 (https://bitcointalk.org/index.php?topic=439210.msg4825322#msg4825322)) — множественные покупки с централизованным посредником.
Title: Re: bittranshelperawk Post by: yurm on February 13, 2014, 04:35:35 AM Написание скрипта несколько застопорилось, а вот генерация идей нет, поэтому выложу своё ви́дение дальнейшей разработки здесь, чтобы не пропало даром. Итак, sighash.
Beginners. Что такое sighash? Это некий маркер, который позволяет устанавливать, что именно подписывает данная подпись, и в определённых случаях комбинировать выходы и входы вместе с подписями, собирая их из разных транзакций. Принимает следующие значения: SIGHASH_ALL (значение по умолчанию; в подавляющем большинстве случаев мы имеем дело именно с ним): подписываются все входы и все выходы. SIGHASH_SINGLE: подписываются все входы и только один выход, чей номер равен номеру подписываемого входа. Означает, что «я готов потратить свои средства, если остальные потратят такие-то средства (на таких-то непотраченных выходах) и на выход, соответствующий моему входу, придёт столько-то монет; куда будут потрачены остальные монеты, мне безразлично». SIGHASH_NONE: подписываются только входы (все). Означает, что «я готов потратить свои средства, если остальные потратят такие-то средства, и мне всё равно, куда пойдут монеты». Все три вышеперечисленных значения можно комбинировать со значением SIGHASH_ANYONECANPAY, которое означает «из всех входов я подписываю только свой вход, остальные могут добавлять или изменять другие входы при желании». Естественно, на подпись выходов данный флаг не влияет. Intermediate. sighash — это маркер, относящийся именно к подписи, а не ко входу. Это означает, что несколько подписей, тратящих средства с мультисигнатурного адреса и относящихся к одному и тому же входу, в общем случае могут иметь разные sighash. Для ручного формирования подписи с недефолтным sighash можно использовать команду «bitcoind signrawtransaction», 4-й параметр (sighashtype) отвечает именно за него. Advanced. Для подписывания каждого входа создаётся отдельная упрощённая транзакция, хэш которой, собственно, и подписывается. В эту упрощённую транзакцию попадают те или иные поля из реальной транзакции в зависимости от sighash. Остальные поля либо заменяются пустышками (или некоторыми условно-посторонними значениями), либо вообще выбрасываются. Распишу покрытие подписи для каждого значения sighash. Допустим, у нас для каждой транзакции имеются две таблицы, одна для входов, другая для выходов. У таблицы выходов одно поле — «value/scriptPubKey» (в реальной таблице их следует разделять, но подписываются они всегда вместе, поэтому для упрощения объяснения буду считать их единым целым). Далее по тексту буду называть это поле просто «output». А вот у таблицы входов 3 поля: «pos», «txid/vout» (тоже единое; в дальнейшем «prevout») и «nsequence». «Pos» — это номер входа в транзакции; поле виртуальное, в самой транзакции в явном виде не присутствует, однако в некоторых случаях «подписывается» (влияет на подпись), а в некоторых нет. Для единообразия буду считать, что значения «pos» идут с нуля. Есть ещё 2 подписываемых поля, общих для всей транзакции: «version» (версия транзакции, пока только 1) и «nLockTime». Они входят в покрытие при всех вариантах sighash (за одним исключением (http://permalink.gmane.org/gmane.comp.bitcoin.devel/1411), являющимся багом протокола), дальше по тексту на этих полях заострять внимание не буду. Теперь собственно варианты sighash. ALL: в покрытие входят все «prevout» (вместе со всеми «pos»), все «nsequence» и все «output». Подписывание всех входов («prevout») автоматически означает, что подписываются и все «pos», т.е. порядок следования входов в такой транзакции менять нельзя. В противном случае изменится хэш упрощённой транзакции, и подпись станет некорректной. У ALL самое строгое покрытие. ALL|ANYONECANPAY: только свой «prevout», свой «nsequence» и все «output». «Pos» не входит в покрытие (т.е., если у вас все подписи с таким sighash, то входы — но не выходы — можно будет произвольно перетасовывать). SINGLE: все «prevout» (+ все «pos»), только свой «nsequence» и только «свой» «output» (тот, чей номер равен номеру своего входа). Обратите внимание — «nsequence» подписывается тоже только один. SINGLE|ANYONECANPAY: только свой «prevout», свой «nsequence», свой «output» и свой «pos». Последнее кажется странным, учитывая то, что более, казалось бы, строгое значение ALL|ANYONECANPAY не включает «pos» в покрытие, однако этот факт следует из алгоритма формирования упрощённой транзакции для SINGLE (https://en.bitcoin.it/wiki/OP_CHECKSIG#Procedure_for_Hashtype_SIGHASH_SINGLE), в которую включаются pos+1 выходов, из них pos пустышек и только свой соответствует реальному. Т.е. скомбинировать две транзакции, каждая из которых состоит из одного входа и одного выхода и подписана SINGLE|ANYONECANPAY, в одну не получится — каждый вход должен сохранять свой номер. Зачем было выбрано такое решение — не знаю; если это была попытка защиты от Transaction Malleability (https://en.bitcoin.it/wiki/Transaction_Malleability), то, как видно, она не удалась (по другим причинам (https://bitcointalk.org/index.php?topic=461503.0)). Ещё раз напомню про связанный с SINGLE баг в протоколе (http://permalink.gmane.org/gmane.comp.bitcoin.devel/1411), когда у входа нет выхода с таким же номером (число выходов меньше числа входов). NONE: в покрытие входят все «prevout» (+ все «pos») и только свой «nsequence» (аналогично SINGLE). Выходы не подписываются. NONE|ANYONECANPAY: свой «prevout» и свой «nsequence». Самое либеральное покрытие, если не считать вышеупомянутого бага. Теперь касательно реализации. Для скрипта список входов (строк, начинающихся с "i") и выходов ("o") будет считаться эталоном (заготовкой финальной транзакции); nLockTime из строки, начинающейся с "l", тоже будет считаться эталонным (но nSequence из той же строки — лишь рекомендательным). Далее, будут добавлены строки, начинающиеся, скажем, с "p", после которых идут транзакции-поставщики подписей. Примерно как "t", только для "t"-транзакций будет обязательным совпадение всех входов и выходов с эталоном, а для "p" нет. Сам процесс: отбрасываю те транзакции, у которых nLockTime не соответствует эталонному. Из всех оставшихся транзакций (и "p", и "t") вырезаю подходящие подписи, проверяя для каждой из них соответствие их покрытия эталону. После этого шага для некоторых входов теоретически возможно появление нескольких конкурирующих подписей-кандидатов из разных транзакций (я сейчас про обычные, не только про мультисигнатурные адреса). Подбираю значения nSequence для всех входов (возможно, разные) так, чтобы корректным осталось максимальное количество подписей (конкурирующие подписи считая за одну), записываю эти подписи в транзакцию и тогда уже передаю получившийся результат команде «bitcoind signrawtransaction». P.S. Добавил предупреждение в use cases в связи с текущей атакой (https://bitcointalk.org/index.php?topic=461503.0). |