blk.dat

Os arquivos que armazenam a blockchain bruta no disco

Os arquivos blk.dat no diretório ~/.bitcoin/blocks/ contêm os dados brutos dos blocos recebidos pelo seu nó Bitcoin Core.

Esses arquivos blk.dat basicamente armazenam a blockchain inteira.

Localização

Onde a blockchain é armazenada no seu computador?

A localização dos arquivos brutos da blockchain no seu disco depende do sistema operacional que você usa. Estes são os locais padrão:

Você pode mudar a localização do diretório de dados dos blocos definindo a opção datadir=<dir> no arquivo de configuração bitcoin.conf.

Nomes dos Arquivos

Como os arquivos da blockchain são organizados?

Todo bloco que seu nó recebe é anexado a um arquivo blk.dat. Mas, em vez de toda a blockchain ficar armazenada em um único arquivo gigante, ela é dividida em vários arquivos blk*.dat.

Seu nó primeiro adiciona blocos ao blk00000.dat; quando ele enche, passa para o blk00001.dat, depois o blk00002.dat... e assim por diante. Se você está no Linux, pode navegar até o diretório de dados e listar todos os arquivos brutos de blocos com:

$ cd ~/.bitcoin/blocks/
$ ls blk*

blk00000.dat
blk00001.dat
blk00002.dat
blk00003.dat
blk00004.dat
blk00005.dat
blk00006.dat
...

O tamanho máximo de um arquivo blk.dat é 128 MiB (134.217.728 bytes). Esse limite é definido por MAX_BLOCKFILE_SIZE.

Exemplo

Como é um bloco bruto?

Os dados nos arquivos blk.dat são armazenados em binário, que é basicamente um monte de 1s e 0s, não texto legível por humanos.

Mesmo assim, podemos olhar o bloco gênese lendo os primeiros 293 bytes do blk00000.dat. Separei os campos individuais para você vê-los mais claramente:

f9beb4d9 <- magic bytes
1d010000 <- tamanho
01000000 <- versão
0000000000000000000000000000000000000000000000000000000000000000 <- bloco anterior
3ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a <- raiz de merkle
29ab5f49 <- tempo
ffff001d <- bits
1dac2b7c <- nonce
01 <- contagem de transações
01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000 <- transações

Veja o comando od abaixo para exibir os bytes hexadecimais de um arquivo binário.

Estrutura

Qual é a estrutura de um bloco bruto?

Diagrama mostrando a estrutura dos dados brutos do bloco dentro dos arquivos blk.dat.

Os dados acima podem ser divididos em cinco partes:

  1. Os magic bytes (4 bytes) são um delimitador de mensagem que indica o início de um bloco.
  2. O tamanho (4 bytes) indica o tamanho do bloco seguinte em bytes.
  3. O cabeçalho do bloco (80 bytes) é um resumo dos dados do bloco.
  4. A contagem de tx (compact size) indica quantas transações há no bloco.
  5. Os dados de transação (variável) são todas as transações do bloco concatenadas uma após a outra.

O campo tamanho é o que me permitiu descobrir que eu precisava ler 293 bytes para obter o bloco inteiro no exemplo acima. O tamanho do bloco é indicado como 1d010000, então, para obtê-lo em formato humano:

  1. Converta 1d010000 de little-endian para big-endian, obtendo 0000011d
  2. Converta 0000011d de hexadecimal para decimal, obtendo 285
Ícone Ferramenta Little Endian

Little Endian

Converta um número entre as ordens de bytes big-endian e little-endian.

0d
0x
0 bytes
0x
0 bytes
Tamanho do Campo
Ícone Ferramenta Conversor de Números

Conversor de Números

Converta um número entre bases diferentes (binário, decimal, hexadecimal).

0b
0 dígitos
0d
0 dígitos
0x
0 dígitos

Então o bloco em si tem apenas 285 bytes. Mas há ainda 8 bytes extras no início, para os magic bytes + tamanho, então eu precisei ler 293 bytes a partir do início do arquivo bruto da blockchain para obter o bloco de dados completo.

Ferramentas Linux

Como ler os dados brutos da blockchain?

Como mencionado, os dados dentro de um arquivo blk.dat são binários, então você provavelmente não verá nada útil ao abri-lo em um editor de texto comum. Mas sem problema, porque dados binários podem ser facilmente exibidos como bytes hexadecimais, e há alguns comandos que ajudam:

