Hex, Bytes e Buffers: Fundamentos de Blockchain

Caio Sá
6 min readJan 3, 2023

--

Motivação:

Existem diversas formas de representação de um número. Existem, por exemplo, as representações binárias (2), decimais (10), hexadecimais (16), etc. Cada uma delas é a representação do número na base especificada. Por exemplo: os números na base 2 — binários-são aqueles que são compostos por 0 ou 1. Exemplos: 01010101, 11110000, 1111, etc. Vamos agora a alguns conceitos importantes:

  • bit: um dígito binário: 0 ou 1.
  • byte: é uma unidade que consiste de 8-bits. Então todos os números da sequência 00000000, incluindo as alternações 01010101 até 11111111 são cada individualmente um byte.

No entanto, só as máquinas estão acostumadas a essa representação. Por exemplo: você sabe qual número 1010 representa? Ora, é muito fácil descobrir. O número 156, por exemplo, está na base 10 e pode ser representado por 6*10⁰+5*10¹+1*10² = 156. Pode fazer as contas e você verá.

Seguindo o mesmo raciocínio, o número 1010 representa na base decimal o número 0*2⁰+1*2¹+0*2²+1*2³ = 0+2+0+8 = 10! Pronto, entendemos como converter um número de base 2 para a base 10. O mesmo raciocínio será usado para converter um hexadecimal para um decimal.

Os hexadecimais não têm apenas (2) ou (10) dígitos possíveis, mas realmente (16): 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f. Sim, depois do 9 é utilizado letras como dígitos para a representação dos algarismos restantes.

Vamos converter?

Qual o número ff na base decimal?

Fácil! Como aprendemos mais acima: ff = f*16⁰+f*16¹ = 255! Lembrando que f representa o 15 na base decimal.

Lembrando também que em hexadecimais, usamos o “0x” antes do número para simbolizar que o número está na base (16). “0x” ficou tão famoso que virou até nome de empresa.

Lei Geral:

Existe uma lei geral da conversão entre bytes para hexadecimais:

  • 1 byte em decimal = 2 caracteres em hexadecimal

Isso se dá porque o maior byte possível é 1111111 = 255 em decimal = “0xff” em hexadecimal. Logo, o maior byte possível em decimal, tirando o “0x”, tem apenas dois caracteres em hexadecimais.

Analisando uma Transação da rede Ethereum

O Ethereum usa várias representações na base (16). Então é comum encontrar vários números começando com “0x” na rede, desde a identificação das transações, nomes dos métodos das funções no Etherscan e também o calldata das transações.

Identificação da Imagem

O exemplo acima foi retirado de uma transação no Etherscan. O artigo do DeGatchi sobre o assunto é muito útil para o entendimento de como entender o que o calldata de uma transação siginifica. Farei aqui, em português, uma breve explicação do que é explicado no artigo.

A primeira coisa que já chama a atenção é a representação da função withdrawUSDC(uint 256 _amount) como “0xdb81f99b”. De onde vem isso?

Usando a biblioteca web3 no ambiente Node.js, conseguimos perceber que ao pegar os 8 primeiros caracteres do sha3 de withdrawUSDC(uint 256), obtemos exatamente o MethodID como na figura da transação. É importante notar que os 8 primeiros caracteres, usando a regra geral, correspondem a 4 bytes. Usando o mesmo raciocínio, um endereço público da rede ethereum possui 20-bytes, ou, _ caracteres. Sim _ = 42 caracteres, pois há 20x2=40 caracteres mais o “0x”.

Você já deve ter então se perguntado que os endereços públicos representam números gigantescos. De fato, se formos fazer a conversão de um hex de 42 caracteres para um decimal, será algo gigantesco.

Tão grande que dá um erro já que o objeto Number pode apenas armazenar no máximo 53 bits, que são cerca de 6 ou 7 dígitos, aproximadamente, como mostra a imagem abaixo:

1 byte = 8 bits

Para termos uma ideia, o MethodID acima que usamos corresponde ao número 3682728347 na base (10).

3682728347

Mesmo não conseguindo converter de primeira o número a que corresponde o meu endereço público, pelos logs do seguinte erro, consegui entender que é algo de magnitude de 1⁰⁴⁸. Isso é muito grande. Imagina se o Ethereum usasse a base (10). Seria muito louco!

Número para o qual corresponde meu endereço público

Na seguinte transação:

Percebemos que há dois parâmetros: address to e uint256 value. Os valores de cada parâmetro é mostrado dentro de [0] ou [1]. A princípio, o [0] representa address to e o [1] representa o uint256 value. Mas aí você se pergunta: como a pessoa passou o valor de:00000000000000000000000000000000000000000000000000000001a13b8600”

como value? Ela teve a paciência de digitar todos esses zeros? Bem pensado, mas detalhes como esse são automaticamente resolvidos pela EVM.

Vamos ver como funciona?

Todos os dados passados no calldata das transações devem estar no formato de 32 bytes, que são 64 caracteres em hexadecimal!

Removendo dois zeros e adicionando o “0x” no início, obtemos que o número corresponde a:

Logo, o value colocado pelo usuário que chamou essa função foi: “7000000000”. Isso claro não é 7000000000 ETH, mas sim 7000000000 WEI.

in ETH

O valor em ETH corresponde a “0.000000007” ETH!

Buffers

Continuando nossa conversa sobre bytes, chegamos até Buffers.

Um buffer é um espaço em memória (geralmente RAM) que armazena dados binários. Em Nodejs, podemos acessar estes espaços de memória com a classe Buffer. Buffers armazenam um sequência de inteiros, sendo similares aos arrays em Javascript. Diferente de arrays, não há como mudar o tamanho de um buffer uma vez já criado.

Vamos ver em exemplos:

Criar um buffer com 1024 bytes do número 1.

Como dito acima, buffers contém dados binários. Então ao criarmos um buffer de 1024 bytes, ou 1kB, temos que todos os inteiros são salvos na base (2). 01 na base (10) é 0*²¹ + 1*²⁰ = 1, exatamente o que dizemos o comando alloc para fazer.

O interessante é que ao ver minha chave pública na função Buffer.from, vejo o seguinte:

Buffer Byte-array

Esse é um byte-array. Como o nome já disse, é um array de bytes. Mas ao que corresponde cada um desses números?

Ao salvar esse byte-array numa variável e checar se o primeiro valor do byte-array é um hexadecimal, a resposta é “true”, ou seja, verdadeiro!

Ao converter para string o Buffer que temos, voltamos a ter minha chave pública!

É isso, pessoal! Espero que tenham aprendido algo. O conceito mais importante do artigo era realmente destacar que:

  • 1 byte em decimal = 2 caracteres em hexadecimal

--

--

Caio Sá

Current Brazilian student of Machine Learning & Blockchain that loves math since High School competitions.