Bytes

Unidades de armazenamento para dados

Diagrama mostrando 8 bits individuais formando um único byte hexadecimal.

Este é um guia rápido para explicar o que são bytes e como são usados no bitcoin. Será útil se você está trabalhando com transações e outros dados brutos do bitcoin.

Você vai fazer alguma programação de baixo nível ao trabalhar com dados do bitcoin, então é bom saber o que está olhando.

Introdução

Ao usar bitcoin no dia a dia, você verá frequentemente bytes brutos de dados:

Ao trabalhar com bitcoin, você logo descobre que todos os dados centrais também são feitos de bytes brutos:

O Bitcoin é um programa de computador, e computadores leem e se comunicam usando bytes. Então, se você planeja trabalhar com bitcoin no nível técnico, é útil saber o que é um byte, como ele é exibido e os tipos de dados que ele armazena.

Básico

Antes de chegar aos bytes, precisamos saber o que é um "bit".

Bit

Um bit é a menor unidade de dados que um computador pode guardar. Pode ser um 1 ou um 0 (ou seja, um único transistor "ligado" ou "desligado"):

Bit

Clique no bit para alterná-lo entre 0 e 1.

desligado (0)

Esses bits são os blocos de construção para armazenar dados dentro dos computadores.

O Ben Eater tem ótimos vídeos sobre como computadores funcionam.

Se você travar ao programar, lembre que, no fim das contas, é tudo só um monte de uns e zeros.

Byte

Um byte é só um grupo de 8 bits individuais:

Byte

Clique nos bits para alterná-los e veja as diferentes interpretações de um byte.

00000000

Um byte é uma unidade conveniente de armazenamento de dados em um computador. Assim como às vezes é mais prático medir peso em quilogramas em vez de gramas, às vezes é mais prático medir dados em bytes em vez de bits.

Bytes são, na verdade, a medida padrão para dados, e a maioria dos dados com que você vai trabalhar no bitcoin é medida em bytes.

Um byte são 8 bits individuais. Então uma chave privada de 32 bytes tem 256 bits (32 × 8 = 256).

Representando Bytes

Podemos representar um byte de duas formas:

Isso funciona porque, se você dividir um byte em duas metades de 4 bits, cada metade consegue lidar com 16 combinações diferentes de 1s e 0s. Então, se você der a cada metade o seu próprio caractere hexadecimal, pode representar um byte usando apenas dois caracteres em vez de oito.

Essa representação mais curta é chamada de byte hexadecimal.

Byte → Hexadecimal

Cada metade de 4 bits (nibble) de um byte corresponde a um caractere hexadecimal.

Frequentemente exibimos bytes em formato hexadecimal para economizar espaço, o que é muito mais conveniente ao representar vários bytes de dados:

Bytes

Digite uma string hexadecimal e veja-a dividida em bytes individuais.

Bytes

Então, da próxima vez que você ver uma chave privada assim:

61dc9ff8b15450212970c6fa997338bb205dd48ffaca3a056b09a3b44a244d76

Você está, na verdade, olhando para 32 bytes individuais:

61 dc 9f f8 b1 54 50 21 29 70 c6 fa 99 73 38 bb 20 5d d4 8f fa ca 3a 05 6b 09 a3 b4 4a 24 4d 76

Quando comecei a trabalhar com Bitcoin, cometi o erro de interpretar coisas como chaves privadas como strings de letras e números individuais. Mas, na realidade, é uma sequência de bytes, em que cada dois caracteres são um byte.

Não importa se os caracteres hexadecimais são maiúsculos ou minúsculos. Por exemplo, 6b é o mesmo que 6B.

Armazenando Dados em Bytes

Então, como podemos armazenar coisas úteis como números, texto e outras coisas dentro desses bytes?

Honestamente, depende só de como você interpreta os bits.

Um byte pode conter diferentes tipos de dados dependendo de como você o interpreta. Por exemplo, o byte 01100111 poderia representar a letra "g" ou o número 103. Tudo depende de que tipo de dado você está trabalhando.

Números

Você pode usar bytes para armazenar números inteiros:

Byte → Inteiro

Converta entre um byte e o número inteiro que ele representa.

