Ordem de Bytes
Por que os hashes de transações e blocos aparecem invertidos?
Uma peculiaridade divertida do Bitcoin é que os hashes de transação e os hashes de bloco têm a ordem dos bytes invertida quando você os exibe e os procura.
Por exemplo, o hash de bloco do bloco gênese sai da função de hash assim:
6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000Mas, quando você procura por esse bloco no Bitcoin Core ou em um explorador, você vê esta ordem de bytes:
000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26fComo você pode ver, a ordem dos bytes foi invertida.
- Sempre que você trabalha com hashes de transação/bloco internamente (ex.: dentro de dados brutos do Bitcoin), você usa a ordem de bytes natural.
- Sempre que você está exibindo ou procurando hashes de transação/bloco, você usa a ordem de bytes inversa.
Então, parte de trabalhar com dados do Bitcoin é pegar o jeito de inverter a ordem dos bytes para hashes de transação e bloco. Provavelmente vai te confundir algumas vezes (como ainda confunde a mim), mas, como eu disse, é só uma peculiaridade do desenvolvimento em Bitcoin.
Quais são as diferentes ordens de bytes?
Há duas ordens de bytes usadas no Bitcoin:
- Ordem Natural (Natural Byte Order)
- Ordem Inversa (Reverse Byte Order)
Essas ordens de bytes não devem ser confundidas com as ordens little-endian e big-endian, que são as ordens usadas para representar números (e não necessariamente hashes, como estamos falando aqui).
Ordem Natural
Também conhecida como: Native Byte Order, Internal Byte Order
É a ordem dos bytes como eles saem da função de hash. É a ordem que você usa ao se referir a um bloco anterior em um cabeçalho de bloco bruto, ou ao se referir a um TXID anterior em uma transação bruta.
Em outras palavras, esta é a ordem de bytes com que os programadores do Bitcoin lidam ao trabalhar com dados brutos.
Hash de Bloco: 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000
Hash de Transação: 169e1e83e930853391bc6f35f605c6754cfead57cf8387639d3b4096c54f18f4
Essa ordem de bytes às vezes é chamada de "little-endian".
Ordem Inversa
Também conhecida como: RPC Byte Order, Network Byte Order
É a ordem inversa dos bytes como eles saem da função de hash. É a ordem que você usa ao procurar transações/blocos em exploradores, ou ao fazer requisições RPC ao Bitcoin Core.
Em outras palavras, esta é a ordem de bytes que os usuários do Bitcoin veem e usam.
Hash de Bloco: 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
Hash de Transação: f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
Essa ordem de bytes às vezes é chamada de "big-endian".
Onde usamos a ordem natural?
Você usa a ordem natural ao trabalhar com dados brutos do Bitcoin internamente.
Por exemplo, dentro de transações brutas, cada entrada se refere à saída de uma transação anterior usando o hash dessa transação (o TXID), na ordem natural. Se você procurar c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704 em um explorador, não vai encontrar nada. Mas, se inverter a ordem dos bytes para 0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9, vai encontrar.
O mesmo vale para o hash do bloco anterior dentro de um cabeçalho de bloco, e até para a raiz de Merkle (que também aparece invertida nos exploradores).
Por que inverter a ordem dos bytes?
Só o Satoshi sabe por quê, e ele nunca explicou o raciocínio.
Então aqui está a minha teoria sobre por que invertemos a ordem dos bytes de hashes de bloco e de transação…
Hashes de Bloco
Os bytes que saem de uma função de hash não têm significado. São só bytes aleatórios.
Ao programar o Bitcoin, o Satoshi teve a ideia engenhosa de interpretar os hashes de bloco como inteiros. Isso permite comparar o hash de bloco com um valor alvo, e isso forma parte do mecanismo central da mineração.
Porém, o Satoshi estava trabalhando em um computador com arquitetura little-endian. Isso significava que, quando esses hashes de bloco eram convertidos em inteiros, o computador lia os bytes da esquerda para a direita. Então, ao ver os bytes do hash de bloco e do alvo, eles ficavam "ao contrário", o que é incômodo para humanos, que estão acostumados a ver o maior valor à esquerda (a ordem big-endian).
Ordem Natural (Little Endian):
Hash de Bloco (Bytes): 6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000
Alvo (Bytes): 0000000000000000000000000000000000000000000000000000ffff00000000
Então, acredito que o Satoshi decidiu inverter a ordem dos bytes para deixar os hashes de bloco mais amigáveis ao exibi-los:
Ordem Inversa (Big-Endian):
Hash de Bloco (Bytes): 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f
Alvo (Bytes): 00000000ffff0000000000000000000000000000000000000000000000000000
Olhando para o hash de bloco agora, dá para ver facilmente que ele está abaixo do alvo.
Então, internamente, todos os hashes de bloco são tratados na ordem natural. Mas externamente, ao fazer requisições RPC ou procurar em um explorador, os hashes ficam na ordem inversa, amigável aos humanos.
RPC Byte Order. A ordem inversa era originalmente usada ao fazer requisições RPC ao Bitcoin Core, e é por isso que às vezes é chamada de RPC Byte Order.
TXIDs
Além de inverter a ordem dos bytes dos hashes de bloco, o Satoshi decidiu inverter também a dos hashes de transação (TXIDs):
Hash de Transação: 169e1e83e930853391bc6f35f605c6754cfead57cf8387639d3b4096c54f18f4
TXID: f4184fc596403b9d638783cf57adfe4c75c605f6356fbc91338530e9831e9e16
Não há necessidade de fazer isso, já que hashes de transação nunca são interpretados como inteiros dentro do Bitcoin. Meu palpite é que o Satoshi quis ter consistência ao procurar blocos e transações: como os hashes de bloco já eram invertidos, ele fez o mesmo para as transações.
Por um acidente histórico, os hashes de tx e de bloco que o Bitcoin Core usa têm os bytes invertidos. Não tenho total certeza do porquê. Pode ser algo como usar o bignum do openssl para armazenar hashes, e então imprimi-los como número.
Por que não mudar isso?
Seria muito mais simples, da perspectiva de um desenvolvedor, não ter que lidar com a inversão de bytes o tempo todo.
Porém, essa configuração foi adotada por tantos exploradores e carteiras por tanto tempo que está enraizada demais para ser mudada. É mais fácil novos desenvolvedores pegarem o jeito dessa peculiaridade do que todo mundo inverter a forma como procura transações e blocos.
Minha implicância, relacionada à endianness, é tratar hashes como números e invertê-los. Um hash é uma sequência de bytes com uma ordem bem definida, e não faz mais sentido invertê-lo do que inverter um texto.
Como inverter a ordem dos bytes?
Aqui estão alguns exemplos de código:
# string hexadecimal
hash = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000"
# inverte a ordem dos bytes
reversed = hash.scan(/../).reverse.join
puts reversed #=> 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f // string hexadecimal
$hex_string = "6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000";
// inverte a ordem dos bytes
$reversed = implode('', array_reverse(str_split($hex_string, 2)));
echo $reversed; //=> 000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f Se você está trabalhando com strings, lembre que um byte são 2 caracteres hexadecimais (ex.: 6f). Então não cometa o erro de simplesmente inverter a string inteira; você precisa inverter a ordem dos bytes.
Resumo
Usamos duas ordens de bytes diferentes para hashes de transação e de bloco no Bitcoin:
- Ordem Natural. É o que você obtém da função de hash ao fazer o hash dos dados de transação e dos cabeçalhos de bloco. É usada internamente, ao trabalhar com dados brutos, para se referir a transações e blocos anteriores.
- Ordem Inversa. São os mesmos hashes, mas com a ordem dos bytes invertida. Usamos essa ordem ao procurar transações e blocos em exploradores.
Então, ao trabalhar com dados do Bitcoin, você usa os bytes como eles saem da função de hash. Mas, ao exibir hashes de bloco e de transação como strings hexadecimais, você precisa inverter a ordem dos bytes.
Todos nós já nos confundimos com isso em algum momento, mas faz parte da diversão de trabalhar com dados brutos do Bitcoin.