Firmando tx con OpenSSL, pero falla al enviar con bitcoin qt. ¿Cómo depurar?

Intento crear un tx con 4 entradas y firmarlo a través de cmdline y openssl en una máquina de almacenamiento en frío. Luego quiero enviarlo a través de bitcoin qt (o blockchain.info). La firma con una sola entrada y una sola salida ya funciona para mí (a través de la línea cmd de Unix y OpenSSL). Ahora quiero ir con múltiples entradas, con un poco de ayuda de stackexchange aquí: ¿Cómo firmar una transacción con múltiples entradas?

Creé una transacción, con 4 entradas, y no puedo hacer que funcione. La transacción se ve así y se decodifica correctamente en bitcoin qt (decoderrawtransaction) o con https://blockchain.info/de/decode-tx :

0100000004b0772e6ef46c2d3c60418bb7d5c2c015d6b943e5fca07570eb82c26dc7c9d248010000006B483045022100D675D9F98E5B1F01BDB05E5389ACB453EFA14EF399361099736376611D9BF88E02206B8FEF37F2C2316D3629D6C43945AFA23B0EB0CA2C1163E33B28066B01A4E241012103cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0ffffffff74fb6858c31a292d20c9744187032bddb7ddea02a3aa3ef523c8524a21481881010000006A47304402200F921996446FBAD78ECB6C95303ECD13145CA7E13E769B44742FA499325B347402202F8378D7D154F4F8D345525696F8E31DF8A96C05AA58B2F53012E3550CC90AE7012103cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0ffffffffcc21e36eedb509c660681c1e949dd294bd4c11692439221004c2235d565b74bb000000006A47304402206B7C2D73EC787ADD99740DAB78F7252ED49A1BA42BA358A667BF118B8740BB0C0220366EF47DE6339C261946513C939A301ECF940A294036ED72A5FD893C5CAFEED8012103cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0ffffffffea919487e04ed509d6cb9c7297c277b9be3a68f3836c7d86df378714a75949e8000000006A47304402205B595472AA821D96DBFB6E531A663571A9DB14DDFCF8AC0B4330B6264955C15F02203FDF004B19996654490F00FA9BDFC175265CF95BA6DC86943B4B7C50EAF23616012103cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0ffffffff0120830c00000000001976a91418ec49b27624086a2b6f35f263d951d25dbe24b688ac00000000

Subir con bitcoin qt (sendrawtransaction) da este error:

16: obligatorio-script-verify-flag-failed (La firma debe ser cero para la operación fallida CHECK(MULTI)SIG) (código -26)

¿Qué opciones tengo para averiguar los próximos pasos, en caso de que salga mal?


LO QUE SE HA HECHO HASTA AHORA:

Esto me ayudó a ponerlo en marcha para una sola transacción de entrada: ¿Verificar un trx de bitcoin en la línea cmd de Unix con OpenSSL?

Ahora tengo cuatro entradas y claves públicas comprimidas. Creé el tx anterior exactamente de nuevo en Electrum (las mismas 4 entradas, la misma salida única), lo firmé. Pero no lo envió. Luego descodifiqué el Electrum generado y mi tx generado, y verifiqué línea por línea que no hay diferencias en los valores (además de las líneas de firma, difieren, por supuesto). Después de esto, creé un archivo de texto con comandos, que escupe la clave pública, el hash (doble sha256) y la firma para cada entrada. De esta manera podría verificar las firmas con openssl de esta manera:

openssl pkeyutl <tx_hash.hex -verify -pubin -inkey pubkey.pem -sigfile tx_sig.hex

Cuando reviso cada firma en la transacción, siempre recibo un mensaje de éxito de OpenSSL (el archivo completo se adjunta al final a continuación). Sin embargo, cuando lo envío a través de bitcoin qt, aparece el mensaje de error mencionado anteriormente. El mensaje de error viene de "la red bitcoin"... ¿Cómo puedo emularlo?

Para confirmar un tx, se ejecuta una verificación desde el SigScript de entrada de tx y el PKScript de salida de tx anterior. Así que verifiqué dos veces el script de salida de la transacción anterior (para cada entrada). Son todos iguales:

