Creación de transacciones de Bitcoin firmadas en Java

He estado intentando construir mi propio generador de transacciones sin procesar en Java que luego puedo transmitir en la red de testnet. El problema es que cuando intento transmitir la transacción, dice que hay un error en el script de canje, que no entiendo.

Aquí hay un ejemplo de transacción de testnet:

0100000001e468833270cf713f3bbccc62b7b5b0fc0b0a4570608718530816795a6589f322000000008a473044022051646b77924f6bb7c411c5aa890110ab55db8812b8998fe24c8bdce39795ebd602200bc4de18fd5524ad8b946ee57604424e2b943ef2a14fc7199a7853dda0743cbe014104b97c679207532e0f4ee2515aedaba5f87700bbe0138f7457baa58e89a53153823ab29632e6c3c804ecaab5913656512339792479a1b898b7e5dc31f075ff8660ffffffff0176df1710000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac00000000

El script que estoy tratando de desbloquear:

76a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac

Siendo el script de redención:

473044022051646b77924f6bb7c411c5aa890110ab55db8812b8998fe24c8bdce39795ebd602200bc4de18fd5524ad8b946ee57604424e2b943ef2a14fc7199a7853dda0743cbe014104b97c679207532e0f4ee2515aedaba5f87700bbe0138f7457baa58e89a53153823ab29632e6c3c804ecaab5913656512339792479a1b898b7e5dc31f075ff8660

Este es el código Java para firmar el hash doble sha256 inverso:

  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;
        }
    }

¿Qué está mal?

Tanto las transacciones como los scripts parecen estar bien, ¿puede ser más específico sobre el error que está recibiendo? ¿Ha intentado transmitir la transacción utilizando otras fuentes, como las API en línea? Puede deberse a la transmisión en sí misma en lugar de a la corrección de la transacción.
Intenté transmitir la transacción usando ambos blockcyper , lo que me da el error: "Error al enviar la transacción: error al ejecutar el script para la entrada 0 que hace referencia a 22f389655a7916085318876070450a0bfcb0b5b762ccbc3b3f71cf70328368e4 en 0: el script NO se verificó con éxito ..." También intenté usar blockexplorer que me da el error "16: obligatorio-script-verify-flag-failed (Script evaluado sin error pero terminado con un elemento de pila superior falso/vacío). Código: -26".
Bien, ahora veo. Parece que la validación está fallando ya que la firma no es correcta. La evaluación del script está fallando en OP_CHECKSIG, justo después de verificar que el ripemd160 proporcionado en el tx scriptPubKey anterior coincide con el ripemd160 de la clave pública proporcionada en scriptSig.
Sin embargo, no entiendo por qué este es el caso. Al firmar, coloco el script de desbloqueo de la transacción que estoy tratando de gastar donde se coloca la firma y también agrego un tipo de código hash. Finalmente, hago un hash sha256 de la transacción dos veces, invierto el hash antes de finalmente firmarlo. ¿Es esto correcto?
Debe sha256 duplicar la transacción completa, firmarla y luego agregar el tipo de hash. Aquí está bastante bien explicado (desde el paso 13 en adelante) bitcoin.stackexchange.com/questions/3374/…

Respuestas (1)

Decodificando rápidamente el 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

Entonces, la transacción es correcta y la firma se ensambla correctamente. También sigue las reglas de "comprobación estricta de firmas". El tx está intentando pasar de mnAEswb3Aiz5YTsfCJc8vQETnSe19mc5Am a mnAEswb3Aiz5YTsfCJc8vQETnSe19mc5Am. Así que verifiqué la firma contra el doble hash del tx (vea la famosa transacción de pizza: ¿Cómo funciona el algoritmo de verificación ECDSA durante la transacción? ):

El raw_tx.txt es:

0100000001e468833270cf713f3bbccc62b7b5b0fc0b0a4570608718530816795a6589f322000000008a473044022051646b77924f6bb7c411c5aa890110ab55db8812b8998fe24c8bdce39795ebd602200bc4de18fd5524ad8b946ee57604424e2b943ef2a14fc7199a7853dda0743cbe014104b97c679207532e0f4ee2515aedaba5f87700bbe0138f7457baa58e89a53153823ab29632e6c3c804ecaab5913656512339792479a1b898b7e5dc31f075ff8660ffffffff0176df1710000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac00000000

Se eliminó la firma e ingresó su secuencia de comandos pubkey en raw_tx_SIGHASH_ALL.txt:

0100000001e468833270cf713f3bbccc62b7b5b0fc0b0a4570608718530816795a6589f322000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688acffffffff0176df1710000000001976a91448ddfd3891f3f422d5c3c9c25e35b382667fc6e688ac0000000001000000

y doble hash (los valores binarios, ¡por lo tanto, alguna conversión primero!)

  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 

Entonces, el dsha256 está en forma legible: 6b37475a5388fb52f227817a0ddec00e7b69495b492cd7cdc6aff2afc44810e4

Utilizo openssl para verificar el archivo hexadecimal de doble hash (dsha256.hex), con la firma y una clave "pem" (necesito convertir pubkey de hexadecimal a pem), siempre recibo un error:

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

mientras que al ejecutar el ejemplo de la pizza, devuelve una "Firma verificada con éxito".

Es un poco confuso que escriba los elementos de la firma codificada en DER como "códigos de operación" y al mismo nivel que el resto del scriptSig. Desde la perspectiva de Bitcoin, toda la firma es una matriz de bytes de caja negra, no una secuencia de códigos de operación (OP_SEQUENCE a OP_SIGHASH).