Raw SegWit TX parece sólido y se verifica, pero se rechaza. Necesito ayuda para averiguar por qué

Considere la siguiente transacción expresada como hexadecimal:

010000000001019F92908527985B1ED14E7F6127684808523750076AB82A03A6A5A50107AC40190000000000FFFFFFFF02446C1E0000000000160014ABFA07FB45DE4B7187360F0B5E4014991666ACCB40420F0000000000160014DFFE4A0C857A04597E0DCB446DB284DD0C068E8602473044022051F99A39749B925E32BF1ED0573ED4CCFC6965F92CD0F016F644C8CF17CA57E102202FCC06C7C26A7E5F71A2DF3353BDE494561E4A6394955DE9C825D08DB573BB1D0121024B2512FFD4AA888E0E0D358083770A4A45093952EE68F3753651932849F008EC00000000

Si lo pego en decodificadores/validadores de transacciones en línea como los de aquí y aquí , todo parece sólido.

Pero enviarlos a cualquier API pushTX pública genera un error. El problema es que los mensajes de error son tan vagos (por ejemplo, "transacción no válida") que ni siquiera estoy seguro de dónde buscar.

BlockCypher da el siguiente error:

"Error al ejecutar el script para la entrada 0 que hace referencia a 1940ac0701a5a5a6032ab86a0750375208486827617f4ed11e5b98278590929f en 0: el script NO se verificó correctamente".

He investigado este error en particular y la gente ocasionalmente lo encuentra cuando una firma es mala. Pero he comprobado la firma innumerables veces. es valido De hecho, pegar la transacción aquí en realidad muestra una pequeña marca de verificación junto al ícono "firmado" que indica una firma adecuada.

Además, todos los decodificadores muestran las entradas, salidas, cantidades, etc. correctas. Por lo tanto, los datos serializados no parecen estar corruptos o ilegibles.

¿Qué podría estar haciendo mal? He pasado todo el día investigando pero no puedo encontrar nada.

¿Qué software utilizó para crear esta transacción?

Respuestas (2)

es valido De hecho, pegar la transacción aquí en realidad muestra una pequeña marca de verificación junto al ícono "firmado" que indica una firma adecuada.

No es válido. Ese sitio web no validó nada. Simplemente verificó si había algo en el campo scriptWitness y, si lo había, se marcó como firmado. Esa marca de verificación no significa que sea válida.

Su firma es simplemente incorrecta. No hay otra manera de decirlo. Ejecutarlo a través de mi nodo Bitcoin Core da

64: non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation)

que es el típico error de firma inválida.

Vuelva a verificar que esté creando el sighash correctamente. Problemas como este generalmente son el resultado de serializar incorrectamente la transacción para firmar.

Gracias, ese mensaje de error que proporcionó al menos me indica la dirección correcta. Estoy usando este componente .NET llamado BouncyCastle que ha estado haciendo fielmente el trabajo de encriptación/firma durante años, absolutamente impecable. "Actualicé" el componente recientemente y las cosas se están descomponiendo. He podido encontrar soluciones para la mayoría de los problemas, pero este problema de sighash/firma me tiene perplejo. Me mantendré en ello. gracias de nuevo
@FestusMartingale: probablemente ayudaría si agrega su código a la pregunta.

Sospecho que se considera de mal gusto en los círculos de programadores publicar una respuesta a la propia pregunta, pero en este caso mi error fue bastante peligroso, así que me gustaría compartir los detalles con todos ustedes.

RESUMEN:

Mi pregunta decía que las API públicas 'push tx' no estaban dando mensajes de error viables después de rechazar mis transacciones sin procesar con formato hexadecimal. La respuesta del Sr. Chow indicó que el problema era un error de firma, que recibió después de intentar enviar mi TX con formato incorrecto a través de su nodo personal. Eso me apuntó en la dirección correcta.

