miércoles, 24 de noviembre de 2021

Minar Bitcoin con Java Protocolo Crudo Bitcoin


Introducción:

     Soy un aficionado a la programación, Venezolano, programo en diversos lenguajes de programación, pero mi leguaje preferido es Java, desarrollo de manera independiente por pasión al área, hoy voy a compartir mi conocimiento en el área de criptografía, específicamente en critomonedas, orientado especialmente al bitcoin por ser la primera moneda digital descentralizada, también  una de las más seguras y confiables del mundo, y además por tener la red más grande de todas.

    Voy a explicar y escribir un programa minador de bitcoin en lenguaje Java puro, sin APIS ni librerías externas, independiente del código fuente bitcoin, solo basándome en el protocolo crudo bitcoin, y implementándolo en este lenguaje, este programa  minador funciona perfectamente para minar un nuevo bloque bitcoin o verificar uno ya extraido.

    Este programa está orientado a fines educativos, ya que no contamos con suficiente poder de cómputo o equipos y granjas de minado, además el lenguaje Java es un lenguaje medianamente rápido por correr sobre una maquina virtual, para el uso real se necesita el uso de lenguajes como C/C++, que son lenguajes de alto rendimiento por comunicarse directamente con la maquina, esto no quiere decir que no sea posible minar con Java.

    E dedicado parte de mi tiempo en este tutorial, y lo estoy compartiendo con todos ustedes, también dejare los códigos fuentes libres en mi repositorio, para que cualquiera lo pueda descargar, si les ha servido de algo este tutorial o he apoyado indirectamente es sus proyectos, les pediría una pequeña ayuda, donado a mi dirección Bitcoin o mi cuenta PayPal, para seguir desarrollando tutoriales sobre esta área y dedicarle más tiempo a los mismos, esto también sería una gran apoyo para mi, gracias.

Cuenta PayPal: Link

  Dirección Bitcoin1: bc1qkmp5d8cxyza4yzhwsp0mm0pec5wzat7xd7n686


Dirección Bitcoin2: 1EqsDHXRCG9FmpxkiJVFo5bTSfEGyD59AV

El Byte unidad básica de la informática:

 

    En la informática la unidad básica de información es el byte, un byte está compuesto por 8 bit, ósea una combinación de ceros y unos, de longitud 8 o un número binario de longitud 8, ejemplo de  un número binario que representa un byte podría ser: 01011101 en decimal será el numero 93, y en hexadecimal seria 5D y en octal seria 135, en un byte se puede almacenar un numero de 0 a 255, ósea 256 combinaciones de ceros y unos, el numero más grande que almacena un byte en binario es: 11111111 en decimal es 255 en hexadecimal FF y en octal 377.

    Porque esto del byte, en Java no existe enteros sin signo, todos los números enteros en Java se pueden expresar en positivos y negativos, el rango de números que cabe en un byte en Java es de     -128 a 127, el numero más grande que puede guardar un byte Java es 127 positivo y el menor -128 negativo, a decencia de otros lenguajes que el rango va de 0 a 255.

    Tengo que aclarar esto para poder implementar el protocolo bitcoin correctamente e Java y hacer las debidas conversiones.

 

Bitcoin trabaja en hexadecimal pero invertido:

 

    Como invertido, por lo general vemos, no enseñan y estamos acostumbrados a ver números hexadecimal con digito o cero más significativo en el lugar derecho y forma más comprensible para el humano ya sea decimal, hexadecimal, octal o binario, este formato u orden se llama big-endian, el BlockChain podemos ver los hash de los bloques y transacciones en formato big-endian, pero bitcoin no trabaja de esas manera, es un vista para ordenar los números para la facilidad de comprensión para las personas, existe otro formato de orden numérico que se llama little-endian donde le decimal y cero más significativo esta a la derecha, ósea los bytes se representan de manera invertida, otra cosa a aclarar el byte puede almacenar dos dígitos o dos carácter hexadecimal ya que dos dígitos o dos caracteres hexadecimal representan un byte, cada carácter o digito hexadecimal representa medio byte.

    Un byte en hexadecimal en formato big-endian podría ser 4D en decimal es 77, y seria little-endian seria 4D igualmente ya este número hexadecimal solo representa un solo byte, como he dicho el formato little-endian invierte los bytes, ahora veamos un numero de dos bytes en big-endian seria C78E y en little-endian es 8EC7 se invirtió la posición de los bytes el primer número que usamos fue un numero de 8 bit y el segundo fue un numero de 16 bit.

 

