Hace años diseñé un módulo .NET que facilita la transmisión de BTC a mis clientes. Crea una representación binaria de la transacción deseada basada en el material presentado aquí y aquí :
La representación binaria luego se convierte a hexadecimal y se envía a través de varios servicios gratuitos como BlockExplorer, BlockCypher, etc.
El sistema ha estado funcionando sin problemas durante años. Hasta ayer. Un cliente solicitó que se enviaran 0.0284377 BTC a 38MRMGjMBMp4k7vZhKLHhcM9Pm8AMLy18v . Nunca lo recibió. Efectivamente, tenía razón. Nunca fue enviado.
Revisé todos mis registros y vi que mi software, de hecho, envió una transacción con las siguientes entradas y salidas:
Todo se veía bien de mi lado, pero efectivamente, cuando miré el explorador de bloques, lo que vi me sorprendió. Considere la transacción 9a138b14dcc8ae740073c06933ae04e3b08fe6be6ada0dc175e6484250dfe269 y observe las entradas y salidas:
Como puede ver, mi entrada es correcta. Mi dirección de cambio XU7 también es correcta. Pero ahora mira la dirección de mi cliente. Dice 17fQRjEudTVgexE8aDfhGyzDFEqSnnJJCA (al que me referiré como JCA) en lugar del esperado 38MRMGjMBMp4k7vZhKLHhcM9Pm8AMLy18v (al que me referiré como 18v). ¿Que demonios? Ni yo ni mi cliente conocemos la dirección de JCA. Es una dirección completamente desconocida.
Claramente, el problema está en mi código en alguna parte, así que profundicé más para descubrir qué hizo que esta transacción en particular fuera única. Identifiqué que su dirección deseada (18v) comienza con un '3', mientras que todas las demás transacciones salientes que he completado a lo largo del historial de mi aplicación tienen direcciones específicas que comienzan con '1'.
Mi medida provisional fue obligar a los usuarios a especificar solo las direcciones que comienzan con 1. Pero esa es una solución temporal. Necesito averiguar qué estoy haciendo mal.
Me volví a Google. La investigación indicó que las direcciones que comienzan con '3' son típicamente direcciones SegWit, mientras que las que comienzan con '1' son direcciones tradicionales de la vieja escuela. Recuerdo toda la conversación sobre SegWit hace unos meses, pero pensé que no me afectaba y que mis transacciones heredadas aún se liquidarían correctamente. Obviamente, fue una decisión equivocada de mi parte, así que ahora tengo que averiguar qué hice incorrectamente y de dónde provino la dirección JCA falsa.
Ahí es donde estoy atascado. Creo que mi problema podría tener que ver con las claves públicas sin comprimir frente a las comprimidas según las discusiones aquí y aquí :
Esto es lo que sé: cuando llega el momento de crear los resultados de mi transacción, mi código hace lo siguiente (a continuación se explica):
Func<String,byte[]> makeOutScript=(btcAddrHex)=>{
byte[] addrBytes=BTCUtils.Base58Decode(btcAddrHex);
byte[] pubKeyBytes=addrBytes.Take(addrBytes.Length-4).Skip(1).ToArray();
using(MemoryStream ms=new MemoryStream()){
using(BinaryWriter bw=new BinaryWriter(ms)){
bw.Write((byte)0x76); //op_dup
bw.Write((byte)0xa9); //op_hash160
bw.Write((byte)20); //size of public key
bw.Write(pubKeyBytes); //public key
bw.Write((byte)0x88); //op_equalverify
bw.Write((byte)0xac); //op_checksig
bw.Flush();
return ms.ToArray();
}
}
};
Lo que estoy haciendo aquí es: convertir la dirección de salida en bytes decodificándola en Base58. Elimino los últimos cuatro bytes, que son la suma de comprobación, y el primer byte, que siempre es 0x00 (aparentemente, un indicador de red de algún tipo). Eso me deja con la clave pública sin procesar. En realidad, mirando mis notas internas, en realidad no es la clave pública sino 0x04 antepuesto a la clave pública y luego pasó a través de SHA256 y luego RIPEMD160. Pero me referiré a este blob como la clave pública. A partir de ahí, la secuencia de bytes es 0x76, 0xa9, 20 (el tamaño del blob de clave pública), el propio blob de clave pública, 0x88 y 0xac.
No pretendo entender lo que significan todas esas entradas de un solo byte, pero las publicaciones de blog anteriores decían que las incluyera, así que eso es lo que hice, y hasta ahora funcionó bien.
La pregunta es: ¿Cómo diablos se transformó la dirección de 18v en la dirección JCA cuando se envió a la red? ¿Es mi valor de "tamaño de clave pública" (20) incorrecto, tal vez? A primera vista, parece extraño que una clave pública comprimida (18v) y sin comprimir (JCA) tengan un tamaño constante de 20. Pero tal vez estoy en el camino equivocado y la compresión no tiene nada que ver con eso.
No tengo que decirte lo terrible que sería si esta transacción fuera de 100 BTC en lugar de 0,0284377. Tuve la suerte de que mi error se activó en una transacción de bajo valor. Pero realmente quiero resolver esto. ¿Puedes señalarme el camino correcto?
Las direcciones que comienzan con 3...
son direcciones P2SH y existen desde hace casi 6 años. Históricamente vieron un uso limitado, utilizado por billeteras multisig como Copay y GreenAddress. Sin embargo, el modo de compatibilidad de SegWit también usa P2SH, lo que hace que su uso sea más común recientemente.
El problema principal es que su código para convertir una dirección en un scriptPubKey no mira el byte de la versión. Después de que Base58 decodifique la dirección, debe obtener una estructura de <versión de 1 byte> <hash de 20 bytes> <suma de comprobación de 4 bytes>. Si esa versión es 0, que es para 1...
direcciones, ese hash es un hash de clave pública y construye correctamente el scriptPubKey correspondiente.
Sin embargo, si ese número de versión es 5, es una dirección P2SH, que se especificó en BIP13 , que usa un scriptPubKey correspondiente especificado en BIP16 .
Si bien SegWit inicialmente solo usaba el antiguo formato P2SH, también se está introduciendo un nuevo formato de dirección para SegWit (que brinda un rendimiento, flexibilidad y capacidades de detección de errores ligeramente mejores), descrito en BIP173 . (descargo de responsabilidad: soy autor de BIP173). Estas direcciones comenzarán con bc1...
.
Le aconsejo que detenga su servicio y, como mínimo, implemente la verificación de que se espera el número de versión, junto con un conjunto de prueba que puede encontrar en muchas implementaciones. Si lo desea, también puede implementar direcciones BIP13 y BIP173, las cuales probablemente serán más frecuentes pronto.
Festo Martingala
pieter wuille
pieter wuille
pieter wuille
Festo Martingala
pieter wuille
gordito
ático