1. xxd

Este é simples. Ele despeja o conteúdo de arquivos binários brutos em hexadecimal.

$ xxd -p -s 8 -l 285 blk00000.dat

010000000000000000000000000000000000000000000000000000000000
0000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a5132
3a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c01010000000100000000
00000000000000000000000000000000000000000000000000000000ffff
ffff4d04ffff001d0104455468652054696d65732030332f4a616e2f3230
3039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f
6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01
000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a6
7962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b
8d578a4c702b6bf11d5fac00000000

# -p      <- mostra os bytes em hexadecimal puro
# -s 8    <- pula para uma posição no arquivo (usando 8 para pular os campos magic bytes e tamanho do bloco)
# -l 285  <- número de bytes a ler (o bloco gênese são os próximos 285 bytes)

Se você está rodando o Bitcoin Core v28.0 ou mais novo, talvez precise desofuscar (XOR) os dados brutos do bloco primeiro para obter o mesmo resultado acima.

2. od

Este é outro simples. Ele despeja o conteúdo de arquivos no formato de sua escolha.

$ od -x --endian=big -N 293 -An blk00000.dat

 f9be b4d9 1d01 0000 0100 0000 0000 0000
 0000 0000 0000 0000 0000 0000 0000 0000
 0000 0000 0000 0000 0000 0000 3ba3 edfd
 7a7b 12b2 7ac7 2c3e 6776 8f61 7fc8 1bc3
 888a 5132 3a9f b8aa 4b1e 5e4a 29ab 5f49
 ffff 001d 1dac 2b7c 0101 0000 0001 0000
 0000 0000 0000 0000 0000 0000 0000 0000
 0000 0000 0000 0000 0000 0000 0000 ffff
 ffff 4d04 ffff 001d 0104 4554 6865 2054
 696d 6573 2030 332f 4a61 6e2f 3230 3039
 2043 6861 6e63 656c 6c6f 7220 6f6e 2062
 7269 6e6b 206f 6620 7365 636f 6e64 2062
 6169 6c6f 7574 2066 6f72 2062 616e 6b73
 ffff ffff 0100 f205 2a01 0000 0043 4104
 678a fdb0 fe55 4827 1967 f1a6 7130 b710
 5cd6 a828 e039 09a6 7962 e0ea 1f61 deb6
 49f6 bc3f 4cef 38c4 f355 04e5 1ec1 12de
 5c38 4df7 ba0b 8d57 8a4c 702b 6bf1 1d5f
 ac00 0000 0000

# -x           <- mostra em hexadecimal
# --endian=big <- exibe os bytes em big endian
# -N 293       <- número de bytes a ler
# -An          <- não mostra o offset do arquivo

"od" é a abreviação de octal dump (despejo octal), mas você pode despejar dados em outros formatos além do octal.

3. hexdump

Este é semelhante ao xxd e ao od, mas também dá a você a opção de exibir texto ASCII a partir dos dados (o que também é útil para olhar mensagens contidas dentro dos dados de transação).

$ hexdump -C -s 8 -n 285 blk00000.dat

00000008  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000018  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000028  00 00 00 00 3b a3 ed fd  7a 7b 12 b2 7a c7 2c 3e  |....;...z{..z.,>|
00000038  67 76 8f 61 7f c8 1b c3  88 8a 51 32 3a 9f b8 aa  |gv.a......Q2:...|
00000048  4b 1e 5e 4a 29 ab 5f 49  ff ff 00 1d 1d ac 2b 7c  |K.^J}._I......+||
00000058  01 01 00 00 00 01 00 00  00 00 00 00 00 00 00 00  |................|
00000068  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000078  00 00 00 00 00 00 ff ff  ff ff 4d 04 ff ff 00 1d  |..........M.....|
00000088  01 04 45 54 68 65 20 54  69 6d 65 73 20 30 33 2f  |..EThe Times 03/|
00000098  4a 61 6e 2f 32 30 30 39  20 43 68 61 6e 63 65 6c  |Jan/2009 Chancel|
000000a8  6c 6f 72 20 6f 6e 20 62  72 69 6e 6b 20 6f 66 20  |lor on brink of |
000000b8  73 65 63 6f 6e 64 20 62  61 69 6c 6f 75 74 20 66  |second bailout f|
000000c8  6f 72 20 62 61 6e 6b 73  ff ff ff ff 01 00 f2 05  |or banks........|
000000d8  2a 01 00 00 00 43 41 04  67 8a fd b0 fe 55 48 27  |*....CA.g....UH'|
000000e8  19 67 f1 a6 71 30 b7 10  5c d6 a8 28 e0 39 09 a6  |.g..q0..\..(.9..|
000000f8  79 62 e0 ea 1f 61 de b6  49 f6 bc 3f 4c ef 38 c4  |yb...a..I..?L.8.|
00000108  f3 55 04 e5 1e c1 12 de  5c 38 4d f7 ba 0b 8d 57  |.U......\8M....W|
00000118  8a 4c 70 2b 6b f1 1d 5f  ac 00 00 00 00           |.Lp+k.._.....|)
0000125