OP_DUP OP_HASH160 c2df275d78e506e17691fd6f0c63c43d15c897fc OP_EQUALVERIFY OP_CHECKSIG 

Mis actividades de verificación se verían así: elimino SIG y PUBKEY del SigScript de entrada tx en una pila virtual y luego ejecuto el PKScript en él, paso a paso, para todas las entradas. Aquí un ejemplo para la primera entrada (más o menos los pasos de emulación de pila):

  step       action      result  
1 SIGSCRIPT  SIGNATURE   3045022100D675D9F98E5B1F . . . B28066B01A4E241  
2 SIGSCRIPT  PUBKEY      03cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0  
3 OP_DUP     PUBKEY      03cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0  
4 OP_HASH160 xxd -r -p <PUBKEY.txt >pubkey.hex
             openssl dgst -binary -sha256 <pubkey.hex >pk_sha256.hex
             openssl dgst -binary -ripemd160 <pk_sha256.hex >pkhash.hex
             xxd -ps pkhash.hex
                         c2df275d78e506e17691fd6f0c63c43d15c897fc
5 PKSCRIPT   PKH         c2df275d78e506e17691fd6f0c63c43d15c897fc
6 OP_EQUALVERIFY         YES !

Aquí he generado para las 4 entradas el mismo PUBKEY_HASH, de modo que OP_Equalverify debería ejecutarse correctamente. Lo que queda en la pila es:

1 Input       SIGNATURE  3045022100D675D9F98E5B1F . . . B28066B01A4E241
2 Input       PUBKEY     03cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0
3 OP_CHECKSIG

No sé qué sucede aquí en la red, me gustaría pensar que decodifica la transacción en sus elementos (por entrada), crea un hash sobre ella y hace algo similar a lo que hice (por supuesto con otras bibliotecas ): VERIFICAR FIRMA. Lo hice así:

openssl pkeyutl <tx_hash.hex -verify -pubin -inkey pubkey.pem -sigfile tx_sig.hex 

¿Hay alguna manera de obtener un mensaje de error más específico al enviar una transacción "incorrecta"? "mandatory-script-verify-flag-failed" y "(code -26)" no me ayudan a depurar más...

Este es el archivo que se generó a partir del proceso de transacción y ayuda a verificar la firma de cada entrada:

##############################################
### Bitcoin prep file to verify signatures ###
##############################################
# Bitcoin (and here with openssl) works only on binary files. For each input, convert to binary.
# the pubkey for above example:
echo 3036301006072a8648ce3d020106052b8104000a032200 > pubkey.txt
echo 03cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0 >> pubkey.txt
xxd -r -p <pubkey.txt | openssl pkey -pubin -inform der >pubkey.pem

# TX_IN[0], double sha256 and signature:
echo afc7f91ceb9754ebb644d058334817babf8414d1ce75fd0683ad172e950348c9 > tx_hash.txt
echo 3045022100d675d9f98e5b1f01bdb05e5389acb453efa14ef399361099736376611d9bf88e02206b8fef37f2c2316d3629d6c43945afa23b0eb0ca2c1163e33b28066b01a4e241 > tx_sig.txt
xxd -r -p <tx_hash.txt >tx_hash.hex
xxd -r -p <tx_sig.txt >tx_sig.hex
openssl pkeyutl <tx_hash.hex -verify -pubin -inkey pubkey.pem -sigfile tx_sig.hex

# TX_IN[1], double sha256 and signature:
echo 11ded58d693d72a23b6191c20222b20ee4947bac63c34d1ccdac07461486a3b0 > tx_hash.txt
echo 304402200f921996446fbad78ecb6c95303ecd13145ca7e13e769b44742fa499325b347402202f8378d7d154f4f8d345525696f8e31df8a96c05aa58b2f53012e3550cc90ae7 > tx_sig.txt
xxd -r -p <tx_hash.txt >tx_hash.hex
xxd -r -p <tx_sig.txt >tx_sig.hex
openssl pkeyutl <tx_hash.hex -verify -pubin -inkey pubkey.pem -sigfile tx_sig.hex