Que es lo que se mina en un bloque Bitcoin:

 

    Para extraer o minar un bloque bitcoin, que se necesita, en bitcoin se mina es la cabecera del bloque que está compuesta por 160 bit o 80 bytes de información, aunque no se incluyen todos los campos si se necesitan.

    Los campos necesarios para minar un bloque bitcoin son, la versión actual del protocolo bitcoin en hexadecimal little-endian, el hash del bloque anterior en hexadecimal little-endian, el hash del

     Merkle root que no es más que el hash de todas las transacciones del bloque incluyendo la transacción de base de moneda que va al minero o CoinBase, que se explicara como generar más adelante, también en formato hexadecimal little-endian, el Timestamp o tiempo master o marca de tiempo en hexadecimal little-endian que no es más que la fecha y hora, minutos, segundos exacto cuando se mino el bloque  y se representa en segundos desde la fecha 1 de enero de 1970, 0:00:00, que luego es convertida en la hora media de Greenwich o GM, más adelante se enseña como calcular este tiempo en segundos en Java, el Bits en hexadecimal little-endian que se usa para calcular la dificultad de minado, el  Nonce o numero de trabajo un numero aleatorio que se tiene que conseguir para generar un bloque valido con la cantidad de ceros estipulada.

 

    Cada campó tiene un tamaño en bytes, la versión está compuesta por 4 bytes o 32 bit, el hash anterior por 32 bytes o 256 bit, el Merkle root por 32 bytes o 256 bit, el Timestamp por 4 bytes o 32 bit, el  Bits por 4 bytes o 32 bit y el Nonce por 4 bytes o 32 bit.

 

    Estos son los campos necesarios o que se necesitan para minar un bloque bitcoin o verificar uno ya extraido.

 

 

En Bitcoin se usa el algoritmo criptográfico SHA-256 para minar un bloque:

 

    Para minar un bloque se usa el algoritmo SHA-256 para el proceso de minado, este algoritmo ya esta implementado en casi todos los lenguajes de programación, aunque se podría implementar desde cero, el proceso de minado se basa en hacer un doble hash con este algoritmo SHA-256 (SHA-256) de los siguientes campos, versión, hash anterior, Merkle root, Timestamp, Bits y Nonce y hay que aclarar que todos estos bytes se concatenan y se les aplica el doble hash en formato invertido little-endian, el nonce va cambiando constantemente hasta que se encuentre un bloque valido que cumpla con los criterios correctos, para que un hash sea valido tiene que tener al final si esta en little-endian el numero de ceros estipulado por la red bitcoin, esta dificultad se calcula con el campo Bits, algunos de los campos son fijados por la comunidad bitcoin estos quiere decir que están públicamente en el blockchain , como el Bits o la versión de protocolo, los demás campos son fácil calcularlos, el siguiente criterio para que un bloque o hash sea valido es que el hash encontrado con la cantidad de ceros correcto sea menor que la dificultad actual esto también se calculara más adelante.

 

Minado un bloque bitcoin o verificarlo uno ya extraído:

 

    Ya entendido el protocolo bitcoin para minar un bloque lo podemos implementar en el leguaje de programación de nuestra preferencia, y minar un bloque valido desde cero o verificar uno ya extraído en el BlockChain, pero aun nos falta calcular algunos campos necesarios, pero por el momento voy a mostrar el funcionamiento del programa escrito en Java para minar bitcoin y los demás programas de herramientas para calcular esos campos necesarios. El código fuento estará disponible en mi repositorio.

Muestra del programa minador de bitcon para minar o verificar el bloque 709130 o

 (Block 709130)

 

    Para el minado del bloque en este programa los datos se introducen en hexadecimal big-endian por conveniencia. Para calcular el Timestamp es necesario los segundos de la fecha indicada, algunos exploradores de bloques no lo incluyen, buscar uno explorador que lo incluya.

Verificar un bloque ya extraído

 

Campos necesarios:

 

Versión de protocolo: 3fffe004

 

Hash del bloque anterior: 00000000000000000000bfa3e0adec9293bfd9ce1919e8e141e61c99bc834421

 

Merkle root: 2150562a61f408a257a4a954f551d2325088cd7939befb89c130aa308bf3fceb

 

Timestamp: 2021-11-10 18:04:13, decimal: 1636581853, hexadecimal: 618C41DD

 

Bits: decimal: 386727631, hexadecimal: 170CFECF

 

Nonce ya extraído: decimal: 822226586, hexadecimal: 31022E9A

 

Al introducir esto en el programa podemos empezar a minar un bloque o extraer uno

 

 

Veamos en la imagen

 

 
 

El programa ha generado el hash valido del bloque utilizado Block 709130: 

0000000000000000000aa425b68a7e425b01ec4fa966b42e330e13535b7ee947

 

Programa Java para encontrar el tiempo máster o marca de tiempo

 


    Con respecto al cálculo de la marca de tiempo, para calcularlo en Java se introduce en el código la fecha que equivalga a la hora local de tu región, bitcoin publica en el blockchain la fecha y hora en formato GMT-4 y depende que locación GTM te encuentres debes hacer el ajuste de horas adecuadas. Mi país desde el 2016 se encuentra en GTM-4 pero antes de esa fecha estaba en GTM-4:30 ósea tenía media hora de retraso con respecto a la red bitcoin.

 

Explicando el código en Java:

    Como lo he dicho el código estará libre en mi repositorio pero no está de más explicar  algunas partes más importante del mismo, sinceramente no quiero confundirte con esto, algunas clases son solo de utilidad y la UI no es tan importante.

