Puzzle 67: divida o range do Bitcoin e grave no Neon
TL;DR
- O que é: script em Python que divide o range de chaves do Bitcoin Puzzle 67 em faixas e grava no Neon (PostgreSQL serverless).
- O que muda: conexão persistente, retry com backoff, progresso atômico, validações e alerta de volume — sem estourar o plano gratuito.
- Segurança: credenciais fora do código (`.env`/Secret), SSL obrigatório e workflow GitHub Actions corrigido.
Substitua o ID DUDla8Bya8M pelo vídeo oficial do @CanalQb
O que é o Bitcoin Puzzle 67 e o que este script faz?
Em 2015, um usuário anônimo enviou bitcoins para 64 endereços cujas chaves privadas estão em faixas numéricas conhecidas. O endereço do Puzzle 67 tem a chave entre 0x40000000000000000 (2^66) e 0x7fffffffffffffff (2^67 - 1) — um espaço com cerca de 7,37 x 10^19 valores. Este projeto não resolve a chave: ele particiona o espaço em faixas (inicio, fim) e armazena cada faixa na tabela puzzle67 do Neon, servindo de base para qualquer estudo futuro do desafio.
O valor real está no processo: trabalhar com números de 2^66, conexão a um banco serverless, retomada de processamento e automação em CI. É um laboratório prático de Python, PostgreSQL e boas práticas de engenharia.
Quais falhas foram encontradas e corrigidas?
| # | Problema original | Correção |
|---|---|---|
| 1 | Credencial do banco no código-fonte | Lida do ambiente (`.env` / Secret) |
| 2 | Workflow rodava `python seu_script.py` (não existe) | Aponta para `puzzle67_to_db.py` + `py_compile` |
| 3 | Actions `@v2` desatualizadas | `checkout@v4`, `setup-python@v5` com cache |
| 4 | 1 conexão nova por lote (≈10 milhões) | Conexão persistente reutilizada |
| 5 | `os.execv` reiniciava o processo a cada erro (quebrado no Windows) | Retry com backoff exponencial |
| 6 | Progresso gravado a cada intervalo (1e9 escritas) | Progresso atômico a cada checkpoint + após commit |
| 7 | Contador de progresso dobrado | Contagem real por lote enviado |
| 8 | `gc.collect()` desnecessário | Removido |
| 9 | Porta fixa ignorando a URL | Porta lida do `DATABASE_URL` |
| 10 | Sem validação (percentual 0 quebrava) | Validações + alerta de volume |
| 11 | Duas conexões para checar/criar tabela | Uma única conexão |
| 12 | Sem testes, sem `requirements.txt`, sem `.env.example` | Adicionados (7 testes) |
Como reduzir o consumo de hardware e do banco?
O problema de consumo aqui não é CPU nem RAM — o script é I/O-bound. O desperdício estava no banco e no disco:
- Conexões: abrir e fechar uma conexão por lote de 100 linhas significa ~10 milhões de conexões para 1e9 intervalos. Agora a conexão é persistente e só é refeita em falha.
- Disco: o progresso era gravado a cada intervalo — até 1 bilhão de escritas. Agora a gravação é atômica e acontece a cada
--checkpoint(padrão 1000). - Volume: o
--statsestima o tamanho em GB antes de rodar. Com o percentual padrão, são 1e9 faixas (~100 GB) — o script alerta e sugere aumentar--percentual(ex.:0.001gera 100 mil faixas, ~10 MB). - CPU:
--quietreduz a saída no console; sem prints por linha (antes eram 1e9 linhas no terminal).
Regra prática: python puzzle67_to_db.py --stats antes de qualquer execução. Se o volume estiver acima de ~500 MB, aumente o percentual.
Como as credenciais ficam protegidas?
Segurança aplicada em três camadas:
- No código: a connection string é lida de
DATABASE_URL(ambiente) ou--db-url. Não há credencial versionada. - No .env: o script carrega um
.envlocal (copiado de.env.example) e o arquivo está no.gitignore— nunca é commitado. - No CI: o workflow usa o GitHub Secret
DATABASE_URLe tempermissions: contents: read(menor privilégio).
A conexão usa sslmode=require e ssl_context=True: o tráfego é criptografado. Se uma credencial vazar por engano, a ação correta é revogar a senha no Neon imediatamente.
Como usar na prática?
git clone https://github.com/canalqb/puzzle-67-neon.git
cd puzzle-67-neon
pip install -r requirements.txt
# 2) Credenciais (copie, edite e NUNCA commite o .env)
cp .env.example .env
# 3) Prévia do tamanho do trabalho
python puzzle67_to_db.py --stats
# 4) Simulação sem tocar no banco
python puzzle67_to_db.py --dry-run --amostras 10
# 5) Execução real (volume reduzido p/ plano gratuito)
python puzzle67_to_db.py --percentual 0.001 --batch 500
A retomada é automática: o arquivo progresso.txt guarda o último intervalo commitado, e a inserção usa ON CONFLICT DO NOTHING — reprocessar não duplica nada. Um Ctrl+C salva o progresso e encerra com segurança.
Perguntas Frequentes
O Bitcoin Puzzle é um desafio criado em 2015 por um usuário anônimo que enviou bitcoins para 64 endereços cujas chaves privadas estão em faixas numéricas conhecidas. O endereço do Puzzle 67, identificado pela sigla no site bitcoinpuzzle.org, tem a chave privada entre 0x40000000000000000 (2^66) e 0x7fffffffffffffff (2^67 - 1). Isso representa um espaço de busca de aproximadamente 7,37 x 10^19 valores — um número gigantesco, impossível de testar por força bruta na prática. O desafio é famoso porque combina matemática, criptografia e engenharia: quem conseguir a chave pode gastar o saldo do endereço. É usado como material educacional para ensinar sobre curvas elípticas, aritmética de inteiros grandes e ferramentas de armazenamento de dados. A dificuldade do Puzzle 67 é a mesma de puzzles adjacentes: quanto maior o intervalo, mais difícil é encontrar a chave. Este projeto trata apenas da primeira etapa: organizar o espaço de busca em faixas armazenáveis.
Não. O script divide o intervalo em faixas e grava cada faixa na tabela puzzle67 do Neon. Ele não testa chaves, não deriva endereços e não tenta gastar saldo. A divisão do espaço de busca é apenas o pré-processamento: o resultado é uma lista organizada de faixas (inicio, fim) que uma ferramenta futura poderia usar para verificar endereços. Encontrar a chave exigiria testar cada valor dentro de cada faixa, o que envolve aritmética de curva elíptica (secp256k1) e é computacionalmente inviável mesmo com hardware moderno — o espaço tem cerca de 7,4 x 10^19 valores e a rede Bitcoin inteira não conseguiria varrer isso em tempo útil. Portanto, o objetivo é didático: ensinar a trabalhar com ranges grandes, bancos serverless, retomada de processamento e automação. Não espere lucro nem resolução do desafio a partir deste código. Qualquer promessa de "encontrar a chave" deve ser tratada com ceticismo.
O volume depende do percentual de divisão. O valor padrão do projeto é 0.0000001, que gera 100 / 0.0000001 = 1 bilhão de intervalos. Cada linha da tabela guarda dois valores hexadecimais (inicio e fim) mais as colunas durante e bloqueada, o que ocupa cerca de 100 bytes por linha. Multiplicando, 1 bilhão de linhas ocupa aproximadamente 100 GB — muito acima do plano gratuito do Neon, que oferece cerca de 500 MB de storage. Por isso o script implementou o modo --stats, que mostra o volume estimado antes de executar, e um alerta que avisa quando o tamanho projetado ultrapassa o limite. A solução recomendada é aumentar o percentual: com 0.001, o número de faixas cai para 100 mil e o volume para cerca de 10 MB, perfeitamente comportável no plano gratuito. O percentual define a granularidade: menor percentual gera faixas mais finas e mais linhas; maior percentual gera menos linhas, mas faixas mais largas. A escolha depende do que você pretende estudar com os dados.
A versão original abria e fechava uma conexão com o banco a cada lote de 100 linhas. Para 1 bilhão de intervalos, isso significa cerca de 10 milhões de conexões, cada uma com custo de handshake e autenticação — um desperdício enorme de rede, CPU e tempo. A nova versão usa uma conexão persistente, reutilizada ao longo de toda a execução, e só a refaz quando há falha, com retry de backoff exponencial (2s, 4s, 8s, até 60s). Além disso, o progresso era gravado em disco a cada intervalo — até 1 bilhão de escritas. Agora ele é salvo de forma atômica (arquivo temporário + os.replace) a cada checkpoint (padrão 1000) e após cada commit. O script também removeu chamadas desnecessárias de gc.collect() e parou de imprimir uma linha por intervalo no console, o que reduzia muito a saída. Por fim, o --stats permite dimensionar o percentual antes de rodar, evitando horas de execução que estourariam o plano do banco. O resultado é um consumo mínimo de recursos locais e do Neon.
Nunca coloque a senha no código. O padrão adotado tem três camadas. Primeiro, localmente: copie o arquivo .env.example para .env e preencha a variável DATABASE_URL com a connection string do Neon. O script carrega o .env automaticamente sem bibliotecas extras, e o arquivo está no .gitignore, então ele nunca será commitado por acidente. Segundo, no GitHub Actions: crie um Secret chamado DATABASE_URL em Settings, Secrets and variables, Actions — o workflow passa esse secret como variável de ambiente para o processo. Terceiro, na conexão: o script usa ssl_context=True (SSL obrigatório), então os dados trafegam criptografados. Se por qualquer motivo uma credencial vazar, a ação correta é revogar a senha no Neon e gerar uma nova imediatamente. Evite também compartilhar prints do terminal que exibam a URL completa. Essas práticas valem para qualquer projeto: segredos fora do repositório, mínimos privilégios no CI e rotação de senhas em caso de suspeita de exposição.
A execução não é perdida. O script usa duas proteções. Primeira: o envio de cada lote tem retry com backoff exponencial — se a conexão cair, ele tenta reconectar dentro do próprio processo até 5 vezes, com espera crescente (2s, 4s, 8s, 16s, 32s), em vez de reiniciar o processo inteiro como na versão original (que usava os.execv, quebrado no Windows e agressivo em falhas longas). Segunda: o progresso é salvo de forma atômica a cada checkpoint e após cada commit, então, se o processo for encerrado de verdade, a próxima execução lê o progresso.txt e retoma exatamente do último intervalo commitado. A inserção usa ON CONFLICT (inicio, fim) DO NOTHING, o que garante idempotência: mesmo que um intervalo seja reprocessado após uma retomada, ele não é duplicado no banco. Se a rede estiver muito instável, a recomendação é reduzir o --checkpoint para 100, de modo que menos trabalho seja refeito entre um salvamento e outro. Um Ctrl+C também dispara o salvamento do progresso antes de encerrar.
Sim. O script aceita os limites --inicio e --fim em hexadecimal, então ele funciona para qualquer puzzle da série Bitcoin com intervalo conhecido. Por exemplo, o Puzzle 66 tem a chave entre 0x20000000000000000 e 0x3ffffffffffffffff, e o Puzzle 68 entre 0x80000000000000000 e 0xfffffffffffffffff. Basta executar: python puzzle67_to_db.py --inicio 0x20000000000000000 --fim 0x3ffffffffffffffff --percentual 0.001. Atenção a dois pontos. Primeiro, o volume: puzzles com intervalos maiores geram mais faixas para o mesmo percentual, então use --stats para conferir o tamanho antes. Segundo, a tabela padrão se chama puzzle67 — se for estudar outro puzzle, use --tabela com outro nome para não misturar os dados. As demais funcionalidades — retomada, retry, idempotência e alerta de volume — funcionam igualmente. Lembre-se de que o propósito continua sendo educacional: o script organiza o espaço de busca, mas não resolve a chave de nenhum puzzle.