Bech32
Formato de endereço para scripts de travamento segwit
Bech32 ("besh trinta e dois") é um formato de endereço usado para representar scripts de travamento segwit como P2WPKH, P2WSH e P2TR.
Ele foi introduzido logo após a atualização Segregated Witness para fornecer um formato de endereço melhor para os novos scripts de travamento segwit. É uma melhoria em relação ao formato legado Base58.
Aqui estão alguns exemplos de como eles se parecem:
| Tipo | Endereço | Comprimento |
|---|---|---|
| P2WPKH | bc1qx5y8r50l39cap3r9cd65fz7xfvlkjrl258hs8m | 42 caracteres |
| P2WSH | bc1q37qzzxnhnwdsde7yjzq5hc0wktmnqejp3zt0pj9l0pgn9f2gpyls4hahsc | 62 caracteres |
| P2TR | bc1p4pzgu93t5nlw9mscn0v6s7spfya7qntp0afynva267lr0vp0sxyqqj24dc | 62 caracteres |
Você identifica um endereço Bech32 pelo prefixo bc1.
Nesta página vou mostrar como codificar e decodificar um endereço Bech32, e explicar por que eles são melhores que os endereços Base58.
Benefícios
Quais são as vantagens do Bech32?
O Bech32 é mais amigável e mais eficiente que o formato legado Base58 para endereços.
Em resumo, é uma evolução em comparação ao Base58.
Mais amigável
Os endereços Bech32 são mais amigáveis que os endereços Base58 por três motivos:
1. Insensível a maiúsculas/minúsculas
Os endereços Bech32 não usam uma combinação de letras maiúsculas e minúsculas, o que os torna mais fáceis de digitar e de ler em voz alta em comparação ao Base58.
Então, se você estiver passando o seu endereço para alguém por telefone, não precisa mais ficar alternando entre "x maiúsculo, y minúsculo...", e assim por diante.
Os endereços Bech32 são tipicamente todos em minúsculas, mas é perfeitamente válido usar tudo em maiúsculas (que é como eles ficam armazenados em um QR code).
Para evitar confusão, os endereços Bech32 não devem usar uma combinação de letras maiúsculas e minúsculas. Uma carteira deve considerar um endereço Bech32 com letras misturadas como inválido.
O fato de o Bech32 usar menos caracteres que o Base58 (ou seja, base 32 em vez de base 58) significa que os endereços são tipicamente mais longos, mas ter todos os caracteres em um único caso torna os endereços Bech32 mais fáceis de usar no geral.
2. Conjunto de caracteres amigável
Isto não é exatamente uma "evolução", já que o Base58 faz algo parecido, mas o Bech32 não usa caracteres parecidos entre si.
Para ser preciso, a parte de dados do endereço usa todos os caracteres alfanuméricos exceto "1", "b", "i" e "o":
Conjunto de Caracteres Base32 do Bitcoin
0 1 2 3 4 5 6 7 8 9
a b c d e f g h i j k l m n o p q r s t u v w x y z
- O número "1" foi removido porque se parece com um "l" minúsculo.
- A letra "b" foi removida porque um "B" maiúsculo se parece com o número 8 (obrigado bordalix).
- A letra "o" foi removida porque um "O" maiúsculo muitas vezes se parece com o número "0".
- A letra "i" foi removida porque um "I" maiúsculo pode se parecer com um "l" minúsculo (em algumas fontes).
Então, dos 36 caracteres alfanuméricos possíveis, 4 dos menos distintos foram removidos para deixar 32 caracteres no total.
Isto é um pouco diferente dos conjuntos de caracteres "base 32" já existentes. Por exemplo:
A parte legível por humanos não está restrita aos caracteres base 32 acima. Ela pode conter qualquer caractere US-ASCII (na faixa 33-126), e é por isso que você pode usar um "b" no início de um endereço.
3. Checksum melhor
Os endereços Bech32 usam um algoritmo de checksum aprimorado que permite detectar e corrigir erros no endereço.
Para resumir as diferenças:
- Checksum do Base58 — Permite identificar se o endereço foi digitado correta ou incorretamente.
- Checksum do Bech32 — Permite identificar se o endereço foi digitado correta ou incorretamente. Além disso, se o endereço foi digitado incorretamente, ele consegue localizar onde estão os erros e oferecer sugestões para corrigi-los.
Então, em resumo, o checksum do Bech32 é mais esperto.
Mais eficiente
Os endereços Bech32 são mais eficientes que os endereços Base58 por três motivos:
1. QR codes menores
O fato de os endereços Bech32 serem de caso único permite codificá-los em QR codes usando o modo alfanumérico.
Isso significa que você pode criar QR codes mais compactos, porque o Base58 exige ambas as letras maiúsculas e minúsculas (o que impede o uso do modo alfanumérico), enquanto o Bech32 não.
Por exemplo:
Endereço Base58
1MHKKX3cN2RnWrxnzg9kLfPzi6vCd1B2H7
Endereço Bech32
BC1QMEU8D90HWHFHM49GHGN5ZFGEXDPJSSPZGN28T4
Como você pode ver, embora o endereço Bech32 tenha mais caracteres, a capacidade de usar o modo alfanumérico faz com que o QR code use menos dados no total.
O modo alfanumérico dos QR codes só suporta letras maiúsculas. É por isso que, ao escanear um QR code contendo um endereço Bech32, ele normalmente aparece todo em maiúsculas. Isso é perfeitamente válido, pois os endereços Bech32 são insensíveis a maiúsculas/minúsculas.
2. Codificação mais rápida
É mais rápido calcular um checksum Bech32 do que um checksum Base58, o que torna mais rápido codificar endereços Bech32.
Eu sei que o algoritmo de checksum do Bech32 parece bem complicado, mas ele ainda é mais rápido que o SHA256 duplo necessário para criar o checksum do Base58.
3. Decodificação mais rápida
Os caracteres em um endereço Bech32 mapeiam para valores específicos, o que é mais rápido que a aritmética modular sobre números grandes necessária para decodificar um endereço Base58.
O Base58 não é absurdamente lento para codificar/decodificar em comparação ao Bech32, mas o Bech32 é mais eficiente mesmo assim.
Ferramenta
Codificar
Converter um ScriptPubKey em um endereço Bech32
Um ScriptPubKey de um script de travamento segwit (ex.: P2WPKH, P2WSH, P2TR) pode ser convertido em um endereço Bech32.
A maior parte do processo de codificação envolve converter os bytes do ScriptPubKey em binário (1s e 0s), dividir esses 1s e 0s em grupos de 5 bits e depois converter esses grupos de 5 bits em seus caracteres base32 correspondentes.
Essa é uma explicação simplificada do processo, mas é basicamente assim que funciona. A parte mais difícil é calcular o checksum.
Aqui está um guia passo a passo:
A codificação Bech32 foi projetada para funcionar apenas com scripts de travamento segwit (ou seja, P2WPKH, P2WSH, P2TR). Isso acontece porque eles seguem um padrão específico exigido pela codificação Bech32 (eles essencialmente exigem um número de versão e alguns bytes de dados).
1. Parte legível por humanos
Para começar, você precisa escolher qual será o prefixo do endereço final.
No Bitcoin, você tem 3 opções para esse prefixo:
- bc = mainnet
- tb = testnet
- bcrt = regtest
Isso é chamado de parte legível por humanos, e indica se o endereço deve ser usado na mainnet, testnet ou regtest.
Você precisa escolher a parte legível por humanos logo no início, pois ela será usada ao calcular o checksum.
2. ScriptPubKey
Em seguida, pegue o ScriptPubKey completo que você quer converter para Bech32.
Aqui está um exemplo de ScriptPubKey P2WPKH:
0014751e76e8199196d454941c45d1b3a323f1433bd6
Se você ainda não sabe, cada ScriptPubKey segwit segue uma estrutura parecida. Essa estrutura pode ser dividida em 3 partes:
1. Versão
O primeiro byte corresponde a um opcode OP_N.
| Byte | Opcode |
|---|---|
| 00 | OP_0 |
| 51 | OP_1 |
| 52 | OP_2 |
| 53 | OP_3 |
| 54 | OP_4 |
| 55 | OP_5 |
| 56 | OP_6 |
| 57 | OP_7 |
| 58 | OP_8 |
| 59 | OP_9 |
| 5a | OP_10 |
| 5b | OP_11 |
| 5c | OP_12 |
| 5d | OP_13 |
| 5e | OP_14 |
| 5f | OP_15 |
| 60 | OP_16 |
O valor inteiro que esse opcode representa indica a versão do script de travamento segwit.
No nosso exemplo, o byte 00 corresponde ao opcode OP_0, que indica um script de travamento segwit versão 0 (ou seja, P2WPKH ou P2WSH).
O byte de versão deve ser 00, ou de 51 a 60 (ou seja, OP_0 a OP_16).
2. Tamanho
O segundo byte indica o tamanho do programa de testemunha que vem a seguir.
Esse byte é 14 no nosso exemplo, o que indica que o programa de testemunha a seguir tem 20 bytes de comprimento.
O byte de tamanho não é incluído na codificação Bech32. Isso acontece porque você consegue descobrir o tamanho do programa de testemunha após decodificar um endereço Bech32, então não há necessidade de incluir o byte de tamanho explicitamente. Isso economiza um ou dois caracteres no endereço final.
3. Programa de testemunha
Os dados restantes no ScriptPubKey são o programa de testemunha.
Essa é a parte única do ScriptPubKey, e geralmente contém um de 3 tipos de dados:
- hash de chave pública de 20 bytes (P2WPKH)
- hash de script de 32 bytes (P2WSH)
- chave pública com tweak de 32 bytes (P2TR)
No nosso exemplo P2WPKH, o programa de testemunha é um hash de chave pública de 20 bytes:
751e76e8199196d454941c45d1b3a323f1433bd6
Esses dados têm a maior influência sobre como o nosso endereço Bech32 final vai ficar.
3. Versão (5 bits)
Em seguida, precisamos converter o número de versão do ScriptPubKey para um valor inteiro de 5 bits.
No nosso exemplo, o byte de versão é 00, que corresponde ao opcode OP_0. Portanto, o número de versão desse ScriptPubKey é 0. Isso pode então ser representado como um valor binário de 5 bits:
version = 00000
Use o número representado pelo opcode, não o valor do byte. Os opcodes OP_1 a OP_16 usam os bytes na faixa de 51 a 60. Então é importante converter o byte para o seu opcode OP_N correspondente para obter o número de versão correto, em vez de usar o valor do byte diretamente.
- O número de versão influencia o primeiro caractere após o prefixo do endereço final.
- Um script de travamento segwit versão 0 começa com bc1q (P2WPKH ou P2WSH)
- Um script de travamento segwit versão 1 começa com bc1p (P2TR)
- O número de versão deve estar entre 0 e 16, então ele nunca excederá o valor máximo de 5 bits (que é 31).
4. Programa de testemunha (grupos de 8 bits)
Em seguida, convertemos o programa de testemunha em grupos de 8 bits.
Este é o nosso programa de testemunha como um array de bytes:
witness program = 75 1e 76 e8 19 91 96 d4 54 94 1c 45 d1 b3 a3 23 f1 43 3b d6
Se convertermos cada byte em bits, obtemos:
witness program = 01110101 00011110 01110110 11101000 00011001 10010001 10010110 11010100 01010100 10010100 00011100 01000101 11010001 10110011 10100011 00100011 11110001 01000011 00111011 11010110
Há 8 bits em um byte.
5. Programa de testemunha (grupos de 5 bits)
Rearranje o programa de testemunha de grupos de 8 bits para grupos de 5 bits.
witness program = 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110
Como você pode ver, rearranjamos o programa de testemunha de 20 grupos de 8 bits para 32 grupos de 5 bits.
Preenchimento (padding). Se você não tiver bits suficientes nos grupos iniciais de 8 bits para converter em grupos completos de 5 bits, preencha o último grupo de 5 bits com zeros.
6. Checksum
O checksum é calculado usando a parte legível por humanos, a versão de 5 bits e os grupos de 5 bits do programa de testemunha.
Então estes são os dados que vamos usar como entrada do algoritmo de checksum:
hrp = 'bc' version = 00000 witness program = 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110
O checksum resultante para o nosso exemplo é:
checksum = 01100 00111 01001 10001 01011 10101
Esse processo é bem trabalhoso, então pulei ele por enquanto. Veja o algoritmo de checksum para os detalhes.
7. Combinar
Adicione o checksum que acabamos de calcular ao final da versão de 5 bits e dos grupos de 5 bits do programa de testemunha:
version + witness_program + checksum = 00000 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110 01100 00111 01001 10001 01011 10101
8. Base32
Converta os grupos de 5 bits combinados do passo anterior em inteiros, depois use esses inteiros para selecionar o caractere base32 correspondente de cada um:
Caracteres Base32 0 = q 1 = p 2 = z 3 = r 4 = y 5 = 9 6 = x 7 = 8 8 = g 9 = f 10 = 2 11 = t 12 = v 13 = d 14 = w 15 = 0 16 = s 17 = 3 18 = j 19 = n 20 = 5 21 = 4 22 = k 23 = h 24 = c 25 = e 26 = 6 27 = m 28 = u 29 = a 30 = 7 31 = l
Os caracteres base32 foram organizados nesta ordem específica para melhorar a capacidade de correção de erros do checksum.
Por exemplo:
version + witness_program + checksum (grupos de 5 bits) = 00000 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110 01100 00111 01001 10001 01011 10101 version + witness_program + checksum (inteiros) = 0 14 20 15 7 13 26 0 25 18 6 11 13 8 21 4 20 3 17 2 29 3 12 29 3 4 15 24 20 6 14 30 22 12 7 9 17 11 21 version + witness_program + checksum (base32) = q w 5 0 8 d 6 q e j x t d g 4 y 5 r 3 z a r v a r y 0 c 5 x w 7 k v 8 f 3 t 4
Essa string base32 forma a parte de dados do endereço Bech32 final.
9. Bech32
Por fim, adicione a parte legível por humanos e o separador ao início da string base32 para obter o endereço Bech32 final.
hrp = bc separator = 1 base32 = qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 bech32 = bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
O separador é sempre 1. Ele é usado para separar a parte legível por humanos da parte de dados base32, porque a parte legível por humanos pode variar em comprimento. Um "1" não pode aparecer na parte de dados do endereço (já que foi excluído do conjunto de caracteres base32), e é por isso que ele funciona de forma confiável como caractere separador.
Código
# --------
# configurações
# --------
# prefixo legível por humanos a ser usado no endereço Bech32 final
hrp = "bc" # bc = mainnet, tb = testnet
# separador entre a parte legível por humanos e a parte de dados
separator = "1" # isto é sempre 1
# conjunto de caracteres base32
characters = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" # todos os caracteres alfanuméricos minúsculos exceto "1", "b", "i", "o"
# exibe o algoritmo de checksum completo
display_checksum_algorithm = true # true ou false
puts "------------"
puts "scriptpubkey"
puts "------------"
puts
# scriptpubkey que queremos converter para Bech32
scriptpubkey = "0014751e76e8199196d454941c45d1b3a323f1433bd6" # scriptpubkey P2WPKH de exemplo
puts "scriptpubkey: #{scriptpubkey}"
puts
# divide o scriptpubkey em versão e programa de testemunha
version = scriptpubkey[0..1] # primeiro byte
witness_program = scriptpubkey[4..-1] # do byte 2 até o fim
puts "version: #{version}"
puts "program: #{witness_program}"
puts
# nota: o byte de tamanho entre a versão e o programa de testemunha não é usado na codificação Bech32
puts "-------"
puts "version"
puts "-------"
puts
# converte o byte de versão para o valor inteiro representado pelo seu opcode correspondente
#
# 0x00 = OP_0 = 0 (P2WPKH and P2WSH)
# 0x51 = OP_1 = 1 (P2TR)
# 0x52 = OP_2 = 2
# ...
# 0x60 = OP_16 = 16
#
# cuidado: OP_1 a OP_16 começam em 0x51, não em 0x01
if (version == "00")
version_opcode_int = 0
elsif (version.to_i(16) >= 0x51 && version.to_i(16) <= 0x60)
version_opcode_int = version.to_i(16) - 0x50
else
puts "Invalid version byte." # o byte de versão deve estar entre OP_0 e OP_16
exit
end
# converte a versão para um valor de 5 bits
version_5_bits = version_opcode_int & 0b11111 # a versão já deveria ter 5 bits (ou seja, 0 a 16), mas pega os últimos 5 bits por garantia
# exibe os detalhes da versão
puts "version (byte): #{version}"
puts "version (opcode): OP_#{version_opcode_int}"
puts "version (5-bits): #{version_5_bits.to_s(2).rjust(5, "0")}"
puts
puts "------------"
puts "8-bit groups"
puts "------------"
puts
# converte o programa de testemunha para um array de inteiros de 8 bits
version_8_bits = [version_opcode_int] # coloca o valor inteiro de 8 bits em um array
witness_program_8_bits = [witness_program].pack("H*").unpack("C*") # converte a string hex para binário e depois de volta para arrays de inteiros de 8 bits (1 byte)
# exibe o array de 8 bits do programa de testemunha como strings binárias
puts "program: #{witness_program_8_bits.map { |v| v.to_s(2).rjust(8, "0") }.join(' ')}"
puts
puts "------------"
puts "5-bit groups"
puts "------------"
puts
# rearranja o programa de testemunha de um array de inteiros de 8 bits para um array de inteiros de 5 bits
from = 8 # número de bits no inteiro de partida
to = 5 # número de bits no inteiro de destino
accumulator = 0
counter = 0
max_value = (1 << to) - 1 # valor máximo (5 bits = 0b100000 = 32)
max_accumulator = (1 << (from + to -1)) -1 # 8 bits + 5 bits - 1 bit = 12 bits = 4095 = 0b111111111111
witness_program_5_bits = [] # o array de retorno
# percorre cada inteiro de 8 bits no array do programa de testemunha
witness_program_8_bits.each do |int_8_bits|
# retorna nil se for negativo
if int_8_bits < 0
return nil
end
# retorna nil se o tamanho do inteiro for maior que o tamanho especificado do grupo de bits de partida
if (int_8_bits >> from) != 0
return nil
end
# adiciona os bits de partida ao acumulador
accumulator = accumulator << from # << = deslocamento de bits à esquerda
accumulator = accumulator | int_8_bits # | = OU bit a bit (nota: este comentário é necessário para o realce de sintaxe funcionar corretamente no site learnmeabitcoin.com — um pipe sozinho em uma linha quebra o realce por algum motivo)
accumulator = accumulator & max_accumulator # & = E bit a bit
# incrementa o contador
counter += from
# enquanto houver bits suficientes para produzir um novo grupo de 5 bits
while counter >= to
# decrementa o contador
counter -= to
# adiciona o valor de 5 bits ao resultado
witness_program_5_bits << ((accumulator >> counter) & max_value)
end
end
# adiciona preenchimento (padding)
if (counter > 0)
witness_program_5_bits << ((accumulator << (to - counter)) & max_value)
end
# exibe o resultado
puts "program: #{witness_program_5_bits.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
puts
puts "------------------"
puts "checksum algorithm"
puts "------------------"
puts
# exibe a parte legível por humanos
puts "hrp: #{hrp}"
# converte a parte legível por humanos para os valores de caractere UTF-8 correspondentes
hrp_values = hrp.split("").map { |c| c.ord }
puts "hrp_values: #{hrp_values.map { |v| v.to_s(2).rjust(8, "0") }.join(' ')}"
# expande a parte legível por humanos em grupos de 5 bits
hrp_expanded = []
# pega os primeiros 3 bits do valor inteiro de cada caractere
hrp_values.each do |v|
hrp_expanded << (v >> 5) # desloca 5 bits à direita para obter os primeiros 3 bits
end
# adiciona o separador
hrp_expanded << 0
# pega os últimos 5 bits do valor inteiro de cada caractere
hrp_values.each do |v|
hrp_expanded << (v & 0b11111) # usa uma máscara de bits para extrair os últimos 5 bits
end
# exibe o resultado
puts "hrp_expanded: #{hrp_expanded.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
puts
# combina a parte legível por humanos expandida com a versão e o programa de testemunha (todos em grupos de 5 bits)
combined = hrp_expanded + [version_5_bits] + witness_program_5_bits
# adiciona preenchimento (padding)
combined_with_padding = combined + [0, 0, 0, 0, 0, 0]
puts "hrp + version + program + padding: #{combined_with_padding.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
puts
# valores geradores do checksum
generator = [
0b111011011010100101011110110010, # 0x3b6a57b2
0b100110010100001000111001101101, # 0x26508e6d
0b011110101000010001100111111010, # 0x1ea119fa
0b111101010000100011001111011101, # 0x3d4233dd
0b101010000101000110001010110011, # 0x2a1462b3
]
# valor inicial do checksum (o resultado final do checksum terá 30 bits)
checksum = 1
# percorre cada grupo de 5 bits no array combinado
combined_with_padding.each do |v|
# exibe o valor atual do checksum
puts "checksum: #{checksum.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
# remove 25 bits da direita do valor atual do checksum e guarda o resultado
top = checksum >> 25
puts "top: #{top.to_s(2).rjust(5, "0")}" if display_checksum_algorithm
# extrai os 25 bits inferiores do valor atual do checksum usando uma máscara de bits
checksum = checksum & 0b1111111111111111111111111
puts "bottom: #{checksum.to_s(2).rjust(25, "0")}" if display_checksum_algorithm
# desloca o valor do checksum à esquerda (adiciona 5 bits zero ao final, criando espaço para o xor com o valor atual de 5 bits)
checksum = checksum << 5
puts "padded: #{checksum.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
# faz o xor do checksum atual com o próximo valor de 5 bits
checksum = checksum ^ v
puts "5-bit group: #{v.to_s(2).rjust(5, "0")}" if display_checksum_algorithm
puts "xor: #{checksum.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
# percorre os últimos 5 bits do valor "top"
5.times do |i|
# mostra o gerador atual
print "generator #{i}: #{generator[i].to_s(2).rjust(30, "0")}" if display_checksum_algorithm
# se o próximo bit do valor "top" for 1
if ((top >> i) & 1) == 1
# faz o xor do checksum atual com o valor gerador correspondente
checksum = checksum ^ generator[i]
# adiciona um indicador se este valor gerador foi usado no xor
puts " xor" if display_checksum_algorithm
else
puts if display_checksum_algorithm
end
end
# exibe o valor do checksum após este grupo de 5 bits
puts "checksum: #{checksum.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
puts if display_checksum_algorithm
end
# exibe o valor do checksum após percorrer todos os grupos de 5 bits
puts "checksum: #{checksum.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
# define a constante para o xor do checksum com base no número da versão
if (version_opcode_int == 0)
constant = 1 # bech32
else
constant = 0x2bc830a3 # bech32m
end
# exibe a constante
puts "constant: #{constant.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
# faz o xor do checksum com a constante
checksum = checksum ^ constant
# exibe a constante e o valor final do checksum
puts "checksum: #{checksum.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
puts if display_checksum_algorithm
# converte o checksum em 6 grupos de 5 bits
checksum_5_bits = []
# queremos 6 grupos de 5 bits
6.times do |i|
# calcula o deslocamento à direita para pegar cada grupo de 5 bits do checksum de 30 bits
right_shift = 5 * (5 - i)
# desloca o valor do checksum à direita
shifted = (checksum >> right_shift)
# extrai os últimos 5 bits do valor deslocado
bits = shifted & 0b11111
# adiciona o grupo de 5 bits ao array do checksum
checksum_5_bits << bits
end
# exibe o resultado
puts "checksum: #{checksum_5_bits.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
puts
puts "------"
puts "base32"
puts "------"
puts
# combina a versão, o programa e o checksum
data = [version_5_bits] + witness_program_5_bits + checksum_5_bits
puts "version + program + checksum: #{data.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
puts "version + program + checksum: #{data.map { |v| v }.join(' ')}"
# converte cada grupo de 5 bits para o seu caractere base32 correspondente
base32 = data.map { |i| characters[i] }
puts "base32: #{base32.join(' ')}"
puts
puts "------"
puts "bech32"
puts "------"
puts
# exibe a hrp e o separador
puts "hrp: #{hrp}"
puts "separator: #{separator}"
puts
# combina a parte legível por humanos, o separador e os dados base32
bech32 = hrp + separator + base32.join
puts "bech32: #{bech32}" # bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 Decodificar
Converter um endereço Bech32 em um ScriptPubKey
Um endereço Bech32 pode ser decodificado em um ScriptPubKey segwit.
Para fazer isso, basicamente você converte os caracteres base32 em seus valores de 5 bits correspondentes, rearranja esses bits em grupos de 8 bits para obter os valores em bytes hex e depois reconstrói o ScriptPubKey completo.
Aqui está um guia passo a passo:
1. Endereço
Primeiro de tudo, pegue o endereço que você quer converter em um ScriptPubKey.
Aqui está um exemplo:
bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
Em seguida, você precisa dividir o endereço em 3 partes:
- Parte legível por humanos. Os caracteres antes do separador são a parte legível por humanos. Essa parte pode ter entre 1 e 83 caracteres de comprimento. No Bitcoin, ela indica se o endereço é usado na mainnet ("bc"), testnet ("tb") ou regtest ("bcrt").
- Separador. A última ocorrência de um "1" no endereço é o separador. Ele separa a parte legível por humanos da parte de dados. Um endereço válido precisa conter um caractere separador "1".
- Dados. Tudo após o separador é a parte de "dados". Esta é uma codificação base32 da versão, do programa de testemunha e do checksum.
A forma de dividir um endereço Bech32 na parte legível por humanos e na parte de dados é procurando o separador, que é a última ocorrência de um caractere "1".
Então, para este endereço, temos:
- Parte legível por humanos = bc
- Separador = 1
- Dados = qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4
- Não leia um número fixo de caracteres para extrair a parte legível por humanos. Normalmente são 2 ou 4 caracteres, mas tecnicamente ela pode ter entre 1 e 83 caracteres de comprimento no futuro.
- Não procure a primeira ocorrência de um "1". É tecnicamente possível que a parte legível por humanos contenha um "1", então você acabaria dividindo o endereço cedo demais e teria uma parte de dados incorreta. Para ter certeza de que está obtendo a posição correta do separador, você deve procurar a última ocorrência de um "1".
O separador não é mais usado daqui em diante. A partir daqui, vamos usar apenas a parte legível por humanos e a parte de dados.
2. Grupos de 5 bits
Converta a parte de dados de caracteres base32 em seus valores inteiros de 5 bits correspondentes.
Por exemplo:
base32 = qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4 integers = 0 14 20 15 7 13 26 0 25 18 6 11 13 8 21 4 20 3 17 2 29 3 12 29 3 4 15 24 20 6 14 30 22 12 7 9 17 11 21
Caracteres Base32 0 = q 1 = p 2 = z 3 = r 4 = y 5 = 9 6 = x 7 = 8 8 = g 9 = f 10 = 2 11 = t 12 = v 13 = d 14 = w 15 = 0 16 = s 17 = 3 18 = j 19 = n 20 = 5 21 = 4 22 = k 23 = h 24 = c 25 = e 26 = 6 27 = m 28 = u 29 = a 30 = 7 31 = l
Esse array de inteiros pode ser dividido em 3 partes:
- Versão. O primeiro inteiro representa a versão.
- Programa de testemunha. Os inteiros entre a versão e o checksum são o programa de testemunha.
- Checksum. Os últimos 6 inteiros são o checksum.
Então temos:
version = 0 witness program = 14 20 15 7 13 26 0 25 18 6 11 13 8 21 4 20 3 17 2 29 3 12 29 3 4 15 24 20 6 14 30 22 checksum = 12 7 9 17 11 21
Se quiser, você pode exibir esses inteiros em suas representações binárias de 5 bits:
version = 00000 witness program = 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110 checksum = 01100 00111 01001 10001 01011 10101
Estou exibindo os inteiros de 5 bits em binário apenas para fins visuais. Você não precisa fazer isso ao decodificar, pois pode mantê-los como arrays de inteiros de 5 bits.
3. Verificar o checksum
Em seguida, devemos verificar se o checksum do endereço é válido.
Para fazer isso, calculamos o checksum usando a parte legível por humanos do passo 1 e a versão e o programa de testemunha que decodificamos no passo 2:
checksum (calculado) = 01100 00111 01001 10001 01011 10101
Em seguida, comparamos isso ao checksum fornecido no endereço:
checksum (endereço) = 01100 00111 01001 10001 01011 10101
Se os checksums coincidem, sabemos que o endereço foi digitado corretamente e que nenhum erro foi cometido.
Esta é apenas uma verificação simples. O checksum, na verdade, permite detectar a posição de possíveis erros e fornecer sugestões de como corrigi-los.
4. Versão
Em seguida, o inteiro da versão precisa ser convertido em um byte hex correspondente a um opcode OP_N.
Por exemplo:
| Versão | Opcode | Hex |
|---|---|---|
| 0 | OP_0 | 00 |
| 1 | OP_1 | 51 |
| 2 | OP_2 | 52 |
| ... | ... | ... |
| 16 | OP_16 | 60 |
A versão é 0 no nosso exemplo, que corresponde ao opcode OP_0, representado pelo seguinte byte hex:
version = 00
Conversão rápida. Se a versão for maior que zero, some 0x51 para obter o byte do opcode OP_N.
- Não converta o valor inteiro da versão diretamente para um byte hex. Por exemplo, a versão 1 corresponde a
OP_1, representado pelo byte51. Se você convertê-la diretamente para hex, vai obter01, que é um opcode inválido para o número de versão. - O número de versão deve estar entre 0 e 16. Qualquer outro número de versão é inválido.
5. Grupos de 8 bits
Rearranje o programa de testemunha de grupos de 5 bits para grupos de 8 bits:
witness program (grupos de 5 bits) = 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110 witness program (grupos de 8 bits) = 01110101 00011110 01110110 11101000 00011001 10010001 10010110 11010100 01010100 10010100 00011100 01000101 11010001 10110011 10100011 00100011 11110001 01000011 00111011 11010110
Como você pode ver, simplesmente reagrupamos os bits em grupos de 8 bits.
Verifique se os grupos de 5 bits não contêm preenchimento demais. Se o resto da divisão do total de bits nos grupos de 5 bits pelo total de bits nos grupos de 8 bits resultantes for 5 ou maior, então o endereço continha preenchimento demais.
Verifique se o preenchimento contém apenas zeros. A quantidade de preenchimento é o total de bits nos grupos de 5 bits menos o total de bits nos grupos de 8 bits. Se esse preenchimento contiver qualquer coisa além de zeros, o preenchimento é inválido.
Se convertermos esses grupos de 8 bits em bytes hex, obtemos:
witness program (bytes) = 75 1e 76 e8 19 91 96 d4 54 94 1c 45 d1 b3 a3 23 f1 43 3b d6
6. ScriptPubKey
Por fim, para construir o ScriptPubKey final, primeiro precisamos calcular o tamanho do programa de testemunha do passo anterior.
No nosso exemplo, o programa de testemunha tem 20 bytes de comprimento, que como byte hex é 14. Isso nos dá:
version = 00 size = 14 witness program = 75 1e 76 e8 19 91 96 d4 54 94 1c 45 d1 b3 a3 23 f1 43 3b d6
Se combinarmos essas 3 partes de dados, temos o nosso ScriptPubKey completo:
scriptpubkey = 0014751e76e8199196d454941c45d1b3a323f1433bd6
Como esta é uma versão 0 com um programa de testemunha de 20 bytes, conseguimos dizer que este é um script de travamento P2WPKH.
Código
# --------
# configurações
# --------
# conjunto de caracteres base32
characters = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" # todos os caracteres alfanuméricos minúsculos exceto "1", "b", "i", "o"
# exibe o algoritmo de checksum completo
display_checksum_algorithm = false # true ou false
puts "-------"
puts "address"
puts "-------"
puts
# endereço Bech32 que queremos converter para um scriptpubkey
address = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4" # scriptpubkey P2WPKH de exemplo
puts "address: #{address}"
puts
# encontra a posição do separador (a última ocorrência de um "1")
separator_position = address.rindex("1")
# nota: o separador é sempre "1"
# cuidado: você quer a _última_ ocorrência de "1", pois a parte legível por humanos pode conter um "1"
# tudo antes do separador é a parte legível por humanos
hrp = address[0..separator_position - 1]
# tudo depois do separador é a parte de dados
data = address[separator_position + 1..-1]
puts "hrp: #{hrp}"
puts "data: #{data}"
puts
# nota: o separador não é usado ao converter o endereço para um scriptpubkey
puts "------------"
puts "5-bit groups"
puts "------------"
puts
# converte cada caractere base32 para o seu valor inteiro de 5 bits correspondente
data_5_bits = data.split("").map { |c| characters.index(c) }
# o primeiro grupo de 5 bits é a versão
version_5_bits = data_5_bits.shift
# os últimos 6 grupos de 5 bits são o checksum
checksum_5_bits = data_5_bits.pop(6)
# os grupos de 5 bits restantes são o programa de testemunha
witness_program_5_bits = data_5_bits
# exibe os grupos de 5 bits como inteiros
puts "version: #{version_5_bits}"
puts "program: #{witness_program_5_bits.join(' ')}"
puts "checksum: #{checksum_5_bits.join(' ')}"
puts
# mostra os grupos de 5 bits como strings binárias
puts "version: #{version_5_bits.to_s(2).rjust(5, "0")}"
puts "program: #{witness_program_5_bits.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
puts "checksum: #{checksum_5_bits.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
puts
puts "---------------------"
puts "checksum verification"
puts "---------------------"
puts
# calcula o checksum a partir da hrp e do programa de testemunha fornecidos
# converte a parte legível por humanos para os valores de caractere UTF-8 correspondentes
hrp_values = hrp.split("").map { |c| c.ord }
# expande a parte legível por humanos em grupos de 5 bits
hrp_expanded = []
# pega os primeiros 3 bits do valor inteiro de cada caractere
hrp_values.each do |v|
hrp_expanded << (v >> 5) # desloca 5 bits à direita para obter os primeiros 3 bits
end
# adiciona o separador
hrp_expanded << 0
# pega os últimos 5 bits do valor inteiro de cada caractere
hrp_values.each do |v|
hrp_expanded << (v & 0b11111) # usa uma máscara de bits para extrair os últimos 5 bits
end
# combina a parte legível por humanos expandida com a versão e o programa de testemunha (todos em grupos de 5 bits)
combined = hrp_expanded + [version_5_bits] + witness_program_5_bits
# adiciona preenchimento (padding)
combined_with_padding = combined + [0, 0, 0, 0, 0, 0]
# valores geradores do checksum
generator = [
0b111011011010100101011110110010, # 0x3b6a57b2
0b100110010100001000111001101101, # 0x26508e6d
0b011110101000010001100111111010, # 0x1ea119fa
0b111101010000100011001111011101, # 0x3d4233dd
0b101010000101000110001010110011, # 0x2a1462b3
]
# valor inicial do checksum (o resultado final do checksum terá 30 bits)
checksum = 1
# percorre cada grupo de 5 bits no array combinado
combined_with_padding.each do |v|
# remove 25 bits da direita do valor atual do checksum e guarda o resultado
top = checksum >> 25
# extrai os 25 bits inferiores do valor atual do checksum usando uma máscara de bits
checksum = checksum & 0b1111111111111111111111111
# desloca o valor do checksum à esquerda (adiciona 5 bits zero ao final, criando espaço para o xor com o valor atual de 5 bits)
checksum = checksum << 5
# faz o xor do checksum atual com o próximo valor de 5 bits
checksum = checksum ^ v
# percorre os últimos 5 bits do valor "top"
5.times do |i|
# se o próximo bit do valor "top" for 1
if ((top >> i) & 1) == 1
# faz o xor do checksum atual com o valor gerador correspondente
checksum = checksum ^ generator[i]
end
end
end
# define a constante para o xor do checksum com base no número da versão
if (version_5_bits == 0)
constant = 1 # bech32
else
constant = 0x2bc830a3 # bech32m
end
# exibe a constante
puts "constant: #{constant.to_s(2).rjust(30, "0")}" if display_checksum_algorithm
# faz o xor do checksum com a constante
checksum = checksum ^ constant
# converte o checksum em 6 grupos de 5 bits
checksum_verify = []
# queremos 6 grupos de 5 bits
6.times do |i|
# calcula o deslocamento à direita para pegar cada grupo de 5 bits do checksum de 30 bits
right_shift = 5 * (5 - i)
# desloca o valor do checksum à direita
shifted = (checksum >> right_shift)
# extrai os últimos 5 bits do valor deslocado
bits = shifted & 0b11111
# adiciona o grupo de 5 bits ao array do checksum
checksum_verify << bits
end
# exibe o checksum calculado
print "checksum: #{checksum_verify.map { |v| v.to_s(2).rjust(5, "0") }.join(' ')}"
# verifica se o checksum calculado bate com o checksum do endereço
if checksum_5_bits == checksum_verify
puts " ✓"
else
puts " ✗"
end
puts
puts "-------"
puts "version"
puts "-------"
puts
# converte o valor inteiro da versão para o seu byte hex OP_N correspondente
if (version_5_bits == 0)
version_op_n_hex = version_5_bits.to_s(16).rjust(2, "0") # OP_0
else
version_op_n_hex = (version_5_bits + 0x50).to_s(16).rjust(2, "0") # OP_1 a OP_16
end
puts "5-bits: #{version_5_bits.to_s(2).rjust(5, "0")}"
puts "opcode: OP_#{version_5_bits}"
puts "hex: #{version_op_n_hex}"
puts
puts "------------"
puts "8-bit groups"
puts "------------"
puts
# rearranja o programa de testemunha de um array de inteiros de 5 bits para um array de inteiros de 8 bits
from = 5 # número de bits no inteiro de partida
to = 8 # número de bits no inteiro de destino
accumulator = 0
counter = 0
max_value = (1 << to) - 1 # valor máximo (5 bits = 0b100000 = 32)
max_accumulator = (1 << (from + to -1)) -1 # 8 bits + 5 bits - 1 bit = 12 bits = 4095 = 0b111111111111
witness_program_8_bits = [] # o array de retorno
# percorre cada inteiro de 5 bits no array do programa de testemunha
witness_program_5_bits.each do |int_5_bits|
# retorna nil se for negativo
if int_5_bits < 0
return nil
end
# retorna nil se o tamanho do inteiro for maior que o tamanho especificado do grupo de bits de partida
if (int_5_bits >> from) != 0
return nil
end
# adiciona os bits de partida ao acumulador
accumulator = accumulator << from # << = deslocamento de bits à esquerda
accumulator = accumulator | int_8_bits # | = OU bit a bit (nota: este comentário é necessário para o realce de sintaxe funcionar corretamente no site learnmeabitcoin.com — um pipe sozinho em uma linha quebra o realce por algum motivo)
accumulator = accumulator & max_accumulator # & = E bit a bit
# incrementa o contador
counter += from
# enquanto houver bits suficientes para produzir um novo grupo de 8 bits
while counter >= to
# decrementa o contador
counter -= to
# adiciona o valor de 8 bits ao resultado
witness_program_8_bits << ((accumulator >> counter) & max_value)
end
end
# exibe o resultado
puts "program: #{witness_program_8_bits.map { |v| v.to_s(2).rjust(8, "0") }.join(' ')}"
puts
puts "------------"
puts "scriptpubkey"
puts "------------"
puts
# calcula o tamanho do programa de testemunha e converte para byte hex
witness_program_size_hex = witness_program_8_bits.size.to_s(16).rjust(2, "0")
# converte o programa de testemunha para hex
witness_program_hex = witness_program_8_bits.map { |v| v.to_s(16).rjust(2, "0") }.join
# exibe a versão, o tamanho e o programa de testemunha em hex
puts "version: #{version_op_n_hex}"
puts "size: #{witness_program_size_hex}"
puts "program: #{witness_program_hex}"
puts
# combina a versão, o tamanho e o programa de testemunha em um scriptpubkey
scriptpubkey = version_op_n_hex + witness_program_size_hex + witness_program_hex
puts "scriptpubkey: #{scriptpubkey}" # 0014751e76e8199196d454941c45d1b3a323f1433bd6 Checksum
Como calcular um checksum Bech32?
A parte mais complexa da codificação Bech32 é calcular o checksum.
O algoritmo de checksum usa códigos BCH, que permitem correção de erros ao longo dos dados (o que não é possível com o checksum simples usado no Base58). Isso torna o checksum do Bech32 muito mais útil, mas também mais complexo de calcular.
Eu não conheço o suficiente sobre códigos BCH para explicar o projeto do algoritmo, então vou apenas mostrar como calcular o checksum.
1. Preparar os dados
O checksum de um endereço Bech32 é calculado a partir dos seguintes dados:
- Parte legível por humanos. O prefixo que você quer usar no endereço final precisa ser escolhido antes de criar o checksum. Essa string normalmente é 'bc' (mainnet), 'tb' (testnet) ou 'bcrt' (regtest).
- Versão. Este é o número de versão do ScriptPubKey segwit. É um valor inteiro de 5 bits indicado por um opcode
OP_N. - Programa de testemunha. Este é o programa de testemunha do ScriptPubKey como um array de valores de 5 bits. Então, se você está calculando o checksum a partir de um ScriptPubKey bruto, primeiro precisa rearranjar o programa de testemunha de um array de bytes de 8 bits para um array de valores de 5 bits.
Aqui estão alguns dados de exemplo:
hrp = 'bc' version = 00000 witness program = 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110
O byte de tamanho do ScriptPubKey e o separador do endereço não são cobertos pelo checksum.
2. Expandir a parte legível por humanos
A parte legível por humanos é usada como prefixo do endereço. Ela precisa ser incluída no checksum para que possamos detectar se foi digitada corretamente.
Esse prefixo é uma string de 1 a 83 caracteres US-ASCII. Portanto, precisamos "expandir" esses caracteres para transformá-los em um array de valores de 5 bits.
É assim que você expande a parte legível por humanos:
- Converta cada caractere para o seu valor de byte ASCII de 8 bits.
- Pegue os primeiros 3 bits do valor de byte de cada caractere.
- Adicione um zero como separador.
- Pegue os últimos 5 bits do valor de byte de cada caractere.
Então, basicamente, como cada caractere ASCII representa um valor de 8 bits, nós os dividimos para obter um array de valores de 5 bits.
Por exemplo:
hrp = 'bc' hrp (ASCII) = 01100010 01100011 hrp (expandido) = 00011 00011 00000 00010 00011
A parte legível por humanos agora está representada como um array de inteiros de 5 bits. Isso combina com a estrutura da versão e do programa de testemunha (que também estão em arrays de inteiros de 5 bits).
3. Construir o array de 5 bits
A entrada do algoritmo de checksum é um array de 5 bits com os seguintes dados:
- Parte legível por humanos expandida. O array de inteiros de 5 bits do passo 2.
- Versão. Um inteiro de 5 bits baseado no valor do opcode
OP_N. - Programa de testemunha. Um array de inteiros de 5 bits.
- Preenchimento. Um array de 6 inteiros zero de 5 bits.
Então você basicamente pega a hrp, a versão e o programa de testemunha, e adiciona um preenchimento ao final.
Por exemplo:
hrp (expandido) = 00011 00011 00000 00010 00011 version = 00000 witness program = 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110 padding = 00000 00000 00000 00000 00000 00000 5-bit array = 00011 00011 00000 00010 00011 00000 01110 10100 01111 00111 01101 11010 00000 11001 10010 00110 01011 01101 01000 10101 00100 10100 00011 10001 00010 11101 00011 01100 11101 00011 00100 01111 11000 10100 00110 01110 11110 10110 00000 00000 00000 00000 00000 00000
Esse array combinado de inteiros de 5 bits é a entrada do algoritmo de checksum principal.
4. Polymod
Esta é a parte divertida.
O checksum final será um valor de 30 bits. O valor inicial do checksum é 1, então o nosso checksum inicial fica assim:
000000000000000000000000000001
Agora vamos percorrer cada inteiro de 5 bits do nosso array e executar os seguintes passos:
1. Top
Pegue os primeiros 5 bits do valor atual do checksum. Este é o valor "top", e será usado em um passo posterior.
top = 00000
2. Bottom
Pegue os 25 bits inferiores do valor atual do checksum. Este é o valor "bottom", e é o que vamos ajustar.
bottom = 0000000000000000000000001
3. Preenchimento
Adicione 5 bits de preenchimento aos 25 bits "bottom" do passo anterior para totalizar 30 bits.
padded = 000000000000000000000000100000
4. XOR com o próximo valor de 5 bits
Pegue o próximo valor de 5 bits do array e faça um XOR com o valor de checksum de 30 bits preenchido do passo anterior:
padded = 000000000000000000000000100000 5-bit value = 00011 xor = 000000000000000000000000100011
5. XOR com os valores geradores
Agora fazemos o XOR do valor do passo anterior com os seguintes valores geradores:
generator 0 = 111011011010100101011110110010 generator 1 = 100110010100001000111001101101 generator 2 = 011110101000010001100111111010 generator 3 = 111101010000100011001111011101 generator 4 = 101010000101000110001010110011
No entanto, nem sempre fazemos o XOR com todos esses valores geradores. Em vez disso, usamos o valor "top" para determinar com quais valores geradores realmente fazemos o XOR.
Usando o valor "top", lemos os bits da direita para a esquerda para determinar com quais valores geradores fazemos o XOR do valor atual do checksum. Se o bit estiver setado (ou seja, "1"), fazemos o XOR com aquele valor gerador correspondente.
Usando o nosso exemplo atual:
top = 00000 generator 0 = 111011011010100101011110110010 (bit 4 = 0, não faz XOR) generator 1 = 100110010100001000111001101101 (bit 3 = 0, não faz XOR) generator 2 = 011110101000010001100111111010 (bit 2 = 0, não faz XOR) generator 3 = 111101010000100011001111011101 (bit 1 = 0, não faz XOR) generator 4 = 101010000101000110001010110011 (bit 0 = 0, não faz XOR) xor = 000000000000000000000000100011 result = 000000000000000000000000100011
Nenhum dos bits em "top" está setado, então não fazemos o XOR do valor atual do checksum com nenhum dos geradores.
Se o bit mais à direita estiver setado, fazemos o XOR com o gerador 0. Se o próximo bit a partir da direita estiver setado, também fazemos o XOR com o gerador 1, e assim por diante.
6. Repetir
Repita os passos anteriores para todos os inteiros de 5 bits do array.
Cálculos passo a passo completos
checksum: 000000000000000000000000000001 top: 00000 bottom: 0000000000000000000000001 padded: 000000000000000000000000100000 5-bit group: 00011 xor: 000000000000000000000000100011 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 000000000000000000000000100011 checksum: 000000000000000000000000100011 top: 00000 bottom: 0000000000000000000100011 padded: 000000000000000000010001100000 5-bit group: 00011 xor: 000000000000000000010001100011 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 000000000000000000010001100011 checksum: 000000000000000000010001100011 top: 00000 bottom: 0000000000000010001100011 padded: 000000000000001000110001100000 5-bit group: 00000 xor: 000000000000001000110001100000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 000000000000001000110001100000 checksum: 000000000000001000110001100000 top: 00000 bottom: 0000000001000110001100000 padded: 000000000100011000110000000000 5-bit group: 00010 xor: 000000000100011000110000000010 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 000000000100011000110000000010 checksum: 000000000100011000110000000010 top: 00000 bottom: 0000100011000110000000010 padded: 000010001100011000000001000000 5-bit group: 00011 xor: 000010001100011000000001000011 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 000010001100011000000001000011 checksum: 000010001100011000000001000011 top: 00001 bottom: 0001100011000000001000011 padded: 000110001100000000100001100000 5-bit group: 00000 xor: 000110001100000000100001100000 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 111101010110100101111111010010 checksum: 111101010110100101111111010010 top: 11110 bottom: 1010110100101111111010010 padded: 101011010010111111101001000000 5-bit group: 01110 xor: 101011010010111111101001001110 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 xor checksum: 000100111011000011110010110111 checksum: 000100111011000011110010110111 top: 00010 bottom: 0111011000011110010110111 padded: 011101100001111001011011100000 5-bit group: 10100 xor: 011101100001111001011011110100 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 111011110101110001100010011001 checksum: 111011110101110001100010011001 top: 11101 bottom: 1110101110001100010011001 padded: 111010111000110001001100100000 5-bit group: 01111 xor: 111010111000110001001100101111 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 xor checksum: 001000011111100000110000001001 checksum: 001000011111100000110000001001 top: 00100 bottom: 0011111100000110000001001 padded: 001111110000011000000100100000 5-bit group: 00111 xor: 001111110000011000000100100111 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 010001011000001001100011011101 checksum: 010001011000001001100011011101 top: 01000 bottom: 1011000001001100011011101 padded: 101100000100110001101110100000 5-bit group: 01101 xor: 101100000100110001101110101101 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 checksum: 010001010100010010100001110000 checksum: 010001010100010010100001110000 top: 01000 bottom: 1010100010010100001110000 padded: 101010001001010000111000000000 5-bit group: 11010 xor: 101010001001010000111000011010 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 checksum: 010111011001110011110111000111 checksum: 010111011001110011110111000111 top: 01011 bottom: 1011001110011110111000111 padded: 101100111001111011100011100000 5-bit group: 00000 xor: 101100111001111011100011100000 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 checksum: 001100100111110101001011100010 checksum: 001100100111110101001011100010 top: 00110 bottom: 0100111110101001011100010 padded: 010011111010100101110001000000 5-bit group: 11001 xor: 010011111010100101110001011001 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 101011000110111100101111001110 checksum: 101011000110111100101111001110 top: 10101 bottom: 1000110111100101111001110 padded: 100011011110010111100111000000 5-bit group: 10010 xor: 100011011110010111100111010010 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 101100101001100101010100101001 checksum: 101100101001100101010100101001 top: 10110 bottom: 0101001100101010100101001 padded: 010100110010101010010100100000 5-bit group: 00110 xor: 010100110010101010010100100110 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 000110001011110101000000000010 checksum: 000110001011110101000000000010 top: 00011 bottom: 0001011110101000000000010 padded: 000101111010100000000001000000 5-bit group: 01011 xor: 000101111010100000000001001011 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 011000110100001101100110010100 checksum: 011000110100001101100110010100 top: 01100 bottom: 0110100001101100110010100 padded: 011010000110110011001010000000 5-bit group: 01101 xor: 011010000110110011001010001101 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 checksum: 111001111110000001100010101010 checksum: 111001111110000001100010101010 top: 11100 bottom: 1111110000001100010101010 padded: 111111000000110001010101000000 5-bit group: 01000 xor: 111111000000110001010101001000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 xor checksum: 110110111101000101110111011100 checksum: 110110111101000101110111011100 top: 11011 bottom: 0111101000101110111011100 padded: 011110100010111011101110000000 5-bit group: 10101 xor: 011110100010111011101110010101 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 xor checksum: 010100111001110011001100100100 checksum: 010100111001110011001100100100 top: 01010 bottom: 0111001110011001100100100 padded: 011100111001100110010010000000 5-bit group: 00100 xor: 011100111001100110010010000100 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 checksum: 000111111101001101100100110100 checksum: 000111111101001101100100110100 top: 00011 bottom: 1111101001101100100110100 padded: 111110100110110010011010000000 5-bit group: 10100 xor: 111110100110110010011010010100 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 100011101000011111111101001011 checksum: 100011101000011111111101001011 top: 10001 bottom: 1101000011111111101001011 padded: 110100001111111110100101100000 5-bit group: 00011 xor: 110100001111111110100101100011 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 100101010000011101110001100010 checksum: 100101010000011101110001100010 top: 10010 bottom: 1010000011101110001100010 padded: 101000001110111000110001000000 5-bit group: 10001 xor: 101000001110111000110001010001 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 100100011111110110000010001111 checksum: 100100011111110110000010001111 top: 10010 bottom: 0011111110110000010001111 padded: 001111111011000001000111100000 5-bit group: 00010 xor: 001111111011000001000111100010 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 000011101010001111110100111100 checksum: 000011101010001111110100111100 top: 00001 bottom: 1101010001111110100111100 padded: 110101000111111010011110000000 5-bit group: 11101 xor: 110101000111111010011110011101 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 001110011101011111000000101111 checksum: 001110011101011111000000101111 top: 00111 bottom: 0011101011111000000101111 padded: 001110101111100000010111100000 5-bit group: 00011 xor: 001110101111100000010111100011 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 001101001001011100010111000110 checksum: 001101001001011100010111000110 top: 00110 bottom: 1001001011100010111000110 padded: 100100101110001011100011000000 5-bit group: 01100 xor: 100100101110001011100011001100 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 011100010010010010111101011011 checksum: 011100010010010010111101011011 top: 01110 bottom: 0010010010010111101011011 padded: 001001001001011110101101100000 5-bit group: 11101 xor: 001001001001011110101101111101 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 checksum: 001100100101100100111100110111 checksum: 001100100101100100111100110111 top: 00110 bottom: 0100101100100111100110111 padded: 010010110010011110011011100000 5-bit group: 00011 xor: 010010110010011110011011100011 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 101010001110000111000101110100 checksum: 101010001110000111000101110100 top: 10101 bottom: 0001110000111000101110100 padded: 000111000011100010111010000000 5-bit group: 00100 xor: 000111000011100010111010000100 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 001000110100010000001001111111 checksum: 001000110100010000001001111111 top: 00100 bottom: 0110100010000001001111111 padded: 011010001000000100111111100000 5-bit group: 01111 xor: 011010001000000100111111101111 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 000100100000010101011000010101 checksum: 000100100000010101011000010101 top: 00010 bottom: 0100000010101011000010101 padded: 010000001010101100001010100000 5-bit group: 11000 xor: 010000001010101100001010111000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 110110011110100100110011010101 checksum: 110110011110100100110011010101 top: 11011 bottom: 0011110100100110011010101 padded: 001111010010011001101010100000 5-bit group: 10100 xor: 001111010010011001101010110100 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 xor checksum: 000101001001010001001000000101 checksum: 000101001001010001001000000101 top: 00010 bottom: 1001001010001001000000101 padded: 100100101000100100000010100000 5-bit group: 00110 xor: 100100101000100100000010100110 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 000010111100101100111011001011 checksum: 000010111100101100111011001011 top: 00001 bottom: 0111100101100111011001011 padded: 011110010110011101100101100000 5-bit group: 01110 xor: 011110010110011101100101101110 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 100101001100111000111011011100 checksum: 100101001100111000111011011100 top: 10010 bottom: 1001100111000111011011100 padded: 100110011100011101101110000000 5-bit group: 11110 xor: 100110011100011101101110011110 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 101010001101010011011101000000 checksum: 101010001101010011011101000000 top: 10101 bottom: 0001101010011011101000000 padded: 000110101001101110100000000000 5-bit group: 10110 xor: 000110101001101110100000010110 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 001001011110011100010011101101 checksum: 001001011110011100010011101101 top: 00100 bottom: 1011110011100010011101101 padded: 101111001110001001110110100000 5-bit group: 00000 xor: 101111001110001001110110100000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 110001100110011000010001011010 checksum: 110001100110011000010001011010 top: 11000 bottom: 1100110011000010001011010 padded: 110011001100001000101101000000 5-bit group: 00000 xor: 110011001100001000101101000000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 xor checksum: 100100011001101101101000101110 checksum: 100100011001101101101000101110 top: 10010 bottom: 0011001101101101000101110 padded: 001100110110110100010111000000 5-bit group: 00000 xor: 001100110110110100010111000000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 xor generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 xor checksum: 000000100111111010100100011110 checksum: 000000100111111010100100011110 top: 00000 bottom: 0100111111010100100011110 padded: 010011111101010010001111000000 5-bit group: 00000 xor: 010011111101010010001111000000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 generator 4: 101010000101000110001010110011 checksum: 010011111101010010001111000000 checksum: 010011111101010010001111000000 top: 01001 bottom: 1111101010010001111000000 padded: 111110101001000111100000000000 5-bit group: 00000 xor: 111110101001000111100000000000 generator 0: 111011011010100101011110110010 xor generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 checksum: 111000100011000001110001101111 checksum: 111000100011000001110001101111 top: 11100 bottom: 0100011000001110001101111 padded: 010001100000111000110111100000 5-bit group: 00000 xor: 010001100000111000110111100000 generator 0: 111011011010100101011110110010 generator 1: 100110010100001000111001101101 generator 2: 011110101000010001100111111010 xor generator 3: 111101010000100011001111011101 xor generator 4: 101010000101000110001010110011 xor checksum: 011000011101001100010101110100
Para o nosso exemplo, isso nos dá um valor de checksum resultante de:
checksum = 011000011101001100010101110100
5. Constante
Por último, fazemos o XOR do valor atual do checksum com uma constante.
A constante que usamos depende da versão do ScriptPubKey (veja Bech32m):
Version 0 = 000000000000000000000000000001 Version 1+ = 101011110010000011000010100011
O nosso exemplo é um ScriptPubKey P2WPKH, que é versão 0:
checksum = 011000011101001100010101110100 constant = 000000000000000000000000000001 result = 011000011101001100010101110101
6. Dividir em grupos de 5 bits
Por fim, podemos dividir o nosso checksum de 30 bits em 6 grupos de 5 bits:
checksum = 011000011101001100010101110101 checksum = 01100 00111 01001 10001 01011 10101
Este é o nosso checksum final.
Código
Veja codificar ou decodificar para o algoritmo de checksum.
Bech32m
Bech32m se refere a um pequeno ajuste feito no algoritmo de checksum para endereços Bech32 da versão 1 em diante.
A única diferença é que você usa uma constante diferente antes de calcular o valor final do checksum:
| Versão | Constante | Tipo(s) de endereço |
|---|---|---|
| 0 | 0b000000000000000000000000000001 | P2WPKH, P2WSH |
| 1+ | 0b101011110010000011000010100011 | P2TR |
Todo o resto da codificação Bech32 permanece igual.
Essa mudança corrige um problema em que, se o último caractere do endereço for "p", inserir ou remover qualquer número de caracteres "q" não torna o checksum inválido.
Isso não é um problema grave, já que os endereços P2WPKH e P2WSH existentes não são afetados, pelo fato de estarem restritos a dois comprimentos específicos. Mesmo assim, daqui em diante essa mudança garante que o checksum seja confiável para todos os endereços futuros.
É mais fácil pensar nisso como o novo método de codificação padrão do "Bech32". Em outras palavras, os scripts de travamento versão 0 (P2WPKH e P2WSH) usam uma constante "legada" diferente.
Etimologia
De onde vem o nome "Bech32"?
O nome "Bech32" (besh trinta e dois) vem do fato de o endereço usar caracteres base32, e de o checksum usar códigos BCH para o algoritmo de detecção/correção de erros.
Então, se você juntar "base32" e "BCH", obtém "Bech32". Mais ou menos.
"Bech" contém os caracteres BCH (o algoritmo de detecção de erros usado) e soa um pouco como "base".
Não é perfeito, eu sei. Mas serve.
Resumo
O Bech32 é simplesmente um formato melhor para endereços em comparação ao Base58.
O Base58 era/é útil porque te dá um bom conjunto de caracteres para trabalhar e um checksum simples para detectar erros. Mas o Bech32 oferece um monte de melhorias úteis:
- QR codes menores.
- Mais fácil de digitar manualmente.
- Checksum mais esperto para correção de erros.
- Codificação e decodificação mais rápidas.
- Mais flexibilidade com os prefixos.
O Base58 foi um esforço bem decente, e você não pode culpar o Satoshi por ele não ser perfeito, já que ele provavelmente não tinha meses para passar construindo o formato de endereço definitivo; era simples e eficaz. Mas, desde então, tivemos o luxo do tempo para construir algo melhor.
Ainda usamos o Base58 para scripts de travamento legados, chaves privadas e chaves estendidas:
- P2PKH (2009 - presente)
- P2SH (2012 - presente)
- Chaves privadas WIF (2011 - presente)
- Chaves estendidas (2012 - presente)
Mas o Bech32 agora é usado para todos os scripts de travamento modernos:
A única desvantagem cruel do Bech32 é que o conjunto de caracteres base32 não inclui a letra "b", então eu não consigo construir um endereço vanity com a palavra "beer" (cerveja) nele. Mas isso é só algo com que eu preciso conviver.