Algoritmo SHA-256 en Java:

 

    Para implementar este algoritmo en Java se puede usar las Apis nativas de Java estándar de la siguiente manera:

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

 

 

       public String cifrarTexto(String texto) {

 

             MessageDigest codigo = null;

 

             try {

                    codigo = MessageDigest.getInstance("SHA-256");

 

             } catch (NoSuchAlgorithmException e) {

 

                    return "No existe";

             }

 

             byte[] hash = codigo.digest(texto.getBytes());

 

             StringBuilder cifrado = new StringBuilder();

 

             for (byte bit : hash) {

 

                    cifrado.append(String.format("%02x", bit));

 

             }

 

             return cifrado.toString();

 

       }

 

    Aclaro este es un método que puede ir en cualquier clase solo invóquelo donde usted vea conveniente, el método recibe un cadena String y codifica en un hash. Que retorna como un String en formato hexadecimal.

 

    Lo que hace el método internamente es extraer los bytes de la cadena y aplicarle el Algoritmo SHA-256 a esos bytes devolviendo un array de bytes ya codificado y que luego se transforman a su respectiva representación en hexadecimal.

Codificando un número de 4 bytes o 256 bit hexadecimal en  Java:


       private int hexadecimalDecimal(String texto) {

 

             int[] numero = new int[texto.length()];

 

             for (int i = 0; i < texto.length(); i++) {

 

                    if (texto.charAt(i) == '0') {

 

                           numero[i] = 0;

 

                    }

                    if (texto.charAt(i) == '1') {

 

                           numero[i] = 1;

 

                    }

                    if (texto.charAt(i) == '2') {

 

                           numero[i] = 2;

 

                    }

                    if (texto.charAt(i) == '3') {

 

                           numero[i] = 3;

 

                    }

                    if (texto.charAt(i) == '4') {

 

                           numero[i] = 4;

 

                    }

                    if (texto.charAt(i) == '5') {

 

                           numero[i] = 5;

 

                    }

                    if (texto.charAt(i) == '6') {

 

                           numero[i] = 6;

 

                    }

                    if (texto.charAt(i) == '7') {

 

                           numero[i] = 7;

 

                    }

                    if (texto.charAt(i) == '8') {

 

                           numero[i] = 8;

 

                    }

                    if (texto.charAt(i) == '9') {

                           numero[i] = 9;

 

                    }

                    if (texto.charAt(i) == 'a') {

 

                           numero[i] = 10;

 

                    }

                    if (texto.charAt(i) == 'b') {

 

                           numero[i] = 11;

 

                    }

                    if (texto.charAt(i) == 'c') {

 

                           numero[i] = 12;

 

                    }

                    if (texto.charAt(i) == 'd') {

 

                           numero[i] = 13;

 

                    }

                    if (texto.charAt(i) == 'e') {

                           numero[i] = 14;

 

                    }

                    if (texto.charAt(i) == 'f') {

 

                           numero[i] = 15;

 

                    }

 

                    if (texto.charAt(i) == 'A') {

 

                           numero[i] = 10;

 

                    }

                    if (texto.charAt(i) == 'B') {

 

                           numero[i] = 11;

 

                    }

                    if (texto.charAt(i) == 'C') {

 

                           numero[i] = 12;

 

                    }

                    if (texto.charAt(i) == 'D') {

 

                           numero[i] = 13;

 

                    }

                    if (texto.charAt(i) == 'E') {

 

                           numero[i] = 14;

 

                    }

                    if (texto.charAt(i) == 'F') {

 

                           numero[i] = 15;

 

                    }

 

             }

 

             int numeroDecimal = numero[1] + numero[0] * (16);

 

             return numeroDecimal;

 

       }

 

    Este es un método privado que sirve de utilidad para transformar cadena hexadecimal de 2 bytes a un número decimal sirve de apoyo para el siguiente método

     

public byte[] bytesNoInvertidos(String texto) {

 

             byte[] pares = new byte[texto.length() / 2];

 

             if (!(texto == "" || texto == " " || texto == null)) {

 

                    StringBuilder[] paresTexto = new StringBuilder[texto.length()/ 2];

 

                    for (int i = 0; i < paresTexto.length; i++) {

 

                           paresTexto[i] = new StringBuilder();

 

                    }

 

                    int j = 0;

 

                    for (int i = 0; i < paresTexto.length; i++) {

 

                           paresTexto[i].append(texto.charAt(j));

 

                           j++;

 

                           paresTexto[i].append(texto.charAt(j));

 

                           j++;

 

                    }

 

                    for (int i = 0; i < paresTexto.length; i++) {

 

                           pares[i] = (byte) hexadecimalDecimal(paresTexto[i].toString());

 

                    }

 

             }

 

             return pares;

 

       }

 

 

    Este método sirve de utilidad apara transformar cualquier cadena hexadecimal en un array de bytes validos  ósea devuelve byte completo no medio byte la representación en bytes de dos dígitos hexadecimal en formato big-endian.

 

