Un programa corto para minar un bloque.

¿Alguien puede dar una explicación paso a paso sobre el código a continuación (si es posible, con el equivalente ac/c++)?

import hashlib, struct

ver = 2
prev_block = "000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717"
mrkl_root = "871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a"
time_ = 0x53058b35 # 2014-02-20 04:57:25
bits = 0x19015f53

# https://en.bitcoin.it/wiki/Difficulty
exp = bits >> 24
mant = bits & 0xffffff
target_hexstr = '%064x' % (mant * (1<<(8*(exp - 3))))
target_str = target_hexstr.decode('hex')

nonce = 0
while nonce < 0x100000000:
    header = ( struct.pack("<L", ver) + prev_block.decode('hex')[::-1] +
          mrkl_root.decode('hex')[::-1] + struct.pack("<LLL", time_, bits, nonce))
    hash = hashlib.sha256(hashlib.sha256(header).digest()).digest()
    print nonce, hash[::-1].encode('hex')
    if hash[::-1] < target_str:
        print 'success'
        break
    nonce += 1

Estoy tratando de crear mi propia implementación; Ya leí los datos del bloque con json-rpc y los almacené en esta estructura:

struct block {
  unsigned char* hash;
  int confirmations;
  long strippedsize;
  long size;
  long weight;
  long height;
  long version;
  unsigned char* versionHex;
  unsigned char* merkleroot;
  long blocktime;
  long mediantime;
  long nonce;
  unsigned char* bits;
  double difficulty;
  unsigned char* chainwork;
  unsigned char* previousblockhash;
};

Ahora quiero extraer los datos de esta estructura y usarlos para ejecutar los mismos cálculos en este código, pero necesito entender qué hace el código con más detalles.

Respuestas (1)

Primero, definimos cierta información que va en el bloque, a saber, qué versión es el bloque, cuál era el hash del bloque anterior (porque cada bloque hace referencia al anterior para crear la cadena), cuál es la raíz Merkle de todas las transacciones en este bloque. (consulte Wikipedia si no sabe qué es esto), la marca de tiempo actual y la dificultad actual codificada en la bitsvariable:

import hashlib, struct

ver = 2
prev_block = "000000000000000117c80378b8da0e33559b5997f2ad55e2f7d18ec1975b9717"
mrkl_root = "871714dcbae6c8193a2bb9b2a69fe1c0440399f38d94b3a0f1b447275a29978a"
time_ = 0x53058b35 # 2014-02-20 04:57:25
bits = 0x19015f53

Luego tomamos el bitsvalor y lo convertimos en un objetivo de dificultad según los pasos enumerados en la página wiki vinculada en dificultad. El objetivo es básicamente un número que queremos encontrar debajo de un hash (por ejemplo, si el objetivo fuera 10, solo los hash menores a 10 serían válidos):

# https://en.bitcoin.it/wiki/Difficulty
exp = bits >> 24
mant = bits & 0xffffff
target_hexstr = '%064x' % (mant * (1<<(8*(exp - 3))))
target_str = target_hexstr.decode('hex')

Ahora comenzamos un bucle, procesando el bloque muchas veces con diferentes valores de nonce hasta que encontramos un hash menor que el objetivo. Comenzamos el nonce en 0 y lo aumentamos en 1 en cada ciclo para obtener un hash diferente cada vez:

nonce = 0
while nonce < 0x100000000:

Esto es solo juntar los datos que definimos anteriormente (incluido el nonce) en el bloque serializado listo para ser procesado:

    header = ( struct.pack("<L", ver) + prev_block.decode('hex')[::-1] +
          mrkl_root.decode('hex')[::-1] + struct.pack("<LLL", time_, bits, nonce))

Aquí es donde en realidad hacemos un hash de los datos del bloque serializados, obteniendo un hash, que imprimimos (codificado como una cadena hexadecimal) con el nonce para fines de registro:

    hash = hashlib.sha256(hashlib.sha256(header).digest()).digest()
    print nonce, hash[::-1].encode('hex')

Finalmente, comparamos el valor hash con el objetivo, y si es más bajo que el objetivo, hemos encontrado con éxito un bloque válido; de lo contrario, intente con el siguiente nonce:

    if hash[::-1] < target_str:
        print 'success'
        break
    nonce += 1

Tenga en cuenta que este es un programa muy simple, bueno para la comprensión, pero realmente no funcionaría en la minería real porque cambiar el nonce solo ya no es suficiente variación para encontrar un bloque, los datos también varían en la transacción de coinbase para cambiar la raíz merkle también. Eso es solo porque el objetivo es tan bajo (dificultad tan alta) que la posibilidad de alcanzarlo es bastante baja para combatir el poder minero masivo en la red. El programa tampoco calcula la raíz de merkle en sí, lo que debe hacerse si está comenzando directamente desde las transacciones.

¿Cómo obtengo los datos que se incluirán en el bloque? Si obtengo algún bloque de la red bitcoin (con getbestblokchashel comando json-rpc, por ejemplo), ¿puedo usar el extracto de datos de este bloque como base para el encabezado generado? ¿O solo necesito crear todos los datos nuevos para un bloque completamente nuevo? En este último caso, ¿cómo obtengo estas transacciones para generar la raíz de Merkle? ¿O se genera solo con la transacción coinbase?
getbestblockhashprev_blockte dará La dificultad se encuentra utilizando el algoritmo de retargeting de dificultad. Las transacciones son recogidas con el tiempo por nodos conectados a la red, en el mempool. No haga varias preguntas como comentarios, edite su pregunta original o publique una nueva.