# -C <- exibe os dados na mesma ordem de bytes usada no bitcoin, e também o texto ascii
# -s <- ponto de início (offset em bytes)
# -n <- número de bytes a ler

Essa é uma forma popular de exibir o bloco gênese, e você vai encontrá-la espalhada pela internet em vários lugares.

De qualquer forma, você pode encadear alguns comandos para obter apenas os bytes hexadecimais brutos, sem formatação, se preferir:

$ hexdump -C -s 8 -n 285 blk00000.dat | cut -c 11-58 | tr '\n' ' ' | tr -d ' '

0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000%

# cut -c 11-58 <- recorta o que está fora das colunas 11 a 58 (em cada linha)
# tr '\n' ' ' <- traduz quebras de linha em espaços
# tr -d ' ' <- apaga todos os espaços

Mas, se você vai ter o trabalho de fazer isso, é melhor extrair os dados brutos do bloco diretamente do Bitcoin Core usando:

$ bitcoin-cli getblock <hash> 0

4. bitcoin-iterate

O bitcoin-iterate é uma excelente ferramenta para extrair dados dos arquivos brutos da blockchain. E é surpreendentemente rápido. Aqui estão alguns exemplos simples:

# Uso
bitcoin-iterate -h

# retorna os cabeçalhos dos 100 primeiros blocos
bitcoin-iterate -q --block='%bH' --end=100 > headers.txt

# retorna todas as transações brutas do bloco 123.456
bitcoin-iterate -q --tx='%tX' --start=123456 --end=123456 > transactions.txt

# retorna todos os scriptPubKeys da blockchain junto com o txid da transação em que estavam
bitcoin-iterate -q --output='%th %os' > scriptpubkeys.txt

Eu o uso o tempo todo para procurar blocos e transações interessantes na blockchain.

XOR

Desde a v28.0, os dados brutos dos blocos nos arquivos blkXXXXX.dat são ofuscados por padrão. É fácil o suficiente de desofuscar, mas isso significa que os dados brutos não ficam mais armazenados em "texto plano" como antes.

ofuscar — tornar algo menos claro e mais difícil de entender, especialmente de forma intencional
Cambridge Dictionary

A razão para isso é que você não tem controle sobre o que outras pessoas podem decidir armazenar dentro da blockchain; então, para evitar que softwares antivírus detectem problemas com os dados brutos do bloco, eles são levemente "embaralhados" ao serem armazenados no seu computador. Mas, como eu disse, é fácil desembaralhá-los de volta à forma natural.

Então, se você quer ler os dados brutos do bloco do disco, precisa aprender a desofuscá-los primeiro.

  • Você pode desligar a ofuscação definindo blocksxor=0 no seu arquivo bitcoin.conf. Porém, isso só funciona se você estiver começando com um download novo da blockchain.
  • Se você já rodava um nó bitcoin antes de atualizar para a v28.0, seus arquivos de dados brutos de blocos permanecerão em texto plano. Então os dados de blocos existentes e novos não serão ofuscados daqui para frente.

Exemplo

É assim que o bloco gênese se parece em sua forma natural:

$ hexdump -C -s 8 -n 285 blk00000.dat