public byte[] bytesInvertidos(String texto) {

 

             byte[] paresInvertidos = new byte[texto.length() / 2];

 

             StringBuilder[] paresTexto = new StringBuilder[texto.length() / 2];

 

             for (int i = 0; i < paresTexto.length; i++) {

 

                    paresTexto[i] = new StringBuilder();

 

             }

 

             int j = 0;

 

             for (int i = 0; i < paresTexto.length; i++) {

 

                    paresTexto[i].append(texto.charAt(j));

 

                    j++;

 

                    paresTexto[i].append(texto.charAt(j));

 

                    j++;

 

             }

 

             byte[] pares = new byte[texto.length() / 2];

 

             for (int i = 0; i < paresTexto.length; i++) {

 

                    pares[i] = (byte) hexadecimalDecimal(paresTexto[i].toString());

 

             }

 

             int indice = 0;

 

             for (int i = pares.length - 1; i >= 0; i--) {

 

                    paresInvertidos[indice] = pares[i];

 

                    indice++;

 

             }

 

             return paresInvertidos;

 

       }

 

    Este método sirve de utilidad apara transformar cualquier cadena hexadecimal en un array de bytes validos  ósea devuelve byte completo no medio byte la representación en bytes de dos dígitos hexadecimal en formato big-endian.

 

 

public byte[] bytesInvertidos(String texto) {

 

             byte[] paresInvertidos = new byte[texto.length() / 2];

 

             StringBuilder[] paresTexto = new StringBuilder[texto.length() / 2];

 

             for (int i = 0; i < paresTexto.length; i++) {

 

                    paresTexto[i] = new StringBuilder();

 

             }

 

             int j = 0;

 

             for (int i = 0; i < paresTexto.length; i++) {

 

                    paresTexto[i].append(texto.charAt(j));

 

                    j++;

 

                    paresTexto[i].append(texto.charAt(j));

 

                    j++;

 

             }

 

             byte[] pares = new byte[texto.length() / 2];

 

             for (int i = 0; i < paresTexto.length; i++) {

 

                    pares[i] = (byte) hexadecimalDecimal(paresTexto[i].toString());

 

             }

 

             int indice = 0;

 

             for (int i = pares.length - 1; i >= 0; i--) {

 

                    paresInvertidos[indice] = pares[i];

 

                    indice++;

 

             }

 

             return paresInvertidos;

 

       }

 

 

    Este método hace lo mismo que el método anterior pero invierte los bytes de la cadena a formato little-endian.

 

 

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

 

 

 

 

       public String cifrarTextoManualHex(String versionHexadecimal, String hashAnterirHexadecimal,

                    String MerkleRootHexadecimal, String tiempoHexadecimal, String tamanoBytesHexadecimal,

                    String nonceHexadecimal) {

 

             byte[] cabecera = new byte[versionHexadecimal.length() / 2 + hashAnterirHexadecimal.length() / 2

                           + MerkleRootHexadecimal.length() / 2 + tiempoHexadecimal.length() / 2

                           + tamanoBytesHexadecimal.length() / 2 + nonceHexadecimal.length() / 2];

 

             byte[] version = new byte[versionHexadecimal.length() / 2];

 

             byte[] hashAnterior = new byte[hashAnterirHexadecimal.length() / 2];

 

             byte[] merkle = new byte[MerkleRootHexadecimal.length() / 2];

 

             byte[] tiempo = new byte[tiempoHexadecimal.length() / 2];

 

             byte[] tamanoBytes = new byte[tamanoBytesHexadecimal.length() / 2];

 

             byte[] nonce = new byte[nonceHexadecimal.length() / 2];

 

             version = binario.bytesInvertidos(versionHexadecimal);

 

             hashAnterior = binario.bytesInvertidos(hashAnterirHexadecimal);

 

             merkle = binario.bytesInvertidos(MerkleRootHexadecimal);

 

             tiempo = binario.bytesInvertidos(tiempoHexadecimal);

 

             tamanoBytes = binario.bytesInvertidos(tamanoBytesHexadecimal);

 

             nonce = binario.bytesInvertidos(nonceHexadecimal);

 

             int indice = 0;

 

             for (int i = 0; i < version.length; i++) {

 

                    cabecera[indice] = version[i];

 

                    indice++;

 

             }

 

             for (int i = 0; i < hashAnterior.length; i++) {

 

                    cabecera[indice] = hashAnterior[i];

 

                    indice++;

             }

 

             for (int i = 0; i < merkle.length; i++) {

 

                    cabecera[indice] = merkle[i];

 

                    indice++;

             }

 

             for (int i = 0; i < tiempo.length; i++) {

 

                    cabecera[indice] = tiempo[i];

 

                    indice++;

             }

 

             for (int i = 0; i < tamanoBytes.length; i++) {

 

                    cabecera[indice] = tamanoBytes[i];

 

                    indice++;

             }

 

             for (int i = 0; i < nonce.length; i++) {

 

                    cabecera[indice] = nonce[i];

 

                    indice++;

 

             }

 

             MessageDigest codigo = null;

 

             try {

                    codigo = MessageDigest.getInstance("SHA-256");

 

             } catch (NoSuchAlgorithmException e) {

 

                    return "No existe";

             }

 

             byte[] hash = codigo.digest(codigo.digest(cabecera));

 

             StringBuilder cifrado = new StringBuilder();

 

             for (byte bit : hash) {

 

                    cifrado.append(String.format("%02x", bit));

 

             }

 

             StringBuilder[] datos = new StringBuilder[cifrado.toString().length() / 2];

 

             for (int i = 0; i < datos.length; i++) {

 

                    datos[i] = new StringBuilder();

 

             }

 

             int in = 0;

 

             for (int i = 0; i < datos.length; i++) {

 

                    datos[i].append(cifrado.toString().charAt(in));

 

                    in++;

 

                    datos[i].append(cifrado.toString().charAt(in));

 

                    in++;

 

             }

 

             StringBuilder invertir = new StringBuilder();

 

             for (int i = datos.length - 1; i >= 0; i--) {

 

                    invertir.append(datos[i].toString());

 

             }

 

             return invertir.toString();

 

       }

 

 

 

    Este método mina un bloque bitcoin implementa el protocolo bitcoin crudo, devuelve un hash hexadecimal en formato big-endian el método internamente hace doble hash de los campos adecuados en formato little-endian y devuelve el hash en formato little-endian, luego lo invierte en formato big-endian.

 

