BLOG |
Como fazer
Recuperando 100 mil sats com backups da semente e do estado.
Uma saída unilateral real da rede Spark: os números, as falhas e as lições. Tudo o que se segue ocorreu na mainnet do Bitcoin e pode ser verificado publicamente na cadeia de blocos.
Eu realizei uma saída forçada de uma carteira Blink sem custódia (usando o Spark) na rede principal do Bitcoin. Tudo o que foi necessário para a saída foi derivado da semente da carteira: o financiamento da taxa, a assinatura da transação e a limpeza final. O único elemento adicional é um pacote de recuperação, um instantâneo dos dados de saída da carteira salvo enquanto a rede Spark ainda estava acessível. Com esse pacote em mãos, a saída em si não exigiu a cooperação de ninguém.
Os números reais para uma carteira de 100.000 sat:
“Sem custódia” significa que você sempre pode ir embora. Mas a escada de incêndio é estreita. Aqui está a história completa, com custos, falhas e lições aprendidas.
“Sem custódia” é uma expressão fácil de incluir em textos de marketing. Queríamos que fosse algo que você pudesse verificar, não algo em que você fosse obrigado a acreditar. Por isso, pegamos uma carteira Blink de verdade, fingimos que a rede Spark havia desaparecido e transferimos todos os sat recuperáveis de volta para a blockchain do Bitcoin usando nada além da semente, um pacote de recuperação salvo e ferramentas de código aberto que qualquer pessoa pode executar. Esta postagem é a prova disso.
Uma carteira móvel do Spark com 100.000 sats distribuídos por 22 folhas.

