Создание подписанной биткойн-транзакции в Java

Я пытался создать свой собственный генератор необработанных транзакций на Java, который затем я могу транслировать в сети testnet. Проблема в том, что когда я пытаюсь транслировать транзакцию, он говорит, что в сценарии выкупа есть ошибка, которую я не понимаю.

Вот пример транзакции в тестовой сети:

0100000001e468833270cf713f3bbccc62b7b5b0fc0b0a4570608718530816795a6589f322000000008a473044022051646b77924f6bb7c411c5aa890110ab55db8812b8998fe24c8bdce39795ebd602200bc4de18fd5524ad8b946ee57604424e2b943ef2a14fc7199a7853dda0743cbe014104b97c679207532e0f4ee2515aedaba5f87700bbe0138f7457baa58e89a53153823ab29632e6c3c804ecaab5913656512339792479a1b898b7e5dc31f075ff8660ffffffff0176df1710000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac00000000

Сценарий, который я пытаюсь разблокировать:

76a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac

Со сценарием выкупа:

473044022051646b77924f6bb7c411c5aa890110ab55db8812b8998fe24c8bdce39795ebd602200bc4de18fd5524ad8b946ee57604424e2b943ef2a14fc7199a7853dda0743cbe014104b97c679207532e0f4ee2515aedaba5f87700bbe0138f7457baa58e89a53153823ab29632e6c3c804ecaab5913656512339792479a1b898b7e5dc31f075ff8660

Это код Java для подписи обратного двойного хэша sha256:

  public static byte[] sign(byte[] hash, BigInteger priv){

        ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest()));
        signer.init(true, new ECPrivateKeyParameters(priv, domain));

        BigInteger[] signature = signer.generateSignature(hash);
        ByteArrayOutputStream s = new ByteArrayOutputStream();

        try {
            DERSequenceGenerator seq = new DERSequenceGenerator(s);
            seq.addObject(new ASN1Integer(signature[0]));
            seq.addObject(new ASN1Integer(signature[1]));
            seq.close();
            return s.toByteArray();
        }
        catch(IOException e){
            return null;
        }
    }

Что случилось?

И транзакции, и скрипты выглядят нормально. Не могли бы вы более подробно рассказать об ошибке, которую вы получаете? Пытались ли вы транслировать транзакцию с помощью других источников, таких как онлайн-API? Это может быть из-за самой трансляции, а не из-за правильности транзакции.
Я попытался транслировать транзакцию, используя оба blockcyper , что дает мне ошибку: «Ошибка отправки транзакции: ошибка запуска сценария для ввода 0, ссылающегося на 22f389655a7916085318876070450a0bfcb0b5b762ccbc3b3f71cf70328368e4 в 0: сценарий НЕ был успешно проверен ..» Я также пытался использовать blockexplorer , который дает мне ошибка «16: обязательное-скрипт-проверки-флаг-сбой (скрипт выполнен без ошибок, но завершен с ложным/пустым верхним элементом стека). Код:-26».
Хорошо, теперь я вижу. Кажется, что проверка не проходит, поскольку подпись неверна. Оценка сценария завершается сбоем в OP_CHECKSIG сразу после проверки того, что ripemd160, предоставленный в предыдущем tx scriptPubKey, совпадает с ripemd160 открытого ключа, предоставленного в scriptSig.
Я не понимаю, почему это так, хотя. При подписании я помещаю сценарий разблокировки из транзакции, которую я пытаюсь потратить, где ставится подпись, а также добавляю тип хэш-кода. Наконец, я дважды хэширую транзакцию sha256, переворачиваю хеш, прежде чем окончательно подписать ее. Это правильно?
Вы должны дважды выполнить sha256 всю транзакцию, подписать ее, а затем добавить хэш-тип. Здесь довольно хорошо объяснено (начиная с шага 13) bitcoin.stackexchange.com/questions/3374/…

Ответы (1)

