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:
- Linux:
~/.bitcoin/blocks/ - Mac:
~/Library/Application Support/Bitcoin/blocks/ - Windows:
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.
- ~/.bitcoin/blocks/
- blk00000.dat
- blk00001.dat
- blk00002.dat
- blk00003.dat
- blk00004.dat
- e assim por diante...
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?
Os dados acima podem ser divididos em cinco partes:
- Os magic bytes (4 bytes) são um delimitador de mensagem que indica o início de um bloco.
- O tamanho (4 bytes) indica o tamanho do bloco seguinte em bytes.
- O cabeçalho do bloco (80 bytes) é um resumo dos dados do bloco.
- A contagem de tx (compact size) indica quantas transações há no bloco.
- 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:
- Converta
1d010000de little-endian para big-endian, obtendo0000011d - Converta
0000011dde hexadecimal para decimal, obtendo285
Little Endian
Conversor de Números
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
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=0no seu arquivobitcoin.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.
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.