Se queremos armazenar um número em um computador, nós o armazenamos usando sua representação binária dentro dos bits de um byte. E, se queremos armazenar números maiores, simplesmente usamos mais bytes.

Exemplos

Números são muito comuns dentro dos dados do bitcoin: chave privada (número de 32 bytes), valor de saída (8 bytes), VOUT (4 bytes), nonce (4 bytes), alvo (32 bytes), locktime (4 bytes) e tempo do bloco (4 bytes, em Unix Time).

Texto

Você também pode usar bytes para armazenar texto, atribuindo a cada caractere o seu próprio byte.

Não há um padrão oficial para codificar texto dentro dos dados do bitcoin, mas o mapeamento de caracteres-para-bytes mais usado é o ASCII:

Byte → ASCII

Converta entre um byte e um caractere ASCII.

Como você pode ver, cada byte representa um caractere diferente. Você só precisa da tabela ASCII à mão para ver qual byte corresponde a qual caractere.

Exemplos

Texto não é frequentemente armazenado nos dados do Bitcoin, mas aparece em alguns lugares: OP_RETURN (dados arbitrários em uma saída), o scriptsig da transação coinbase (texto do minerador), o tipo/"comando" no cabeçalho de cada mensagem (12 bytes ASCII) e o campo User-Agent na mensagem "version".

Configurações (Bit Field)

Às vezes você pode usar os bits dentro de bytes de dados para representar várias configurações de ligado/desligado.

Isso é conhecido como bit field (campo de bits), e é uma forma eficiente de armazenar várias configurações no menor espaço possível.

Bit Field

Cada bit de um campo de 32 bits pode ser uma configuração de ligado/desligado. Clique para alterná-los.

Exemplos

Bit fields são usados em alguns lugares no bitcoin: o campo Services na mensagem "version" (8 bytes/64 bits indicando os serviços de um nó), o campo version do cabeçalho de bloco (4 bytes/32 bits para sinalizar prontidão para atualizações) e o campo sequence de uma transação (interpretado como bit field ao definir o locktime relativo).

Apenas Bytes

Às vezes os bytes não precisam representar nada em particular. Eles podem ser úteis simplesmente como um pedaço único de dados.

Exemplos

A saída de uma função de hash é só uma série única de bytes, e isso os torna úteis como "impressões digitais" de dados: TXID (identificador único de transação), hash de bloco (identificador único de bloco) e hash de chave pública. E às vezes usamos apenas um conjunto de bytes de aparência aleatória, sem significado, como os magic bytes (f9beb4d9) usados como marcador entre mensagens.

Então você não precisa interpretar bytes como nada em especial. Ter um conjunto único de bytes para usar como "impressão digital" dos dados já é útil por si só.

Um hash de bloco na verdade é interpretado tanto como um identificador único quanto como um número. Geralmente é só um identificador único para o bloco, mas também é interpretado como um número durante o processo de mineração para verificar se o hash está abaixo do valor de alvo atual.

Dados Personalizados

No fim, você pode usar bytes para representar o que quiser. Não precisa ser só números e texto (embora sejam os mais comuns). Tudo depende de como você escolhe interpretar as combinações de 1s e 0s dentro desses bytes.

Exemplos

Estas são algumas codificações exclusivas do bitcoin: Script (um byte em um scriptpubkey/scriptsig pode se referir a uma operação "OP_CODE"), Compact Size e o campo bits do cabeçalho de bloco (armazena o alvo atual de forma compacta).

Trabalhando com Bytes

Conseguir trabalhar com bytes brutos no bitcoin é especialmente importante quando se trata de rede ou quando você está fazendo o hash de dados.

A boa notícia é que você pode trabalhar com bytes brutos em qualquer linguagem de programação decente. Isso varia entre linguagens, mas normalmente envolve escrever o valor de cada byte individual em algum tipo de string ou array especial, assim:

"\xF9\xBE\xB4\xD9"
{0xF9, 0xBE, 0xB4, 0xD9}

Mas esse tipo de formato não é fácil de passar adiante, então você frequentemente vai converter esses bytes em strings hexadecimais para fins de exibição:

"f9beb4d9"

