Trabas
¿Qué impide que los bitcoins sean robados?
Toda salida en una transacción tiene una traba. Esta traba es un conjunto de requisitos que deben cumplirse para gastar la salida en una transacción futura.
En otras palabras, estas trabas impiden que los bitcoins sean robados (es decir, que otra persona gaste tus bitcoins), ya que cada salida que recibimos está gravada con una traba.
Por ejemplo, una traba típica dice algo así:
¿Cuándo se colocan las trabas en las salidas?
Como sabemos, una transacción toma salidas existentes y crea nuevas a partir de ellas:
Y es durante la creación de esas salidas que les damos su propia "traba":
Entonces, cuando queremos enviar bitcoins a un amigo, creamos una nueva salida y añadimos una traba que dice "solo el dueño de la dirección 1amigo1234567890... puede usar esta salida":
Como resultado, esa nueva salida efectivamente "pertenecerá" a nuestro amigo, porque él es la única persona que tiene la clave privada necesaria para desbloquear los bitcoins bloqueados a esa dirección, así que nadie más podrá gastarla.
¿Cómo es una traba?
Las trabas se escriben en un lenguaje de programación básico llamado Script.
Es un poco complicado explicar el funcionamiento de un lenguaje de programación entero en un diagrama, pero ahí va:

Este es un ejemplo simplificado de un script de bloqueo; no es exactamente como se ve Script.
Ahora, la parte más interesante de ese script de bloqueo es la parte CHECKPRIVATEKEY, que es una función que usamos para ayudar a definir los requisitos de la traba.
Entonces, para esta salida específica, definimos una traba que quiere comparar la dirección 1EUXSxuUVy2PC5enGXR1a3yxbEjNWMHuem con una clave privada.
Si podemos proporcionar a esa traba la clave privada correcta (que el dueño de la dirección mantiene en secreto), podemos desbloquearla y gastarla en una transacción.
¿Cómo desbloqueas una traba?
Cuando construyes los datos de la transacción, incluyes un "script de desbloqueo" junto a cada salida que quieres gastar:
Entonces, por ejemplo, para desbloquear un script de bloqueo típico (por ejemplo: [dirección] CHECKPRIVATEKEY), necesitamos demostrar que somos dueños de la dirección dentro de la traba. Para ello, proporcionamos la clave privada conectada a la dirección.
Entonces, cuando un nodo recibe esos datos de la transacción, ejecuta juntos los scripts de "bloqueo"+"desbloqueo" para ver si tu clave privada está conectada matemáticamente a la dirección.
Si todo está bien, el nodo acepta la transacción y la reenvía a otros nodos, que a su vez ejecutarán el script de "bloqueo"+"desbloqueo" antes de aceptar la transacción.
Y así es como desbloqueas una traba en una salida.
¿No estamos entregando nuestra clave privada?
Observación astuta.
Confesión: en realidad no ponemos nuestra clave privada en el script de desbloqueo.
Verás, para no tener que entregar nuestra clave privada dentro de los datos de la transacción, usamos la clave privada para crear algo llamado firma digital:
Obviamente también mentí sobre esa función CHECKPRIVATEKEY.
Sin embargo, sí existe una función que compara una dirección con una firma digital, y se llama CHECKSIG:
Y gracias a la matemática de las firmas digitales y a la función CHECKSIG, todavía podemos bloquear salidas a direcciones y desbloquearlas sin tener que entregar la clave privada.
Maravilla.