public String cifrarTextoManualHexValido(String versionHexadecimal, String hashAnterirHexadecimal,

                    String MerkleRootHexadecimal, String tiempoHexadecimal, String tamanoBytesHexadecimal) {

 

             StringBuilder inv = new StringBuilder();

 

             if (trabajo) {

 

                    StringBuilder invertir = new StringBuilder();

 

                    String nonceHexadecimal = binario.decimalHexadecimal(numeroDeTrabajo);

 

                    numeroDeTrabajo++;

 

                    byte[] cabecera = new byte[versionHexadecimal.length() / 2 + hashAnterirHexadecimal.length() / 2

                                  + MerkleRootHexadecimal.length() / 2 + tiempoHexadecimal.length() / 2

                                  + tamanoBytesHexadecimal.length() / 2 + nonceHexadecimal.length() / 2];

 

                    byte[] version = new byte[versionHexadecimal.length() / 2];

 

                    byte[] hashAnterior = new byte[hashAnterirHexadecimal.length() / 2];

 

                    byte[] merkle = new byte[MerkleRootHexadecimal.length() / 2];

 

                    byte[] tiempo = new byte[tiempoHexadecimal.length() / 2];

 

                    byte[] tamanoBytes = new byte[tamanoBytesHexadecimal.length() / 2];

 

                    byte[] nonce = new byte[nonceHexadecimal.length() / 2];

 

                    version = binario.bytesInvertidos(versionHexadecimal);

 

                    hashAnterior = binario.bytesInvertidos(hashAnterirHexadecimal);

 

                    merkle = binario.bytesInvertidos(MerkleRootHexadecimal);

 

                    tiempo = binario.bytesInvertidos(tiempoHexadecimal);

 

                    tamanoBytes = binario.bytesInvertidos(tamanoBytesHexadecimal);

 

                    nonce = binario.bytesInvertidos(nonceHexadecimal);

 

                    int indice = 0;

 

                    for (int i = 0; i < version.length; i++) {

 

                           cabecera[indice] = version[i];

 

                           indice++;

 

                    }

 

                    for (int i = 0; i < hashAnterior.length; i++) {

 

                           cabecera[indice] = hashAnterior[i];

 

                           indice++;

                    }

 

                    for (int i = 0; i < merkle.length; i++) {

 

                           cabecera[indice] = merkle[i];

 

                           indice++;

                    }

 

                    for (int i = 0; i < tiempo.length; i++) {

 

                           cabecera[indice] = tiempo[i];

 

                           indice++;

                    }

 

                    for (int i = 0; i < tamanoBytes.length; i++) {

 

                           cabecera[indice] = tamanoBytes[i];

 

                           indice++;

                    }

 

                    for (int i = 0; i < nonce.length; i++) {

 

                           cabecera[indice] = nonce[i];

 

                           indice++;

 

                    }

 

                    MessageDigest codigo = null;

 

                    try {

                           codigo = MessageDigest.getInstance("SHA-256");

 

                    } catch (NoSuchAlgorithmException e) {

 

                           return "No existe";

                    }

 

                    byte[] hash = codigo.digest(codigo.digest(cabecera));

 

                    StringBuilder cifrado = new StringBuilder();

 

                    for (byte bit : hash) {

 

                           cifrado.append(String.format("%02x", bit));

 

                    }

 

                    StringBuilder[] datos = new StringBuilder[cifrado.toString().length() / 2];

 

                    for (int i = 0; i < datos.length; i++) {

 

                           datos[i] = new StringBuilder();

 

                    }

 

                    int in = 0;

 

                    for (int i = 0; i < datos.length; i++) {

 

                           datos[i].append(cifrado.toString().charAt(in));

 

                           in++;

 

                           datos[i].append(cifrado.toString().charAt(in));

 

                           in++;

 

                    }

 

                    for (int i = datos.length - 1; i >= 0; i--) {

 

                           invertir.append(datos[i].toString());

 

                    }

 

                    String objetivo = binario.obtenerObjetivo(tamanoBytesHexadecimal);

 

                    BigInteger nume1 = new BigInteger(binario.hexadecimalDecimalGigante(objetivo));

 

                    BigInteger nume2 = new BigInteger(binario.hexadecimalDecimalGigante(invertir.toString()));

 

                    if (nume2.compareTo(nume1) < 0) {

 

                           trabajo = false;

 

                           has = invertir;

 

                    }

 

                    /*

                     * if (invertir.toString().charAt(0) == '0' && invertir.toString().charAt(1) ==

                     * '0' && invertir.toString().charAt(2) == '0' && invertir.toString().charAt(3)

                     * == '0' && invertir.toString().charAt(4) == '0' &&

                     * invertir.toString().charAt(5) == '0' && invertir.toString().charAt(6) == '0'

                     * && invertir.toString().charAt(7) == '0' && invertir.toString().charAt(8) ==

                     * '0' && invertir.toString().charAt(9) == '0' && invertir.toString().charAt(10)

                     * == '0' && invertir.toString().charAt(11) == '0' &&

                     * invertir.toString().charAt(12) == '0' && invertir.toString().charAt(13) ==

                     * '0' && invertir.toString().charAt(14) == '0' &&

                     * invertir.toString().charAt(15) == '0' && invertir.toString().charAt(16) ==

                     * '0' && invertir.toString().charAt(17) == '0' &&

                     * invertir.toString().charAt(18) == '0') {

                     *

                     * trabajo = false;

                     *

                     * has = invertir;

                     *

                     * }

                     */

 

                    inv = invertir;

 

             }

 

             if (trabajo) {

 

                    return inv.toString();

 

             } else {

 

                    return has.toString();

 

             }

 

       }

 

 

    Este método genera hash valido bitcoin tal cual el anterior pero calcula el objetivo de dificultad bitcoin tiene barios métodos de apoyo este fragmento de código comprueba si el hash calculado es inferior al objetivo

 

 