É assim que eu normalmente exibo bytes neste site, e como você normalmente verá bytes sendo exibidos em exploradores de blockchain.

Às vezes você também vai querer obter o valor inteiro que esses bytes representam:

4190024921

Então, ao trabalhar com dados do Bitcoin, você quer conseguir converter de um lado para o outro entre bytes de verdade, strings hexadecimais e inteiros na sua linguagem de programação preferida.

Aqui estão alguns exemplos rápidos de como fazer isso em algumas linguagens de programação comuns:

Ruby

# Trabalhe com bytes diretamente usando caracteres hexadecimais para representar cada byte.
bytes = "\xF9\xBE\xB4\xD9" #=> (jargão - tenta exibir a codificação de caracteres para cada valor de byte)

# Bytes -> String Hexadecimal
hex_string = bytes.unpack("H*")[0] #=> "f9beb4d9"

# String Hexadecimal -> Bytes
bytes = [hex_string].pack("H*") #=> (jargão - tenta exibir uma codificação de caracteres para cada valor de byte)

# String Hexadecimal -> Inteiro
integer = hex_string.to_i(16) #=> 4190024921

Ruby pode ser um pouco estranho ao exibir bytes. Se você tentar imprimir bytes diretamente, o Ruby tentará exibi-los usando a codificação de caracteres de cada byte (em vez de sua representação hexadecimal), então você precisa converter para hexadecimal para exibi-los ao depurar.

As funções "pack" e "unpack" são as mais úteis quando se trata de trabalhar com bytes brutos em linguagens como Ruby e Python, então vale a pena conhecê-las.

Python

# NOTA: Python 3.5+

# Cria bytes
raw_bytes = b'\xf9\xbe\xb4\xd9'

# Converte bytes para string hex
hex_string = b'\xf9\xbe\xb4\xd9'.hex()
print(hex_string) #=> f9beb4d9

# Converte string hex para bytes
raw_bytes = bytes.fromhex('f9beb4d9')
print(raw_bytes) #=> b'\xf9\xbe\xb4\xd9'

# Converte bytes para inteiro
integer = int.from_bytes(b'\xf9\xbe\xb4\xd9', byteorder="big") # segundo argumento é a endianness
print(integer) #=> 4190024921

Veja little-endian para detalhes sobre endianness no bitcoin.

Go

package main

import "fmt"
import "encoding/hex"
import "math/big"

func main() {

    // Cria um array de bytes
    bytes := []byte{0xF9, 0xBE, 0xB4, 0xD9}
    fmt.Println(bytes) //=> [249 190 180 217]

    // Converte array de bytes para string hexadecimal
    hex_string := hex.EncodeToString(bytes)
    fmt.Println(hex_string) //=> f9beb4d9

    // Converte string hexadecimal para array de bytes
    byte_array, _ := hex.DecodeString(hex_string)
    fmt.Println(byte_array) //=> [249 190 180 217]

    // Converte array de bytes para inteiro
    integer := new(big.Int).SetBytes(byte_array)
    fmt.Println(integer) //=> 4190024921

    // Converte inteiro para array de bytes
    byte_array_from_integer := integer.Bytes()
    fmt.Println(byte_array_from_integer) //=> [249 190 180 217]

}

Resumo

Um byte é apenas um grupo de 8 bits:

byte (binário):
┌─┬─┬─┬─┬─┬─┬─┬─┐
│0│1│1│0│1│0│1│1│
└─┴─┴─┴─┴─┴─┴─┴─┘

Para economizar espaço, geralmente representamos cada byte usando 2 caracteres hexadecimais em vez de usar 8 bits individuais:

byte (hexadecimal):
┌─┬─┐
│6│B│
└─┴─┘

Um bit é a menor unidade de dados (um 1 ou 0), e um byte é um grupo de 8 bits. Os bytes são a unidade básica dos dados brutos do Bitcoin.

Normalmente exibimos bytes em formato hexadecimal (2 caracteres por byte) para economizar espaço. E um byte pode armazenar diferentes tipos de dados — números, texto, configurações, impressões digitais — dependendo de como você interpreta os seus bits.

Quando você começa a trabalhar com dados brutos do Bitcoin, é tudo só uma questão de saber o que aqueles bytes representam.