Capítulo 06 de 10 · 13 min de lectura

Minería y dificultad

Minar es comprar boletos de lotería a base de electricidad. Lo ingenioso no es la lotería, sino que el premio salga cada diez minutos tanto si juegan diez personas como diez millones, sin que nadie esté al mando.

El acertijo, con precisión

Hasta ahora hemos dicho que un bloque vale si su hash “empieza por muchos ceros”. La regla real es un poco más fina, y merece la pena verla tal cual:

Un bloque es válido si el hash de su cabecera, leído como un número, es menor o igual que el target.

El hash son 256 bits, así que es un número entre 0 y 2256. El target es otro número de ese rango, que fija la red. Cuanto más pequeño es el target, menos hashes quedan por debajo y más difícil es acertar. Los ceros iniciales son solo la consecuencia visible: un número pequeño, escrito con todas sus cifras, empieza por ceros.

En código es exactamente eso, una comparación:

// internal/core/pow.go
func CheckProofOfWork(hash crypto.Hash, bits uint32, powLimit *big.Int) bool {
	target := CompactToBig(bits)
	if target.Sign() <= 0 || target.Cmp(powLimit) > 0 {
		return false
	}
	return HashToBig(hash).Cmp(target) <= 0
}

Para buscar un hash que cumpla, el minero solo puede hacer una cosa: cambiar el nonce de la cabecera, calcular el hash y mirar. Si no, el siguiente nonce. Ya lo hiciste en la portada y en el capítulo anterior.

Por qué es una lotería, y por qué eso es justo

Como el hash es impredecible (capítulo 2), cada intento es un boleto con exactamente la misma probabilidad de premio: target / 2²⁵⁶. De ahí salen tres propiedades que hacen que esto funcione como sistema de votación:

  • No hay progreso. Haber fallado un billón de veces no te acerca ni un milímetro al premio. No existe “voy por el 80 %”. Así que nadie lleva ventaja por haber empezado antes: en cuanto aparece un bloque nuevo, todos parten otra vez de cero.
  • Tus posibilidades son tu porcentaje de la potencia total. Si calculas el 5 % de los hashes de la red, a la larga minarás el 5 % de los bloques. Ni más ni menos. Es el “un voto por unidad de cálculo” del capítulo 1: las identidades falsas no dan boletos; la electricidad, sí.
  • El tiempo entre bloques es muy irregular. Que la media sea 10 minutos no significa que salga uno cada 10. En Bitcoin, uno de cada diez bloques tarda menos de un minuto, y uno de cada veinte, más de media hora. No es un fallo: es lo que pasa cuando un suceso puede ocurrir en cualquier instante con la misma probabilidad.

Cuando se acaban los nonces

El nonce ocupa 4 bytes: unos 4.300 millones de valores. Parece mucho, pero una máquina de minar moderna los prueba todos en menos de un milisegundo, y lo normal es que ninguno sirva. Entonces hay que cambiar otra cosa de la cabecera para tener 4.300 millones de boletos nuevos. El truco habitual es tocar un contador dentro de la primera transacción del bloque, el extra nonce: cambia esa transacción, cambia la raíz de Merkle, cambia la cabecera. xavicoin hace lo mismo, y además reparte el espacio de nonces entre los hilos de la CPU para que no repitan trabajo:

// internal/miner/miner.go — el bucle de cada hilo (resumido)
buf := header.Serialize()
nonce := start // cada hilo empieza en un nonce distinto…
for {
	binary.LittleEndian.PutUint32(buf[core.HeaderSize-4:], nonce)
	if hash := crypto.Hash256(buf); meetsTarget(hash, target) {
		// ¡premio!
	}
	next := nonce + uint32(threads) // …y avanza saltando de N en N
	if next < nonce { // espacio de nonces agotado
		return
	}
	nonce = next
}

Eso es todo lo que es minar. No hay nada más inteligente que hacer.

Los bits: el target en cuatro bytes

Un target de 256 bits no cabe en los 4 bytes que le reserva la cabecera, así que se guarda comprimido, en una especie de notación científica: el primer byte dice cuántos bytes mide el número y los otros tres son sus cifras más significativas. Los bits del génesis de xavicoin, 1e00ffff, significan “el número 00ffff seguido de ceros hasta ocupar 0x1e = 30 bytes”:

000000ffff000000000000000000000000000000000000000000000000000000

Un hash tiene que quedar por debajo de eso: de media, uno de cada 16,7 millones lo consigue. Lo que se suele mostrar como dificultad es simplemente cuántas veces más pequeño es el target actual que el más fácil que admite la red. Dificultad 4 significa cuatro veces más intentos que al mínimo.

El problema: la potencia cambia

Si el target fuese fijo, el ritmo de bloques dependería de cuánta gente mina. Con el doble de máquinas, bloques el doble de rápido; y la emisión de monedas, que va a tanto por bloque, se dispararía. Peor aún: con bloques muy rápidos, los nodos de medio mundo no llegarían a enterarse de uno antes de que saliese el siguiente, y la red se llenaría de versiones contradictorias.

Hace falta un termostato. Y tiene que funcionar sin que nadie mida la potencia de la red, porque no hay nadie para medirla.