Su sugerencia fue que tal vez no estaba serializando mi sighash correctamente, ya que eso a menudo conduce al mensaje de error al que se refiere. Pero el código que formula ese sighash ha estado funcionando bien durante años, así que pensé que el problema estaba en otra parte. De hecho fue. La historia corta es que mi lógica utilizada para derivar una clave pública de una clave privada, que había funcionado perfectamente desde siempre, ahora estaba fallando después de una actualización del componente de cifrado BouncyCastle .NET.

Aquí hay un extracto de mi código que convierte una clave privada en una clave pública:

public class BTCUtils2{
    public static byte[] DerivePublicKey(byte[] privKey,bool isCompr){
        ECPoint pubPt=ComputePublicECPoint(privKey);

        //byte[] xCoord = pubPt.X.ToBigInteger().ToByteArrayUnsigned(),     //2015: worked properly in with BouncyCastle 1.7.4114.6375
        //       yCoord = pubPt.Y.ToBigInteger().ToByteArrayUnsigned();

        //byte[] xCoord = pubPt.XCoord.GetEncoded(),                        //2019-07-01: upgraded to 1.8.5; generates bad data
        //       yCoord = pubPt.YCoord.GetEncoded();

        ECPublicKeyParameters publicParams = new ECPublicKeyParameters(pubPt,ECParams); //2019-07-16; proper usage of 1.8.5
        byte[] xCoord=publicParams.Q.XCoord.GetEncoded(),
               yCoord=publicParams.Q.YCoord.GetEncoded();

        if(xCoord.Length!=32 || yCoord.Length!=32){throw new ApplicationException("SANITY CHECK: Expected 32 bytes for X/Y coords");}
        byte[] bytes=new byte[isCompr?33:65];                               //public key consists of a one byte prefix (0x04=uncompress,0x02=compressed w\ y=even, 0x03=compressed w\ y=odd) prior to the payload
        if(isCompr){
            bytes[0]=((yCoord[31] & 0x01)==0)?(byte)0x02:(byte)0x03;        //compressed; set prefix based on whether Y is even or odd
            Array.Copy(xCoord,0,bytes,1,32);                                //x coord
        }
        else{
            bytes[0]=0x04;                                                  //uncompressed; set prefix to 0x04
            Array.Copy(xCoord,0,bytes,1,32);                                //x coord
            Array.Copy(yCoord,0,bytes,33,32);                               //y coord
        }
        return bytes;
    }
    private static ECPoint ComputePublicECPoint(byte[] privKey){return ECParams.G.Multiply(new BigInteger(1,privKey));}
    private static readonly ECDomainParameters  ECParams=null;
    static BTCUtils2(){
        X9ECParameters crv = Org.BouncyCastle.Asn1.Sec.SecNamedCurves.GetByName("secp256k1");
        ECParams=new ECDomainParameters(crv.Curve, crv.G, crv.N, crv.H);
    }
}

El problema ocurre en la parte superior, donde estoy convirtiendo una clave privada, expresada como una matriz de bytes, en una clave pública, expresada en términos de sus coordenadas x/y. Ver mis comentarios. El código de 2015 funcionó bien durante años con la edición de 2011 de BouncyCastle.

En julio de 2019-07-01, tuve que actualizar a la versión 1.8.5 del castillo hinchable. El código ya no se compila. Pensé, bueno, deben haber cambiado el nombre de algunas cosas o lo que sea, así que apliqué las dos líneas indicadas como 2019-07-01, pensando que estaba logrando lo mismo. GRAN ERROR. No estoy seguro exactamente de qué devuelven esas llamadas de función, pero no son la coordenada x/y de la clave pública. Son algo completamente diferente.

Como puede imaginar, tener una clave pública que no se corresponde correctamente con su clave privada genera todo tipo de problemas, incluidas direcciones de Bitcoin mal formadas, errores de firma, etc.

Hoy, reemplacé esas líneas malas con las etiquetadas 2019-07-16. Eso hizo el truco. Verifiqué que esas líneas de hecho generaban una clave pública válida. Las transacciones ahora se envían sin errores.

Espero que esto sea valioso para alguien.

Mejor,

Festo