# TX_IN[2], double sha256 and signature:
echo 12f904b9d0b37c368d24d132aaff2d2fe47fd9a5e3083a501e63410c6d4671ea > tx_hash.txt
echo 304402206b7c2d73ec787add99740dab78f7252ed49a1ba42ba358a667bf118b8740bb0c0220366ef47de6339c261946513c939a301ecf940a294036ed72a5fd893c5cafeed8 > tx_sig.txt
xxd -r -p <tx_hash.txt >tx_hash.hex
xxd -r -p <tx_sig.txt >tx_sig.hex
openssl pkeyutl <tx_hash.hex -verify -pubin -inkey pubkey.pem -sigfile tx_sig.hex

# TX_IN[3], double sha256 and signature:
echo 183b7652c2785d57483c5f70575e027f513743aad01100828d9eb4aeb48ea4ed > tx_hash.txt
echo 304402205b595472aa821d96dbfb6e531a663571a9db14ddfcf8ac0b4330b6264955c15f02203fdf004b19996654490f00fa9bdfc175265cf95ba6dc86943b4b7c50eaf23616 > tx_sig.txt
xxd -r -p <tx_hash.txt >tx_hash.hex
xxd -r -p <tx_sig.txt >tx_sig.hex
openssl pkeyutl <tx_hash.hex -verify -pubin -inkey pubkey.pem -sigfile tx_sig.hex

El código completo para este ejemplo de crear/firmar está aquí: https://github.com/pebwindkraft/trx_cl_suite Los tres comandos para crear, firmar y decodificar un trx son:

./tcls_create.sh -v -f svn_4inputs.txt 820000 13GnHB51piDBf1avocPL7tSKLugK4F7U2B 33
./tcls_sign.sh -v -f tmp_c_urtx.txt  -w Kxyz...your_priv_key_in_WIF_format...abc -p 03cc5debc62369bd861900b167bc6add5f1a6249bdab4146d5ce698879988dced0
./tcls_tx2txt.sh -vv -r 0100000004b0772e6ef46c2d3c60...b6f35f263d951d25dbe24b688ac00000000
¿Ha comprobado las firmas LOW-S ( github.com/bitcoin/bips/blob/master/bip-0062.mediawiki )?
Hola Jonas, sí, he eliminado en la documentación anterior la estricta verificación de firmas DER, para que sea más fácil de leer. Esto se incluye en el archivo tcls_strict_sig_verify.sh.
Encontré el código original para este error en bitcoin-master (13.1), archivo main.cpp, línea 2087. No parece una codificación DER no estándar. El código detecta el error después de las comprobaciones de DER. Mi conocimiento no es suficiente para decodificar los detalles, por lo que busco una respuesta en un foro o similar. Esperemos que pueda proporcionar una respuesta aquí más adelante...

Respuestas (1)

Encontré un error en esos scripts de shell, aunque no estoy seguro de que sea el único. Una vez arreglado, ahora está generando los mismos sha256d de los txs sin firmar como otra herramienta que probé (antes no).

Si mira aquí , verá que el scriptSig de txin se está configurando correctamente en el scriptPubKey de UTXO que se va a firmar, sin embargo, para los scriptPubKeys que deben borrarse, el script no está emitiendo nada, debería estar emitiendo un vacío cadena como lo hace esta cláusula else:

if [ $i -eq $j ] ; then
  printf ${TX_IN_ScriptBytes_hex[$i]} >> $utxhex_tmp_fn
  printf ${TX_IN_Sig_Script[$i]} >> $utxhex_tmp_fn
else
  printf 00 >> $utxhex_tmp_fn
fi

Como dije, no revisé el resto de los guiones, pero espero que esto te acerque un paso más.

¡SIP eso es! No puse un cero hexadecimal en la cadena, como recomienda la página web bitcoin.stackexchange.com/questions/41209/… . Ahí dice, que el valor pasa a ser: **'script': '', #Nada**