/////

 

if (nume2.compareTo(nume1) < 0)


/////

 

 

public String hexadecimalDecimalGigante(String textoHexadecimal) {

 

             StringBuilder[] numeroDecimal = new StringBuilder[textoHexadecimal.length()];

 

             for (int i = 0; i < textoHexadecimal.length(); i++) {

 

                    numeroDecimal[i] = new StringBuilder();

 

             }

 

             for (int i = 0; i < textoHexadecimal.length(); i++) {

 

                    if (textoHexadecimal.charAt(i) == '0') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '1') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '2') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '3') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '4') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '5') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '6') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '7') {

 

                          numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '8') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == '9') {

 

                           numeroDecimal[i].append(textoHexadecimal.charAt(i));

 

                    }

                    if (textoHexadecimal.charAt(i) == 'a') {

 

                           numeroDecimal[i].append("10");

                    }

                    if (textoHexadecimal.charAt(i) == 'b') {

 

                           numeroDecimal[i].append("11");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'c') {

 

                           numeroDecimal[i].append("12");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'd') {

 

                           numeroDecimal[i].append("13");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'e') {

 

                           numeroDecimal[i].append("14");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'f') {

 

                          numeroDecimal[i].append("15");

 

                    }

 

                    if (textoHexadecimal.charAt(i) == 'A') {

 

                           numeroDecimal[i].append("10");

                    }

                    if (textoHexadecimal.charAt(i) == 'B') {

 

                           numeroDecimal[i].append("11");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'C') {

 

                           numeroDecimal[i].append("12");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'D') {

 

                           numeroDecimal[i].append("13");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'E') {

 

                           numeroDecimal[i].append("14");

 

                    }

                    if (textoHexadecimal.charAt(i) == 'F') {

 

                           numeroDecimal[i].append("15");

 

                    }

 

             }

 

             BigInteger[] numeros = new BigInteger[numeroDecimal.length];

 

             BigInteger[] partes = new BigInteger[numeroDecimal.length];

 

             BigInteger[] suma = new BigInteger[numeroDecimal.length - 1];

 

             BigInteger base = new BigInteger("16");

 

             for (int i = 0; i < numeros.length; i++) {

 

                    numeros[i] = new BigInteger(numeroDecimal[i].toString());

 

             }

 

             int indice = numeros.length - 1;

 

             for (int i = 0; i < numeros.length; i++) {

 

                    partes[i] = numeros[indice].multiply(base.pow(i));

 

                    indice--;

 

             }

 

             suma[0] = partes[0].add(partes[1]);

 

             for (int i = 1; i < numeros.length - 1; i++) {

 

                    suma[i] = suma[i - 1].add(partes[i + 1]);

 

             }

 

             BigInteger numeroDe = suma[suma.length - 1];

 

             return numeroDe.toString();

 

       }

 

    Este método devuelve un numero entero representado como cadena con la entrada de un numero hexadecimal el numero es un numero entero muy grande que solo se puede representar en Java con la clase BigInteger

 