00000008  01 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000018  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000028  00 00 00 00 3b a3 ed fd  7a 7b 12 b2 7a c7 2c 3e  |....;...z{..z.,>|
00000038  67 76 8f 61 7f c8 1b c3  88 8a 51 32 3a 9f b8 aa  |gv.a......Q2:...|
00000048  4b 1e 5e 4a 29 ab 5f 49  ff ff 00 1d 1d ac 2b 7c  |K.^J}._I......+||
00000058  01 01 00 00 00 01 00 00  00 00 00 00 00 00 00 00  |................|
00000068  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
00000078  00 00 00 00 00 00 ff ff  ff ff 4d 04 ff ff 00 1d  |..........M.....|
00000088  01 04 45 54 68 65 20 54  69 6d 65 73 20 30 33 2f  |..EThe Times 03/|
00000098  4a 61 6e 2f 32 30 30 39  20 43 68 61 6e 63 65 6c  |Jan/2009 Chancel|
000000a8  6c 6f 72 20 6f 6e 20 62  72 69 6e 6b 20 6f 66 20  |lor on brink of |
000000b8  73 65 63 6f 6e 64 20 62  61 69 6c 6f 75 74 20 66  |second bailout f|
000000c8  6f 72 20 62 61 6e 6b 73  ff ff ff ff 01 00 f2 05  |or banks........|
000000d8  2a 01 00 00 00 43 41 04  67 8a fd b0 fe 55 48 27  |*....CA.g....UH'|
000000e8  19 67 f1 a6 71 30 b7 10  5c d6 a8 28 e0 39 09 a6  |.g..q0..\..(.9..|
000000f8  79 62 e0 ea 1f 61 de b6  49 f6 bc 3f 4c ef 38 c4  |yb...a..I..?L.8.|
00000108  f3 55 04 e5 1e c1 12 de  5c 38 4d f7 ba 0b 8d 57  |.U......\8M....W|
00000118  8a 4c 70 2b 6b f1 1d 5f  ac 00 00 00 00           |.Lp+k.._.....|)
0000125

Porém, a minha xor_key é 17 7a 35 a3 e4 32 54 ff, então é assim que o meu bloco gênese se parece no disco:

$ hexdump -C -s 8 -n 285 blk00000.dat