El termostato

La solución usa lo único que todos los nodos pueden ver: las horas escritas en las cabeceras. Cada cierto número de bloques, cada nodo hace por su cuenta esta cuenta:

target nuevo = target anterior × (lo que ha tardado el periodo ÷ lo que debería haber tardado)

Si los bloques han salido el doble de rápido de lo previsto, el target se reduce a la mitad: el doble de difícil. Como todos los nodos tienen las mismas cabeceras y hacen la misma división, todos llegan al mismo resultado sin hablarse. Un bloque cuyos bits no sean exactamente los que tocan es inválido, sin más.

Esta es una red simulada con los parámetros de la red de pruebas de xavicoin (un bloque cada 10 segundos, reajuste cada 20 bloques) y la misma fórmula, byte a byte, que ejecuta el nodo. Tú controlas cuánta potencia tiene la red. Súbela de golpe y mira.

Altura
0
Dificultad
×1
Media últimos 20 bloques
Próximo reajuste
Reloj de la simulación
0 s
Tiempo entre bloques segundos, escala logarítmica · últimos 100 bloques
  • un bloque
  • media del periodo
Dificultad veces la mínima de la red, escala logarítmica

Ver los periodos como tabla
BloquesBitsDificultadMedia entre bloquesAjuste aplicado después

Experimentos que enseñan algo:

  1. Sube la potencia a ×16. Los bloques pasan a salir cada 0,6 s. En el siguiente reajuste la dificultad se multiplica… pero solo por 4. Hacen falta dos reajustes para volver a los 10 s.
  2. Ahora bájala a ×1 de golpe. La dificultad sigue siendo la de una red 16 veces más potente, así que los bloques tardan más de dos minutos. Y aquí está la trampa: el reajuste llega tras 20 bloques, no tras un tiempo. Mira el reloj de la simulación. Cuando una red pierde potencia de golpe, tarda en recuperarse justo cuando peor va.
  3. Bájala a ×0,25. La dificultad no puede bajar del mínimo de la red, así que los bloques se quedan en 40 s. El mínimo existe para que fabricar una cadena falsa nunca sea gratis.

Por qué el tope de ×4

Las horas de las cabeceras las ponen los mineros, y pueden mentir dentro de los márgenes del capítulo anterior. El tope limita el daño: nadie puede hundir la dificultad de golpe falseando tiempos. El precio es la lentitud que has visto al subir o bajar la potencia de forma brusca.

El código del ajuste

Es una de las funciones con más consecuencias de todo el sistema, y cabe en una pantalla:

// internal/chain/node.go — NextBits (fragmento)
expected := int64(p.TargetSpacing/time.Second) * int64(parent.Height-firstHeight)
actual := int64(parent.Header.Timestamp) - int64(first.Header.Timestamp)
if actual < expected/4 {
	actual = expected / 4
}
if actual > expected*4 {
	actual = expected * 4
}
target := core.CompactToBig(parent.Header.Bits)
target.Mul(target, big.NewInt(actual))
target.Div(target, big.NewInt(expected))
if target.Cmp(p.PowLimit()) > 0 {
	target.Set(p.PowLimit())
}
return core.BigToCompact(target)

Bitcoin hace exactamente esto con un bloque cada 10 minutos y un reajuste cada 2.016 bloques (dos semanas). Con una diferencia curiosa: por un despiste de 2009, mide el tiempo de 2.015 intervalos en lugar de 2.016, y ya no se puede arreglar, porque cambiarlo partiría la red en dos. xavicoin mide el periodo completo. Es un buen recordatorio de que en un sistema sin dueño los errores de consenso son para siempre.

El trabajo acumulado

Cada bloque representa una cantidad de trabajo esperado: 2²⁵⁶ / (target + 1) hashes. Sumando el de todos los bloques de una cadena se obtiene su trabajo acumulado:

// internal/core/pow.go
func CalcWork(bits uint32) *big.Int {
	target := CompactToBig(bits)
	if target.Sign() <= 0 {
		return new(big.Int)
	}
	return new(big.Int).Div(twoTo256, target.Add(target, bigOne))
}

Este número va a ser el juez del capítulo 9. Cuando haya dos versiones de la historia, no ganará la que tenga más bloques —sería trivial fabricar muchos bloques fáciles— sino la que acumule más trabajo.

El elefante en la habitación

La prueba de trabajo gasta electricidad de verdad, y mucha. No es un efecto secundario que se pueda optimizar: ese gasto es la seguridad. Reescribir la historia cuesta lo que costó escribirla, y eso solo es cierto si escribirla costó algo que no se puede falsificar. Si ese precio compensa es un debate legítimo, y es la razón por la que otras cadenas usan mecanismos distintos, como la prueba de participación. Aquí nos quedamos con entender por qué el diseño original lo necesita.

Lo que viene

Minar cuesta dinero. Nadie lo haría gratis, y sin mineros no hay orden, ni bloques, ni sistema. Falta la pieza que cierra el círculo: qué gana quien mina un bloque, de dónde sale ese dinero, y por qué eso determina que solo vayan a existir 21 millones de bitcoins.