public String obtenerObjetivo(String bitHex) {

 

             int exponente = hexadecimalDecimal8(bitHex) >> 24;

 

             int numoroObtenido = hexadecimalDecimal8(bitHex) & 0xffffff;

 

             BigInteger numeroGrande = new BigInteger("1");

 

             int desplazar = (8 * (exponente - 3));

 

             BigInteger numeroGrande2 = new BigInteger(String.valueOf(numoroObtenido));

 

             BigInteger numeroGrande3 = numeroGrande.shiftLeft(desplazar);

 

             BigInteger numero = numeroGrande2.multiply(numeroGrande3);

 

             BigInteger base = new BigInteger("16");

 

             int potencia = 0;

 

             for (int i = 0; i < 1000; i++) {

 

                    if (base.pow(i).compareTo(numero) >= 1) {

 

                           potencia = i - 1;

 

                           break;

 

                    }

 

             }

 

             BigInteger[] division = new BigInteger[potencia];

 

             BigInteger[] resto = new BigInteger[potencia];

 

             division[0] = numero.divide(base.pow(potencia));

 

             resto[0] = numero.mod(base.pow(potencia));

 

             for (int i = 1; i < potencia; i++) {

 

                    division[i] = resto[i - 1].divide(base.pow(potencia - i));

 

                    resto[i] = resto[i - 1].mod(base.pow(potencia - i));

 

             }

 

             String[] num = new String[potencia + 1];

 

             for (int i = 0; i < potencia; i++) {

 

                    num[i] = division[i].toString();

 

                    if (i == potencia - 1) {

 

                           num[i + 1] = resto[i].toString();

 

                    }

 

             }

 

             StringBuilder numeroHex = new StringBuilder();

 

             for (int i = 0; i < num.length; i++) {

 

                    for (int j = 0; j < 16; j++) {

 

                           if (num[i].equals("" + j)) {

 

                                  if (j == 10) {

 

                                        numeroHex.append("A");

 

                                  } else if (j == 11) {

 

                                        numeroHex.append("B");

 

                                  } else if (j == 12) {

 

                                        numeroHex.append("C");

 

                                  } else if (j == 13) {

 

                                        numeroHex.append("D");

 

                                  } else if (j == 14) {

 

                                        numeroHex.append("E");

 

                                  } else if (j == 15) {

 

                                        numeroHex.append("F");

 

                                  } else {

 

                                        numeroHex.append("" + j);

 

                                  }

 

                           }

 

                    }

 

             }

 

             String objetivo = hexadecimalAhexadecimal64(numeroHex.toString());

 

             return objetivo;

 

       }

 

 

Este método calcula el objetivo con el Bits y tiene métodos de apoyo

 

 

public int hexadecimalDecimal8(String texto) {

 

             int[] numero = new int[texto.length()];

 

             for (int i = 0; i < texto.length(); i++) {

 

                    if (texto.charAt(i) == '0') {

 

                           numero[i] = 0;

 

                    }

                    if (texto.charAt(i) == '1') {

 

                           numero[i] = 1;

 

                    }

                    if (texto.charAt(i) == '2') {

 

                           numero[i] = 2;

 

                    }

                    if (texto.charAt(i) == '3') {

 

                           numero[i] = 3;

 

                    }

                    if (texto.charAt(i) == '4') {

 

                           numero[i] = 4;

 

                    }

                    if (texto.charAt(i) == '5') {

 

                           numero[i] = 5;

 

                    }

                    if (texto.charAt(i) == '6') {

 

                           numero[i] = 6;

 

                    }

                    if (texto.charAt(i) == '7') {

 

                           numero[i] = 7;

 

                    }

                    if (texto.charAt(i) == '8') {

 

                           numero[i] = 8;

 

                    }

                    if (texto.charAt(i) == '9') {

                           numero[i] = 9;

 

                    }

                    if (texto.charAt(i) == 'a') {

                           numero[i] = 10;

 

                    }

                    if (texto.charAt(i) == 'b') {

 

                           numero[i] = 11;

 

                    }

                    if (texto.charAt(i) == 'c') {

 

                           numero[i] = 12;

 

                    }

                    if (texto.charAt(i) == 'd') {

 

                           numero[i] = 13;

 

                    }

                    if (texto.charAt(i) == 'e') {

 

                           numero[i] = 14;

 

                    }

                    if (texto.charAt(i) == 'f') {

 

                           numero[i] = 15;

 

                    }

 

                    if (texto.charAt(i) == 'A') {

 

                           numero[i] = 10;

 

                    }

                    if (texto.charAt(i) == 'B') {

 

                           numero[i] = 11;

 

                    }

                    if (texto.charAt(i) == 'C') {

 

                           numero[i] = 12;

 

                    }

                    if (texto.charAt(i) == 'D') {

 

                           numero[i] = 13;

 

                    }

                    if (texto.charAt(i) == 'E') {

 

                           numero[i] = 14;

 

                    }

                    if (texto.charAt(i) == 'F') {

 

                           numero[i] = 15;

 

                    }

 

             }

 

             int numeroDecimal = (int) (numero[7] + numero[6] * (16) + numero[5] * Math.pow(16, 2)

                           + numero[4] * Math.pow(16, 3) + numero[3] * Math.pow(16, 4) + numero[2] * Math.pow(16, 5)

                           + numero[1] * Math.pow(16, 6) + numero[0] * Math.pow(16, 7));

 

             return numeroDecimal;

 

       }

 

 