00000008  16 7a 35 a3 e4 32 54 ff  17 7a 35 a3 e4 32 54 ff  |.z5..2T..z5..2T.|
00000018  17 7a 35 a3 e4 32 54 ff  17 7a 35 a3 e4 32 54 ff  |.z5..2T..z5..2T.|
00000028  17 7a 35 a3 df 91 b9 02  6d 01 27 11 9e f5 78 c1  |.z5.....m.'...x.|
00000038  70 0c ba c2 9b fa 4f 3c  9f f0 64 91 de ad ec 55  |p.....O<..d....U|
00000048  5c 64 6b e9 cd 99 0b b6  e8 85 35 be f9 9e 7f 83  |\dk.......5.....|
00000058  16 7b 35 a3 e4 33 54 ff  17 7a 35 a3 e4 32 54 ff  |.{5..3T..z5..2T.|
00000068  17 7a 35 a3 e4 32 54 ff  17 7a 35 a3 e4 32 54 ff  |.z5..2T..z5..2T.|
00000078  17 7a 35 a3 e4 32 ab 00  e8 85 78 a7 1b cd 54 e2  |.z5..2....x...T.|
00000088  16 7e 70 f7 8c 57 74 ab  7e 17 50 d0 c4 02 67 d0  |.~p..Wt.~.P...g.|
00000098  5d 1b 5b 8c d6 02 64 c6  37 39 5d c2 8a 51 31 93  |].[...d.79]..Q1.|
000000a8  7b 15 47 83 8b 5c 74 9d  65 13 5b c8 c4 5d 32 df  |{.G..\t.e.[..]2.|
000000b8  64 1f 56 cc 8a 56 74 9d  76 13 59 cc 91 46 74 99  |d.V..Vt.v.Y..Ft.|
000000c8  78 08 15 c1 85 5c 3f 8c  e8 85 ca 5c e5 32 a6 fa  |x....\?....\.2..|
000000d8  3d 7b 35 a3 e4 71 15 fb  70 f0 c8 13 1a 67 1c d8  |={5..q..p....g..|
000000e8  0e 1d c4 05 95 02 e3 ef  4b ac 9d 8b 04 0b 5d 59  |........K.....]Y|
000000f8  6e 18 d5 49 fb 53 8a 49  5e 8c 89 9c a8 dd 6c 3b  |n..I.S.I^.....l;|
00000108  e4 2f 31 46 fa f3 46 21  4b 42 78 54 5e 39 d9 a8  |./1F..F!KBxT^9..|
00000118  9d 36 45 88 8f c3 49 a0  bb 7a 35 a3 e4           |.6E...I..z5..|
00000125

A xor_key é gerada aleatoriamente pelo seu nó, então o seu bloco gênese vai se parecer diferente no seu disco.

Desofuscar

Os dados brutos do bloco são ofuscados usando a xor_key armazenada no arquivo xor.dat dentro da sua pasta /blocks/.

Por exemplo:

$ xxd -p ~/.bitcoin/blocks/xor.dat

177a35a3e43254ff

Para desofuscar os dados brutos do bloco, você simplesmente usa essa xor_key e aplica XOR ao longo dos dados brutos, o que "inverte os bits" de volta à forma natural.

Essa xor_key tem 8 bytes de comprimento, então é preciso aplicar XOR repetidamente a cada 8 bytes dos dados brutos do bloco para desofuscá-los.

Diagrama mostrando como usar a xor_key para desofuscar os dados brutos de um bloco a partir de um arquivo blk.dat.

Aqui está um código simples para mostrar como funciona:

# obtém a xor key
file_xor = File.open("/home/username/.bitcoin/blocks/xor.dat", "r") # não esqueça de mudar o caminho
xor_key = file_xor.read # isto tem 8 bytes

# define a posição de onde você está lendo no arquivo de dados brutos do bloco
offset = 8 # pula os campos magic bytes (4 bytes) e tamanho do bloco (4 bytes)

# lê os dados brutos do bloco gênese a partir do arquivo blk.dat
file_blk = File.open("/home/username/.bitcoin/blocks/blk00000.dat", "r") # não esqueça de mudar o caminho
file_blk.seek(offset) # move para onde você quer começar a ler no arquivo
blk_data = file_blk.read(285) # os próximos 285 bytes são os dados de fato do bloco (eu já sei disso)

# converte a xor key e os dados brutos do bloco para arrays de bytes
xor_key_bytes = xor_key.bytes
blk_data_bytes = blk_data.bytes

# cria um array para armazenar os bytes resultantes do xor
result = []

# percorre cada byte dos dados brutos do bloco
blk_data_bytes.each_with_index do |byte, i|

  # há apenas 8 bytes na xor key, então usamos o operador módulo para circular e usar cada byte conforme avançamos
  xor_i = (offset + i) % 8 # o offset nos permite começar do byte correto na xor key

  # aplica XOR a cada byte dos dados brutos usando o próximo byte da xor key, e armazena cada byte no array de resultado
  result[i] = byte ^ xor_key_bytes[xor_i] # ^ é o operador XOR
end

# converte o resultado de array de bytes para string de bytes, depois para string hexadecimal (para exibição)
result_hex = result.pack("C*").unpack("H*")

# mostra o resultado
puts result_hex #=> 0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a29ab5f49ffff001d1dac2b7c0101000000010000000000000000000000000000000000000000000000000000000000000000ffffffff4d04ffff001d0104455468652054696d65732030332f4a616e2f32303039204368616e63656c6c6f72206f6e206272696e6b206f66207365636f6e64206261696c6f757420666f722062616e6b73ffffffff0100f2052a01000000434104678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5fac00000000

Como você pode ver, a parte importante é garantir o uso do byte correto da xor_key para cada byte dos dados brutos do bloco.

É um pouco chato ter que desofuscar os dados brutos do bloco depois da v28.0, especialmente se você já escreveu uma ferramenta que lê os arquivos blkXXXXX.dat. Mas é bem simples desembaralhar os dados brutos, então não deve exigir muito esforço atualizar o seu código e fazê-lo funcionar de novo.

Notas

Ordem dos Blocos

Se você está analisando os arquivos blk.dat com o seu próprio script, saiba que os blocos não estarão em ordem. Por exemplo, você pode encontrar blocos nesta ordem ao percorrer o arquivo:

A B C E F D G

Isso porque o seu nó bitcoin vai baixar blocos em paralelo, para conseguir baixar a blockchain o mais rápido possível. Então, em vez de esperar receber cada bloco em ordem, o seu nó baixa blocos mais adiante do atual conforme avança.

A distância máxima à frente de onde o seu nó busca (ou a "máxima desordem") é controlada por BLOCK_DOWNLOAD_WINDOW no código-fonte do Bitcoin.

Recursos