Быстро расшифровать tx:

 VERSION      01000000
 TX_IN COUNT  hex=01, decimal=1
 TX_IN[0]
  TX_IN[0]    OutPoint hash 22F389655A7916085318876070450A0BFCB0B5B762CCBC3B3F71CF70328368E4 
  TX_IN[0]    OutPoint index hex=00000000, reversed=00000000, decimal=0
  TX_IN[0]    Script Length hex=8A, decimal=138
  TX_IN[0]    Script Sig  473044022051646B77924F6BB7C411C5AA890110AB55DB8812B8998FE24C8BDCE39795EBD602200BC4DE18FD5524AD8B946EE57604424E2B943EF2A14FC7199A7853DDA0743CBE014104B97C679207532E0F4EE2515AEDABA5F87700BBE0138F7457BAA58E89A53153823AB29632E6C3C804ECAAB5913656512339792479A1B898B7E5DC31F075FF8660
        47: OP_DATA_0x47:     push hex 47 (decimal 71) bytes as data
        30: OP_SEQUENCE_0x30: type tag indicating SEQUENCE, begin sigscript
        44: OP_LENGTH_0x44:   length of R + S
        02: OP_INT_0x02:      type tag indicating INTEGER
        20: OP_LENGTH_0x20:   this is SIG R
            51646B77924F6BB7:C411C5AA890110AB:55DB8812B8998FE2:4C8BDCE39795EBD6
        02: OP_S_INT_0x02
        20: OP_LENGTH_0x20:   this is SIG S
            0BC4DE18FD5524AD:8B946EE57604424E:2B943EF2A14FC719:9A7853DDA0743CBE
        01: OP_SIGHASHALL:    this terminates the ECDSA signature (ASN1-DER structure)
        Minimum and maximum size constraints                        - ok
        scriptsig always starts with 0x30                           - ok
        length 136 chars is less than actual sig length (140 chars) - ok
               (hex 0x44, decimal 68, 136 chars)
        length of R coordinate (64) >= 0                            - ok
        length of S coordinate (64) >= 0                            - ok
        S-Value is within scriptsig boundaries                      - ok
        Make sure the R & S length covers the entire signature      - ok
        S-value must be smaller than N/2                            - ok
        strictly check DER-encoded signature                        - ok
        41: OP_DATA_0x41:     push hex 41 (decimal 65) bytes as data
        04: OP_LENGTH_0X04
            B97C679207532E0F:4EE2515AEDABA5F8:7700BBE0138F7457:BAA58E89A5315382
            3AB29632E6C3C804:ECAAB59136565123:39792479A1B898B7:E5DC31F075FF8660
        * This terminates the Public Key, corresponding bitcoin address is:    mnAEswb3Aiz5YTsfCJc8vQETnSe19mc5Am 
 TX_IN[0] Sequence FFFFFFFF

TX_OUT COUNT, hex=01, decimal=1
 TX_OUT[0] Value hex=76DF171000000000, rev_hex=000000001017DF76, dec=269999990 
 TX_OUT[0] PK_Script Length hex=19, dec=25 
 TX_OUT[0] pk_script 76A91448DDFD3891F3F422D5C3C9C25E35B382667FC6E688AC
        76: OP_DUP
        A9: OP_HASH160
        14: OP_Data14 (= decimal 20)
            48DDFD3891F3F422:D5C3C9C25E35B382
            667FC6E6
        88: OP_EQUALVERIFY
        AC: OP_CHECKSIG   This is a P2PKH script and translates base58 encoded into this bitcoin address:    mnAEswb3Aiz5YTsfCJc8vQETnSe19mc5Am 
 LOCK_TIME 00000000

Значит транзакция правильная, и подпись собрана правильно. Он также следует правилам «строгой проверки подписи». Тх пытается провести от mnAEswb3Aiz5YTsfCJc8vQETnSe19mc5Am до mnAEswb3Aiz5YTsfCJc8vQETnSe19mc5Am. Итак, я проверил подпись по двойному хешу tx (см. знаменитое прохождение транзакции пиццы: Как работает алгоритм проверки ECDSA во время транзакции? ):

Raw_tx.txt:

0100000001e468833270cf713f3bbccc62b7b5b0fc0b0a4570608718530816795a6589f322000000008a473044022051646b77924f6bb7c411c5aa890110ab55db8812b8998fe24c8bdce39795ebd602200bc4de18fd5524ad8b946ee57604424e2b943ef2a14fc7199a7853dda0743cbe014104b97c679207532e0f4ee2515aedaba5f87700bbe0138f7457baa58e89a53153823ab29632e6c3c804ecaab5913656512339792479a1b898b7e5dc31f075ff8660ffffffff0176df1710000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac00000000

Удалил sig и вписал свой скрипт pubkey в raw_tx_SIGHASH_ALL.txt:

0100000001e468833270cf713f3bbccc62b7b5b0fc0b0a4570608718530816795a6589f322000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688acffffffff0176df1710000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac0000000001000000

и двойной хэш (двоичные значения, следовательно, сначала некоторое преобразование!)

  result=$( cat raw_tx_SIGHASH_ALL.txt | sed 's/[[:xdigit:]]\{2\}/\\x&/g' )
  printf $result > raw_tx.hex
  hexdump -C raw_tx.hex 
  openssl dgst -binary -sha256 <raw_tx.hex >ssha256.hex
  openssl dgst -binary -sha256 <ssha256.hex >dsha256.hex
  hexdump -C dsha256.hex 

Таким образом, dsha256 находится в удобочитаемой форме: 6b37475a5388fb52f227817a0ddec00e7b69495b492cd7cdc6aff2afc44810e4.

Я использую openssl для проверки шестнадцатеричного файла с двойным хешем (dsha256.hex) с подписью и ключом «pem» (необходимо преобразовать pubkey из шестнадцатеричного в pem), я всегда получаю сообщение об ошибке:

openssl pkeyutl -verify -pubin -inkey pubkey.pem -sigfile tmp_sig.hex -in dsha256.hex

при выполнении примера с пиццей он возвращает «Подпись успешно проверена».

Немного сбивает с толку то, что вы записываете элементы подписи в кодировке DER как «коды операций» и на том же уровне, что и остальная часть scriptSig. С точки зрения Биткойна вся эта подпись представляет собой массив байтов черного ящика, а не последовательность кодов операций (от OP_SEQUENCE до OP_SIGHASH).