Este método devuelve un entero de una cadena hexadecimal de 64 bit o 8 bytes

 

       public String hexadecimalAhexadecimal64(String texto) {

 

             String t = texto;

 

             String tHex = "";

 

             StringBuilder ceros = new StringBuilder();

 

             for (int i = 0; i < 64; i++) {

 

                    if (t.length() == i + 1) {

 

                           for (int j = 0; j < 64 - (i + 1); j++) {

 

                                  ceros.append("0");

 

                           }

 

                           tHex = ceros + t;

 

                    }

 

             }

 

             return tHex;

 

       }

 

Este método transforma un hexadecimal a un hexadecimal de 32 bytes o 256 bit 

Todos estos métodos son de utilidad y se usan para calcular el objetivo con el Bits 

 

 

Algoritmo para calcular el  Merkle root en Java:

 

 

public String obtenerMerkle(String[] textos) {

 

             if (textos.length == 1) {

 

                    return textos[0];

 

             }

 

             ArrayList<String> textosHashLista = new ArrayList<String>();

 

             for (int i = 0; i < textos.length - 1; i += 2) {

 

                    textosHashLista.add(cifrarTransiciones(textos[i], textos[i + 1]));

 

             }

 

             if (textos.length % 2 == 1) {

 

                    textosHashLista.add(cifrarTransiciones(textos[textos.length - 1], textos[textos.length - 1]));

 

             }

 

             String[] textosHash = new String[textosHashLista.size()];

 

             for (int i = 0; i < textosHash.length; i++) {

 

                    textosHash[i] = textosHashLista.get(i);

             }

 

             return obtenerMerkle(textosHash);

 

       }

 

    Este método calcula el Merkle root de todas las transacciones bitcoin incluyendo la transacción base de moneda tiene un método de apoyo

 

 

       public String cifrarTransiciones(String texto1, String texto2) {

 

             MessageDigest codigo = null;

 

             byte[] cabecera = new byte[texto1.length() / 2 + texto2.length() / 2];

 

             byte[] a = binario.bytesInvertidos(texto1);

 

             byte[] b = binario.bytesInvertidos(texto2);

 

             int indice = 0;

 

             for (int i = 0; i < a.length; i++) {

 

                    cabecera[indice] = a[i];

 

                    indice++;

 

             }

 

             for (int i = 0; i < b.length; i++) {

 

                    cabecera[indice] = b[i];

 

                    indice++;

 

             }

 

             try {

                    codigo = MessageDigest.getInstance("SHA-256");

 

             } catch (NoSuchAlgorithmException e) {

 

                    return "No existe";

             }

 

             byte[] hash = codigo.digest(codigo.digest(cabecera));

 

             StringBuilder cifrado = new StringBuilder();

 

             for (byte bit : hash) {

 

                    cifrado.append(String.format("%02x", bit));

 

             }

 

             StringBuilder[] datos = new StringBuilder[cifrado.toString().length() / 2];

 

             for (int i = 0; i < datos.length; i++) {

 

                    datos[i] = new StringBuilder();

 

             }

 

             int in = 0;

 

             for (int i = 0; i < datos.length; i++) {

 

                    datos[i].append(cifrado.toString().charAt(in));

 

                    in++;

 

                    datos[i].append(cifrado.toString().charAt(in));

 

                    in++;

 

             }

 

             StringBuilder invertir = new StringBuilder();

 

             for (int i = datos.length - 1; i >= 0; i--) {

 

                    invertir.append(datos[i].toString());

 

             }

 

             return invertir.toString();

 

       }

 

Este método genera el hash de cada transacción


El programa para generar el Timestamp en Java:

 

Para este programa no voy a explicar el código solo voy a dejar el código fuente


Esto sería todo en esta entrada espero crear más tutoriales sobre la red Bitcoin espero su apoyo

 

Cuenta PayPal: Link

  Dirección Bitcoin1: bc1qkmp5d8cxyza4yzhwsp0mm0pec5wzat7xd7n686


Dirección Bitcoin2: 1EqsDHXRCG9FmpxkiJVFo5bTSfEGyD59AV

Codigo  fuente del miminador en Java:link

Codigo  fuente del generador de marca de tiempo en Java:link

 

 

Bitcoin mining the hard way: the algorithms, protocols, and bytes: http://www.righto.com/2014/02/bitcoin-mining-hard-way-algorithms.html

 Mining Bitcoin with pencil and paper: 0.67 hashes per day : http://www.righto.com/2014/09/mining-bitcoin-with-pencil-and-paper.html

Bitcoins the hard way: Using the raw Bitcoin protocol: http://www.righto.com/2014/02/bitcoins-hard-way-using-raw-bitcoin.html

Minar Bitcoin con Java Protocolo Crudo Bitcoin

Introducción:        Soy un aficionado a la programación, Venezolano, programo en diversos lenguajes de programación, pero mi leguaje pref...