Um brevecontexto para os leitores que ainda não conhecem o Spark: os fundos de uma carteira Spark ficam em uma árvore compartilhada na cadeia, gerenciada por um conjunto de operadores, e cada folha dessa árvore é um trecho distinto do saldo da carteira, com seu próprio caminho pré-assinado de volta ao Bitcoin. Saída unilateral significa transmitir esse caminho — a cadeia de saída da folha, composta por transações na cadeia, confirmando nível por nível ao longo da árvore — para forçar a folha a migrar para o Bitcoin sem a cooperação dos operadores.
Uma carteira Spark móvel contendo 100.000 sats distribuídos por 22 folhas. As carteiras Spark acumulam folhas por meio do uso normal (os pagamentos dividem e redividem a árvore), e cada folha possui sua própria cadeia de saída: as transações na cadeia que devem ser confirmadas, nível por nível, para forçar a folha a entrar no Bitcoin sem a cooperação dos operadores. Para essa carteira, isso significou 253 pacotes de transações distribuídos pelas 22 cadeias. Para 100 mil sats.
A recuperação apenas com a semente não é possível depois que os operadores do Spark ficam offline: as folhas atuais não podem ser identificadas apenas a partir da semente. A saída requer um pacote de recuperação — um instantâneo em JSON das folhas da carteira e de suas transações ancestrais — atualizado enquanto os operadores ainda estiverem online:
execute o comando `refresh-recovery-bundle` com SEED_FILE=../.spark-seed.txt e BUNDLE=../recovery-bundle.json
Primeira lição na prática: nosso primeiro pacote estava incompleto, sem que percebêssemos. O volume dos operadores query_nodes(include_parents=true) A API omite o nó raiz da árvore para árvores da mainnet legadas; portanto, cada uma das 22 cadeias de saída apresentava uma lacuna no topo, e a construção do pacote offline falhou com A cadeia de saída está incompleta. A recuperação dos ancestrais ausentes por ID de nó (o que contorna a omissão da raiz) resolveu o problema — o exportador agora faz isso automaticamente e se recusa a gravar um pacote com cadeias abertas.
Verifique as cadeias de ancestrais do seu pacote antes que você precise delas; um pacote incompleto descoberto durante uma interrupção no serviço é irrecuperável.
As transações de saída são pré-assinadas sem taxa e pagam as taxas por meio de âncoras efêmeras do CPFP; portanto, a saída precisa de um UTXO independente da L1 do Bitcoin para financiar os aumentos nas taxas. A ferramenta gera um endereço de financiamento dedicado a partir da própria semente da carteira (caminho m/8797556'/<account>/0: apenas o índice de finalidade é protegido; os índices de conta e endereço não são, de modo que uma carteira apenas para observação pode derivar e monitorar o endereço de depósito a partir de um xpub sem precisar de nenhuma chave privada):
execute o comando cpfp-address com os seguintes parâmetros: SEED_FILE=../.spark-seed.txt, BUNDLE=../recovery-bundle.json e FEE_RATE=1
A uma taxa de 1 sat/vB, foram necessários 78.573 sats para sair de todas as 22 folhas. Quase 79% do saldo da carteira foi gasto em taxas. Nós fizemos o depósito em 3ab20a4c…7262 antes de fazer os cálculos por folha. Mais detalhes sobre isso abaixo.
Na primeira tentativa, todos os 253 pacotes foram empacotados, assinados e enviados consecutivamente por meio da chamada POST /txs/package do Esplora. O primeiro pacote foi confirmado (pai 16895bc8…9619, filho CPFP 384cdc6d…3b37). Os outros 252 foram rejeitados:
"error": "Violação do TRUC: a transação 16895bc8… excederia o limite de contagem de descendentes"
"error": "entradas-de-transações-inválidas-ausentes-ou-já-utilizadas"
As transações de saída do Spark são do tipo v3, também chamadas de TRUC (Topologically Restricted Until Confirmation, BIP 431). A política do mempool limita um cluster v3 a um pai não confirmado mais um filho. É isso que torna as transações de saída pré-assinadas e sem taxa passíveis de aumento de taxa com segurança, e também significa que cada nível de uma cadeia de saída deve ser confirmado antes que o próximo nível possa entrar no mempool. Uma cadeia de 15 pacotes leva pelo menos 15 blocos, independentemente de como seja enviada. Qualquer ferramenta que dispare pacotes consecutivamente deixará tudo após o primeiro pacote de cada cadeia sem processamento.
Antes de automatizar o ciclo, calculamos o preço de cada folha: as taxas do CPFP para sua cadeia mais a retirada final de ~111 vB, em relação ao valor da folha. O resultado para essa carteira real a 1 sat/vB:
Quatro folhas (32.768 + 32.768 + 16.384 + 8.192 sats) representavam 90% do saldo. Os outros 18 — resíduos de atividades rotineiras da carteira, alguns tão pequenos quanto 1 sat — teriam custado, cada um, entre 2.100 e 4.600 sats para serem retirados: retirar tudo teria consumido cerca de 77,5 mil sats em taxas para recuperar 100 mil. A ferramenta agora calcula isso por folha e ignora as folhas não econômicas por padrão (INCLUDE_UNECONOMICAL=1 substitui essa configuração; os cálculos econômicos são testados por regressão com base nesse mesmo pacote, adaptado ao regtest).
Acompanhando cada sat da carteira de 100.000 sats até a saída econômica a 1 sat/vB. Há dois montantes em jogo: o próprio saldo da carteira e os 9.388 sats de taxa que financiam a saída econômica, efetivamente consumidos do endereço de financiamento.
Os 100.000 sats da carteira:
As transações de saída e reembolso do Spark são pré-assinadas sem taxa (as taxas são determinadas pelas âncoras do CPFP); portanto, cada saída de reembolso contém o valor total da folha; a única dedução do próprio saldo da carteira é a taxa final de varredura.
Os 9.388 sats de recursos para cobrir taxas foram retirados de:
Conclusão: 89.668 de 100.000 sats (~90%) chegam ao destino. O custo total da saída é de ~18.700 sats — 9.888 abandonados como “poeira”, 8.388 em taxas do CPFP, 444 em taxas de varredura — além da taxa na cadeia para a transação que financiou o endereço do CPFP inicialmente. Com taxas de comissão mais altas, todos os termos aumentam e mais “folhas” ficam abaixo do limiar econômico; a 10 sat/vB, essa carteira abandonaria mais duas “folhas” e pagaria dez vezes mais em taxas pelo restante.
O fluxo reescrito consiste em um único comando:
executar recuperação SEED_FILE=../.spark-seed.txt BUNDLE=../recovery-bundle.json NETWORK=mainnet FEE_RATE=1
A cada rodada, ele reconstrói os pacotes a partir do estado atual da cadeia, assina o aumento do CPFP com a semente, envia um pacote por cadeia folha, aguarda a confirmação e repete o processo. As transações já confirmadas são ignoradas na reconstrução, de modo que o loop não mantém estado: limites de taxa, travamentos e reinicializações não causam nenhum impacto — basta executar novamente e ele retoma o processo. Os problemas no Esplora são repetidos com recuo exponencial, em vez de interromper uma espera que pode se estender por horas.
Há dois detalhes estruturais importantes:
Executamos essa recuperação sequencialmente (padrão). Com FAN_OUT=1, o loop teria, primeiro, transmitido uma transação adicional com 4 saídas — os 9.388 sats de financiamento divididos em um UTXO por folha econômica, cada um com valor correspondente às taxas CPFP restantes daquela folha mais um buffer de 1.000 sats, sendo que a última saída absorveria o restante. Uma transação P2WPKH de 1 entrada/4 saídas tem cerca de 203 vB, ou seja, cerca de 203 sats a 1 sat/vB.
A partir daí, quatro cadeias correm em paralelo — uma por folha. Cada cadeia de folha é um cluster TRUC independente, de modo que todos os quatro pacotes de uma rodada são independentes e podem ser confirmados no mesmo bloco. Cada uma das quatro folhas dessa carteira tinha uma cadeia de aproximadamente seis pacotes antes de seu reembolso:
Um ganho de aproximadamente 3,5× em tempo real para cerca de 203 sats, e o mesmo padrão se repete após o vencimento do reembolso: os quatro pacotes de reembolso são transmitidos em um único bloco com UTXOs por folha, em vez de quatro blocos consecutivos. O ganho varia de acordo com o número de folhas (soma das profundidades da cadeia em comparação com a cadeia mais profunda isoladamente); portanto, uma carteira com dezenas de folhas econômicas deve sempre adotar a estratégia de dispersão; para um punhado de folhas, a abordagem sequencial é mais simples e, neste caso, foi rápida o suficiente.
Após o término do ciclo, o reembolso de cada folha econômica aguarda o término do bloqueio de tempo CSV. Ao executar novamente o mesmo fazer, recuperar após o vencimento, divulga os reembolsos; assim que forem confirmados, fazer uma varredura gera e assina transações de gasto com caminho de chave Taproot de uma entrada, a partir das saídas de reembolso para qualquer endereço de destino, e as transmite como transações comuns. No momento da redação deste artigo, as quatro saídas econômicas dessa carteira estão em suas respectivas cadeias e aguardam o vencimento do reembolso.
Vale a pena ser preciso, pois é aqui que o discurso de marketing costuma se antecipar aos fatos:
Não é um sistema sem confiança, e não afirmamos que seja. No espectro: mais fraco do que um nó Lightning, que mantém seu próprio estado de canal por definição, mas muito mais seguro do que um custodiante que mantém tanto suas chaves quanto seus dados e cuja retenção parece um serviço normal até o congelamento da retirada.
Violação do TRUC e entradas ausentes como sinais de sequenciamento normais, e nunca trate um lote de pacotes como algo do tipo “enviar e esquecer”.Tudo o que foi descrito acima foi feito manualmente com ferramentas de código aberto. Esse é o lugar certo para comprovar isso, mas não é onde a maioria dos usuários está. O caminho que estamos traçando é uma saída para a qual o usuário nunca precise se preparar.
Concretamente: o aplicativo Blink atualizará e manterá o pacote de recuperação automaticamente após cada mudança de estado — a cada transação —, salvando-o no armazenamento local do dispositivo e, caso o usuário configure, em seu próprio armazenamento na nuvem, de forma criptografada. O pacote não possui autoridade para realizar transações; trata-se de dados de saída, não de chaves; portanto, fazer backup dele automaticamente amplia a janela de saída sem ampliar a superfície de ataque. A semente continua sendo o único segredo a ser protegido, exatamente como hoje.
A questão é que a frase “você sempre pode sair” não deveria exigir nenhuma preparação. Seja qual for a última transação realizada, os dados necessários para transferir esses fundos de volta para o Bitcoin já estão salvos. Se o Spark estiver inacessível, a saída será executada a partir do aplicativo Blink ou da ferramenta de código aberto, utilizando o mesmo pacote — sem a necessidade de correr para obter o estado de operadores que talvez já tenham saído do ar.
Isso resolve a lacuna que este estudo de caso revelou: a ferramenta pode ser encerrada, mas somente em relação a um pacote completo e recente. Automatizar a atualização para que ela esteja sempre atualizada e sempre tenha um backup é o que transforma uma saída de emergência que você precisa construir primeiro em uma que simplesmente já está lá.
Todasas ferramentas utilizadas aqui são de código aberto, e o estudo de caso técnico, com todos os comandos e IDs de transação, está disponível junto com o código:
Experimente com o pacote da sua própria carteira antes de precisar dele. É essa a ideia.

Comece a receber e enviar bitcoin agora mesmo